summaryrefslogtreecommitdiffstats
path: root/src/modules/colors
diff options
context:
space:
mode:
authorsumuel <samuel@yakubos.org>2026-08-17 20:44:55 +0000
committersumuel <samuel@yakubos.org>2026-08-17 20:44:55 +0000
commit76424950e373d3b04ac3dd13019151bfba3e8423 (patch)
tree4c3cfdbda039e592b9186be3e28f8d7cfd439e8a /src/modules/colors
Add the files
Diffstat (limited to 'src/modules/colors')
-rw-r--r--src/modules/colors/colors.c305
-rw-r--r--src/modules/colors/colors.h11
-rw-r--r--src/modules/colors/option.h35
3 files changed, 351 insertions, 0 deletions
diff --git a/src/modules/colors/colors.c b/src/modules/colors/colors.c
new file mode 100644
index 0000000..ff821d1
--- /dev/null
+++ b/src/modules/colors/colors.c
@@ -0,0 +1,305 @@
+#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,
+};
diff --git a/src/modules/colors/colors.h b/src/modules/colors/colors.h
new file mode 100644
index 0000000..c88ccd7
--- /dev/null
+++ b/src/modules/colors/colors.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include "option.h"
+
+#define FF_COLORS_MODULE_NAME "Colors"
+
+bool ffPrintColors(FFColorsOptions* options);
+void ffInitColorsOptions(FFColorsOptions* options);
+void ffDestroyColorsOptions(FFColorsOptions* options);
+
+extern FFModuleBaseInfo ffColorsModuleInfo;
diff --git a/src/modules/colors/option.h b/src/modules/colors/option.h
new file mode 100644
index 0000000..c9971c7
--- /dev/null
+++ b/src/modules/colors/option.h
@@ -0,0 +1,35 @@
+#pragma once
+
+#include "common/option.h"
+
+typedef enum FF_A_PACKED FFColorsSymbol {
+ FF_COLORS_SYMBOL_BLOCK,
+ FF_COLORS_SYMBOL_BACKGROUND,
+ FF_COLORS_SYMBOL_CIRCLE,
+ FF_COLORS_SYMBOL_DIAMOND,
+ FF_COLORS_SYMBOL_SQUARE,
+ FF_COLORS_SYMBOL_TRIANGLE,
+ FF_COLORS_SYMBOL_STAR,
+} FFColorsSymbol;
+
+typedef struct FFBlockConfig {
+ uint8_t width;
+ uint8_t range[2];
+} FFBlockConfig;
+
+typedef enum FF_A_PACKED FFColorsBrightness {
+ FF_COLORS_BRIGHTNESS_DEFAULT,
+ FF_COLORS_BRIGHTNESS_NORMAL,
+ FF_COLORS_BRIGHTNESS_LIGHT,
+} FFColorsBrightness;
+
+typedef struct FFColorsOptions {
+ FFModuleArgs moduleArgs;
+
+ FFColorsSymbol symbol;
+ uint32_t paddingLeft;
+ FFBlockConfig block;
+ FFColorsBrightness brightness;
+} FFColorsOptions;
+
+static_assert(sizeof(FFColorsOptions) <= FF_OPTION_MAX_SIZE, "FFColorsOptions size exceeds maximum allowed size");