#include "common/printing.h" #include "common/jsonconfig.h" #include "logo/logo.h" #include "modules/colors/colors.h" #include "util/textModifier.h" #include "util/stringUtils.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; } void 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) { // 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 // 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; } for (int i = 8; i >= 1; --i) { if (!instance.config.display.pipe) ffStrbufAppendF(&result, "\e[3%dm", 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"); } } bool ffParseColorsCommandOptions(FFColorsOptions* options, const char* key, const char* value) { const char* subKey = ffOptionTestPrefix(key, FF_COLORS_MODULE_NAME); if (!subKey) return false; if (ffOptionParseModuleArgs(key, subKey, value, &options->moduleArgs)) return true; if (ffStrEqualsIgnCase(subKey, "symbol")) { options->symbol = (FFColorsSymbol) ffOptionParseEnum(key, 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 }, {}, }); return true; } if (ffStrEqualsIgnCase(subKey, "padding-left")) { options->paddingLeft = ffOptionParseUInt32(key, value); return true; } if (ffStrEqualsIgnCase(subKey, "block-width")) { options->block.width = (uint8_t) ffOptionParseUInt32(key, value); return true; } if (ffStrEqualsIgnCase(subKey, "block-range-start")) { options->block.range[0] = min((uint8_t) ffOptionParseUInt32(key, value), 15); return true; } if (ffStrEqualsIgnCase(subKey, "block-range-end")) { options->block.range[1] = min((uint8_t) ffOptionParseUInt32(key, value), 15); return true; } return false; } void ffParseColorsJsonObject(FFColorsOptions* 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; if (ffStrEqualsIgnCase(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", key, error); else options->symbol = (FFColorsSymbol) value; continue; } if (ffStrEqualsIgnCase(key, "paddingLeft")) { options->paddingLeft = (uint32_t) yyjson_get_uint(val); continue; } if (ffStrEqualsIgnCase(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", 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", 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]", 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", key); else { options->block.range[0] = start; options->block.range[1] = end; } } } } continue; } ffPrintError(FF_COLORS_MODULE_NAME, 0, NULL, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Unknown JSON key %s", key); } } void ffGenerateColorsJsonConfig(FFColorsOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) { __attribute__((__cleanup__(ffDestroyColorsOptions))) FFColorsOptions defaultOptions; ffInitColorsOptions(&defaultOptions); ffJsonConfigGenerateModuleArgsConfig(doc, module, &defaultOptions.moduleArgs, &options->moduleArgs); if (defaultOptions.symbol != options->symbol) { 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; } } if (defaultOptions.paddingLeft != options->paddingLeft) yyjson_mut_obj_add_uint(doc, module, "paddingLeft", options->paddingLeft); { yyjson_mut_val* block = yyjson_mut_obj(doc); if (defaultOptions.block.width != options->block.width) yyjson_mut_obj_add_uint(doc, block, "width", options->block.width); if (memcmp(defaultOptions.block.range, options->block.range, sizeof(options->block.range)) != 0) { 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]); } if (yyjson_mut_obj_size(block) > 0) yyjson_mut_obj_add_val(doc, module, "block", block); } } static FFModuleBaseInfo ffModuleInfo = { .name = FF_COLORS_MODULE_NAME, .description = "Print some colored blocks", .parseCommandOptions = (void*) ffParseColorsCommandOptions, .parseJsonObject = (void*) ffParseColorsJsonObject, .printModule = (void*) ffPrintColors, .generateJsonConfig = (void*) ffGenerateColorsJsonConfig, }; void ffInitColorsOptions(FFColorsOptions* options) { options->moduleInfo = ffModuleInfo; ffOptionInitModuleArg(&options->moduleArgs, ""); ffStrbufSetStatic(&options->moduleArgs.key, " "); options->symbol = FF_COLORS_SYMBOL_BACKGROUND; options->paddingLeft = 0; options->block = (FFBlockConfig) { .width = 3, .range = { 0, 15 }, }; } void ffDestroyColorsOptions(FF_MAYBE_UNUSED FFColorsOptions* options) { ffOptionDestroyModuleArg(&options->moduleArgs); }