mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-09-13 02:42:09 +02:00
37ab380a6f
Regression in v2.51.0 Fixes #1947
772 lines
24 KiB
C
772 lines
24 KiB
C
#include "logo/logo.h"
|
|
#include "common/io/io.h"
|
|
#include "common/printing.h"
|
|
#include "common/processing.h"
|
|
#include "detection/os/os.h"
|
|
#include "detection/terminalshell/terminalshell.h"
|
|
#include "util/textModifier.h"
|
|
#include "util/stringUtils.h"
|
|
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
|
|
typedef enum __attribute__((__packed__)) FFLogoSize
|
|
{
|
|
FF_LOGO_SIZE_UNKNOWN,
|
|
FF_LOGO_SIZE_NORMAL,
|
|
FF_LOGO_SIZE_SMALL,
|
|
} FFLogoSize;
|
|
|
|
static bool ffLogoPrintCharsRaw(const char* data, size_t length, bool printError)
|
|
{
|
|
FFOptionsLogo* options = &instance.config.logo;
|
|
FF_STRBUF_AUTO_DESTROY buf = ffStrbufCreate();
|
|
|
|
if (!options->width || !options->height)
|
|
{
|
|
if (options->position == FF_LOGO_POSITION_LEFT)
|
|
{
|
|
ffStrbufAppendF(&buf, "\e[2J\e[3J\e[%u;%uH",
|
|
(unsigned) options->paddingTop + 1,
|
|
(unsigned) options->paddingLeft + 1
|
|
);
|
|
}
|
|
else if (options->position == FF_LOGO_POSITION_TOP)
|
|
{
|
|
ffStrbufAppendNC(&buf, options->paddingTop, '\n');
|
|
ffStrbufAppendNC(&buf, options->paddingLeft, ' ');
|
|
}
|
|
else if (options->position == FF_LOGO_POSITION_RIGHT)
|
|
{
|
|
if (!options->width)
|
|
{
|
|
if (printError)
|
|
fputs("Logo (image-raw): Must set logo width when using position right\n", stderr);
|
|
return false;
|
|
}
|
|
ffStrbufAppendF(&buf, "\e[2J\e[3J\e[%u;9999999H\e[%uD", (unsigned) options->paddingTop + 1, (unsigned) options->paddingRight + options->width);
|
|
}
|
|
ffStrbufAppendNS(&buf, (uint32_t) length, data);
|
|
ffWriteFDBuffer(FFUnixFD2NativeFD(STDOUT_FILENO), &buf);
|
|
|
|
if (options->position == FF_LOGO_POSITION_LEFT || options->position == FF_LOGO_POSITION_RIGHT)
|
|
{
|
|
uint16_t X = 0, Y = 0;
|
|
// Windows Terminal doesn't report `\e` for some reason
|
|
const char* error = ffGetTerminalResponse("\e[6n", 2, "%*[^0-9]%hu;%huR", &Y, &X); // %*[^0-9]: ignore optional \e[
|
|
if (error)
|
|
{
|
|
if (printError)
|
|
fprintf(stderr, "\nLogo (image-raw): fail to query cursor position: %s\n", error);
|
|
return true;
|
|
}
|
|
if (options->position == FF_LOGO_POSITION_LEFT)
|
|
{
|
|
if (options->width + options->paddingLeft > X)
|
|
X = (uint16_t) (options->width + options->paddingLeft);
|
|
instance.state.logoWidth = X + instance.config.logo.paddingRight - 1;
|
|
}
|
|
instance.state.logoHeight = Y;
|
|
fputs("\e[H", stdout);
|
|
}
|
|
else if (options->position == FF_LOGO_POSITION_TOP)
|
|
{
|
|
instance.state.logoWidth = instance.state.logoHeight = 0;
|
|
ffPrintCharTimes('\n', options->paddingRight);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ffStrbufAppendNC(&buf, options->paddingTop, '\n');
|
|
|
|
if (options->position == FF_LOGO_POSITION_RIGHT)
|
|
ffStrbufAppendF(&buf, "\e[9999999C\e[%uD", (unsigned) options->paddingRight + options->width);
|
|
else if (options->paddingLeft)
|
|
ffStrbufAppendF(&buf, "\e[%uC", (unsigned) options->paddingLeft);
|
|
|
|
ffStrbufAppendNS(&buf, (uint32_t) length, data);
|
|
ffStrbufAppendC(&buf, '\n');
|
|
|
|
if (options->position == FF_LOGO_POSITION_LEFT)
|
|
{
|
|
instance.state.logoWidth = options->width + options->paddingLeft + options->paddingRight;
|
|
instance.state.logoHeight = options->paddingTop + options->height;
|
|
ffStrbufAppendF(&buf, "\e[%uA", (unsigned) instance.state.logoHeight);
|
|
}
|
|
else if (options->position == FF_LOGO_POSITION_TOP)
|
|
{
|
|
instance.state.logoWidth = instance.state.logoHeight = 0;
|
|
ffStrbufAppendNC(&buf, options->paddingRight, '\n');
|
|
}
|
|
else if (options->position == FF_LOGO_POSITION_RIGHT)
|
|
{
|
|
instance.state.logoWidth = instance.state.logoHeight = 0;
|
|
ffStrbufAppendF(&buf, "\e[%uA", (unsigned) options->height);
|
|
}
|
|
ffWriteFDBuffer(FFUnixFD2NativeFD(STDOUT_FILENO), &buf);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// If result is NULL, calculate logo width
|
|
// Returns logo height
|
|
static uint32_t logoAppendChars(const char* data, bool doColorReplacement, FFstrbuf* result)
|
|
{
|
|
FFOptionsLogo* options = &instance.config.logo;
|
|
uint32_t currentlineLength = options->type == FF_LOGO_TYPE_IMAGE_CHAFA ? 0 : options->width; // For chafa, unit of options->width is pixels
|
|
uint32_t logoHeight = 0;
|
|
|
|
if (result)
|
|
{
|
|
if (options->position != FF_LOGO_POSITION_RIGHT)
|
|
ffStrbufAppendNC(result, options->paddingLeft, ' ');
|
|
else
|
|
ffStrbufAppendF(result, "\e[9999999C\e[%dD", options->paddingRight + instance.state.logoWidth);
|
|
}
|
|
|
|
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'))
|
|
{
|
|
//We have \r\n, skip the \r
|
|
if(*data == '\r')
|
|
++data;
|
|
|
|
if(result) ffStrbufAppendC(result, '\n');
|
|
++data;
|
|
|
|
if (result)
|
|
{
|
|
if (options->position != FF_LOGO_POSITION_RIGHT)
|
|
ffStrbufAppendNC(result, options->paddingLeft, ' ');
|
|
else
|
|
ffStrbufAppendF(result, "\e[9999999C\e[%dD", options->paddingRight + instance.state.logoWidth);
|
|
}
|
|
|
|
if(currentlineLength > instance.state.logoWidth)
|
|
instance.state.logoWidth = currentlineLength;
|
|
|
|
currentlineLength = 0;
|
|
++logoHeight;
|
|
continue;
|
|
}
|
|
|
|
//Always print tabs as 4 spaces, to have consistent spacing
|
|
if(*data == '\t')
|
|
{
|
|
if(result) ffStrbufAppendNC(result, 4, ' ');
|
|
++data;
|
|
continue;
|
|
}
|
|
|
|
//We have an escape sequence directly as bytes. We print it, but don't increase the line length
|
|
if(*data == '\e' && *(data + 1) == '[')
|
|
{
|
|
const char* start = data;
|
|
|
|
if(result) ffStrbufAppendS(result, "\e[");
|
|
data += 2;
|
|
|
|
while(ffCharIsDigit(*data) || *data == ';')
|
|
{
|
|
if(result) ffStrbufAppendC(result, *data); // number
|
|
++data;
|
|
}
|
|
|
|
//We have a valid control sequence, print it and continue with next char
|
|
if(isascii(*data))
|
|
{
|
|
if(result) ffStrbufAppendC(result, *data); // single letter, end of control sequence
|
|
++data;
|
|
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')
|
|
{
|
|
if(result) ffStrbufAppendC(result, '$');
|
|
++currentlineLength;
|
|
++data;
|
|
continue;
|
|
}
|
|
|
|
if(!instance.config.display.pipe)
|
|
{
|
|
//Map the number to an array index, so that '1' -> 0, '2' -> 1, etc.
|
|
int index = *data - '1';
|
|
|
|
//If the index is valid, print the color. Otherwise continue as normal
|
|
if(index < 0 || index >= FASTFETCH_LOGO_MAX_COLORS)
|
|
{
|
|
if(result) ffStrbufAppendC(result, '$');
|
|
++currentlineLength;
|
|
//Don't continue here, we want to print the current char as unicode
|
|
}
|
|
else
|
|
{
|
|
if(result) ffStrbufAppendF(result, "\e[%sm", options->colors[index].chars);
|
|
++data;
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
++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;
|
|
|
|
if(result) ffStrbufAppendC(result, *data);
|
|
++data;
|
|
}
|
|
}
|
|
//Happens if the last line is the longest
|
|
if(currentlineLength > instance.state.logoWidth)
|
|
instance.state.logoWidth = currentlineLength;
|
|
|
|
return options->type != FF_LOGO_TYPE_IMAGE_CHAFA && options->height > logoHeight ? options->height : logoHeight;
|
|
}
|
|
|
|
void ffLogoPrintChars(const char* data, bool doColorReplacement)
|
|
{
|
|
FFOptionsLogo* options = &instance.config.logo;
|
|
|
|
if (options->position == FF_LOGO_POSITION_RIGHT)
|
|
logoAppendChars(data, doColorReplacement, NULL);
|
|
|
|
FF_STRBUF_AUTO_DESTROY result = ffStrbufCreateA(2048);
|
|
|
|
if (!instance.config.display.pipe && instance.config.display.brightColor)
|
|
ffStrbufAppendS(&result, FASTFETCH_TEXT_MODIFIER_BOLT);
|
|
|
|
ffStrbufAppendNC(&result, options->paddingTop, '\n');
|
|
|
|
//Use logoColor[0] as the default color
|
|
if(doColorReplacement && !instance.config.display.pipe)
|
|
ffStrbufAppendF(&result, "\e[%sm", options->colors[0].chars);
|
|
|
|
instance.state.logoHeight = options->paddingTop + logoAppendChars(data, doColorReplacement, &result);
|
|
|
|
if(!instance.config.display.pipe)
|
|
ffStrbufAppendS(&result, FASTFETCH_TEXT_MODIFIER_RESET);
|
|
|
|
if(options->position == FF_LOGO_POSITION_LEFT)
|
|
{
|
|
instance.state.logoWidth += options->paddingLeft + options->paddingRight;
|
|
|
|
//Go to the leftmost position and go up the height
|
|
ffStrbufAppendF(&result, "\e[1G\e[%uA", instance.state.logoHeight);
|
|
}
|
|
else if(options->position == FF_LOGO_POSITION_RIGHT)
|
|
{
|
|
instance.state.logoWidth = 0;
|
|
|
|
//Go to the leftmost position and go up the height
|
|
ffStrbufAppendF(&result, "\e[1G\e[%uA", instance.state.logoHeight);
|
|
}
|
|
else if (options->position == FF_LOGO_POSITION_TOP)
|
|
{
|
|
instance.state.logoWidth = instance.state.logoHeight = 0;
|
|
ffStrbufAppendNC(&result, options->paddingRight, '\n');
|
|
}
|
|
|
|
ffWriteFDBuffer(FFUnixFD2NativeFD(STDOUT_FILENO), &result);
|
|
}
|
|
|
|
static void logoApplyColors(const FFlogo* logo, bool replacement)
|
|
{
|
|
if(instance.config.display.colorTitle.length == 0)
|
|
ffStrbufAppendS(&instance.config.display.colorTitle, logo->colorTitle ? logo->colorTitle : logo->colors[0]);
|
|
|
|
if(instance.config.display.colorKeys.length == 0)
|
|
ffStrbufAppendS(&instance.config.display.colorKeys, logo->colorKeys ? logo->colorKeys : logo->colors[1]);
|
|
|
|
if (replacement)
|
|
{
|
|
FFOptionsLogo* options = &instance.config.logo;
|
|
|
|
const char* const* colors = logo->colors;
|
|
for(int i = 0; *colors != NULL && i < FASTFETCH_LOGO_MAX_COLORS; i++, colors++)
|
|
{
|
|
if(options->colors[i].length == 0)
|
|
ffStrbufAppendS(&options->colors[i], *colors);
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool logoHasName(const FFlogo* logo, const FFstrbuf* name, bool small)
|
|
{
|
|
for(
|
|
const char* const* logoName = logo->names;
|
|
*logoName != NULL && logoName <= &logo->names[FASTFETCH_LOGO_MAX_NAMES];
|
|
++logoName
|
|
) {
|
|
if(small)
|
|
{
|
|
uint32_t logoNameLength = (uint32_t) (strlen(*logoName) - strlen("_small"));
|
|
if(name->length == logoNameLength && strncasecmp(*logoName, name->chars, logoNameLength) == 0) return true;
|
|
}
|
|
if(ffStrbufIgnCaseEqualS(name, *logoName))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static const FFlogo* logoGetBuiltin(const FFstrbuf* name, FFLogoSize size)
|
|
{
|
|
if (name->length == 0 || !isalpha(name->chars[0]))
|
|
return NULL;
|
|
|
|
for(const FFlogo* logo = ffLogoBuiltins[toupper(name->chars[0]) - 'A']; *logo->names; ++logo)
|
|
{
|
|
switch (size)
|
|
{
|
|
// Never use alternate logos
|
|
case FF_LOGO_SIZE_NORMAL:
|
|
if(logo->type != FF_LOGO_LINE_TYPE_NORMAL) continue;
|
|
break;
|
|
case FF_LOGO_SIZE_SMALL:
|
|
if(logo->type != FF_LOGO_LINE_TYPE_SMALL_BIT) continue;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if(logoHasName(logo, name, size == FF_LOGO_SIZE_SMALL))
|
|
return logo;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static const FFlogo* logoGetBuiltinDetected(FFLogoSize size)
|
|
{
|
|
const FFOSResult* os = ffDetectOS();
|
|
|
|
const FFlogo* logo = logoGetBuiltin(&os->id, size);
|
|
if(logo != NULL)
|
|
return logo;
|
|
|
|
logo = logoGetBuiltin(&os->name, size);
|
|
if(logo != NULL)
|
|
return logo;
|
|
|
|
if (ffStrbufContainC(&os->idLike, ' '))
|
|
{
|
|
FF_STRBUF_AUTO_DESTROY buf = ffStrbufCreate();
|
|
for (
|
|
uint32_t start = 0, end = ffStrbufFirstIndexC(&os->idLike, ' ');
|
|
true;
|
|
start = end + 1, end = ffStrbufNextIndexC(&os->idLike, start, ' ')
|
|
)
|
|
{
|
|
ffStrbufSetNS(&buf, end - start, os->idLike.chars + start);
|
|
logo = logoGetBuiltin(&buf, size);
|
|
if(logo != NULL)
|
|
return logo;
|
|
|
|
if (end >= os->idLike.length)
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
logo = logoGetBuiltin(&os->idLike, size);
|
|
if(logo != NULL)
|
|
return logo;
|
|
}
|
|
|
|
logo = logoGetBuiltin(&instance.state.platform.sysinfo.name, size);
|
|
if(logo != NULL)
|
|
return logo;
|
|
|
|
return &ffLogoUnknown;
|
|
}
|
|
|
|
static void logoPrintStruct(const FFlogo* logo)
|
|
{
|
|
logoApplyColors(logo, true);
|
|
|
|
ffLogoPrintChars(logo->lines, true);
|
|
}
|
|
|
|
static void logoPrintNone(void)
|
|
{
|
|
logoApplyColors(logoGetBuiltinDetected(FF_LOGO_SIZE_NORMAL), false);
|
|
instance.state.logoHeight = 0;
|
|
instance.state.logoWidth = 0;
|
|
}
|
|
|
|
static bool logoPrintBuiltinIfExists(const FFstrbuf* name, FFLogoSize size)
|
|
{
|
|
if(name->chars[0] == '~' || name->chars[0] == '.' || name->chars[0] == '/'
|
|
#if _WIN32
|
|
|| (ffCharIsEnglishAlphabet(name->chars[0]) && name->chars[1] == ':') // Windows drive letter
|
|
#endif
|
|
)
|
|
return false; // Paths
|
|
|
|
if(ffStrbufIgnCaseEqualS(name, "none"))
|
|
{
|
|
logoPrintNone();
|
|
return true;
|
|
}
|
|
|
|
const FFlogo* logo = ffStrbufEqualS(name, "?") ? &ffLogoUnknown : logoGetBuiltin(name, size);
|
|
if(logo == NULL)
|
|
return false;
|
|
|
|
logoPrintStruct(logo);
|
|
|
|
return true;
|
|
}
|
|
|
|
static inline void logoPrintDetected(FFLogoSize size)
|
|
{
|
|
logoPrintStruct(logoGetBuiltinDetected(size));
|
|
}
|
|
|
|
static bool logoPrintData(bool doColorReplacement, FFstrbuf* source)
|
|
{
|
|
if(source->length == 0)
|
|
return false;
|
|
|
|
logoApplyColors(logoGetBuiltinDetected(FF_LOGO_SIZE_NORMAL), doColorReplacement);
|
|
ffLogoPrintChars(source->chars, doColorReplacement);
|
|
return true;
|
|
}
|
|
|
|
static bool updateLogoPath(void)
|
|
{
|
|
FFOptionsLogo* options = &instance.config.logo;
|
|
|
|
if(ffPathExists(options->source.chars, FF_PATHTYPE_FILE))
|
|
return true;
|
|
|
|
if (ffStrbufEqualS(&options->source, "-")) // stdin
|
|
return true;
|
|
|
|
FF_STRBUF_AUTO_DESTROY fullPath = ffStrbufCreate();
|
|
if (ffPathExpandEnv(options->source.chars, &fullPath) && ffPathExists(fullPath.chars, FF_PATHTYPE_FILE))
|
|
{
|
|
ffStrbufDestroy(&options->source);
|
|
ffStrbufInitMove(&options->source, &fullPath);
|
|
return true;
|
|
}
|
|
|
|
FF_LIST_FOR_EACH(FFstrbuf, dataDir, instance.state.platform.dataDirs)
|
|
{
|
|
//We need to copy it, because multiple threads might be using dataDirs at the same time
|
|
ffStrbufSet(&fullPath, dataDir);
|
|
ffStrbufAppendS(&fullPath, "fastfetch/logos/");
|
|
ffStrbufAppend(&fullPath, &options->source);
|
|
|
|
if(ffPathExists(fullPath.chars, FF_PATHTYPE_FILE))
|
|
{
|
|
ffStrbufDestroy(&options->source);
|
|
ffStrbufInitMove(&options->source, &fullPath);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool logoPrintFileIfExists(bool doColorReplacement, bool raw)
|
|
{
|
|
FFOptionsLogo* options = &instance.config.logo;
|
|
|
|
FF_STRBUF_AUTO_DESTROY content = ffStrbufCreate();
|
|
|
|
if(ffStrbufEqualS(&options->source, "-")
|
|
? !ffAppendFDBuffer(FFUnixFD2NativeFD(STDIN_FILENO), &content)
|
|
: !ffAppendFileBuffer(options->source.chars, &content)
|
|
)
|
|
{
|
|
if (instance.config.display.showErrors)
|
|
fprintf(stderr, "Logo: Failed to load file content from logo source: %s\n", options->source.chars);
|
|
return false;
|
|
}
|
|
|
|
logoApplyColors(logoGetBuiltinDetected(FF_LOGO_SIZE_NORMAL), doColorReplacement);
|
|
if(raw)
|
|
return ffLogoPrintCharsRaw(content.chars, content.length, instance.config.display.showErrors);
|
|
|
|
ffLogoPrintChars(content.chars, doColorReplacement);
|
|
return true;
|
|
}
|
|
|
|
static bool logoPrintImageIfExists(FFLogoType logo, bool printError)
|
|
{
|
|
if(!ffLogoPrintImageIfExists(logo, printError))
|
|
return false;
|
|
|
|
logoApplyColors(logoGetBuiltinDetected(FF_LOGO_SIZE_NORMAL), false);
|
|
return true;
|
|
}
|
|
|
|
static bool logoTryKnownType(void)
|
|
{
|
|
FFOptionsLogo* options = &instance.config.logo;
|
|
|
|
if(options->type == FF_LOGO_TYPE_NONE)
|
|
{
|
|
logoPrintNone();
|
|
return true;
|
|
}
|
|
|
|
if(options->type == FF_LOGO_TYPE_BUILTIN)
|
|
return logoPrintBuiltinIfExists(&options->source, FF_LOGO_SIZE_UNKNOWN);
|
|
|
|
if(options->type == FF_LOGO_TYPE_SMALL)
|
|
return logoPrintBuiltinIfExists(&options->source, FF_LOGO_SIZE_SMALL);
|
|
|
|
if(options->type == FF_LOGO_TYPE_DATA)
|
|
return logoPrintData(true, &options->source);
|
|
|
|
if(options->type == FF_LOGO_TYPE_DATA_RAW)
|
|
return logoPrintData(false, &options->source);
|
|
|
|
if(options->type == FF_LOGO_TYPE_COMMAND_RAW)
|
|
{
|
|
FF_STRBUF_AUTO_DESTROY source = ffStrbufCreate();
|
|
|
|
const char* error = ffProcessAppendStdOut(&source, (char* const[]){
|
|
#ifdef _WIN32
|
|
"cmd.exe", "/c",
|
|
#else
|
|
"/bin/sh", "-c",
|
|
#endif
|
|
options->source.chars,
|
|
NULL
|
|
});
|
|
|
|
if (error)
|
|
{
|
|
if (instance.config.display.showErrors)
|
|
fprintf(stderr, "Logo: failed to execute command `%s`: %s\n", options->source.chars, error);
|
|
return false;
|
|
}
|
|
|
|
return logoPrintData(false, &source);
|
|
}
|
|
|
|
//We sure have a file, resolve relative paths
|
|
if (!updateLogoPath())
|
|
{
|
|
if (instance.config.display.showErrors)
|
|
fprintf(stderr, "Logo: Failed to resolve logo source: %s\n", options->source.chars);
|
|
return false;
|
|
}
|
|
|
|
if(options->type == FF_LOGO_TYPE_FILE)
|
|
return logoPrintFileIfExists(true, false);
|
|
|
|
if(options->type == FF_LOGO_TYPE_FILE_RAW)
|
|
return logoPrintFileIfExists(false, false);
|
|
|
|
if(options->type == FF_LOGO_TYPE_IMAGE_RAW)
|
|
return logoPrintFileIfExists(false, true);
|
|
|
|
return logoPrintImageIfExists(options->type, instance.config.display.showErrors);
|
|
}
|
|
|
|
void ffLogoPrint(void)
|
|
{
|
|
//When generate JSON result, 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.
|
|
if(instance.state.resultDoc)
|
|
{
|
|
instance.state.logoHeight = 0;
|
|
instance.state.logoWidth = 0;
|
|
return;
|
|
}
|
|
|
|
const FFOptionsLogo* options = &instance.config.logo;
|
|
|
|
if (options->type == FF_LOGO_TYPE_NONE)
|
|
{
|
|
logoPrintNone();
|
|
return;
|
|
}
|
|
|
|
//If the source is not set, we can directly print the detected logo.
|
|
if(options->source.length == 0)
|
|
{
|
|
logoPrintDetected(options->type == FF_LOGO_TYPE_SMALL ? FF_LOGO_SIZE_SMALL : FF_LOGO_SIZE_NORMAL);
|
|
return;
|
|
}
|
|
|
|
//If the source and source type is set to something else than auto, always print with the set type.
|
|
if(options->source.length > 0 && options->type != FF_LOGO_TYPE_AUTO)
|
|
{
|
|
if(!logoTryKnownType())
|
|
{
|
|
if (instance.config.display.showErrors)
|
|
{
|
|
// Image logo should have been handled
|
|
if(options->type == FF_LOGO_TYPE_BUILTIN || options->type == FF_LOGO_TYPE_SMALL)
|
|
fprintf(stderr, "Logo: Failed to load %s logo: %s\n", options->type == FF_LOGO_TYPE_BUILTIN ? "builtin" : "builtin small", options->source.chars);
|
|
}
|
|
|
|
logoPrintDetected(FF_LOGO_SIZE_UNKNOWN);
|
|
}
|
|
return;
|
|
}
|
|
|
|
//If source matches the name of a builtin logo, print it and return.
|
|
if(logoPrintBuiltinIfExists(&options->source, FF_LOGO_SIZE_UNKNOWN))
|
|
return;
|
|
|
|
//Make sure the logo path is set correctly.
|
|
if (updateLogoPath())
|
|
{
|
|
if (ffStrbufEndsWithIgnCaseS(&options->source, ".raw"))
|
|
{
|
|
if(logoPrintFileIfExists(false, true))
|
|
return;
|
|
}
|
|
|
|
if (!ffStrbufEndsWithIgnCaseS(&options->source, ".txt"))
|
|
{
|
|
const FFTerminalResult* terminal = ffDetectTerminal();
|
|
|
|
//Terminal emulators that support kitty graphics protocol.
|
|
bool supportsKitty =
|
|
ffStrbufIgnCaseEqualS(&terminal->processName, "kitty") ||
|
|
ffStrbufIgnCaseEqualS(&terminal->processName, "konsole") ||
|
|
ffStrbufIgnCaseEqualS(&terminal->processName, "wezterm") ||
|
|
ffStrbufIgnCaseEqualS(&terminal->processName, "wayst") ||
|
|
ffStrbufIgnCaseEqualS(&terminal->processName, "ghostty") ||
|
|
#ifdef __APPLE__
|
|
ffStrbufIgnCaseEqualS(&terminal->processName, "WarpTerminal") ||
|
|
#else
|
|
ffStrbufIgnCaseEqualS(&terminal->processName, "warp") ||
|
|
#endif
|
|
false;
|
|
|
|
//Try to load the logo as an image. If it succeeds, print it and return.
|
|
if(logoPrintImageIfExists(supportsKitty ? FF_LOGO_TYPE_IMAGE_KITTY : FF_LOGO_TYPE_IMAGE_CHAFA, false))
|
|
return;
|
|
}
|
|
|
|
//Try to load the logo as a file. If it succeeds, print it and return.
|
|
if(logoPrintFileIfExists(true, false))
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if (instance.config.display.showErrors)
|
|
fprintf(stderr, "Logo: Failed to resolve logo source: %s\n", options->source.chars);
|
|
}
|
|
|
|
logoPrintDetected(FF_LOGO_SIZE_UNKNOWN);
|
|
}
|
|
|
|
void ffLogoPrintLine(void)
|
|
{
|
|
if(instance.state.logoWidth > 0)
|
|
printf("\033[%uC", instance.state.logoWidth);
|
|
|
|
++instance.state.keysHeight;
|
|
}
|
|
|
|
void ffLogoPrintRemaining(void)
|
|
{
|
|
if (instance.state.keysHeight <= instance.state.logoHeight)
|
|
ffPrintCharTimes('\n', instance.state.logoHeight - instance.state.keysHeight + 1);
|
|
instance.state.keysHeight = instance.state.logoHeight + 1;
|
|
}
|
|
|
|
void ffLogoBuiltinPrint(void)
|
|
{
|
|
FFOptionsLogo* options = &instance.config.logo;
|
|
|
|
for(uint8_t ch = 0; ch < 26; ++ch)
|
|
{
|
|
for(const FFlogo* logo = ffLogoBuiltins[ch]; *logo->names; ++logo)
|
|
{
|
|
printf("\033[%sm%s:\033[0m\n", logo->colors[0], logo->names[0]);
|
|
logoPrintStruct(logo);
|
|
ffLogoPrintRemaining();
|
|
|
|
//reset everything
|
|
instance.state.logoHeight = 0;
|
|
instance.state.keysHeight = 0;
|
|
for(uint8_t i = 0; i < FASTFETCH_LOGO_MAX_COLORS; i++)
|
|
ffStrbufClear(&options->colors[i]);
|
|
|
|
putchar('\n');
|
|
}
|
|
}
|
|
}
|
|
|
|
void ffLogoBuiltinList(void)
|
|
{
|
|
uint32_t counter = 0;
|
|
for(uint8_t ch = 0; ch < 26; ++ch)
|
|
{
|
|
for(const FFlogo* logo = ffLogoBuiltins[ch]; *logo->names; ++logo)
|
|
{
|
|
++counter;
|
|
printf("%u)%s ", counter, counter < 10 ? " " : "");
|
|
|
|
for(
|
|
const char* const* names = logo->names;
|
|
*names != NULL && names <= &logo->names[FASTFETCH_LOGO_MAX_NAMES];
|
|
++names
|
|
)
|
|
printf("\"%s\" ", *names);
|
|
|
|
putchar('\n');
|
|
}
|
|
}
|
|
}
|
|
|
|
void ffLogoBuiltinListAutocompletion(void)
|
|
{
|
|
for(uint8_t ch = 0; ch < 26; ++ch)
|
|
{
|
|
for(const FFlogo* logo = ffLogoBuiltins[ch]; *logo->names; ++logo)
|
|
printf("%s\n", logo->names[0]);
|
|
}
|
|
}
|