master ohmu/fastfetch-openbsd / src / modules / display / display.c
ohmu  ·  2026-10-15
  1#include "common/printing.h"
  2#include "common/jsonconfig.h"
  3#include "common/size.h"
  4#include "common/strutil.h"
  5#include "detection/displayserver/displayserver.h"
  6#include "modules/display/display.h"
  7
  8#include <math.h>
  9
 10static int sortByNameAsc(const void* a, const void* b) {
 11    return ffStrbufComp(&((const FFDisplayResult*) a)->name, &((const FFDisplayResult*) b)->name);
 12}
 13
 14static int sortByNameDesc(const void* a, const void* b) {
 15    return -ffStrbufComp(&((const FFDisplayResult*) a)->name, &((const FFDisplayResult*) b)->name);
 16}
 17
 18// `ffConnectDisplayServer()` hands out a process-wide result that other modules read too: `Monitor`
 19// prints the same list, and `Brightness` pairs its own readings with these entries by position. It
 20// must therefore not be reordered in place, which is what this module used to do through a cast
 21// that dropped `const`. Sort a copy instead, so both the text and the JSON path get the requested
 22// order without any other consumer seeing it.
 23//
 24// The copy is shallow: the entries keep owning their `FFstrbuf`s through the original result, and
 25// it is never destroyed element-wise, so only the array itself is freed.
 26static FFlist getOrderedDisplays(const FFDisplayOptions* options) {
 27    const FFlist* displays = &ffConnectDisplayServer()->displays;
 28    FFlist result = ffListCreateA(sizeof(FFDisplayResult), displays->length);
 29    if (displays->length > 0) {
 30        memcpy(result.data, displays->data, (size_t) displays->length * sizeof(FFDisplayResult));
 31        result.length = displays->length;
 32    }
 33
 34    if (options->order != FF_DISPLAY_ORDER_NONE) {
 35        ffListSort(&result, sizeof(FFDisplayResult), options->order == FF_DISPLAY_ORDER_ASC ? sortByNameAsc : sortByNameDesc);
 36    }
 37
 38    return result;
 39}
 40
 41bool ffPrintDisplay(FFDisplayOptions* options) {
 42    FF_LIST_AUTO_DESTROY displays = getOrderedDisplays(options);
 43
 44    if (displays.length == 0) {
 45        ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Display), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Couldn't detect display");
 46        return false;
 47    }
 48
 49    if (options->compactType != FF_DISPLAY_COMPACT_TYPE_NONE) {
 50        ffPrintLogoAndKey(FF_MODULE_GET_DISPLAY_NAME(Display), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT);
 51
 52        FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
 53        FF_LIST_FOR_EACH (FFDisplayResult, result, displays) {
 54            if (options->compactType & FF_DISPLAY_COMPACT_TYPE_ORIGINAL_BIT) {
 55                ffStrbufAppendF(&buffer, "%ix%i", result->width, result->height);
 56            } else {
 57                uint32_t scaledWidth = (result->width * 96 + result->dpi / 2) / result->dpi;
 58                uint32_t scaledHeight = (result->height * 96 + result->dpi / 2) / result->dpi;
 59                ffStrbufAppendF(&buffer, "%ix%i", scaledWidth, scaledHeight);
 60            }
 61
 62            if (options->compactType & FF_DISPLAY_COMPACT_TYPE_REFRESH_RATE_BIT) {
 63                if (result->refreshRate > 0) {
 64                    const char* space = instance.config.display.freqSpaceBeforeUnit != FF_SPACE_BEFORE_UNIT_NEVER ? " " : "";
 65                    if (options->preciseRefreshRate) {
 66                        ffStrbufAppendF(&buffer, " @ %g%sHz", result->refreshRate, space);
 67                    } else {
 68                        ffStrbufAppendF(&buffer, " @ %i%sHz", (uint32_t) (result->refreshRate + 0.5), space);
 69                    }
 70                }
 71                ffStrbufAppendS(&buffer, ", ");
 72            } else {
 73                ffStrbufAppendC(&buffer, ' ');
 74            }
 75        }
 76        ffStrbufTrimRight(&buffer, ' ');
 77        ffStrbufTrimRight(&buffer, ',');
 78        ffStrbufPutTo(&buffer, stdout);
 79        return true;
 80    }
 81
 82    FF_STRBUF_AUTO_DESTROY key = ffStrbufCreate();
 83
 84    for (uint32_t i = 0; i < displays.length; i++) {
 85        FFDisplayResult* result = FF_LIST_GET(FFDisplayResult, displays, i);
 86        uint32_t moduleIndex = displays.length == 1 ? 0 : i + 1;
 87        const char* displayType = result->type == FF_DISPLAY_TYPE_UNKNOWN ? nullptr : result->type == FF_DISPLAY_TYPE_BUILTIN ? "Built-in"
 88                                                                                                                           : "External";
 89
 90        ffStrbufClear(&key);
 91        if (options->moduleArgs.key.length == 0) {
 92            if (result->name.length) {
 93                ffStrbufAppendF(&key, "%s (%s)", FF_MODULE_GET_DISPLAY_NAME(Display), result->name.chars);
 94            } else if (moduleIndex > 0) {
 95                ffStrbufAppendF(&key, "%s (%d)", FF_MODULE_GET_DISPLAY_NAME(Display), moduleIndex);
 96            } else {
 97                ffStrbufAppendS(&key, FF_MODULE_GET_DISPLAY_NAME(Display));
 98            }
 99        } else {
100            FF_PARSE_FORMAT_STRING_CHECKED(&key, &options->moduleArgs.key, ((FFformatarg[]) {
101                                                                               FF_ARG(moduleIndex, "index"),
102                                                                               FF_ARG(result->name, "name"),
103                                                                               FF_ARG(displayType, "type"),
104                                                                               FF_ARG(options->moduleArgs.keyIcon, "icon"),
105                                                                               FF_ARG(FF_MODULE_GET_DISPLAY_NAME(Display), "module-name"),
106                                                                           }));
107        }
108
109        FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
110        double inch = sqrt(result->physicalWidth * result->physicalWidth + result->physicalHeight * result->physicalHeight) / 25.4;
111        uint32_t scaledWidth = (result->width * 96 + result->dpi / 2) / result->dpi;
112        uint32_t scaledHeight = (result->height * 96 + result->dpi / 2) / result->dpi;
113        double scaleFactor = (double) result->dpi / 96.;
114
115        if (options->moduleArgs.outputFormat.length == 0) {
116            ffPrintLogoAndKey(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY);
117
118            ffStrbufAppendF(&buffer, "%ix%i", result->width, result->height);
119
120            if (result->dpi != 96) {
121                ffStrbufAppendS(&buffer, " @ ");
122                ffStrbufAppendDouble(&buffer, scaleFactor, instance.config.display.fractionNdigits, instance.config.display.fractionTrailingZeros == FF_FRACTION_TRAILING_ZEROS_TYPE_ALWAYS);
123                ffStrbufAppendC(&buffer, 'x');
124            }
125
126            if (inch > 1) {
127                ffStrbufAppendF(&buffer, " in %i\"", (uint32_t) (inch + 0.5));
128            }
129
130            if (result->refreshRate > 0) {
131                ffStrbufAppendS(&buffer, ", ");
132                if (options->preciseRefreshRate) {
133                    ffStrbufAppendDouble(&buffer, result->refreshRate, 3, false);
134                } else {
135                    ffStrbufAppendSInt(&buffer, (int) (result->refreshRate + 0.5));
136                }
137                ffStrbufAppendS(&buffer, instance.config.display.freqSpaceBeforeUnit == FF_SPACE_BEFORE_UNIT_NEVER ? "Hz" : " Hz");
138            }
139
140            bool flag = false;
141            if (result->type != FF_DISPLAY_TYPE_UNKNOWN) {
142                ffStrbufAppendS(&buffer, result->type == FF_DISPLAY_TYPE_BUILTIN ? " [Built-in" : " [External");
143                flag = true;
144            }
145
146            if (result->hdrStatus == FF_DISPLAY_HDR_STATUS_ENABLED) {
147                ffStrbufAppendS(&buffer, flag ? ", HDR" : " [HDR");
148                flag = true;
149            }
150
151            if (flag) {
152                ffStrbufAppendS(&buffer, "]");
153            }
154
155            if (moduleIndex > 0 && result->primary) {
156                ffStrbufAppendS(&buffer, " *");
157            }
158
159            ffStrbufPutTo(&buffer, stdout);
160            ffStrbufClear(&buffer);
161        } else {
162            double ppi = inch == 0 ? 0 : sqrt(result->width * result->width + result->height * result->height) / inch;
163            bool hdrEnabled = result->hdrStatus == FF_DISPLAY_HDR_STATUS_ENABLED;
164            bool hdrCompatible = result->hdrStatus == FF_DISPLAY_HDR_STATUS_SUPPORTED || result->hdrStatus == FF_DISPLAY_HDR_STATUS_ENABLED;
165            uint32_t iInch = (uint32_t) (inch + 0.5), iPpi = (uint32_t) (ppi + 0.5);
166
167            char refreshRate[16];
168            if (result->refreshRate > 0) {
169                if (options->preciseRefreshRate) {
170                    snprintf(refreshRate, ARRAY_SIZE(refreshRate), "%g", ((int) (result->refreshRate * 1000 + 0.5)) / 1000.0);
171                } else {
172                    snprintf(refreshRate, ARRAY_SIZE(refreshRate), "%i", (uint32_t) (result->refreshRate + 0.5));
173                }
174            } else {
175                refreshRate[0] = 0;
176            }
177
178            char preferredRefreshRate[16];
179            if (result->preferredRefreshRate > 0) {
180                if (options->preciseRefreshRate) {
181                    snprintf(preferredRefreshRate, ARRAY_SIZE(preferredRefreshRate), "%g", ((int) (result->preferredRefreshRate * 1000 + 0.5)) / 1000.0);
182                } else {
183                    snprintf(preferredRefreshRate, ARRAY_SIZE(preferredRefreshRate), "%i", (uint32_t) (result->preferredRefreshRate + 0.5));
184                }
185            } else {
186                preferredRefreshRate[0] = 0;
187            }
188
189            FF_PRINT_FORMAT_CHECKED(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY, ((FFformatarg[]) {
190                                                                                                         FF_ARG(result->width, "width"),
191                                                                                                         FF_ARG(result->height, "height"),
192                                                                                                         FF_ARG(refreshRate, "refresh-rate"),
193                                                                                                         FF_ARG(scaledWidth, "scaled-width"),
194                                                                                                         FF_ARG(scaledHeight, "scaled-height"),
195                                                                                                         FF_ARG(result->name, "name"),
196                                                                                                         FF_ARG(displayType, "type"),
197                                                                                                         FF_ARG(result->rotation, "rotation"),
198                                                                                                         FF_ARG(result->primary, "is-primary"),
199                                                                                                         FF_ARG(result->physicalWidth, "physical-width"),
200                                                                                                         FF_ARG(result->physicalHeight, "physical-height"),
201                                                                                                         FF_ARG(iInch, "inch"),
202                                                                                                         FF_ARG(iPpi, "ppi"),
203                                                                                                         FF_ARG(result->bitDepth, "bit-depth"),
204                                                                                                         FF_ARG(hdrEnabled, "hdr-enabled"),
205                                                                                                         FF_ARG(result->manufactureYear, "manufacture-year"),
206                                                                                                         FF_ARG(result->manufactureWeek, "manufacture-week"),
207                                                                                                         FF_ARG(result->serial, "serial"),
208                                                                                                         FF_ARG(result->platformApi, "platform-api"),
209                                                                                                         FF_ARG(hdrCompatible, "hdr-compatible"),
210                                                                                                         FF_ARG(scaleFactor, "scale-factor"),
211                                                                                                         FF_ARG(result->preferredWidth, "preferred-width"),
212                                                                                                         FF_ARG(result->preferredHeight, "preferred-height"),
213                                                                                                         FF_ARG(preferredRefreshRate, "preferred-refresh-rate"),
214                                                                                                         FF_ARG(result->dpi, "dpi"),
215                                                                                                     }));
216        }
217    }
218
219    return true;
220}
221
222void ffParseDisplayJsonObject(FFDisplayOptions* options, yyjson_val* module) {
223    yyjson_val *key, *val;
224    size_t idx, max;
225    yyjson_obj_foreach (module, idx, max, key, val) {
226        if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs)) {
227            continue;
228        }
229
230        if (unsafe_yyjson_equals_str(key, "compactType")) {
231            if (yyjson_is_null(val)) {
232                options->compactType = FF_DISPLAY_COMPACT_TYPE_NONE;
233            } else {
234                int value;
235                const char* error = ffJsonConfigParseEnum(val, &value, (FFKeyValuePair[]) {
236                                                                           { "none", FF_DISPLAY_COMPACT_TYPE_NONE },
237                                                                           { "original", FF_DISPLAY_COMPACT_TYPE_ORIGINAL_BIT },
238                                                                           { "scaled", FF_DISPLAY_COMPACT_TYPE_SCALED_BIT },
239                                                                           { "original-with-refresh-rate", FF_DISPLAY_COMPACT_TYPE_ORIGINAL_BIT | FF_DISPLAY_COMPACT_TYPE_REFRESH_RATE_BIT },
240                                                                           { "scaled-with-refresh-rate", FF_DISPLAY_COMPACT_TYPE_SCALED_BIT | FF_DISPLAY_COMPACT_TYPE_REFRESH_RATE_BIT },
241                                                                           {},
242                                                                       });
243                if (error) {
244                    ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Display), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Invalid %s value: %s", unsafe_yyjson_get_str(key), error);
245                } else {
246                    options->compactType = (FFDisplayCompactType) value;
247                }
248            }
249            continue;
250        }
251
252        if (unsafe_yyjson_equals_str(key, "preciseRefreshRate")) {
253            options->preciseRefreshRate = yyjson_get_bool(val);
254            continue;
255        }
256
257        if (unsafe_yyjson_equals_str(key, "order")) {
258            if (yyjson_is_null(val)) {
259                options->order = FF_DISPLAY_ORDER_NONE;
260            } else {
261                int value;
262                const char* error = ffJsonConfigParseEnum(val, &value, (FFKeyValuePair[]) {
263                                                                           { "asc", FF_DISPLAY_ORDER_ASC },
264                                                                           { "desc", FF_DISPLAY_ORDER_DESC },
265                                                                           { "none", FF_DISPLAY_ORDER_NONE },
266                                                                           {},
267                                                                       });
268                if (error) {
269                    ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Display), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Invalid %s value: %s", unsafe_yyjson_get_str(key), error);
270                } else {
271                    options->order = (FFDisplayOrder) value;
272                }
273            }
274            continue;
275        }
276
277        ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Display), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key));
278    }
279}
280
281[[gnu::cold]]
282void ffGenerateDisplayJsonConfig(FFDisplayOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) {
283    ffJsonConfigGenerateModuleArgsConfig(doc, module, &options->moduleArgs);
284
285    switch ((int) options->compactType) {
286        case FF_DISPLAY_COMPACT_TYPE_NONE:
287            yyjson_mut_obj_add_str(doc, module, "compactType", "none");
288            break;
289        case FF_DISPLAY_COMPACT_TYPE_ORIGINAL_BIT:
290            yyjson_mut_obj_add_str(doc, module, "compactType", "original");
291            break;
292        case FF_DISPLAY_COMPACT_TYPE_SCALED_BIT:
293            yyjson_mut_obj_add_str(doc, module, "compactType", "scaled");
294            break;
295        case FF_DISPLAY_COMPACT_TYPE_ORIGINAL_BIT | FF_DISPLAY_COMPACT_TYPE_REFRESH_RATE_BIT:
296            yyjson_mut_obj_add_str(doc, module, "compactType", "original-with-refresh-rate");
297            break;
298        case FF_DISPLAY_COMPACT_TYPE_SCALED_BIT | FF_DISPLAY_COMPACT_TYPE_REFRESH_RATE_BIT:
299            yyjson_mut_obj_add_str(doc, module, "compactType", "scaled-with-refresh-rate");
300            break;
301    }
302
303    yyjson_mut_obj_add_bool(doc, module, "preciseRefreshRate", options->preciseRefreshRate);
304
305    switch (options->order) {
306        case FF_DISPLAY_ORDER_NONE:
307            yyjson_mut_obj_add_null(doc, module, "order");
308            break;
309        case FF_DISPLAY_ORDER_ASC:
310            yyjson_mut_obj_add_str(doc, module, "order", "asc");
311            break;
312        case FF_DISPLAY_ORDER_DESC:
313            yyjson_mut_obj_add_str(doc, module, "order", "desc");
314            break;
315    }
316}
317
318bool ffGenerateDisplayJsonResult(FFDisplayOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) {
319    FF_LIST_AUTO_DESTROY displays = getOrderedDisplays(options);
320
321    if (displays.length == 0) {
322        yyjson_mut_obj_add_str(doc, module, "error", "Couldn't detect display");
323        return false;
324    }
325
326    yyjson_mut_val* arr = yyjson_mut_obj_add_arr(doc, module, "result");
327    FF_LIST_FOR_EACH (FFDisplayResult, item, displays) {
328        yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr);
329        yyjson_mut_obj_add_uint(doc, obj, "id", item->id);
330        yyjson_mut_obj_add_strbuf(doc, obj, "name", &item->name);
331        yyjson_mut_obj_add_bool(doc, obj, "primary", item->primary);
332
333        yyjson_mut_val* output = yyjson_mut_obj_add_obj(doc, obj, "output");
334        yyjson_mut_obj_add_uint(doc, output, "width", item->width);
335        yyjson_mut_obj_add_uint(doc, output, "height", item->height);
336        yyjson_mut_obj_add_real(doc, output, "refreshRate", item->refreshRate);
337
338        if (item->drrStatus == FF_DISPLAY_DRR_STATUS_UNKNOWN) {
339            yyjson_mut_obj_add_null(doc, output, "drrStatus");
340        } else {
341            switch (item->drrStatus) {
342                case FF_DISPLAY_DRR_STATUS_DISABLED:
343                    yyjson_mut_obj_add_str(doc, output, "drrStatus", "Disabled");
344                    break;
345                case FF_DISPLAY_DRR_STATUS_ENABLED:
346                    yyjson_mut_obj_add_str(doc, output, "drrStatus", "Enabled");
347                    break;
348                default:
349                    yyjson_mut_obj_add_str(doc, output, "drrStatus", "Unknown");
350                    break;
351            }
352        }
353        yyjson_mut_obj_add_uint(doc, output, "dpi", item->dpi);
354
355        uint32_t scaledWidth = (item->width * 96 + item->dpi / 2) / item->dpi;
356        uint32_t scaledHeight = (item->height * 96 + item->dpi / 2) / item->dpi;
357        yyjson_mut_val* scaled = yyjson_mut_obj_add_obj(doc, obj, "scaled");
358        yyjson_mut_obj_add_uint(doc, scaled, "width", scaledWidth);
359        yyjson_mut_obj_add_uint(doc, scaled, "height", scaledHeight);
360
361        yyjson_mut_val* preferred = yyjson_mut_obj_add_obj(doc, obj, "preferred");
362        yyjson_mut_obj_add_uint(doc, preferred, "width", item->preferredWidth);
363        yyjson_mut_obj_add_uint(doc, preferred, "height", item->preferredHeight);
364        yyjson_mut_obj_add_real(doc, preferred, "refreshRate", item->preferredRefreshRate);
365
366        yyjson_mut_val* physical = yyjson_mut_obj_add_obj(doc, obj, "physical");
367        yyjson_mut_obj_add_uint(doc, physical, "width", item->physicalWidth);
368        yyjson_mut_obj_add_uint(doc, physical, "height", item->physicalHeight);
369
370        yyjson_mut_obj_add_uint(doc, obj, "rotation", item->rotation);
371        yyjson_mut_obj_add_uint(doc, obj, "bitDepth", item->bitDepth);
372        if (item->hdrStatus == FF_DISPLAY_HDR_STATUS_UNKNOWN) {
373            yyjson_mut_obj_add_null(doc, obj, "hdrStatus");
374        } else {
375            switch (item->hdrStatus) {
376                case FF_DISPLAY_HDR_STATUS_UNSUPPORTED:
377                    yyjson_mut_obj_add_str(doc, obj, "hdrStatus", "Unsupported");
378                    break;
379                case FF_DISPLAY_HDR_STATUS_SUPPORTED:
380                    yyjson_mut_obj_add_str(doc, obj, "hdrStatus", "Supported");
381                    break;
382                case FF_DISPLAY_HDR_STATUS_ENABLED:
383                    yyjson_mut_obj_add_str(doc, obj, "hdrStatus", "Enabled");
384                    break;
385                default:
386                    yyjson_mut_obj_add_str(doc, obj, "hdrStatus", "Unknown");
387                    break;
388            }
389        }
390
391        switch (item->type) {
392            case FF_DISPLAY_TYPE_BUILTIN:
393                yyjson_mut_obj_add_str(doc, obj, "type", "Builtin");
394                break;
395            case FF_DISPLAY_TYPE_EXTERNAL:
396                yyjson_mut_obj_add_str(doc, obj, "type", "External");
397                break;
398            default:
399                yyjson_mut_obj_add_str(doc, obj, "type", "Unknown");
400                break;
401        }
402
403        if (item->manufactureYear) {
404            yyjson_mut_val* manufactureDate = yyjson_mut_obj_add_obj(doc, obj, "manufactureDate");
405            yyjson_mut_obj_add_uint(doc, manufactureDate, "year", item->manufactureYear);
406            yyjson_mut_obj_add_uint(doc, manufactureDate, "week", item->manufactureWeek);
407        } else {
408            yyjson_mut_obj_add_null(doc, obj, "manufactureDate");
409        }
410
411        if (item->serial.length) {
412            yyjson_mut_obj_add_strbuf(doc, obj, "serial", &item->serial);
413        } else {
414            yyjson_mut_obj_add_null(doc, obj, "serial");
415        }
416
417        yyjson_mut_obj_add_str(doc, obj, "platformApi", item->platformApi);
418    }
419
420    return true;
421}
422
423void ffInitDisplayOptions(FFDisplayOptions* options) {
424    ffOptionInitModuleArg(&options->moduleArgs, "󰍹");
425    options->compactType = FF_DISPLAY_COMPACT_TYPE_NONE;
426    options->preciseRefreshRate = false;
427    options->order = FF_DISPLAY_ORDER_NONE;
428}
429
430void ffDestroyDisplayOptions(FFDisplayOptions* options) {
431    ffOptionDestroyModuleArg(&options->moduleArgs);
432}
433
434FFModuleBaseInfo ffDisplayModuleInfo = {
435    .name = "Display",
436    .description = "Print resolutions, refresh rates, etc",
437    .displayName = {
438        .en = "Display",
439        .ar = "الشاشة",
440        .cs = "Displej",
441        .de = "Anzeige",
442        .es = "Pantalla",
443        .fi = "Näyttö",
444        .fr = "Écran",
445        .gl = "Pantalla",
446        .he = "תצוגה",
447        .id = "Tampilan",
448        .it = "Display",
449        .ja = "ディスプレイ",
450        .ko = "디스플레이",
451        .pl = "Wyświetlacz",
452        .pt = "Tela",
453        .ru = "Дисплей",
454        .tr = "Ekran",
455        .uk = "Дисплей",
456        .vi = "Hiển thị",
457        .zh_CN = "显示器",
458        .zh_TW = "顯示器",
459    },
460    .initOptions = (void*) ffInitDisplayOptions,
461    .destroyOptions = (void*) ffDestroyDisplayOptions,
462    .parseJsonObject = (void*) ffParseDisplayJsonObject,
463    .printModule = (void*) ffPrintDisplay,
464    .generateJsonResult = (void*) ffGenerateDisplayJsonResult,
465    .generateJsonConfig = (void*) ffGenerateDisplayJsonConfig,
466    .formatArgs = FF_FORMAT_ARG_LIST(((FFModuleFormatArg[]) {
467        { "Screen configured width (in pixels)", "width" },
468        { "Screen configured height (in pixels)", "height" },
469        { "Screen configured refresh rate (in Hz)", "refresh-rate" },
470        { "Screen scaled width (in pixels)", "scaled-width" },
471        { "Screen scaled height (in pixels)", "scaled-height" },
472        { "Screen name *", "name" },
473        { "Screen type (Built-in or External) *", "type" },
474        { "Screen rotation (in degrees)", "rotation" },
475        { "True if being the primary screen", "is-primary" },
476        { "Screen physical width (in millimeters)", "physical-width" },
477        { "Screen physical height (in millimeters)", "physical-height" },
478        { "Physical diagonal length in inches", "inch" },
479        { "Pixels per inch (PPI)", "ppi" },
480        { "Bits per color channel", "bit-depth" },
481        { "True if high dynamic range (HDR) mode is enabled", "hdr-enabled" },
482        { "Year of manufacturing", "manufacture-year" },
483        { "Nth week of manufacturing in the year", "manufacture-week" },
484        { "Serial number", "serial" },
485        { "The platform API used when detecting the display", "platform-api" },
486        { "True if the display is HDR compatible", "hdr-compatible" },
487        { "HiDPI scale factor", "scale-factor" },
488        { "Screen preferred width (in pixels)", "preferred-width" },
489        { "Screen preferred height (in pixels)", "preferred-height" },
490        { "Screen preferred refresh rate (in Hz)", "preferred-refresh-rate" },
491        { "DPI", "dpi" },
492    })),
493    .defaultOrder = 17,
494};