summaryrefslogtreecommitdiffstats
path: root/src/common/impl/percent.c
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/common/impl/percent.c
Add the files
Diffstat (limited to 'src/common/impl/percent.c')
-rw-r--r--src/common/impl/percent.c293
1 files changed, 293 insertions, 0 deletions
diff --git a/src/common/impl/percent.c b/src/common/impl/percent.c
new file mode 100644
index 0000000..266588c
--- /dev/null
+++ b/src/common/impl/percent.c
@@ -0,0 +1,293 @@
+#include "fastfetch.h"
+#include "common/percent.h"
+#include "common/color.h"
+#include "common/option.h"
+#include "common/jsonconfig.h"
+#include "common/textModifier.h"
+#include "common/strutil.h"
+
+static void appendOutputColor(FFstrbuf* buffer, const FFModuleArgs* module) {
+ if (module->outputColor.length) {
+ ffStrbufAppendF(buffer, "\e[%sm", module->outputColor.chars);
+ } else if (instance.config.display.colorOutput.length) {
+ ffStrbufAppendF(buffer, "\e[%sm", instance.config.display.colorOutput.chars);
+ }
+}
+
+const char* ffPercentParseTypeJsonConfig(yyjson_val* jsonVal, FFPercentageTypeFlags* result) {
+ if (yyjson_is_uint(jsonVal)) {
+ *result = (FFPercentageTypeFlags) yyjson_get_uint(jsonVal);
+ return NULL;
+ }
+ if (yyjson_is_arr(jsonVal)) {
+ FFPercentageTypeFlags flags = 0;
+
+ yyjson_val* item;
+ size_t idx, max;
+ yyjson_arr_foreach (jsonVal, idx, max, item) {
+ const char* flag = yyjson_get_str(item);
+ if (!flag) {
+ return "Error: percent.type: invalid flag string";
+ }
+ if (ffStrEqualsIgnCase(flag, "num")) {
+ flags |= FF_PERCENTAGE_TYPE_NUM_BIT;
+ } else if (ffStrEqualsIgnCase(flag, "bar")) {
+ flags |= FF_PERCENTAGE_TYPE_BAR_BIT;
+ } else if (ffStrEqualsIgnCase(flag, "hide-others")) {
+ flags |= FF_PERCENTAGE_TYPE_HIDE_OTHERS_BIT;
+ } else if (ffStrEqualsIgnCase(flag, "num-color")) {
+ flags |= FF_PERCENTAGE_TYPE_NUM_COLOR_BIT;
+ } else if (ffStrEqualsIgnCase(flag, "bar-monochrome")) {
+ flags |= FF_PERCENTAGE_TYPE_BAR_MONOCHROME_BIT;
+ } else {
+ return "Error: percent.type: unknown flag string";
+ }
+ }
+
+ *result = flags;
+ return NULL;
+ }
+
+ return "Error: usage: percent.type must be a number or an array of strings";
+}
+
+void ffPercentAppendBar(FFstrbuf* buffer, double percent, FFPercentageModuleConfig config, const FFModuleArgs* module) {
+ uint8_t green = config.green, yellow = config.yellow;
+ assert(green <= 100 && yellow <= 100);
+
+ const FFOptionsDisplay* options = &instance.config.display;
+
+ const bool borderAsValue = options->barBorderLeftElapsed.length && options->barBorderRightElapsed.length;
+
+ if (!borderAsValue && options->barBorderLeft.length) {
+ if (!options->pipe && options->barColorBorder.length > 0) {
+ ffStrbufAppendF(buffer, "\e[%sm", options->barColorBorder.chars);
+ }
+ ffStrbufAppend(buffer, &options->barBorderLeft);
+ }
+
+ if (percent == -DBL_MAX) {
+ // Use total color for simplification
+ if (!options->pipe && options->barColorTotal.length > 0) {
+ ffStrbufAppendS(buffer, "\e[" FF_COLOR_FG_LIGHT_BLACK "m");
+ }
+
+ for (uint8_t i = 0; i < options->barWidth; ++i) {
+ ffStrbufAppend(buffer, borderAsValue && i == 0 ? &options->barBorderLeft : borderAsValue && i == options->barWidth - 1 ? &options->barBorderRight
+ : &options->barCharTotal);
+ }
+ } else {
+ const char* colorGreen = options->percentColorGreen.chars;
+ const char* colorYellow = options->percentColorYellow.chars;
+ const char* colorRed = options->percentColorRed.chars;
+
+ FFPercentageTypeFlags percentType = config.type == 0 ? options->percentType : config.type;
+
+ uint8_t blocksPercent = (uint8_t) (percent / 100.0 * options->barWidth + 0.5);
+ assert(blocksPercent <= options->barWidth);
+
+ bool autoColorElapsed = ffStrbufIgnCaseEqualS(&options->barColorElapsed, "auto");
+
+ bool monochrome = (percentType & FF_PERCENTAGE_TYPE_BAR_MONOCHROME_BIT) || !autoColorElapsed;
+ if (!options->pipe && options->barColorElapsed.length > 0 && monochrome) {
+ const char* color = NULL;
+ if (!autoColorElapsed) {
+ color = options->barColorElapsed.chars;
+ } else if (green <= yellow) {
+ if (percent < green) {
+ color = colorGreen;
+ } else if (percent < yellow) {
+ color = colorYellow;
+ } else {
+ color = colorRed;
+ }
+ } else {
+ if (percent < yellow) {
+ color = colorRed;
+ } else if (percent < green) {
+ color = colorYellow;
+ } else {
+ color = colorGreen;
+ }
+ }
+ ffStrbufAppendF(buffer, "\e[%sm", color);
+ }
+ for (uint8_t i = 0; i < blocksPercent; ++i) {
+ if (!options->pipe && options->barColorElapsed.length > 0 && !monochrome) {
+ uint32_t section1Begin = (uint32_t) ((green <= yellow ? green : yellow) / 100.0 * options->barWidth + 0.5);
+ uint32_t section2Begin = (uint32_t) ((green > yellow ? green : yellow) / 100.0 * options->barWidth + 0.5);
+ if (i == section2Begin) {
+ ffStrbufAppendF(buffer, "\e[%sm", (green > yellow ? colorGreen : colorRed));
+ } else if (i == section1Begin) {
+ ffStrbufAppendF(buffer, "\e[%sm", colorYellow);
+ } else if (i == 0) {
+ ffStrbufAppendF(buffer, "\e[%sm", (green <= yellow ? colorGreen : colorRed));
+ }
+ }
+ ffStrbufAppend(buffer, borderAsValue && i == 0 ? &options->barBorderLeftElapsed : borderAsValue && i == options->barWidth - 1 ? &options->barBorderRightElapsed
+ : &options->barCharElapsed);
+ }
+
+ if (blocksPercent < options->barWidth) {
+ if (!options->pipe && options->barColorTotal.length > 0) {
+ ffStrbufAppendF(buffer, "\e[%sm", options->barColorTotal.chars);
+ }
+ for (uint8_t i = blocksPercent; i < options->barWidth; ++i) {
+ ffStrbufAppend(buffer, borderAsValue && i == 0 ? &options->barBorderLeft : borderAsValue && i == options->barWidth - 1 ? &options->barBorderRight
+ : &options->barCharTotal);
+ }
+ }
+ }
+
+ if (!borderAsValue && options->barBorderRight.length) {
+ if (!options->pipe && options->barColorBorder.length > 0) {
+ ffStrbufAppendF(buffer, "\e[%sm", options->barColorBorder.chars);
+ }
+ ffStrbufAppend(buffer, &options->barBorderRight);
+ }
+
+ if (!options->pipe && (options->barColorElapsed.length > 0 || options->barColorTotal.length > 0 || options->barColorBorder.length > 0)) {
+ ffStrbufAppendS(buffer, FASTFETCH_TEXT_MODIFIER_RESET);
+ appendOutputColor(buffer, module);
+ }
+}
+
+void ffPercentAppendNum(FFstrbuf* buffer, double percent, FFPercentageModuleConfig config, bool parentheses, const FFModuleArgs* module) {
+ uint8_t green = config.green, yellow = config.yellow;
+ assert(green <= 100 && yellow <= 100);
+
+ const FFOptionsDisplay* options = &instance.config.display;
+ FFPercentageTypeFlags percentType = config.type == 0 ? options->percentType : config.type;
+
+ bool colored = !!(percentType & FF_PERCENTAGE_TYPE_NUM_COLOR_BIT);
+
+ if (parentheses) {
+ ffStrbufAppendC(buffer, '(');
+ }
+
+ if (colored && !options->pipe) {
+ const char* colorGreen = options->percentColorGreen.chars;
+ const char* colorYellow = options->percentColorYellow.chars;
+ const char* colorRed = options->percentColorRed.chars;
+
+ if (percent == -DBL_MAX) {
+ ffStrbufAppendS(buffer, "\e[" FF_COLOR_FG_LIGHT_BLACK "m");
+ } else if (green <= yellow) {
+ if (percent > yellow) {
+ ffStrbufAppendF(buffer, "\e[%sm", colorRed);
+ } else if (percent > green) {
+ ffStrbufAppendF(buffer, "\e[%sm", colorYellow);
+ } else {
+ ffStrbufAppendF(buffer, "\e[%sm", colorGreen);
+ }
+ } else {
+ if (percent < yellow) {
+ ffStrbufAppendF(buffer, "\e[%sm", colorRed);
+ } else if (percent < green) {
+ ffStrbufAppendF(buffer, "\e[%sm", colorYellow);
+ } else {
+ ffStrbufAppendF(buffer, "\e[%sm", colorGreen);
+ }
+ }
+ }
+ if (percent == -DBL_MAX) {
+ ffStrbufAppendS(buffer, "-");
+ } else {
+ ffStrbufAppendF(buffer, "%*.*f%s%%", options->percentWidth, options->percentNdigits, percent, options->percentSpaceBeforeUnit == FF_SPACE_BEFORE_UNIT_ALWAYS ? " " : "");
+ }
+
+ if (colored && !options->pipe) {
+ ffStrbufAppendS(buffer, FASTFETCH_TEXT_MODIFIER_RESET);
+ appendOutputColor(buffer, module);
+ }
+
+ if (parentheses) {
+ ffStrbufAppendC(buffer, ')');
+ }
+}
+
+bool ffPercentParseCommandOptions(const char* key, const char* subkey, const char* value, FFPercentageModuleConfig* config) {
+ if (!ffStrStartsWithIgnCase(subkey, "percent-")) {
+ return false;
+ }
+
+ subkey += strlen("percent-");
+
+ if (ffStrEqualsIgnCase(subkey, "green")) {
+ uint32_t num = ffOptionParseUInt32(key, value);
+ if (num > 100) {
+ fprintf(stderr, "Error: usage: %s must be between 0 and 100\n", key);
+ exit(480);
+ }
+ config->green = (uint8_t) num;
+ return true;
+ }
+
+ if (ffStrEqualsIgnCase(subkey, "yellow")) {
+ uint32_t num = ffOptionParseUInt32(key, value);
+ if (num > 100) {
+ fprintf(stderr, "Error: usage: %s must be between 0 and 100\n", key);
+ exit(480);
+ }
+ config->yellow = (uint8_t) num;
+ return true;
+ }
+
+ if (ffStrEqualsIgnCase(subkey, "type")) {
+ config->type = (FFPercentageTypeFlags) ffOptionParseUInt32(key, value);
+ return true;
+ }
+
+ return false;
+}
+
+bool ffPercentParseJsonObject(yyjson_val* key, yyjson_val* value, FFPercentageModuleConfig* config) {
+ assert(key);
+
+ if (!unsafe_yyjson_equals_str(key, "percent")) {
+ return false;
+ }
+
+ if (!yyjson_is_obj(value)) {
+ fprintf(stderr, "Error: usage: %s must be an object\n", unsafe_yyjson_get_str(key));
+ exit(480);
+ }
+
+ yyjson_val* greenVal = yyjson_obj_get(value, "green");
+ if (greenVal) {
+ int num = yyjson_get_int(greenVal);
+ if (num < 0 || num > 100) {
+ fputs("Error: usage: percent.green must be between 0 and 100\n", stderr);
+ exit(480);
+ }
+ config->green = (uint8_t) num;
+ }
+
+ yyjson_val* yellowVal = yyjson_obj_get(value, "yellow");
+ if (yellowVal) {
+ int num = yyjson_get_int(yellowVal);
+ if (num < 0 || num > 100) {
+ fputs("Error: usage: percent.yellow must be between 0 and 100\n", stderr);
+ exit(480);
+ }
+ config->yellow = (uint8_t) num;
+ }
+
+ yyjson_val* typeVal = yyjson_obj_get(value, "type");
+ if (typeVal) {
+ const char* error = ffPercentParseTypeJsonConfig(typeVal, &config->type);
+ if (error) {
+ fputs(error, stderr);
+ exit(480);
+ }
+ }
+
+ return true;
+}
+
+void ffPercentGenerateJsonConfig(yyjson_mut_doc* doc, yyjson_mut_val* module, FFPercentageModuleConfig config) {
+ yyjson_mut_val* percent = yyjson_mut_obj_add_obj(doc, module, "percent");
+ yyjson_mut_obj_add_uint(doc, percent, "green", config.green);
+ yyjson_mut_obj_add_uint(doc, percent, "yellow", config.yellow);
+ yyjson_mut_obj_add_uint(doc, percent, "type", config.type);
+}