From ec6195133b9fe6b01a8a5123c905eb6aed8d4256 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Mon, 22 Jan 2024 16:03:32 +0800 Subject: [PATCH] TerminalTheme: add new module --- CMakeLists.txt | 2 + src/common/modules.c | 1 + src/detection/terminaltheme/terminaltheme.c | 91 +++++++++++++ src/detection/terminaltheme/terminaltheme.h | 19 +++ src/modules/modules.h | 1 + src/modules/options.h | 1 + src/modules/terminaltheme/option.h | 11 ++ src/modules/terminaltheme/terminaltheme.c | 134 ++++++++++++++++++++ src/modules/terminaltheme/terminaltheme.h | 9 ++ src/options/modules.c | 6 +- src/options/modules.h | 3 +- 11 files changed, 275 insertions(+), 3 deletions(-) create mode 100644 src/detection/terminaltheme/terminaltheme.c create mode 100644 src/detection/terminaltheme/terminaltheme.h create mode 100644 src/modules/terminaltheme/option.h create mode 100644 src/modules/terminaltheme/terminaltheme.c create mode 100644 src/modules/terminaltheme/terminaltheme.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 0834a2d6c..fb5da6f14 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -293,6 +293,7 @@ set(LIBFASTFETCH_SRC src/detection/os/os.c src/detection/packages/packages.c src/detection/publicip/publicip.c + src/detection/terminaltheme/terminaltheme.c src/detection/terminalfont/terminalfont.c src/detection/terminalshell/terminalshell.c src/detection/version/version.c @@ -348,6 +349,7 @@ set(LIBFASTFETCH_SRC src/modules/swap/swap.c src/modules/media/media.c src/modules/terminal/terminal.c + src/modules/terminaltheme/terminaltheme.c src/modules/terminalfont/terminalfont.c src/modules/terminalsize/terminalsize.c src/modules/theme/theme.c diff --git a/src/common/modules.c b/src/common/modules.c index 580e946f6..fa486261f 100644 --- a/src/common/modules.c +++ b/src/common/modules.c @@ -124,6 +124,7 @@ static FFModuleBaseInfo* T[] = { (void*) &instance.config.modules.terminal, (void*) &instance.config.modules.terminalFont, (void*) &instance.config.modules.terminalSize, + (void*) &instance.config.modules.terminalTheme, (void*) &instance.config.modules.title, (void*) &instance.config.modules.theme, NULL, diff --git a/src/detection/terminaltheme/terminaltheme.c b/src/detection/terminaltheme/terminaltheme.c new file mode 100644 index 000000000..845eec637 --- /dev/null +++ b/src/detection/terminaltheme/terminaltheme.c @@ -0,0 +1,91 @@ +#include "terminaltheme.h" +#include "common/io/io.h" +#include "util/stringUtils.h" + +#include + +static bool detectByEscapeCode(FFTerminalThemeResult* result) +{ + int command = 0; + + if (ffGetTerminalResponse("\e]10;?\e\\", "\e]%d;rgb:%" SCNx16 "/%" SCNx16 "/%" SCNx16 "\e\\", &command, &result->fg.r, &result->fg.g, &result->fg.b) == NULL) + { + if (command != 10) + return false; + if (result->fg.r > 0x0100 || result->fg.g > 0x0100 || result->fg.b > 0x0100) + result->fg.r /= 0x0100, result->fg.g /= 0x0100, result->fg.b /= 0x0100; + } + else + return false; + + if (ffGetTerminalResponse("\e]11;?\e\\", "\e]%d;rgb:%" SCNx16 "/%" SCNx16 "/%" SCNx16 "\e\\", &command, &result->bg.r, &result->bg.g, &result->bg.b) == NULL) + { + if (command != 11) + return false; + if (result->bg.r > 0x0100 || result->bg.g > 0x0100 || result->bg.b > 0x0100) + result->bg.r /= 0x0100, result->bg.g /= 0x0100, result->bg.b /= 0x0100; + } + else + return false; + + return true; +} + +static FFTerminalThemeColor fgbgToColor(int num) +{ + // https://github.com/dalance/termbg/blob/13c478a433fa182e65c401d26a1e7792a7f7f453/src/lib.rs#L251 + switch (num) + { + case 0: return (FFTerminalThemeColor){ 0, 0, 0, false}; // black + case 1: return (FFTerminalThemeColor){205, 0, 0, false}; // red + case 2: return (FFTerminalThemeColor){ 0, 205, 0, false}; // green + case 3: return (FFTerminalThemeColor){205, 205, 0, false}; // yellow + case 4: return (FFTerminalThemeColor){ 0, 0, 238, false}; // blue + case 5: return (FFTerminalThemeColor){205, 0, 205, false}; // magenta + case 6: return (FFTerminalThemeColor){ 0, 205, 205, false}; // cyan + case 7: return (FFTerminalThemeColor){229, 229, 229, false}; // white + + case 8: return (FFTerminalThemeColor){127, 127, 127, false}; // bright black + case 9: return (FFTerminalThemeColor){255, 0, 0, false}; // bright red + case 10: return (FFTerminalThemeColor){ 0, 255, 0, false}; // bright green + case 11: return (FFTerminalThemeColor){255, 255, 0, false}; // bright yellow + case 12: return (FFTerminalThemeColor){ 92, 92, 255, false}; // bright blue + case 13: return (FFTerminalThemeColor){255, 0, 255, false}; // bright magenta + case 14: return (FFTerminalThemeColor){ 0, 255, 255, false}; // bright cyan + case 15: return (FFTerminalThemeColor){255, 255, 255, false}; // bright white + + default: return (FFTerminalThemeColor){ 0, 0, 0, false}; // invalid + } +} + +static bool detectByEnv(FFTerminalThemeResult* result) +{ + const char* color = getenv("COLORFGBG"); // 7;0 + + if (!ffStrSet(color)) + return false; + + int f, g; + if (sscanf(color, "%d;%d", &f, &g) != 2) + return false; + + result->fg = fgbgToColor(f); + result->bg = fgbgToColor(g); + return true; +} + +static inline bool detectColor(FFTerminalThemeResult* result) +{ + if (detectByEscapeCode(result)) + return true; + + return detectByEnv(result); +} + +bool ffDetectTerminalTheme(FFTerminalThemeResult* result) +{ + if (!detectColor(result)) return false; + result->fg.dark = result->fg.r * 299 + result->fg.g * 587 + result->fg.b * 114 < 128000; + result->bg.dark = result->bg.r * 299 + result->bg.g * 587 + result->bg.b * 114 < 128000; + return true; +} diff --git a/src/detection/terminaltheme/terminaltheme.h b/src/detection/terminaltheme/terminaltheme.h new file mode 100644 index 000000000..08bb4ea0f --- /dev/null +++ b/src/detection/terminaltheme/terminaltheme.h @@ -0,0 +1,19 @@ +#pragma once + +#include "fastfetch.h" + +typedef struct FFTerminalThemeColor +{ + uint16_t r; + uint16_t g; + uint16_t b; + bool dark; +} FFTerminalThemeColor; + +typedef struct FFTerminalThemeResult +{ + FFTerminalThemeColor fg; + FFTerminalThemeColor bg; +} FFTerminalThemeResult; + +bool ffDetectTerminalTheme(FFTerminalThemeResult* result); diff --git a/src/modules/modules.h b/src/modules/modules.h index 35d6523c4..78fe2f33b 100644 --- a/src/modules/modules.h +++ b/src/modules/modules.h @@ -49,6 +49,7 @@ #include "modules/terminal/terminal.h" #include "modules/terminalfont/terminalfont.h" #include "modules/terminalsize/terminalsize.h" +#include "modules/terminaltheme/terminaltheme.h" #include "modules/theme/theme.h" #include "modules/title/title.h" #include "modules/uptime/uptime.h" diff --git a/src/modules/options.h b/src/modules/options.h index aa124c24a..54dd7ca15 100644 --- a/src/modules/options.h +++ b/src/modules/options.h @@ -49,6 +49,7 @@ #include "modules/terminal/option.h" #include "modules/terminalfont/option.h" #include "modules/terminalsize/option.h" +#include "modules/terminaltheme/option.h" #include "modules/theme/option.h" #include "modules/title/option.h" #include "modules/uptime/option.h" diff --git a/src/modules/terminaltheme/option.h b/src/modules/terminaltheme/option.h new file mode 100644 index 000000000..aecbe18d7 --- /dev/null +++ b/src/modules/terminaltheme/option.h @@ -0,0 +1,11 @@ +#pragma once + +// This file will be included in "fastfetch.h", do NOT put unnecessary things here + +#include "common/option.h" + +typedef struct FFTerminalThemeOptions +{ + FFModuleBaseInfo moduleInfo; + FFModuleArgs moduleArgs; +} FFTerminalThemeOptions; diff --git a/src/modules/terminaltheme/terminaltheme.c b/src/modules/terminaltheme/terminaltheme.c new file mode 100644 index 000000000..43e76c65c --- /dev/null +++ b/src/modules/terminaltheme/terminaltheme.c @@ -0,0 +1,134 @@ +#include "common/printing.h" +#include "common/jsonconfig.h" +#include "detection/terminaltheme/terminaltheme.h" +#include "modules/terminaltheme/terminaltheme.h" +#include "util/stringUtils.h" + +#include + +#define FF_TERMINALTHEME_DISPLAY_NAME "Terminal Theme" +#define FF_TERMINALTHEME_NUM_FORMAT_ARGS 4 + +void ffPrintTerminalTheme(FFTerminalThemeOptions* options) +{ + FFTerminalThemeResult result = {}; + + if(!ffDetectTerminalTheme(&result)) + { + ffPrintError(FF_TERMINALTHEME_DISPLAY_NAME, 0, &options->moduleArgs, "Failed to detect terminal theme"); + } + else + { + if(options->moduleArgs.outputFormat.length == 0) + { + ffPrintLogoAndKey(FF_TERMINALTHEME_DISPLAY_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); + printf("#%02" PRIX16 "%02" PRIX16 "%02" PRIX16 " (FG) - #%02" PRIX16 "%02" PRIX16 "%02" PRIX16 " (BG) [%s]\n", + result.fg.r, result.fg.g, result.fg.b, + result.bg.r, result.bg.g, result.bg.b, + result.bg.dark ? "Dark" : "Light"); + } + else + { + char fg[32], bg[32]; + snprintf(fg, sizeof(fg), "#%02" PRIX16 "%02" PRIX16 "%02" PRIX16, result.fg.r, result.fg.g, result.fg.b); + snprintf(bg, sizeof(bg), "#%02" PRIX16 "%02" PRIX16 "%02" PRIX16, result.bg.r, result.bg.g, result.bg.b); + ffPrintFormat(FF_TERMINALTHEME_DISPLAY_NAME, 0, &options->moduleArgs, FF_TERMINALTHEME_NUM_FORMAT_ARGS, (FFformatarg[]){ + {FF_FORMAT_ARG_TYPE_STRING, fg}, + {FF_FORMAT_ARG_TYPE_STRING, result.fg.dark ? "Dark" : "Light"}, + {FF_FORMAT_ARG_TYPE_STRING, bg}, + {FF_FORMAT_ARG_TYPE_STRING, result.bg.dark ? "Dark" : "Light"}, + }); + } + } +} + +bool ffParseTerminalThemeCommandOptions(FFTerminalThemeOptions* options, const char* key, const char* value) +{ + const char* subKey = ffOptionTestPrefix(key, FF_TERMINALTHEME_MODULE_NAME); + if (!subKey) return false; + if (ffOptionParseModuleArgs(key, subKey, value, &options->moduleArgs)) + return true; + + return false; +} + +void ffParseTerminalThemeJsonObject(FFTerminalThemeOptions* options, yyjson_val* module) +{ + yyjson_val *key_, *val; + size_t idx, max; + yyjson_obj_foreach(module, idx, max, key_, val) + { + const char* key = yyjson_get_str(key_); + if(ffStrEqualsIgnCase(key, "type")) + continue; + + if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs)) + continue; + + ffPrintError(FF_TERMINALTHEME_DISPLAY_NAME, 0, &options->moduleArgs, "Unknown JSON key %s", key); + } +} + +void ffGenerateTerminalThemeJsonConfig(FFTerminalThemeOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + __attribute__((__cleanup__(ffDestroyTerminalThemeOptions))) FFTerminalThemeOptions defaultOptions; + ffInitTerminalThemeOptions(&defaultOptions); + + ffJsonConfigGenerateModuleArgsConfig(doc, module, &defaultOptions.moduleArgs, &options->moduleArgs); +} + +void ffGenerateTerminalThemeJsonResult(FF_MAYBE_UNUSED FFTerminalOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) +{ + FFTerminalThemeResult result = {}; + + if(!ffDetectTerminalTheme(&result)) + { + yyjson_mut_obj_add_str(doc, module, "error", "Failed to detect terminal theme"); + return; + } + + yyjson_mut_val* obj = yyjson_mut_obj_add_obj(doc, module, "result"); + + yyjson_mut_val* fg = yyjson_mut_obj_add_obj(doc, obj, "fg"); + yyjson_mut_obj_add_uint(doc, fg, "r", result.fg.r); + yyjson_mut_obj_add_uint(doc, fg, "g", result.fg.g); + yyjson_mut_obj_add_uint(doc, fg, "b", result.fg.b); + yyjson_mut_obj_add_bool(doc, fg, "dark", result.fg.dark); + + yyjson_mut_val* bg = yyjson_mut_obj_add_obj(doc, obj, "bg"); + yyjson_mut_obj_add_uint(doc, bg, "r", result.bg.r); + yyjson_mut_obj_add_uint(doc, bg, "g", result.bg.g); + yyjson_mut_obj_add_uint(doc, bg, "b", result.bg.b); + yyjson_mut_obj_add_bool(doc, bg, "dark", result.bg.dark); +} + +void ffPrintTerminalThemeHelpFormat(void) +{ + ffPrintModuleFormatHelp(FF_TERMINALTHEME_MODULE_NAME, "{1} (FG) {3} (BG) [{4}]", FF_TERMINALTHEME_NUM_FORMAT_ARGS, (const char* []) { + "Terminal foreground color", + "Terminal foreground type (Dark / Light)", + "Terminal background color", + "Terminal background type (Dark / Light)", + }); +} + +void ffInitTerminalThemeOptions(FFTerminalThemeOptions* options) +{ + ffOptionInitModuleBaseInfo( + &options->moduleInfo, + FF_TERMINALTHEME_MODULE_NAME, + "Print current terminal theme (foreground and background colors)", + ffParseTerminalThemeCommandOptions, + ffParseTerminalThemeJsonObject, + ffPrintTerminalTheme, + ffGenerateTerminalThemeJsonResult, + ffPrintTerminalThemeHelpFormat, + ffGenerateTerminalThemeJsonConfig + ); + ffOptionInitModuleArg(&options->moduleArgs); +} + +void ffDestroyTerminalThemeOptions(FFTerminalThemeOptions* options) +{ + ffOptionDestroyModuleArg(&options->moduleArgs); +} diff --git a/src/modules/terminaltheme/terminaltheme.h b/src/modules/terminaltheme/terminaltheme.h new file mode 100644 index 000000000..fb5cbb58a --- /dev/null +++ b/src/modules/terminaltheme/terminaltheme.h @@ -0,0 +1,9 @@ +#pragma once + +#include "fastfetch.h" + +#define FF_TERMINALTHEME_MODULE_NAME "TerminalTheme" + +void ffPrintTerminalTheme(FFTerminalThemeOptions* options); +void ffInitTerminalThemeOptions(FFTerminalThemeOptions* options); +void ffDestroyTerminalThemeOptions(FFTerminalThemeOptions* options); diff --git a/src/options/modules.c b/src/options/modules.c index 68b835d82..bc636ff0c 100644 --- a/src/options/modules.c +++ b/src/options/modules.c @@ -47,9 +47,10 @@ void ffOptionsInitModules(FFOptionsModules* options) ffInitShellOptions(&options->shell); ffInitSoundOptions(&options->sound); ffInitSwapOptions(&options->swap); - ffInitTerminalFontOptions(&options->terminalFont); ffInitTerminalOptions(&options->terminal); + ffInitTerminalFontOptions(&options->terminalFont); ffInitTerminalSizeOptions(&options->terminalSize); + ffInitTerminalThemeOptions(&options->terminalTheme); ffInitThemeOptions(&options->theme); ffInitTitleOptions(&options->title); ffInitUptimeOptions(&options->uptime); @@ -109,9 +110,10 @@ void ffOptionsDestroyModules(FFOptionsModules* options) ffDestroyShellOptions(&options->shell); ffDestroySoundOptions(&options->sound); ffDestroySwapOptions(&options->swap); - ffDestroyTerminalFontOptions(&options->terminalFont); ffDestroyTerminalOptions(&options->terminal); + ffDestroyTerminalFontOptions(&options->terminalFont); ffDestroyTerminalSizeOptions(&options->terminalSize); + ffDestroyTerminalThemeOptions(&options->terminalTheme); ffDestroyThemeOptions(&options->theme); ffDestroyTitleOptions(&options->title); ffDestroyUptimeOptions(&options->uptime); diff --git a/src/options/modules.h b/src/options/modules.h index e9836e140..7a96a0f40 100644 --- a/src/options/modules.h +++ b/src/options/modules.h @@ -48,9 +48,10 @@ typedef struct FFOptionsModules FFShellOptions shell; FFSoundOptions sound; FFSwapOptions swap; - FFTerminalFontOptions terminalFont; FFTerminalOptions terminal; + FFTerminalFontOptions terminalFont; FFTerminalSizeOptions terminalSize; + FFTerminalThemeOptions terminalTheme; FFThemeOptions theme; FFTitleOptions title; FFUptimeOptions uptime;