master ohmu/fastfetch-openbsd / src / modules / loadavg / loadavg.c
ohmu  ·  2026-10-15
  1#include "common/printing.h"
  2#include "common/jsonconfig.h"
  3#include "common/percent.h"
  4#include "common/strutil.h"
  5#include "detection/loadavg/loadavg.h"
  6#include "detection/cpu/cpu.h"
  7#include "modules/loadavg/loadavg.h"
  8
  9bool ffPrintLoadavg(FFLoadavgOptions* options) {
 10    double result[3] = { 0.0 / 0.0, 0.0 / 0.0, 0.0 / 0.0 };
 11
 12    const char* error = ffDetectLoadavg(result);
 13    if (error) {
 14        ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Loadavg), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error);
 15        return false;
 16    }
 17
 18    if (options->moduleArgs.outputFormat.length == 0) {
 19        if (options->compact) {
 20            ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Loadavg), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT);
 21            printf("%.*f, %.*f, %.*f\n", options->ndigits, result[0], options->ndigits, result[1], options->ndigits, result[2]);
 22        } else {
 23            FFCPUResult cpu = {
 24                .temperature = FF_CPU_TEMP_UNSET,
 25                .frequencyMax = 0,
 26                .frequencyBase = 0,
 27                .name = ffStrbufCreate(),
 28                .vendor = ffStrbufCreate(),
 29            };
 30            ffDetectCPU(&(FFCPUOptions) {}, &cpu);
 31
 32            FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
 33            for (uint32_t index = 0; index < 3; index++) {
 34                uint32_t duration = index == 0 ? 1 : index == 1 ? 5
 35                                                                : 15;
 36                if (options->moduleArgs.key.length == 0) {
 37                    ffStrbufSetF(&buffer, "%s (%d min)", FF_MODULE_GET_DISPLAY_NAME(Loadavg), duration);
 38                } else {
 39                    ffStrbufClear(&buffer);
 40                    FF_PARSE_FORMAT_STRING_CHECKED(&buffer, &options->moduleArgs.key, ((FFformatarg[]) {
 41                                                                                          FF_ARG(index, "index"),
 42                                                                                          FF_ARG(duration, "duration"),
 43                                                                                          FF_ARG(options->moduleArgs.keyIcon, "icon"),
 44                                                                                          FF_ARG(FF_MODULE_GET_DISPLAY_NAME(Loadavg), "module-name"),
 45                                                                                      }));
 46                }
 47
 48                ffPrintLogoAndKey(buffer.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY);
 49
 50                ffStrbufClear(&buffer);
 51                double percent = result[index] * 100 / cpu.coresOnline;
 52                FFPercentageTypeFlags percentType = options->percent.type == 0 ? instance.config.display.percentType : options->percent.type;
 53
 54                if (percentType & FF_PERCENTAGE_TYPE_BAR_BIT) {
 55                    ffPercentAppendBar(&buffer, percent, options->percent, &options->moduleArgs);
 56                }
 57
 58                if (!(percentType & FF_PERCENTAGE_TYPE_HIDE_OTHERS_BIT)) {
 59                    if (buffer.length > 0) {
 60                        ffStrbufAppendC(&buffer, ' ');
 61                    }
 62                    ffStrbufAppendF(&buffer, "%.*f", options->ndigits, result[index]);
 63                }
 64
 65                if (percentType & FF_PERCENTAGE_TYPE_NUM_BIT) {
 66                    if (buffer.length > 0) {
 67                        ffStrbufAppendC(&buffer, ' ');
 68                    }
 69                    ffPercentAppendNum(&buffer, percent, options->percent, buffer.length > 0, &options->moduleArgs);
 70                }
 71
 72                ffStrbufPutTo(&buffer, stdout);
 73            }
 74        }
 75    } else {
 76        FF_PRINT_FORMAT_CHECKED(FF_MODULE_GET_DISPLAY_NAME(Loadavg), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, ((FFformatarg[]) {
 77                                                                                                            FF_ARG(result[0], "loadavg1"),
 78                                                                                                            FF_ARG(result[1], "loadavg2"),
 79                                                                                                            FF_ARG(result[2], "loadavg3"),
 80                                                                                                        }));
 81    }
 82
 83    return true;
 84}
 85
 86void ffParseLoadavgJsonObject(FFLoadavgOptions* options, yyjson_val* module) {
 87    yyjson_val *key, *val;
 88    size_t idx, max;
 89    yyjson_obj_foreach (module, idx, max, key, val) {
 90        if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs)) {
 91            continue;
 92        }
 93
 94        if (unsafe_yyjson_equals_str(key, "ndigits")) {
 95            // The schema documents 0..9 and the value ends up as the precision of `printf("%.*f")`,
 96            // so a wider value is not truncated to fit `uint8_t` silently any more.
 97            uint32_t ndigits = 0;
 98            if (!ffJsonConfigParseUInt32(val, &ndigits, 9)) {
 99                ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Loadavg), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Property 'ndigits' must be an integer between 0 and 9");
100                continue;
101            }
102            options->ndigits = (uint8_t) ndigits;
103            continue;
104        }
105
106        if (unsafe_yyjson_equals_str(key, "compact")) {
107            options->compact = yyjson_get_bool(val);
108            continue;
109        }
110
111        if (ffPercentParseJsonObject(key, val, &options->percent)) {
112            continue;
113        }
114
115        ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Loadavg), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key));
116    }
117}
118
119[[gnu::cold]]
120void ffGenerateLoadavgJsonConfig(FFLoadavgOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) {
121    ffJsonConfigGenerateModuleArgsConfig(doc, module, &options->moduleArgs);
122
123    yyjson_mut_obj_add_uint(doc, module, "ndigits", options->ndigits);
124
125    yyjson_mut_obj_add_bool(doc, module, "compact", options->compact);
126
127    ffPercentGenerateJsonConfig(doc, module, options->percent);
128}
129
130bool ffGenerateLoadavgJsonResult([[maybe_unused]] FFLoadavgOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) {
131    double result[3] = { 0.0 / 0.0, 0.0 / 0.0, 0.0 / 0.0 };
132
133    const char* error = ffDetectLoadavg(result);
134    if (error) {
135        yyjson_mut_obj_add_str(doc, module, "error", error);
136        return false;
137    }
138
139    yyjson_mut_val* arr = yyjson_mut_obj_add_arr(doc, module, "result");
140    for (size_t i = 0; i < 3; i++) {
141        yyjson_mut_arr_add_real(doc, arr, result[i]);
142    }
143
144    return true;
145}
146
147void ffInitLoadavgOptions(FFLoadavgOptions* options) {
148    ffOptionInitModuleArg(&options->moduleArgs, "");
149
150    options->percent = (FFPercentageModuleConfig) { 50, 80, 0 };
151    options->ndigits = 2;
152    options->compact = true;
153}
154
155void ffDestroyLoadavgOptions(FFLoadavgOptions* options) {
156    ffOptionDestroyModuleArg(&options->moduleArgs);
157}
158
159FFModuleBaseInfo ffLoadavgModuleInfo = {
160    .name = "Loadavg",
161    .description = "Print system load averages",
162    .displayName = {
163        .en = "Load Average",
164        .ar = "متوسط الحمل",
165        .cs = "Průměrná zátěž",
166        .de = "Systemlast",
167        .es = "Promedio de carga",
168        .fi = "Kuormituksen Keskiarvo",
169        .fr = "Charge moyenne",
170        .gl = "Carga media",
171        .he = "עומס ממוצע",
172        .id = "Beban Rata-rata",
173        .it = "Carico medio",
174        .ja = "ロードアベレージ",
175        .ko = "시스템 부하 평균",
176        .pl = "Średnie obciążenie",
177        .pt = "Média de carga",
178        .ru = "Средняя нагрузка",
179        .tr = "Sistem Yükü",
180        .uk = "Середнє навантаження",
181        .vi = "Tải trung bình",
182        .zh_CN = "系统负载",
183        .zh_TW = "系統負載",
184    },
185    .initOptions = (void*) ffInitLoadavgOptions,
186    .destroyOptions = (void*) ffDestroyLoadavgOptions,
187    .parseJsonObject = (void*) ffParseLoadavgJsonObject,
188    .printModule = (void*) ffPrintLoadavg,
189    .generateJsonResult = (void*) ffGenerateLoadavgJsonResult,
190    .generateJsonConfig = (void*) ffGenerateLoadavgJsonConfig,
191    .formatArgs = FF_FORMAT_ARG_LIST(((FFModuleFormatArg[]) {
192        { "Load average over 1min", "loadavg1" },
193        { "Load average over 5min", "loadavg2" },
194        { "Load average over 15min", "loadavg3" },
195    })),
196    .defaultOrder = 12,
197};