From cbca6a965ca1cde59470b736137fb1c27020e807 Mon Sep 17 00:00:00 2001 From: Carter Li Date: Fri, 22 May 2026 16:47:55 +0800 Subject: [PATCH] Logo: prints ASCII logo line by line --- src/fastfetch.h | 7 ++ src/logo/logo.c | 248 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 253 insertions(+), 2 deletions(-) diff --git a/src/fastfetch.h b/src/fastfetch.h index 7e79dc302..f88369b31 100644 --- a/src/fastfetch.h +++ b/src/fastfetch.h @@ -21,6 +21,12 @@ typedef struct FFconfig { FFOptionsGeneral general; } FFconfig; +typedef struct FFLogoLineCacheState { + FFlist lines; + uint32_t nextLine; + uint32_t rightOffset; +} FFLogoLineCacheState; + typedef struct FFstate { uint32_t logoWidth; uint32_t logoHeight; @@ -29,6 +35,7 @@ typedef struct FFstate { bool titleFqdn; uint32_t dynamicInterval; FFPlatform platform; + FFLogoLineCacheState logoLineCache; } FFstate; typedef struct FFinstance { diff --git a/src/logo/logo.c b/src/logo/logo.c index 9efc3b6ec..246ba458d 100644 --- a/src/logo/logo.c +++ b/src/logo/logo.c @@ -12,6 +12,196 @@ #include #include +typedef struct FFLogoCachedLine { + FFstrbuf chars; + uint32_t width; +} FFLogoCachedLine; + +static void logoLineCacheClear(FFLogoLineCacheState* cache) { + FF_LIST_FOR_EACH (FFLogoCachedLine, line, cache->lines) { + ffStrbufDestroy(&line->chars); + } + ffListDestroy(&cache->lines); + cache->nextLine = 0; + cache->rightOffset = 0; +} + +static void logoLineCachePush(const FFstrbuf* chars, uint32_t width, FFLogoLineCacheState* cache) { + FFLogoCachedLine* line = FF_LIST_ADD(FFLogoCachedLine, cache->lines); + if (width > 0) { + ffStrbufInitCopy(&line->chars, chars); + } else { + ffStrbufInit(&line->chars); + } + line->width = width; +} + +static void logoLineCacheBuild(const char* data, bool doColorReplacement) { + FFOptionsLogo* options = &instance.config.logo; + bool left = options->position == FF_LOGO_POSITION_LEFT; + bool right = options->position == FF_LOGO_POSITION_RIGHT; + bool keepCarryColor = options->type != FF_LOGO_TYPE_IMAGE_CHAFA; + FFLogoLineCacheState* cache = &instance.state.logoLineCache; + + logoLineCacheClear(cache); + + uint32_t maxLineWidth = 0; + uint32_t parsedHeight = 0; + + FF_STRBUF_AUTO_DESTROY carryColor = ffStrbufCreate(); + if (keepCarryColor && doColorReplacement && !instance.config.display.pipe) { + ffStrbufSetF(&carryColor, "\e[%sm", options->colors[0].chars); + } + + for (uint32_t i = 0; i < options->paddingTop; ++i) { + logoLineCachePush(NULL, 0, cache); + } + + if (*data != '\0') { + while (true) { + FF_STRBUF_AUTO_DESTROY line = ffStrbufCreateA(256); + uint32_t lineWidth = 0; + + if (!instance.config.display.pipe && instance.config.display.brightColor) { + ffStrbufAppendS(&line, FASTFETCH_TEXT_MODIFIER_BOLT); + } + + if (keepCarryColor && carryColor.length > 0) { + ffStrbufAppend(&line, &carryColor); + } + + if (left && options->paddingLeft > 0) { + ffStrbufAppendNC(&line, options->paddingLeft, ' '); + lineWidth += options->paddingLeft; + } + + while (*data != '\0' && *data != '\n' && !(*data == '\r' && *(data + 1) == '\n')) { + if (*data == '\t') { + ffStrbufAppendNC(&line, 4, ' '); + lineWidth += 4; + ++data; + continue; + } + + if (*data == '\e' && *(data + 1) == '[') { + const char* start = data; + data += 2; + + while (ffCharIsDigit(*data) || *data == ';') { + ++data; + } + + if (isascii(*data)) { + ++data; + + uint32_t escLen = (uint32_t) (data - start); + ffStrbufAppendNS(&line, escLen, start); + + if (keepCarryColor && start[escLen - 1] == 'm') { + ffStrbufSetNS(&carryColor, escLen, start); + } + continue; + } + + lineWidth += (uint32_t) (data - start - 1); + } + + if (doColorReplacement && *data == '$') { + ++data; + + if (*data == '$' || *data == '\0') { + ffStrbufAppendC(&line, '$'); + ++lineWidth; + ++data; + continue; + } + + if (!instance.config.display.pipe) { + int index = *data - '1'; + if (index >= 0 && index < FASTFETCH_LOGO_MAX_COLORS) { + if (keepCarryColor) { + ffStrbufSetF(&carryColor, "\e[%sm", options->colors[index].chars); + ffStrbufAppend(&line, &carryColor); + } else { + ffStrbufAppendF(&line, "\e[%sm", options->colors[index].chars); + } + ++data; + continue; + } + + ffStrbufAppendC(&line, '$'); + ++lineWidth; + } else { + ++data; + continue; + } + } + + ++lineWidth; + + 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; + } + + for (uint8_t i = 0; i < bytes; ++i) { + if (*data == '\0') { + break; + } + + ffStrbufAppendC(&line, *data); + ++data; + } + } + + logoLineCachePush(&line, lineWidth, cache); + if (lineWidth > maxLineWidth) { + maxLineWidth = lineWidth; + } + + if (*data == '\n' || (*data == '\r' && *(data + 1) == '\n')) { + if (*data == '\r') { + ++data; + } + ++data; + ++parsedHeight; + continue; + } + + break; + } + } + + if (options->type != FF_LOGO_TYPE_IMAGE_CHAFA && options->height > parsedHeight) { + parsedHeight = options->height; + } + + instance.state.logoHeight = options->paddingTop + parsedHeight; + if (left) { + instance.state.logoWidth = maxLineWidth + options->paddingRight; + } else if (right) { + instance.state.logoWidth = 0; + } + + uint32_t totalLines = instance.state.logoHeight + 1; + while (cache->lines.length < totalLines) { + logoLineCachePush(NULL, 0, cache); + } + + cache->nextLine = 0; + cache->rightOffset = maxLineWidth + options->paddingRight - 1; +} + static bool ffLogoPrintCharsRaw(const char* data, size_t length, bool printError) { FFOptionsLogo* options = &instance.config.logo; FF_STRBUF_AUTO_DESTROY buf = ffStrbufCreate(); @@ -249,6 +439,11 @@ static uint32_t logoAppendChars(const char* data, bool doColorReplacement, FFstr void ffLogoPrintChars(const char* data, bool doColorReplacement) { FFOptionsLogo* options = &instance.config.logo; + if (options->position == FF_LOGO_POSITION_LEFT || options->position == FF_LOGO_POSITION_RIGHT) { + logoLineCacheBuild(data, doColorReplacement); + return; + } + if (options->position == FF_LOGO_POSITION_RIGHT) { logoAppendChars(data, doColorReplacement, NULL); } @@ -557,7 +752,7 @@ static bool logoTryKnownType(void) { if (options->type == FF_LOGO_TYPE_COMMAND_RAW) { FF_STRBUF_AUTO_DESTROY source = ffStrbufCreate(); - const char* error = ffProcessAppendStdOut(&source, (char* const[]) { + const char* error = ffProcessAppendStdOut(&source, (char* const[]){ #ifdef _WIN32 "cmd.exe", "/c", #else @@ -684,8 +879,33 @@ void ffLogoPrint(void) { } void ffLogoPrintLine(void) { + uint32_t printedLineWidth = 0; + FFLogoLineCacheState* cache = &instance.state.logoLineCache; + FFOptionsLogo* logo = &instance.config.logo; + + if (cache->lines.length > 0 && (logo->position == FF_LOGO_POSITION_LEFT || logo->position == FF_LOGO_POSITION_RIGHT) && cache->nextLine < cache->lines.length) { + FFLogoCachedLine* line = FF_LIST_GET(FFLogoCachedLine, cache->lines, cache->nextLine); + + if (logo->position == FF_LOGO_POSITION_RIGHT && line->chars.length > 0) { + printf("\033[9999999C\033[%uD", cache->rightOffset); + ffStrbufWriteTo(&line->chars, stdout); + fputs("\033[G", stdout); + } else { + ffStrbufWriteTo(&line->chars, stdout); + printedLineWidth = line->width; + } + + ++cache->nextLine; + } + if (instance.state.logoWidth > 0) { - printf("\033[%uC", instance.state.logoWidth); + if (instance.config.logo.position == FF_LOGO_POSITION_LEFT) { + uint32_t remaining = instance.state.logoWidth; + remaining = printedLineWidth < remaining ? remaining - printedLineWidth : 0; + ffPrintCharTimes(' ', remaining); + } else { + printf("\033[%uC", instance.state.logoWidth); + } } if (instance.state.dynamicInterval > 0) { @@ -696,6 +916,30 @@ void ffLogoPrintLine(void) { } void ffLogoPrintRemaining(void) { + FFLogoLineCacheState* cache = &instance.state.logoLineCache; + FFOptionsLogo* logo = &instance.config.logo; + + if (cache->lines.length > 0 && (logo->position == FF_LOGO_POSITION_LEFT || logo->position == FF_LOGO_POSITION_RIGHT)) { + while (cache->nextLine < cache->lines.length) { + FFLogoCachedLine* line = FF_LIST_GET(FFLogoCachedLine, cache->lines, cache->nextLine); + + if (logo->position == FF_LOGO_POSITION_RIGHT) { + printf("\033[9999999C\033[%uD", cache->rightOffset); + } + ffStrbufPutTo(&line->chars, stdout); + + ++cache->nextLine; + } + + if (!instance.config.display.pipe) { + fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout); + } + + instance.state.keysHeight = instance.state.logoHeight + 1; + logoLineCacheClear(cache); + return; + } + if (instance.state.keysHeight <= instance.state.logoHeight) { ffPrintCharTimes('\n', instance.state.logoHeight - instance.state.keysHeight + 1); }