diff --git a/CMakeLists.txt b/CMakeLists.txt index 615c01531..b85e140e7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1834,10 +1834,18 @@ if (BUILD_TESTS) PRIVATE libfastfetch ) + add_executable(fastfetch-test-color + tests/color.c + ) + target_link_libraries(fastfetch-test-color + PRIVATE libfastfetch + ) + enable_testing() add_test(NAME test-strbuf COMMAND fastfetch-test-strbuf) add_test(NAME test-list COMMAND fastfetch-test-list) add_test(NAME test-format COMMAND fastfetch-test-format) + add_test(NAME test-color COMMAND fastfetch-test-color) endif() ################## diff --git a/src/common/option.c b/src/common/option.c index d2f2cd78d..f1af4b48b 100644 --- a/src/common/option.c +++ b/src/common/option.c @@ -177,6 +177,7 @@ void ffOptionParseColorNoClear(const char* value, FFstrbuf* buffer) if (ffCharIsEnglishAlphabet(value[0])) { FF_APPEND_COLOR_CODE_COND(reset_, FF_COLOR_MODE_RESET) + else FF_APPEND_COLOR_CODE_COND(bold_, FF_COLOR_MODE_BOLD) else FF_APPEND_COLOR_CODE_COND(bright_, FF_COLOR_MODE_BOLD) else FF_APPEND_COLOR_CODE_COND(dim_, FF_COLOR_MODE_DIM) else FF_APPEND_COLOR_CODE_COND(italic_, FF_COLOR_MODE_ITALIC) @@ -212,6 +213,35 @@ void ffOptionParseColorNoClear(const char* value, FFstrbuf* buffer) exit(479); } } + else if (value[0] == '#') + { + // RGB color + ++value; + char* pend = NULL; + uint32_t rgb = (uint32_t) strtoul(value, &pend, 16); + if (pend == value) { + fprintf(stderr, "Error: invalid RGB color code found: %s\n", value); + exit(479); + } + if (pend - value > 6) { + fprintf(stderr, "Error: RGB color code too long: %s\n", value); + exit(479); + } + else if (pend - value == 3) { + rgb = ((rgb & 0xF00) >> 8) * 0x110000 + + ((rgb & 0x0F0) >> 4) * 0x001100 + + ((rgb & 0x00F) >> 0) * 0x000011; + } + else if (pend - value != 6) { + fprintf(stderr, "Error: invalid RGB color code length: %s\n", value); + exit(479); + } + + uint32_t r = rgb >> 16, g = (rgb >> 8) & 0xFF, b = rgb & 0xFF; + ffStrbufAppendF(buffer, FF_COLOR_FG_RGB "%u;%u;%u", r, g, b); + value = pend; + continue; + } ffStrbufAppendC(buffer, *value); ++value; diff --git a/tests/color.c b/tests/color.c new file mode 100644 index 000000000..596f2f473 --- /dev/null +++ b/tests/color.c @@ -0,0 +1,65 @@ +#include "common/format.h" +#include "util/textModifier.h" +#include "fastfetch.h" + +#include + +static void verify(const char* color, const char* expected, int lineNo) +{ + FF_STRBUF_AUTO_DESTROY result = ffStrbufCreate(); + ffOptionParseColorNoClear(color, &result); + if (!ffStrbufEqualS(&result, expected)) + { + fprintf(stderr, FASTFETCH_TEXT_MODIFIER_ERROR "[%d] %s: expected \"%s\", got \"%s\"\n" FASTFETCH_TEXT_MODIFIER_RESET, lineNo, color, expected, result.chars); + exit(1); + } +} + +#define VERIFY(color, expected) verify((color), (expected), __LINE__) + +int main(void) +{ + instance.config.display.pipe = true; + // Initialize dummy config colors for property tests + ffStrbufInitS(&instance.config.display.colorKeys, "94"); // light_blue + ffStrbufInitS(&instance.config.display.colorTitle, "95"); // light_magenta + + { + VERIFY("", ""); + VERIFY("1", "1"); + + VERIFY("red", "31"); + VERIFY("light_green", "92"); + VERIFY("default", "39"); + VERIFY("blue", "34"); + VERIFY("light_cyan", "96"); + + VERIFY("bold_red", "1;31"); + VERIFY("dim_light_yellow", "2;93"); + VERIFY("italic_underline_green", "3;4;32"); + VERIFY("reset_blue", "0;34"); // Reset followed by color + + VERIFY("#ff0000", "38;2;255;0;0"); // RRGGBB + VERIFY("#0f0", "38;2;0;255;0"); // RGB + VERIFY("#123456", "38;2;18;52;86"); + VERIFY("#abc", "38;2;170;187;204"); + + VERIFY("bold_#ff00ff", "1;38;2;255;0;255"); + VERIFY("underline_#123", "4;38;2;17;34;51"); + + VERIFY("\e[32m", "32"); // Direct ANSI code + VERIFY("\e[1;94m", "1;94"); // Direct ANSI code with mode + + // Property colors (ensure dummy config colors are set) + VERIFY("keys", "94"); + VERIFY("title", "95"); + VERIFY("bold_keys", "1;94"); + } + + // Clean up dummy config colors + ffStrbufDestroy(&instance.config.display.colorKeys); + ffStrbufDestroy(&instance.config.display.colorTitle); + + //Success + puts("\033[32mAll tests passed!" FASTFETCH_TEXT_MODIFIER_RESET); +}