Files
fastfetch/src/logo/logo.c
T

475 lines
14 KiB
C
Raw Normal View History

2022-04-02 14:35:43 +02:00
#include "logo.h"
2022-07-25 11:48:48 +02:00
#include "common/io.h"
#include "common/printing.h"
2022-09-04 13:33:18 +02:00
#include "detection/os/os.h"
2022-10-08 16:04:55 +08:00
#include "detection/terminalshell/terminalshell.h"
2022-12-06 17:03:31 +08:00
#include "util/textModifier.h"
2022-04-02 14:35:43 +02:00
#include <ctype.h>
2022-06-08 10:59:33 +02:00
#include <string.h>
2022-04-02 14:35:43 +02:00
2023-01-01 12:37:39 +08:00
static void ffLogoPrintCharsRaw(FFinstance* instance, const char* data, size_t length)
{
fputs(FASTFETCH_TEXT_MODIFIER_BOLT, stdout);
2023-01-01 17:24:37 +08:00
ffPrintCharTimes('\n', instance->config.logo.paddingTop);
2023-01-01 12:37:39 +08:00
ffPrintCharTimes(' ', instance->config.logo.paddingLeft);
fwrite(data, length, 1, stdout);
2023-01-01 17:24:37 +08:00
instance->state.logoHeight = instance->config.logo.paddingTop + instance->config.logo.height;
2023-01-01 12:37:39 +08:00
instance->state.logoWidth = instance->config.logo.paddingLeft + instance->config.logo.width + instance->config.logo.paddingRight;
2023-01-01 17:24:37 +08:00
printf("\033[9999999D\n\033[%uA", instance->state.logoHeight);
2023-01-01 12:37:39 +08:00
}
2022-06-08 10:59:33 +02:00
void ffLogoPrintChars(FFinstance* instance, const char* data, bool doColorReplacement)
2022-04-02 14:35:43 +02:00
{
uint32_t currentlineLength = 0;
fputs(FASTFETCH_TEXT_MODIFIER_BOLT, stdout);
2023-01-01 17:24:37 +08:00
ffPrintCharTimes('\n', instance->config.logo.paddingTop);
2022-07-10 18:28:17 +02:00
ffPrintCharTimes(' ', instance->config.logo.paddingLeft);
2022-04-02 14:35:43 +02:00
2023-01-01 17:24:37 +08:00
instance->state.logoHeight = instance->config.logo.paddingTop;
2022-04-02 14:35:43 +02:00
//Use logoColor[0] as the default color
if(doColorReplacement)
2022-07-10 18:28:17 +02:00
ffPrintColor(&instance->config.logo.colors[0]);
2022-04-02 14:35:43 +02:00
while(*data != '\0')
{
//We are at the end of a line. Print paddings and update max line length
if(*data == '\n' || (*data == '\r' && *(data + 1) == '\n'))
{
2022-07-10 18:28:17 +02:00
ffPrintCharTimes(' ', instance->config.logo.paddingRight);
2022-04-02 14:35:43 +02:00
//We have \r\n, skip the \r
if(*data == '\r')
++data;
putchar('\n');
++data;
2022-07-10 18:28:17 +02:00
ffPrintCharTimes(' ', instance->config.logo.paddingLeft);
2022-04-02 14:35:43 +02:00
2022-04-06 14:02:05 +02:00
if(currentlineLength > instance->state.logoWidth)
instance->state.logoWidth = currentlineLength;
2022-04-02 14:35:43 +02:00
currentlineLength = 0;
++instance->state.logoHeight;
continue;
}
//Always print tabs as 4 spaces, to have consistent spacing
if(*data == '\t')
{
2022-04-08 15:50:49 +02:00
ffPrintCharTimes(' ', 4);
2022-04-02 14:35:43 +02:00
++data;
continue;
}
//We have an escape sequence direclty as bytes. We print it, but don't increase the line length
if(*data == '\033' && *(data + 1) == '[')
{
const char* start = data;
fputs("\033[", stdout);
data += 2;
while(isdigit(*data) || *data == ';')
putchar(*data++); // number
//We have a valid control sequence, print it and continue with next char
if(isascii(*data))
{
putchar(*data++); // single letter, end of control sequence
continue;
}
//Invalid control sequence, try to get most accurate length
currentlineLength += (uint32_t) (data - start - 1); //-1 for \033 which for sure doesn't take any space
//Don't continue here, print the char after the letters with the unicode printing
}
//We have a fastfetch color placeholder. Replace it with the esacape sequence, don't increase the line length
if(doColorReplacement && *data == '$')
{
++data;
//If we have $$, or $\0, print it as single $
if(*data == '$' || *data == '\0')
{
putchar('$');
++currentlineLength;
++data;
continue;
}
//Map the number to an array index, so that '1' -> 0, '2' -> 1, etc.
int index = ((int) *data) - 49;
//If the index is valid, print the color. Otherwise continue as normal
if(index < 0 || index >= FASTFETCH_LOGO_MAX_COLORS)
{
putchar('$');
++currentlineLength;
//Don't continue here, we want to print the current char as unicode
}
else
{
2022-07-10 18:28:17 +02:00
ffPrintColor(&instance->config.logo.colors[index]);
2022-04-02 14:35:43 +02:00
++data;
continue;
}
}
//Do the printing, respecting unicode
++currentlineLength;
int codepoint = (unsigned char) *data;
uint8_t bytes;
if(codepoint <= 127)
bytes = 1;
else if((codepoint & 0xE0) == 0xC0)
bytes = 2;
else if((codepoint & 0xF0) == 0xE0)
bytes = 3;
else if((codepoint & 0xF8) == 0xF0)
bytes = 4;
else
bytes = 1; //Invalid utf8, print it as is, byte by byte
for(uint8_t i = 0; i < bytes; ++i)
{
if(*data == '\0')
break;
putchar(*data++);
}
}
2022-07-10 18:28:17 +02:00
ffPrintCharTimes(' ', instance->config.logo.paddingRight);
2022-04-02 14:35:43 +02:00
fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout);
//Happens if the last line is the longest
2022-04-06 14:02:05 +02:00
if(currentlineLength > instance->state.logoWidth)
instance->state.logoWidth = currentlineLength;
2022-04-02 14:35:43 +02:00
2022-07-10 18:28:17 +02:00
instance->state.logoWidth += instance->config.logo.paddingLeft + instance->config.logo.paddingRight;
2022-04-02 14:35:43 +02:00
2022-04-06 14:02:05 +02:00
//Go to the leftmost position
fputs("\033[9999999D", stdout);
2022-04-02 14:35:43 +02:00
//If the logo height is > 1, go up the height
if(instance->state.logoHeight > 0)
printf("\033[%uA", instance->state.logoHeight);
}
2022-09-07 19:50:10 +02:00
static void logoApplyColors(FFinstance* instance, const FFlogo* logo)
2022-04-02 14:35:43 +02:00
{
2022-09-07 19:50:10 +02:00
if(instance->config.colorKeys.length == 0)
ffStrbufAppendS(&instance->config.colorKeys, logo->colorKeys);
if(instance->config.colorTitle.length == 0)
ffStrbufAppendS(&instance->config.colorTitle, logo->colorTitle);
2022-06-08 10:59:33 +02:00
}
static bool logoHasName(const FFlogo* logo, const char* name)
{
const char** logoName = logo->names;
while(*logoName != NULL)
{
if(strcasecmp(*logoName, name) == 0)
return true;
++logoName;
}
2022-04-02 14:35:43 +02:00
2022-06-08 10:59:33 +02:00
return false;
2022-04-02 14:35:43 +02:00
}
2022-06-08 10:59:33 +02:00
static const FFlogo* logoGetBuiltin(const char* name)
2022-04-02 14:35:43 +02:00
{
2022-06-08 10:59:33 +02:00
GetLogoMethod* methods = ffLogoBuiltinGetAll();
while(*methods != NULL)
2022-04-29 21:52:42 +02:00
{
2022-06-08 10:59:33 +02:00
const FFlogo* logo = (*methods)();
if(logoHasName(logo, name))
return logo;
++methods;
2022-04-29 21:52:42 +02:00
}
2022-04-29 21:44:32 +02:00
2022-06-08 10:59:33 +02:00
return NULL;
}
2022-04-29 21:44:32 +02:00
2022-06-08 10:59:33 +02:00
static const FFlogo* logoGetBuiltinDetected(FFinstance* instance)
{
const FFOSResult* os = ffDetectOS(instance);
2022-04-29 21:44:32 +02:00
2022-06-08 10:59:33 +02:00
const FFlogo* logo = logoGetBuiltin(os->id.chars);
if(logo != NULL)
return logo;
2022-04-29 21:44:32 +02:00
2022-06-08 10:59:33 +02:00
logo = logoGetBuiltin(os->name.chars);
if(logo != NULL)
return logo;
logo = logoGetBuiltin(os->prettyName.chars);
if(logo != NULL)
return logo;
logo = logoGetBuiltin(os->idLike.chars);
if(logo != NULL)
return logo;
logo = logoGetBuiltin(os->systemName.chars);
if(logo != NULL)
return logo;
return ffLogoBuiltinGetUnknown();
}
2022-09-07 19:50:10 +02:00
static inline void logoApplyColorsDetected(FFinstance* instance)
2022-06-08 10:59:33 +02:00
{
2022-09-07 19:50:10 +02:00
logoApplyColors(instance, logoGetBuiltinDetected(instance));
2022-06-08 10:59:33 +02:00
}
static void logoPrintStruct(FFinstance* instance, const FFlogo* logo)
{
2022-09-07 19:50:10 +02:00
logoApplyColors(instance, logo);
2022-06-08 10:59:33 +02:00
const char** colors = logo->builtinColors;
for(int i = 0; *colors != NULL && i < FASTFETCH_LOGO_MAX_COLORS; i++, colors++)
{
2022-07-10 18:28:17 +02:00
if(instance->config.logo.colors[i].length == 0)
ffStrbufAppendS(&instance->config.logo.colors[i], *colors);
2022-06-08 10:59:33 +02:00
}
ffLogoPrintChars(instance, logo->data, true);
}
2022-09-27 15:02:35 +02:00
static void logoPrintNone(FFinstance* instance)
{
logoApplyColorsDetected(instance);
instance->state.logoHeight = 0;
instance->state.logoWidth = 0;
}
2022-06-08 10:59:33 +02:00
static bool logoPrintBuiltinIfExists(FFinstance* instance, const char* name)
{
2022-09-27 15:02:35 +02:00
if(strcasecmp(name, "none") == 0)
{
logoPrintNone(instance);
return true;
}
2022-06-08 10:59:33 +02:00
const FFlogo* logo = logoGetBuiltin(name);
if(logo == NULL)
return false;
logoPrintStruct(instance, logo);
2022-09-27 15:02:35 +02:00
2022-06-08 10:59:33 +02:00
return true;
}
static inline void logoPrintDetected(FFinstance* instance)
{
logoPrintStruct(instance, logoGetBuiltinDetected(instance));
}
2022-12-12 13:43:10 +01:00
static void logoPrintData(FFinstance* instance, bool doColorReplacement) {
ffLogoPrintChars(instance, instance->config.logo.source.chars, doColorReplacement);
logoApplyColorsDetected(instance);
}
2023-01-01 12:37:39 +08:00
static bool logoPrintFileIfExists(FFinstance* instance, bool doColorReplacement, bool raw)
2022-06-08 10:59:33 +02:00
{
FFstrbuf content;
2023-01-01 12:37:39 +08:00
ffStrbufInit(&content);
2022-06-08 10:59:33 +02:00
2022-07-10 18:28:17 +02:00
if(!ffAppendFileBuffer(instance->config.logo.source.chars, &content))
2022-06-08 10:59:33 +02:00
{
ffStrbufDestroy(&content);
2023-01-01 12:37:39 +08:00
fputs("Logo: Failed to load file content from logo source\n", stderr);
2022-06-08 10:59:33 +02:00
return false;
2022-04-02 14:35:43 +02:00
}
2022-09-07 19:50:10 +02:00
logoApplyColorsDetected(instance);
2023-01-01 12:37:39 +08:00
if(raw)
ffLogoPrintCharsRaw(instance, content.chars, content.length);
else
ffLogoPrintChars(instance, content.chars, doColorReplacement);
2022-06-08 10:59:33 +02:00
ffStrbufDestroy(&content);
return true;
}
2022-07-10 18:28:17 +02:00
static bool logoPrintImageIfExists(FFinstance* instance, FFLogoType logo)
2022-06-08 10:59:33 +02:00
{
2022-07-10 18:28:17 +02:00
if(!ffLogoPrintImageIfExists(instance, logo))
2022-06-08 10:59:33 +02:00
return false;
2022-04-29 21:44:32 +02:00
2022-09-07 19:50:10 +02:00
logoApplyColorsDetected(instance);
2022-06-08 10:59:33 +02:00
return true;
2022-04-02 14:35:43 +02:00
}
2022-06-08 10:59:33 +02:00
static void logoPrintKnownType(FFinstance* instance)
2022-04-02 14:35:43 +02:00
{
2022-12-12 13:43:10 +01:00
bool successfull = true;
2022-06-08 10:59:33 +02:00
2022-07-10 18:28:17 +02:00
if(instance->config.logo.type == FF_LOGO_TYPE_BUILTIN)
successfull = logoPrintBuiltinIfExists(instance, instance->config.logo.source.chars);
else if(instance->config.logo.type == FF_LOGO_TYPE_FILE)
2023-01-01 12:37:39 +08:00
successfull = logoPrintFileIfExists(instance, true, false);
2022-12-12 13:43:10 +01:00
else if(instance->config.logo.type == FF_LOGO_TYPE_FILE_RAW)
2023-01-01 12:37:39 +08:00
successfull = logoPrintFileIfExists(instance, false, false);
2022-12-12 13:43:10 +01:00
else if(instance->config.logo.type == FF_LOGO_TYPE_DATA)
logoPrintData(instance, true);
else if(instance->config.logo.type == FF_LOGO_TYPE_DATA_RAW)
logoPrintData(instance, false);
2023-01-01 12:37:39 +08:00
else if(instance->config.logo.type == FF_LOGO_TYPE_IMAGE_RAW)
{
if(instance->config.logo.width == 0 || instance->config.logo.height == 0)
{
fputs("both `--logo-width` and `--logo-height` must be specified\n", stderr);
successfull = false;
}
else
successfull = logoPrintFileIfExists(instance, false, true);
}
else if(instance->config.logo.type == FF_LOGO_TYPE_NONE)
logoApplyColorsDetected(instance);
2022-06-08 10:59:33 +02:00
else //image
2022-07-10 18:28:17 +02:00
successfull = logoPrintImageIfExists(instance, instance->config.logo.type);
2022-06-08 10:59:33 +02:00
if(!successfull)
logoPrintDetected(instance);
}
void ffLogoPrint(FFinstance* instance)
{
//In pipe mode, we don't have a logo or padding.
//We also don't need to set main color, because it won't be printed anyway.
//So we can return quickly here.
2022-06-06 14:02:21 +02:00
if(instance->config.pipe)
{
instance->state.logoHeight = 0;
instance->state.logoWidth = 0;
return;
}
2022-06-08 10:59:33 +02:00
//If the source is not set, we can directly print the detected logo.
2022-07-10 18:28:17 +02:00
if(instance->config.logo.source.length == 0)
{
2022-06-08 10:59:33 +02:00
logoPrintDetected(instance);
return;
}
2022-06-08 10:59:33 +02:00
//If the source and source type is set to something else than auto, always print with the set type.
2022-07-10 18:28:17 +02:00
if(instance->config.logo.source.length > 0 && instance->config.logo.type != FF_LOGO_TYPE_AUTO)
{
2022-06-08 10:59:33 +02:00
logoPrintKnownType(instance);
return;
}
2022-04-02 14:35:43 +02:00
2022-06-08 10:59:33 +02:00
//If source matches the name of a builtin logo, print it and return.
2022-07-10 18:28:17 +02:00
if(logoPrintBuiltinIfExists(instance, instance->config.logo.source.chars))
2022-06-08 10:59:33 +02:00
return;
const FFTerminalShellResult* terminalShell = ffDetectTerminalShell(instance);
//Terminal emulators that support kitty graphics protocol.
bool supportsKitty =
ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "kitty") == 0 ||
ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "konsole") == 0 ||
ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "wezterm") == 0 ||
ffStrbufIgnCaseCompS(&terminalShell->terminalProcessName, "wayst") == 0;
2022-06-06 14:02:21 +02:00
2022-06-08 10:59:33 +02:00
//Try to load the logo as an image. If it succeeds, print it and return.
2022-12-12 13:43:10 +01:00
if(logoPrintImageIfExists(instance, supportsKitty ? FF_LOGO_TYPE_IMAGE_KITTY : FF_LOGO_TYPE_IMAGE_CHAFA))
2022-06-08 10:59:33 +02:00
return;
//Try to load the logo as a file. If it succeeds, print it and return.
2023-01-01 12:37:39 +08:00
if(logoPrintFileIfExists(instance, true, false))
2022-06-08 10:59:33 +02:00
return;
logoPrintDetected(instance);
}
void ffLogoPrintLine(FFinstance* instance)
2022-04-06 14:02:05 +02:00
{
if(instance->state.logoWidth > 0)
printf("\033[%uC", instance->state.logoWidth);
2022-06-06 14:02:21 +02:00
++instance->state.keysHeight;
2022-04-06 14:02:05 +02:00
}
2022-06-08 10:59:33 +02:00
void ffLogoPrintRemaining(FFinstance* instance)
2022-04-02 14:35:43 +02:00
{
2022-06-15 11:07:22 +02:00
while(instance->state.keysHeight <= instance->state.logoHeight)
2022-04-06 14:02:05 +02:00
{
2022-06-08 10:59:33 +02:00
ffLogoPrintLine(instance);
2022-04-06 14:02:05 +02:00
putchar('\n');
}
2022-04-02 14:35:43 +02:00
}
2022-06-08 10:59:33 +02:00
void ffLogoBuiltinPrint(FFinstance* instance)
{
GetLogoMethod* methods = ffLogoBuiltinGetAll();
while(*methods != NULL)
{
const FFlogo* logo = (*methods)();
printf("\033[%sm%s:\033[0m\n", logo->builtinColors[0], logo->names[0]);
logoPrintStruct(instance, logo);
ffLogoPrintRemaining(instance);
2022-06-08 11:28:26 +02:00
//reset everything
2022-06-08 10:59:33 +02:00
instance->state.logoHeight = 0;
2022-06-08 11:28:26 +02:00
instance->state.keysHeight = 0;
2022-06-08 10:59:33 +02:00
for(uint8_t i = 0; i < FASTFETCH_LOGO_MAX_COLORS; i++)
2022-07-10 18:28:17 +02:00
ffStrbufClear(&instance->config.logo.colors[i]);
2022-06-08 10:59:33 +02:00
puts("\n");
++methods;
}
}
void ffLogoBuiltinList()
{
GetLogoMethod* methods = ffLogoBuiltinGetAll();
uint32_t counter = 0;
while(*methods != NULL)
{
const FFlogo* logo = (*methods)();
const char** names = logo->names;
printf("%u)%s ", counter, counter < 10 ? " " : "");
++counter;
while(*names != NULL)
{
printf("\"%s\" ", *names);
++names;
}
putchar('\n');
++methods;
}
}
void ffLogoBuiltinListAutocompletion()
{
GetLogoMethod* methods = ffLogoBuiltinGetAll();
while(*methods != NULL)
{
printf("%s\n", (*methods)()->names[0]);
++methods;
}
}