#include "common/printing.h" #include "common/jsonconfig.h" #include "common/textModifier.h" #include "common/color.h" #include "common/strutil.h" #include "logo/logo.h" #include "modules/colors/colors.h" static inline uint8_t min(uint8_t a, uint8_t b) { return a < b ? a : b; } static inline uint8_t max(uint8_t a, uint8_t b) { return a > b ? a : b; } bool ffPrintColors(FFColorsOptions* options) { bool flag = false; FF_STRBUF_AUTO_DESTROY result = ffStrbufCreateA(128); if (options->symbol == FF_COLORS_SYMBOL_BLOCK || options->symbol == FF_COLORS_SYMBOL_BACKGROUND) { if (options->brightness != FF_COLORS_BRIGHTNESS_LIGHT) { // 3%d: Set the foreground color for (uint8_t i = options->block.range[0]; i <= min(options->block.range[1], 7); i++) { if (options->symbol == FF_COLORS_SYMBOL_BLOCK) { if (!instance.config.display.pipe) { ffStrbufAppendF(&result, "\e[3%dm", i); } for (uint8_t j = 0; j < options->block.width; j++) { ffStrbufAppendS(&result, "█"); } } else { ffStrbufAppendF(&result, "\e[4%dm", i); ffStrbufAppendNC(&result, options->block.width, ' '); } } } if (result.length > 0) { ffPrintLogoAndKey(FF_COLORS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); flag = true; if (options->paddingLeft > 0) { ffPrintCharTimes(' ', options->paddingLeft); } if (!instance.config.display.pipe || options->symbol == FF_COLORS_SYMBOL_BACKGROUND) { ffStrbufAppendS(&result, FASTFETCH_TEXT_MODIFIER_RESET); } ffStrbufPutTo(&result, stdout); ffStrbufClear(&result); } #ifdef __linux__ // Required by Linux Console for light background to work if (options->symbol == FF_COLORS_SYMBOL_BACKGROUND) { const char* term = getenv("TERM"); // Should be "linux", however some terminal mulitplexer overrides $TERM if (term && !ffStrStartsWith(term, "xterm")) { ffStrbufAppendS(&result, "\e[5m"); } } #endif if (options->brightness != FF_COLORS_BRIGHTNESS_NORMAL) { // 9%d: Set the foreground to the bright color for (uint8_t i = max(options->block.range[0], 8); i <= options->block.range[1]; i++) { if (options->symbol == FF_COLORS_SYMBOL_BLOCK) { if (!instance.config.display.pipe) { ffStrbufAppendF(&result, "\e[9%dm", i - 8); } for (uint8_t j = 0; j < options->block.width; j++) { ffStrbufAppendS(&result, "█"); } } else { ffStrbufAppendF(&result, "\e[10%dm", i - 8); ffStrbufAppendNC(&result, options->block.width, ' '); } } } } else { const char* symbol; switch (options->symbol) { case FF_COLORS_SYMBOL_CIRCLE: symbol = "● "; break; case FF_COLORS_SYMBOL_DIAMOND: symbol = "◆ "; break; case FF_COLORS_SYMBOL_TRIANGLE: symbol = "▲ "; break; case FF_COLORS_SYMBOL_SQUARE: symbol = "■ "; break; case FF_COLORS_SYMBOL_STAR: symbol = "★ "; break; default: symbol = "███ "; break; } if (options->brightness == FF_COLORS_BRIGHTNESS_DEFAULT) { for (int i = 8; i >= 1; --i) { if (!instance.config.display.pipe) { ffStrbufAppendF(&result, "\e[" FF_COLOR_FG_256 "%dm", i); } ffStrbufAppendS(&result, symbol); } } else { for (int i = 0; i <= 7; ++i) { if (!instance.config.display.pipe) { ffStrbufAppendF(&result, "\e[%c%dm", options->brightness == FF_COLORS_BRIGHTNESS_NORMAL ? '3' : '9', i); } ffStrbufAppendS(&result, symbol); } } ffStrbufTrimRight(&result, ' '); } if (result.length > 0) { if (flag) { ffLogoPrintLine(); } else { ffPrintLogoAndKey(FF_COLORS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT); flag = true; } if (options->paddingLeft > 0) { ffPrintCharTimes(' ', options->paddingLeft); } if (!instance.config.display.pipe || options->symbol == FF_COLORS_SYMBOL_BACKGROUND) { ffStrbufAppendS(&result, FASTFETCH_TEXT_MODIFIER_RESET); } ffStrbufPutTo(&result, stdout); } if (!flag) { ffPrintError(FF_COLORS_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", "Nothing to print"); return false; } return true; } void ffParseColorsJsonObject(FFColorsOptions* options, yyjson_val* module) { yyjson_val *key, *val; size_t idx, max; yyjson_obj_foreach (module, idx, max, key, val) { if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs)) { continue; } if (unsafe_yyjson_equals_str(key, "symbol")) { int value; const char* error = ffJsonConfigParseEnum(val, &value, (FFKeyValuePair[]) { { "block", FF_COLORS_SYMBOL_BLOCK }, { "background", FF_COLORS_SYMBOL_BACKGROUND }, { "circle", FF_COLORS_SYMBOL_CIRCLE }, { "diamond", FF_COLORS_SYMBOL_DIAMOND }, { "triangle", FF_COLORS_SYMBOL_TRIANGLE }, { "square", FF_COLORS_SYMBOL_SQUARE }, { "star", FF_COLORS_SYMBOL_STAR }, {}, }); if (error) { ffPrintError(FF_COLORS_MODULE_NAME, 0, NULL, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Invalid %s value: %s", unsafe_yyjson_get_str(key), error); } else { options->symbol = (FFColorsSymbol) value; } continue; } if (unsafe_yyjson_equals_str(key, "paddingLeft")) { options->paddingLeft = (uint32_t) yyjson_get_uint(val); continue; } if (unsafe_yyjson_equals_str(key, "block")) { if (!yyjson_is_obj(val)) { ffPrintError(FF_COLORS_MODULE_NAME, 0, NULL, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Invalid %s value: must be an object", unsafe_yyjson_get_str(key)); } else { yyjson_val* width = yyjson_obj_get(val, "width"); if (width) { options->block.width = (uint8_t) yyjson_get_uint(width); } yyjson_val* range = yyjson_obj_get(val, "range"); if (range) { if (!yyjson_is_arr(range) || yyjson_arr_size(range) != 2) { ffPrintError(FF_COLORS_MODULE_NAME, 0, NULL, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Invalid %s.range value: must be an array of 2 elements", unsafe_yyjson_get_str(key)); } else { uint8_t start = (uint8_t) yyjson_get_uint(yyjson_arr_get(range, 0)); uint8_t end = (uint8_t) yyjson_get_uint(yyjson_arr_get(range, 1)); if (start > end) { ffPrintError(FF_COLORS_MODULE_NAME, 0, NULL, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Invalid %s.range value: range[0] > range[1]", unsafe_yyjson_get_str(key)); } else if (end > 15) { ffPrintError(FF_COLORS_MODULE_NAME, 0, NULL, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Invalid %s.range value: range[1] > 15", unsafe_yyjson_get_str(key)); } else { options->block.range[0] = start; options->block.range[1] = end; } } } } continue; } if (unsafe_yyjson_equals_str(key, "brightness")) { int value; const char* error = ffJsonConfigParseEnum(val, &value, (FFKeyValuePair[]) { { "default", FF_COLORS_BRIGHTNESS_DEFAULT }, { "normal", FF_COLORS_BRIGHTNESS_NORMAL }, { "light", FF_COLORS_BRIGHTNESS_LIGHT }, {}, }); if (error) { ffPrintError(FF_COLORS_MODULE_NAME, 0, NULL, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Invalid %s value: %s", unsafe_yyjson_get_str(key), error); } else { options->brightness = (FFColorsBrightness) value; } continue; } ffPrintError(FF_COLORS_MODULE_NAME, 0, NULL, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Unknown JSON key %s", unsafe_yyjson_get_str(key)); } } void ffGenerateColorsJsonConfig(FFColorsOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) { ffJsonConfigGenerateModuleArgsConfig(doc, module, &options->moduleArgs); yyjson_mut_obj_remove_key(module, "format"); // Not supported switch (options->symbol) { case FF_COLORS_SYMBOL_CIRCLE: yyjson_mut_obj_add_str(doc, module, "symbol", "circle"); break; case FF_COLORS_SYMBOL_DIAMOND: yyjson_mut_obj_add_str(doc, module, "symbol", "diamond"); break; case FF_COLORS_SYMBOL_TRIANGLE: yyjson_mut_obj_add_str(doc, module, "symbol", "triangle"); break; case FF_COLORS_SYMBOL_SQUARE: yyjson_mut_obj_add_str(doc, module, "symbol", "square"); break; case FF_COLORS_SYMBOL_STAR: yyjson_mut_obj_add_str(doc, module, "symbol", "star"); break; default: yyjson_mut_obj_add_str(doc, module, "symbol", "block"); break; } yyjson_mut_obj_add_uint(doc, module, "paddingLeft", options->paddingLeft); { yyjson_mut_val* block = yyjson_mut_obj_add_obj(doc, module, "block"); yyjson_mut_obj_add_uint(doc, block, "width", options->block.width); yyjson_mut_val* range = yyjson_mut_obj_add_arr(doc, block, "range"); for (uint8_t i = 0; i < 2; i++) { yyjson_mut_arr_add_uint(doc, range, options->block.range[i]); } } switch (options->brightness) { case FF_COLORS_BRIGHTNESS_NORMAL: yyjson_mut_obj_add_str(doc, module, "brightness", "normal"); break; case FF_COLORS_BRIGHTNESS_LIGHT: yyjson_mut_obj_add_str(doc, module, "brightness", "light"); break; default: yyjson_mut_obj_add_str(doc, module, "brightness", "default"); break; } } void ffInitColorsOptions(FFColorsOptions* options) { ffOptionInitModuleArg(&options->moduleArgs, ""); ffStrbufSetStatic(&options->moduleArgs.key, " "); options->symbol = FF_COLORS_SYMBOL_BACKGROUND; options->paddingLeft = 0; options->block = (FFBlockConfig) { .width = 3, .range = { 0, 15 }, }; options->brightness = FF_COLORS_BRIGHTNESS_DEFAULT; } void ffDestroyColorsOptions(FFColorsOptions* options) { ffOptionDestroyModuleArg(&options->moduleArgs); } FFModuleBaseInfo ffColorsModuleInfo = { .name = FF_COLORS_MODULE_NAME, .description = "Display the terminal's 16-color palette", .initOptions = (void*) ffInitColorsOptions, .destroyOptions = (void*) ffDestroyColorsOptions, .parseJsonObject = (void*) ffParseColorsJsonObject, .printModule = (void*) ffPrintColors, .generateJsonConfig = (void*) ffGenerateColorsJsonConfig, };