ohmu
·
2026-10-15
1#pragma once
2
3#include "common/FFstrbuf.h"
4
5struct yyjson_val;
6struct yyjson_mut_doc;
7struct yyjson_mut_val;
8
9typedef struct FFModuleFormatArg {
10 const char* desc;
11 const char* name;
12} FFModuleFormatArg;
13
14typedef struct FFModuleFormatArgList {
15 FFModuleFormatArg* args;
16 uint32_t count;
17} FFModuleFormatArgList;
18
19#define FF_FORMAT_ARG_LIST(list) { .args = list, .count = sizeof(list) / sizeof(FFModuleFormatArg) }
20
21typedef struct FFModuleDisplayName {
22 const char* en; // English
23
24 const char* ar; // Arabic
25 const char* cs; // Czech
26 const char* de; // German
27 const char* es; // Spanish
28 const char* fi; // Finnish
29 const char* fr; // French
30 const char* gl; // Galician
31 const char* he; // Hebrew
32 const char* id; // Indonesian
33 const char* it; // Italian
34 const char* ja; // Japanese
35 const char* ko; // Korean
36 const char* pl; // Polish
37 const char* pt; // (Brazilian) Portuguese
38 const char* ru; // Russian
39 const char* tr; // Turkish
40 const char* uk; // Ukrainian
41 const char* vi; // Vietnamese
42 const char* zh_CN; // Simplified Chinese
43 const char* zh_TW; // Traditional Chinese
44} FFModuleDisplayName;
45
46// Must be the first field of FFModuleOptions
47typedef struct FFModuleBaseInfo {
48 const char* name;
49 const char* description;
50 FFModuleDisplayName displayName;
51 // A dirty polymorphic implementation in C.
52 // This is UB, because `void*` is not compatible with `FF*Options*`.
53 // However we can't do it better unless we move to C++, so that `option` becomes a `this` pointer
54 // https://stackoverflow.com/questions/559581/casting-a-function-pointer-to-another-type
55
56 void (*initOptions)(void* options);
57 void (*destroyOptions)(void* options);
58 void (*parseJsonObject)(void* options, struct yyjson_val* module);
59 bool (*printModule)(void* options); // true on success
60 bool (*generateJsonResult)(void* options, struct yyjson_mut_doc* doc, struct yyjson_mut_val* module); // true on success
61 void (*generateJsonConfig)(void* options, struct yyjson_mut_doc* doc, struct yyjson_mut_val* obj);
62 FFModuleFormatArgList formatArgs;
63 const uint8_t defaultOrder;
64} FFModuleBaseInfo;
65
66typedef enum FFModuleKeyType: uint8_t {
67 FF_MODULE_KEY_TYPE_NONE = 0,
68 FF_MODULE_KEY_TYPE_STRING = 1 << 0,
69 FF_MODULE_KEY_TYPE_ICON = 1 << 1,
70 FF_MODULE_KEY_TYPE_SPACE_SHIFT = 4,
71 FF_MODULE_KEY_TYPE_BOTH_0 = FF_MODULE_KEY_TYPE_STRING | FF_MODULE_KEY_TYPE_ICON,
72 FF_MODULE_KEY_TYPE_BOTH_1 = FF_MODULE_KEY_TYPE_BOTH_0 | (1 << FF_MODULE_KEY_TYPE_SPACE_SHIFT),
73 FF_MODULE_KEY_TYPE_BOTH = FF_MODULE_KEY_TYPE_BOTH_1, // alias
74 FF_MODULE_KEY_TYPE_BOTH_2 = FF_MODULE_KEY_TYPE_BOTH_0 | (2 << FF_MODULE_KEY_TYPE_SPACE_SHIFT),
75 FF_MODULE_KEY_TYPE_BOTH_3 = FF_MODULE_KEY_TYPE_BOTH_0 | (3 << FF_MODULE_KEY_TYPE_SPACE_SHIFT),
76 FF_MODULE_KEY_TYPE_BOTH_4 = FF_MODULE_KEY_TYPE_BOTH_0 | (4 << FF_MODULE_KEY_TYPE_SPACE_SHIFT),
77} FFModuleKeyType;
78
79typedef struct FFModuleArgs {
80 FFstrbuf key;
81 FFstrbuf keyColor;
82 FFstrbuf keyIcon;
83 FFstrbuf outputFormat;
84 FFstrbuf outputColor;
85 uint32_t keyWidth;
86} FFModuleArgs;
87
88typedef struct FFKeyValuePair {
89 const char* key;
90 int value;
91} FFKeyValuePair;
92
93// `moduleName` and `argumentKey` are both dereferenced unconditionally; reads no global state, so `pure`
94[[gnu::nonnull(1, 2), gnu::pure, nodiscard]] const char* ffOptionTestPrefix(const char* argumentKey, const char* moduleName);
95// `value` is optional (null exits with a usage error), `argumentKey` and `buffer` are not
96[[gnu::nonnull(1, 3)]] void ffOptionParseString(const char* argumentKey, const char* value, FFstrbuf* buffer);
97[[gnu::nonnull(1), nodiscard]] uint32_t ffOptionParseUInt32(const char* argumentKey, const char* value);
98[[gnu::nonnull(1), nodiscard]] int32_t ffOptionParseInt32(const char* argumentKey, const char* value);
99// `requestedKey` is optional (null exits with a usage error); `pairs` is walked until its null key
100[[gnu::nonnull(1, 3), nodiscard]] int ffOptionParseEnum(const char* argumentKey, const char* requestedKey, FFKeyValuePair pairs[]);
101// `str` is optional: a null or empty value is reported as `true`
102[[gnu::pure, nodiscard]] bool ffOptionParseBoolean(const char* str);
103// `value` is optional (a null or empty value is a no-op); `buffer` is not
104[[gnu::nonnull(2)]] void ffOptionParseColorNoClear(const char* value, FFstrbuf* buffer);
105[[gnu::nonnull(2)]] static inline void ffOptionParseColor(const char* value, FFstrbuf* buffer) {
106 ffStrbufClear(buffer);
107 ffOptionParseColorNoClear(value, buffer);
108}
109
110// `icon` is optional, `args` is not
111[[gnu::nonnull(1)]] static inline void ffOptionInitModuleArg(FFModuleArgs* args, const char* icon) {
112 ffStrbufInit(&args->key);
113 ffStrbufInit(&args->keyColor);
114 ffStrbufInitStatic(&args->keyIcon, icon);
115 ffStrbufInit(&args->outputFormat);
116 ffStrbufInit(&args->outputColor);
117 args->keyWidth = 0;
118}
119
120[[gnu::nonnull(1)]] static inline void ffOptionDestroyModuleArg(FFModuleArgs* args) {
121 ffStrbufDestroy(&args->key);
122 ffStrbufDestroy(&args->keyColor);
123 ffStrbufDestroy(&args->keyIcon);
124 ffStrbufDestroy(&args->outputFormat);
125 ffStrbufDestroy(&args->outputColor);
126}
127
128enum { FF_OPTION_MAX_SIZE = 1 << 8 }; // Maximum size of a single option value, used for static allocation
129
130#define FF_MODULE_GET_DISPLAY_NAME(moduleName) (*(const char**) ((uint8_t*) &ff ## moduleName ## ModuleInfo.displayName + instance.config.display.keyLanguage))