i3 - improved tiling WM


Support align and min_width module options

Patch status: superseded

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. 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
* expands all module config option definitions to include the new
  options
* the SEC_CLOSE_MAP() macro has been extended 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/442/raw.patch | git am

b/i3status.c

27
@@ -35,6 +35,8 @@
28
 
29
 #define exit_if_null(pointer, ...) { if (pointer == NULL) die(__VA_ARGS__); }
30
 
31
+#define CFG_CUSTOM_ALIGN_OPT CFG_STR("align", NULL, CFGF_NONE)
32
+
33
 #define CFG_COLOR_OPTS(good, degraded, bad) \
34
     CFG_STR("color_good", good, CFGF_NONE), \
35
     CFG_STR("color_degraded", degraded, CFGF_NONE), \
36
@@ -42,6 +44,9 @@
37
 
38
 #define CFG_CUSTOM_COLOR_OPTS CFG_COLOR_OPTS(NULL, NULL, NULL)
39
 
40
+#define CFG_CUSTOM_MIN_WIDTH_OPT \
41
+    CFG_PTR_CB("min_width", "default", CFGF_NONE, parse_min_width, free)
42
+
43
 /* socket file descriptor for general purposes */
44
 int general_socket;
45
 
46
@@ -89,6 +94,17 @@ static char *sstrdup(const char *str) {
47
         return result;
48
 }
49
 
50
+/*
51
+ * Parses the min_width option whose value can either be a string or an integer.
52
+ */
53
+static int parse_min_width(cfg_t *context, cfg_opt_t *option, const char *value, void *result) {
54
+    struct min_width width = { atoi(value), sstrdup(value) };
55
+
56
+    *(void **)result = scalloc(sizeof(struct min_width));
57
+    memcpy(*(void **)result, &width, sizeof(struct min_width));
58
+
59
+    return 0;
60
+}
61
 
62
 /*
63
  * Validates a color in "#RRGGBB" format
64
@@ -219,35 +235,45 @@ int main(int argc, char *argv[]) {
65
         cfg_opt_t run_watch_opts[] = {
66
                 CFG_STR("pidfile", NULL, CFGF_NONE),
67
                 CFG_STR("format", "%title: %status", CFGF_NONE),
68
+                CFG_CUSTOM_ALIGN_OPT,
69
                 CFG_CUSTOM_COLOR_OPTS,
70
+                CFG_CUSTOM_MIN_WIDTH_OPT,
71
                 CFG_END()
72
         };
73
 
74
         cfg_opt_t path_exists_opts[] = {
75
                 CFG_STR("path", NULL, CFGF_NONE),
76
                 CFG_STR("format", "%title: %status", CFGF_NONE),
77
+                CFG_CUSTOM_ALIGN_OPT,
78
                 CFG_CUSTOM_COLOR_OPTS,
79
+                CFG_CUSTOM_MIN_WIDTH_OPT,
80
                 CFG_END()
81
         };
82
 
83
         cfg_opt_t wireless_opts[] = {
84
                 CFG_STR("format_up", "W: (%quality at %essid, %bitrate) %ip", CFGF_NONE),
85
                 CFG_STR("format_down", "W: down", CFGF_NONE),
86
+                CFG_CUSTOM_ALIGN_OPT,
87
                 CFG_CUSTOM_COLOR_OPTS,
88
+                CFG_CUSTOM_MIN_WIDTH_OPT,
89
                 CFG_END()
90
         };
91
 
92
         cfg_opt_t ethernet_opts[] = {
93
                 CFG_STR("format_up", "E: %ip (%speed)", CFGF_NONE),
94
                 CFG_STR("format_down", "E: down", CFGF_NONE),
95
+                CFG_CUSTOM_ALIGN_OPT,
96
                 CFG_CUSTOM_COLOR_OPTS,
97
+                CFG_CUSTOM_MIN_WIDTH_OPT,
98
                 CFG_END()
99
         };
100
 
101
         cfg_opt_t ipv6_opts[] = {
102
                 CFG_STR("format_up", "%ip", CFGF_NONE),
103
                 CFG_STR("format_down", "no IPv6", CFGF_NONE),
104
+                CFG_CUSTOM_ALIGN_OPT,
105
                 CFG_CUSTOM_COLOR_OPTS,
106
+                CFG_CUSTOM_MIN_WIDTH_OPT,
107
                 CFG_END()
108
         };
109
 
110
@@ -260,35 +286,47 @@ int main(int argc, char *argv[]) {
111
                 CFG_BOOL("last_full_capacity", false, CFGF_NONE),
112
                 CFG_BOOL("integer_battery_capacity", false, CFGF_NONE),
113
                 CFG_BOOL("hide_seconds", false, CFGF_NONE),
114
+                CFG_CUSTOM_ALIGN_OPT,
115
                 CFG_CUSTOM_COLOR_OPTS,
116
+                CFG_CUSTOM_MIN_WIDTH_OPT,
117
                 CFG_END()
118
         };
119
 
120
         cfg_opt_t time_opts[] = {
121
                 CFG_STR("format", "%Y-%m-%d %H:%M:%S", CFGF_NONE),
122
+                CFG_CUSTOM_ALIGN_OPT,
123
+                CFG_CUSTOM_MIN_WIDTH_OPT,
124
                 CFG_END()
125
         };
126
 
127
         cfg_opt_t tztime_opts[] = {
128
                 CFG_STR("format", "%Y-%m-%d %H:%M:%S %Z", CFGF_NONE),
129
                 CFG_STR("timezone", "", CFGF_NONE),
130
+                CFG_CUSTOM_ALIGN_OPT,
131
+                CFG_CUSTOM_MIN_WIDTH_OPT,
132
                 CFG_END()
133
         };
134
 
135
         cfg_opt_t ddate_opts[] = {
136
                 CFG_STR("format", "%{%a, %b %d%}, %Y%N - %H", CFGF_NONE),
137
+                CFG_CUSTOM_ALIGN_OPT,
138
+                CFG_CUSTOM_MIN_WIDTH_OPT,
139
                 CFG_END()
140
         };
141
 
142
         cfg_opt_t load_opts[] = {
143
                 CFG_STR("format", "%1min %5min %15min", CFGF_NONE),
144
                 CFG_FLOAT("max_threshold", 5, CFGF_NONE),
145
+                CFG_CUSTOM_ALIGN_OPT,
146
                 CFG_CUSTOM_COLOR_OPTS,
147
+                CFG_CUSTOM_MIN_WIDTH_OPT,
148
                 CFG_END()
149
         };
150
 
151
         cfg_opt_t usage_opts[] = {
152
                 CFG_STR("format", "%usage", CFGF_NONE),
153
+                CFG_CUSTOM_ALIGN_OPT,
154
+                CFG_CUSTOM_MIN_WIDTH_OPT,
155
                 CFG_END()
156
         };
157
 
158
@@ -296,13 +334,17 @@ int main(int argc, char *argv[]) {
159
                 CFG_STR("format", "%degrees C", CFGF_NONE),
160
                 CFG_STR("path", NULL, CFGF_NONE),
161
                 CFG_INT("max_threshold", 75, CFGF_NONE),
162
+                CFG_CUSTOM_ALIGN_OPT,
163
                 CFG_CUSTOM_COLOR_OPTS,
164
+                CFG_CUSTOM_MIN_WIDTH_OPT,
165
                 CFG_END()
166
         };
167
 
168
         cfg_opt_t disk_opts[] = {
169
                 CFG_STR("format", "%free", CFGF_NONE),
170
                 CFG_STR("prefix_type", "binary", CFGF_NONE),
171
+                CFG_CUSTOM_ALIGN_OPT,
172
+                CFG_CUSTOM_MIN_WIDTH_OPT,
173
                 CFG_END()
174
         };
175
 
176
@@ -312,7 +354,9 @@ int main(int argc, char *argv[]) {
177
                 CFG_STR("device", "default", CFGF_NONE),
178
                 CFG_STR("mixer", "Master", CFGF_NONE),
179
                 CFG_INT("mixer_idx", 0, CFGF_NONE),
180
+                CFG_CUSTOM_ALIGN_OPT,
181
                 CFG_CUSTOM_COLOR_OPTS,
182
+                CFG_CUSTOM_MIN_WIDTH_OPT,
183
                 CFG_END()
184
         };
185
 
186
@@ -333,7 +377,9 @@ int main(int argc, char *argv[]) {
187
                 CFG_SEC("ddate", ddate_opts, CFGF_NONE),
188
                 CFG_SEC("load", load_opts, CFGF_NONE),
189
                 CFG_SEC("cpu_usage", usage_opts, CFGF_NONE),
190
+                CFG_CUSTOM_ALIGN_OPT,
191
                 CFG_CUSTOM_COLOR_OPTS,
192
+                CFG_CUSTOM_MIN_WIDTH_OPT,
193
                 CFG_END()
194
         };
195
 

b/include/i3status.h

200
@@ -88,6 +88,24 @@ enum { O_DZEN2, O_XMOBAR, O_I3BAR, O_TERM, O_NONE } output_format;
201
 #define SEC_CLOSE_MAP \
202
 	do { \
203
 		if (output_format == O_I3BAR) { \
204
+			char *_align = cfg_getstr(sec, "align"); \
205
+			if (_align) { \
206
+				yajl_gen_string(json_gen, (const unsigned char *)"align", strlen("align")); \
207
+				yajl_gen_string(json_gen, (const unsigned char *)_align, strlen(_align)); \
208
+			} \
209
+			struct min_width *_width = cfg_getptr(sec, "min_width"); \
210
+			if (strlen(_width->str) > 0 && strcmp(_width->str, "default") != 0) { \
211
+				/* if the value can be parsed as a number we use the numerical value */ \
212
+				if (_width->num != 0) { \
213
+					if (_width->num > 0) { \
214
+						yajl_gen_string(json_gen, (const unsigned char *)"min_width", strlen("min_width")); \
215
+						yajl_gen_integer(json_gen, _width->num); \
216
+					} \
217
+				} else if (strcmp(_width->str, "0") != 0) { \
218
+					yajl_gen_string(json_gen, (const unsigned char *)"min_width", strlen("min_width")); \
219
+					yajl_gen_string(json_gen, (const unsigned char *)_width->str, strlen(_width->str)); \
220
+				} \
221
+			} \
222
 			const char *_sep = cfg_getstr(cfg_general, "separator"); \
223
 			if (strlen(_sep) == 0) {\
224
 				yajl_gen_string(json_gen, (const unsigned char *)"separator", strlen("separator")); \
225
@@ -132,6 +150,11 @@ enum { O_DZEN2, O_XMOBAR, O_I3BAR, O_TERM, O_NONE } output_format;
226
 
227
 typedef enum { CS_DISCHARGING, CS_CHARGING, CS_FULL } charging_status_t;
228
 
229
+struct min_width {
230
+    int num;
231
+    const char *str;
232
+};
233
+
234
 /* src/general.c */
235
 char *skip_character(char *input, char character, int amount);
236
 void die(const char *fmt, ...);

b/man/i3status.man

241
@@ -404,6 +404,34 @@ volume master {
242
 }
243
 -------------------------------------------------------------
244
 
245
+== Universal module options
246
+
247
+When using the i3bar output format, there are a few additional options that
248
+can be used with all modules to customize their appearance:
249
+
250
+min_width::
251
+	The minimum width (in pixels) the module should occupy. If the module
252
+	takes less space than the specified size, the block will be padded to the
253
+	left and/or the right side, according to the defined alignment policy.
254
+	This is useful when you want to prevent the whole status line from shifting
255
+	when values take more or less space between each iteration.
256
+	The option can also be a string. In this case, the width of the given text
257
+	determines the minimum width of the block. This is useful when you want to
258
+	set a sensible minimum width regardless of which font you are using, and at
259
+	what particular size.
260
+align::
261
+	The alignment policy to use when the minimum width is not reached. Either
262
+	+center+ (default), +right+ or +left+.
263
+
264
+*Example configuration*:
265
+-------------------------------------------------------------
266
+disk "/" {
267
+    format = "%avail"
268
+    align = "left"
269
+    min_width = 100
270
+}
271
+-------------------------------------------------------------
272
+
273
 == Using i3status with dzen2
274
 
275
 After installing dzen2, you can directly use it with i3status. Just ensure that