Support align and min_width module options
Patch status: needinfo
Patch by Marco Hunsicker
Long description:
This patch enables users to define "align" and "min_width" options right in the i3status module config sections. Specifically this patch: * Adds macros for the two new options that are used in the option definitions. As the min_width option can take either a string or a number, a custom type has been added along with a corresponding callback function that parses the provided value (and provides input validation). The align option also uses a callback for input validation * Expands all module config option definitions to include the new options * Extends the SEC_CLOSE_MAP() macro to generate the JSON for the new options as necessary * Updates the manpage to explain the new options
To apply this patch, use:
curl http://cr.i3wm.org/patch/447/raw.patch | git am
b/i3status.c
29 |
@@ -11,6 +11,7 @@ |
30 |
* See file LICENSE for license information. |
31 |
* |
32 |
*/ |
33 |
+#include <limits.h> |
34 |
#include <string.h> |
35 |
#include <stdio.h> |
36 |
#include <stdbool.h> |
37 |
@@ -35,6 +36,9 @@ |
38 |
|
39 |
#define exit_if_null(pointer, ...) { if (pointer == NULL) die(__VA_ARGS__); } |
40 |
|
41 |
+#define CFG_CUSTOM_ALIGN_OPT \ |
42 |
+ CFG_STR_CB("align", NULL, CFGF_NONE, parse_align) |
43 |
+ |
44 |
#define CFG_COLOR_OPTS(good, degraded, bad) \ |
45 |
CFG_STR("color_good", good, CFGF_NONE), \ |
46 |
CFG_STR("color_degraded", degraded, CFGF_NONE), \ |
47 |
@@ -42,6 +46,9 @@ |
48 |
|
49 |
#define CFG_CUSTOM_COLOR_OPTS CFG_COLOR_OPTS(NULL, NULL, NULL) |
50 |
|
51 |
+#define CFG_CUSTOM_MIN_WIDTH_OPT \ |
52 |
+ CFG_PTR_CB("min_width", NULL, CFGF_NONE, parse_min_width, free) |
53 |
+ |
54 |
/* socket file descriptor for general purposes */ |
55 |
int general_socket; |
56 |
|
57 |
@@ -89,6 +96,45 @@ static char *sstrdup(const char *str) { |
58 |
return result; |
59 |
} |
60 |
|
61 |
+/* |
62 |
+ * Parses the "align" module option (to validate input). |
63 |
+ */ |
64 |
+static int parse_align(cfg_t *context, cfg_opt_t *option, const char *value, void *result) { |
65 |
+ if (strcmp(value, "center") != 0 && strcmp(value,"left") != 0 && strcmp(value, "right") != 0) |
66 |
+ die("Invalid alignment attribute found in section %s, line %d: \"%s\"\nValid attributes are: left, center, right\n", context->name, context->line, value); |
67 |
+ |
68 |
+ char **cresult = result; |
69 |
+ *cresult = sstrdup(value); |
70 |
+ |
71 |
+ return 0; |
72 |
+} |
73 |
+ |
74 |
+/* |
75 |
+ * Parses the "min_width" module option whose value can either be a string or an integer. |
76 |
+ */ |
77 |
+static int parse_min_width(cfg_t *context, cfg_opt_t *option, const char *value, void *result) { |
78 |
+ char *end; |
79 |
+ long num = strtol(value, &end, 10); |
80 |
+ |
81 |
+ if (num < 0) |
82 |
+ die("Invalid min_width attribute found in section %s, line %d: %d\nExpected positive integer or string\n", context->name, context->line, num); |
83 |
+ else if (num == LONG_MIN || num == LONG_MAX || (end && *end != '\0')) |
84 |
+ num = 0; |
85 |
+ |
86 |
+ if (strlen(value) == 0) |
87 |
+ die("Empty min_width attribute found in section %s, line %d\nExpected positive integer or non-empty string\n", context->name, context->line); |
88 |
+ |
89 |
+ if (strcmp(value, "0") == 0) |
90 |
+ die("Invalid min_width attribute found in section %s, line %d: \"%s\"\nExpected positive integer or string\n", context->name, context->line, value); |
91 |
+ |
92 |
+ struct min_width *parsed = scalloc(sizeof(struct min_width)); |
93 |
+ parsed->str = sstrdup(value); |
94 |
+ parsed->num = num; |
95 |
+ |
96 |
+ *(void **)result = parsed; |
97 |
+ |
98 |
+ return 0; |
99 |
+} |
100 |
|
101 |
/* |
102 |
* Validates a color in "#RRGGBB" format |
103 |
@@ -219,35 +265,45 @@ int main(int argc, char *argv[]) { |
104 |
cfg_opt_t run_watch_opts[] = { |
105 |
CFG_STR("pidfile", NULL, CFGF_NONE), |
106 |
CFG_STR("format", "%title: %status", CFGF_NONE), |
107 |
+ CFG_CUSTOM_ALIGN_OPT, |
108 |
CFG_CUSTOM_COLOR_OPTS, |
109 |
+ CFG_CUSTOM_MIN_WIDTH_OPT, |
110 |
CFG_END() |
111 |
}; |
112 |
|
113 |
cfg_opt_t path_exists_opts[] = { |
114 |
CFG_STR("path", NULL, CFGF_NONE), |
115 |
CFG_STR("format", "%title: %status", CFGF_NONE), |
116 |
+ CFG_CUSTOM_ALIGN_OPT, |
117 |
CFG_CUSTOM_COLOR_OPTS, |
118 |
+ CFG_CUSTOM_MIN_WIDTH_OPT, |
119 |
CFG_END() |
120 |
}; |
121 |
|
122 |
cfg_opt_t wireless_opts[] = { |
123 |
CFG_STR("format_up", "W: (%quality at %essid, %bitrate) %ip", CFGF_NONE), |
124 |
CFG_STR("format_down", "W: down", CFGF_NONE), |
125 |
+ CFG_CUSTOM_ALIGN_OPT, |
126 |
CFG_CUSTOM_COLOR_OPTS, |
127 |
+ CFG_CUSTOM_MIN_WIDTH_OPT, |
128 |
CFG_END() |
129 |
}; |
130 |
|
131 |
cfg_opt_t ethernet_opts[] = { |
132 |
CFG_STR("format_up", "E: %ip (%speed)", CFGF_NONE), |
133 |
CFG_STR("format_down", "E: down", CFGF_NONE), |
134 |
+ CFG_CUSTOM_ALIGN_OPT, |
135 |
CFG_CUSTOM_COLOR_OPTS, |
136 |
+ CFG_CUSTOM_MIN_WIDTH_OPT, |
137 |
CFG_END() |
138 |
}; |
139 |
|
140 |
cfg_opt_t ipv6_opts[] = { |
141 |
CFG_STR("format_up", "%ip", CFGF_NONE), |
142 |
CFG_STR("format_down", "no IPv6", CFGF_NONE), |
143 |
+ CFG_CUSTOM_ALIGN_OPT, |
144 |
CFG_CUSTOM_COLOR_OPTS, |
145 |
+ CFG_CUSTOM_MIN_WIDTH_OPT, |
146 |
CFG_END() |
147 |
}; |
148 |
|
149 |
@@ -260,35 +316,47 @@ int main(int argc, char *argv[]) { |
150 |
CFG_BOOL("last_full_capacity", false, CFGF_NONE), |
151 |
CFG_BOOL("integer_battery_capacity", false, CFGF_NONE), |
152 |
CFG_BOOL("hide_seconds", false, CFGF_NONE), |
153 |
+ CFG_CUSTOM_ALIGN_OPT, |
154 |
CFG_CUSTOM_COLOR_OPTS, |
155 |
+ CFG_CUSTOM_MIN_WIDTH_OPT, |
156 |
CFG_END() |
157 |
}; |
158 |
|
159 |
cfg_opt_t time_opts[] = { |
160 |
CFG_STR("format", "%Y-%m-%d %H:%M:%S", CFGF_NONE), |
161 |
+ CFG_CUSTOM_ALIGN_OPT, |
162 |
+ CFG_CUSTOM_MIN_WIDTH_OPT, |
163 |
CFG_END() |
164 |
}; |
165 |
|
166 |
cfg_opt_t tztime_opts[] = { |
167 |
CFG_STR("format", "%Y-%m-%d %H:%M:%S %Z", CFGF_NONE), |
168 |
CFG_STR("timezone", "", CFGF_NONE), |
169 |
+ CFG_CUSTOM_ALIGN_OPT, |
170 |
+ CFG_CUSTOM_MIN_WIDTH_OPT, |
171 |
CFG_END() |
172 |
}; |
173 |
|
174 |
cfg_opt_t ddate_opts[] = { |
175 |
CFG_STR("format", "%{%a, %b %d%}, %Y%N - %H", CFGF_NONE), |
176 |
+ CFG_CUSTOM_ALIGN_OPT, |
177 |
+ CFG_CUSTOM_MIN_WIDTH_OPT, |
178 |
CFG_END() |
179 |
}; |
180 |
|
181 |
cfg_opt_t load_opts[] = { |
182 |
CFG_STR("format", "%1min %5min %15min", CFGF_NONE), |
183 |
CFG_FLOAT("max_threshold", 5, CFGF_NONE), |
184 |
+ CFG_CUSTOM_ALIGN_OPT, |
185 |
CFG_CUSTOM_COLOR_OPTS, |
186 |
+ CFG_CUSTOM_MIN_WIDTH_OPT, |
187 |
CFG_END() |
188 |
}; |
189 |
|
190 |
cfg_opt_t usage_opts[] = { |
191 |
CFG_STR("format", "%usage", CFGF_NONE), |
192 |
+ CFG_CUSTOM_ALIGN_OPT, |
193 |
+ CFG_CUSTOM_MIN_WIDTH_OPT, |
194 |
CFG_END() |
195 |
}; |
196 |
|
197 |
@@ -296,13 +364,17 @@ int main(int argc, char *argv[]) { |
198 |
CFG_STR("format", "%degrees C", CFGF_NONE), |
199 |
CFG_STR("path", NULL, CFGF_NONE), |
200 |
CFG_INT("max_threshold", 75, CFGF_NONE), |
201 |
+ CFG_CUSTOM_ALIGN_OPT, |
202 |
CFG_CUSTOM_COLOR_OPTS, |
203 |
+ CFG_CUSTOM_MIN_WIDTH_OPT, |
204 |
CFG_END() |
205 |
}; |
206 |
|
207 |
cfg_opt_t disk_opts[] = { |
208 |
CFG_STR("format", "%free", CFGF_NONE), |
209 |
CFG_STR("prefix_type", "binary", CFGF_NONE), |
210 |
+ CFG_CUSTOM_ALIGN_OPT, |
211 |
+ CFG_CUSTOM_MIN_WIDTH_OPT, |
212 |
CFG_END() |
213 |
}; |
214 |
|
215 |
@@ -312,7 +384,9 @@ int main(int argc, char *argv[]) { |
216 |
CFG_STR("device", "default", CFGF_NONE), |
217 |
CFG_STR("mixer", "Master", CFGF_NONE), |
218 |
CFG_INT("mixer_idx", 0, CFGF_NONE), |
219 |
+ CFG_CUSTOM_ALIGN_OPT, |
220 |
CFG_CUSTOM_COLOR_OPTS, |
221 |
+ CFG_CUSTOM_MIN_WIDTH_OPT, |
222 |
CFG_END() |
223 |
}; |
224 |
|
b/include/i3status.h
229 |
@@ -88,6 +88,22 @@ enum { O_DZEN2, O_XMOBAR, O_I3BAR, O_TERM, O_NONE } output_format; |
230 |
#define SEC_CLOSE_MAP \ |
231 |
do { \ |
232 |
if (output_format == O_I3BAR) { \ |
233 |
+ char *_align = cfg_getstr(sec, "align"); \ |
234 |
+ if (_align) { \ |
235 |
+ yajl_gen_string(json_gen, (const unsigned char *)"align", strlen("align")); \ |
236 |
+ yajl_gen_string(json_gen, (const unsigned char *)_align, strlen(_align)); \ |
237 |
+ } \ |
238 |
+ struct min_width *_width = cfg_getptr(sec, "min_width"); \ |
239 |
+ if (_width) { \ |
240 |
+ /* if the value can be parsed as a number, we use the numerical value */ \ |
241 |
+ if (_width->num > 0) { \ |
242 |
+ yajl_gen_string(json_gen, (const unsigned char *)"min_width", strlen("min_width")); \ |
243 |
+ yajl_gen_integer(json_gen, _width->num); \ |
244 |
+ } else { \ |
245 |
+ yajl_gen_string(json_gen, (const unsigned char *)"min_width", strlen("min_width")); \ |
246 |
+ yajl_gen_string(json_gen, (const unsigned char *)_width->str, strlen(_width->str)); \ |
247 |
+ } \ |
248 |
+ } \ |
249 |
const char *_sep = cfg_getstr(cfg_general, "separator"); \ |
250 |
if (strlen(_sep) == 0) {\ |
251 |
yajl_gen_string(json_gen, (const unsigned char *)"separator", strlen("separator")); \ |
252 |
@@ -132,6 +148,14 @@ enum { O_DZEN2, O_XMOBAR, O_I3BAR, O_TERM, O_NONE } output_format; |
253 |
|
254 |
typedef enum { CS_DISCHARGING, CS_CHARGING, CS_FULL } charging_status_t; |
255 |
|
256 |
+/* |
257 |
+ * The "min_width" module option may either be defined as a string or a number. |
258 |
+ */ |
259 |
+struct min_width { |
260 |
+ long num; |
261 |
+ const char *str; |
262 |
+}; |
263 |
+ |
264 |
/* src/general.c */ |
265 |
char *skip_character(char *input, char character, int amount); |
266 |
void die(const char *fmt, ...); |
b/man/i3status.man
271 |
@@ -404,6 +404,35 @@ volume master { |
272 |
} |
273 |
------------------------------------------------------------- |
274 |
|
275 |
+== Universal module options |
276 |
+ |
277 |
+When using the i3bar output format, there are a few additional options that |
278 |
+can be used with all modules to customize their appearance: |
279 |
+ |
280 |
+align:: |
281 |
+ The alignment policy to use when the minimum width (see below) is not |
282 |
+ reached. Either +center+ (default), +right+ or +left+. |
283 |
+min_width:: |
284 |
+ The minimum width (in pixels) the module should occupy. If the module takes |
285 |
+ less space than the specified size, the block will be padded to the left |
286 |
+ and/or the right side, according to the defined alignment policy. This is |
287 |
+ useful when you want to prevent the whole status line from shifting when |
288 |
+ values take more or less space between each iteration. |
289 |
+ The option can also be a string. In this case, the width of the given text |
290 |
+ determines the minimum width of the block. This is useful when you want to |
291 |
+ set a sensible minimum width regardless of which font you are using, and at |
292 |
+ what particular size. Please note that a number enclosed with quotes will |
293 |
+ still be treated as a number. |
294 |
+ |
295 |
+*Example configuration*: |
296 |
+------------------------------------------------------------- |
297 |
+disk "/" { |
298 |
+ format = "%avail" |
299 |
+ align = "left" |
300 |
+ min_width = 100 |
301 |
+} |
302 |
+------------------------------------------------------------- |
303 |
+ |
304 |
== Using i3status with dzen2 |
305 |
|
306 |
After installing dzen2, you can directly use it with i3status. Just ensure that |