master ohmu/fastfetch-openbsd / src / common / impl / printing.c
ohmu  ·  2026-10-15
  1#include "fastfetch.h"
  2#include "common/printing.h"
  3#include "common/textModifier.h"
  4#include "logo/logo.h"
  5
  6void ffPrintLogoAndKey(const char* moduleName, uint32_t moduleIndex, const FFModuleArgs* moduleArgs, FFPrintType printType) {
  7    ffLogoPrintLine();
  8
  9    // This is used by --set-keyless, in this case we want neither the module name nor the separator
 10    if (moduleName == nullptr) {
 11        return;
 12    }
 13
 14    // This is used as a magic value for hiding keys
 15    if (!(moduleArgs && ffStrbufEqualS(&moduleArgs->key, " ")) && instance.config.display.keyType != FF_MODULE_KEY_TYPE_NONE) {
 16        ffPrintCharTimes(' ', instance.config.display.keyPaddingLeft);
 17
 18        if (!instance.config.display.pipe) {
 19            fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout);
 20            if (instance.config.display.brightColor) {
 21                fputs(FASTFETCH_TEXT_MODIFIER_BOLT, stdout);
 22            }
 23
 24            if (moduleArgs && !(printType & FF_PRINT_TYPE_NO_CUSTOM_KEY_COLOR) && moduleArgs->keyColor.length > 0) {
 25                ffPrintColor(&moduleArgs->keyColor);
 26            } else {
 27                ffPrintColor(&instance.config.display.colorKeys);
 28            }
 29        }
 30
 31        if (instance.config.display.keyType & FF_MODULE_KEY_TYPE_ICON && moduleArgs && moduleArgs->keyIcon.length > 0) {
 32            ffStrbufWriteTo(&moduleArgs->keyIcon, stdout);
 33        }
 34
 35        if (instance.config.display.keyType & FF_MODULE_KEY_TYPE_STRING) {
 36            ffPrintCharTimes(' ', instance.config.display.keyType >> FF_MODULE_KEY_TYPE_SPACE_SHIFT);
 37
 38            // nullptr check is required for modules with custom keys, e.g. disk with the folder path
 39            if ((printType & FF_PRINT_TYPE_NO_CUSTOM_KEY) || !moduleArgs || moduleArgs->key.length == 0) {
 40                fputs(moduleName, stdout);
 41
 42                if (moduleIndex > 0) {
 43                    printf(" %u", moduleIndex);
 44                }
 45            } else {
 46                FF_STRBUF_AUTO_DESTROY key = ffStrbufCreate();
 47                FF_PARSE_FORMAT_STRING_CHECKED(&key, &moduleArgs->key, ((FFformatarg[]) {
 48                                                                           FF_ARG(moduleIndex, "index"),
 49                                                                           FF_ARG(moduleArgs->keyIcon, "icon"),
 50                                                                           FF_ARG(moduleName, "module-name"),
 51                                                                       }));
 52                ffStrbufWriteTo(&key, stdout);
 53            }
 54        }
 55
 56        if (!instance.config.display.pipe) {
 57            fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout);
 58            ffPrintColor(&instance.config.display.colorSeparator);
 59        }
 60
 61        ffStrbufWriteTo(&instance.config.display.keyValueSeparator, stdout);
 62
 63        if (!instance.config.display.pipe && instance.config.display.colorSeparator.length) {
 64            fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout);
 65        }
 66
 67        if (!(printType & FF_PRINT_TYPE_NO_CUSTOM_KEY_WIDTH)) {
 68            uint32_t keyWidth = moduleArgs && moduleArgs->keyWidth > 0 ? moduleArgs->keyWidth : instance.config.display.keyWidth;
 69            if (keyWidth > 0) {
 70                printf("\e[%uG", (unsigned) (keyWidth + instance.state.logoWidth));
 71            }
 72        }
 73    }
 74
 75    if (!instance.config.display.pipe) {
 76        fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout);
 77        if (moduleArgs && moduleArgs->outputColor.length) {
 78            ffPrintColor(&moduleArgs->outputColor);
 79        } else if (instance.config.display.colorOutput.length) {
 80            ffPrintColor(&instance.config.display.colorOutput);
 81        }
 82    }
 83}
 84
 85bool ffPrintFormat(const char* moduleName, uint32_t moduleIndex, const FFModuleArgs* moduleArgs, FFPrintType printType, uint32_t numArgs, const FFformatarg* arguments) {
 86    FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
 87    bool success;
 88    if (__builtin_expect(moduleArgs != nullptr, 1)) {
 89        success = ffParseFormatString(&buffer, &moduleArgs->outputFormat, numArgs, arguments);
 90    } else {
 91        ffStrbufSetStatic(&buffer, "undefined format");
 92        success = false;
 93    }
 94
 95    if (success) {
 96        ffPrintLogoAndKey(moduleName, moduleIndex, moduleArgs, printType);
 97        ffStrbufPutTo(&buffer, stdout);
 98    } else {
 99        ffPrintError(moduleName, moduleIndex, moduleArgs, printType, "%s", buffer.chars);
100    }
101
102    return success;
103}
104
105void ffPrintError(const char* moduleName, uint32_t moduleIndex, const FFModuleArgs* moduleArgs, FFPrintType printType, const char* message, ...) {
106    if (!instance.config.display.showErrors) {
107        return;
108    }
109
110    ffPrintLogoAndKey(moduleName, moduleIndex, moduleArgs, printType);
111
112    if (!instance.config.display.pipe) {
113        fputs(FASTFETCH_TEXT_MODIFIER_ERROR, stdout);
114    }
115
116    va_list arguments;
117    va_start(arguments, message);
118    vprintf(message, arguments);
119    va_end(arguments);
120
121    if (!instance.config.display.pipe) {
122        fputs(FASTFETCH_TEXT_MODIFIER_RESET, stdout);
123    }
124
125    putchar('\n');
126}
127
128void ffPrintColor(const FFstrbuf* colorValue) {
129    // If the color is not set, this would reset in \033[m, which resets everything.
130    // So we only print it, if the main color is at least one char.
131    if (colorValue->length == 0) {
132        return;
133    }
134
135    printf("\e[%sm", colorValue->chars);
136}
137
138void ffPrintCharTimes(char c, uint32_t times) {
139    if (times == 0) {
140        return;
141    }
142
143    if (times == 1) {
144        putchar(c);
145        return;
146    }
147
148    char str[32];
149    memset(str, c, sizeof(str)); // 2 instructions when compiling with AVX2 enabled
150    for (uint32_t i = sizeof(str); i <= times; i += (uint32_t) sizeof(str)) {
151        fwrite(str, 1, sizeof(str), stdout);
152    }
153    uint32_t remaining = times % sizeof(str);
154    if (remaining > 0) {
155        fwrite(str, 1, remaining, stdout);
156    }
157}