]> git.sur5r.net Git - i3/i3/blob - i3bar/src/config.c
423dbc691a45d00622bc3c63b968cfe59cfc3d27
[i3/i3] / i3bar / src / config.c
1 /*
2  * vim:ts=4:sw=4:expandtab
3  *
4  * i3bar - an xcb-based status- and ws-bar for i3
5  * © 2010 Axel Wagner and contributors (see also: LICENSE)
6  *
7  * config.c: Parses the configuration (received from i3).
8  *
9  */
10 #include <string.h>
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include <errno.h>
14 #include <i3/ipc.h>
15 #include <yajl/yajl_parse.h>
16 #include <yajl/yajl_version.h>
17
18 #include <X11/Xlib.h>
19
20 #include "common.h"
21
22 static char *cur_key;
23 static bool parsing_mouse_commands;
24
25 /*
26  * Parse a key.
27  *
28  * Essentially we just save it in cur_key.
29  *
30  */
31 static int config_map_key_cb(void *params_, const unsigned char *keyVal, size_t keyLen) {
32     FREE(cur_key);
33
34     cur_key = smalloc(sizeof(unsigned char) * (keyLen + 1));
35     strncpy(cur_key, (const char *)keyVal, keyLen);
36     cur_key[keyLen] = '\0';
37
38     if (strcmp(cur_key, "mouse_commands") == 0)
39         parsing_mouse_commands = true;
40
41     return 1;
42 }
43
44 static int config_end_map_cb(void *params_) {
45     parsing_mouse_commands = false;
46     return 1;
47 }
48
49 /*
50  * Parse a null value (current_workspace)
51  *
52  */
53 static int config_null_cb(void *params_) {
54     if (!strcmp(cur_key, "id")) {
55         /* If 'id' is NULL, the bar config was not found. Error out. */
56         ELOG("No such bar config. Use 'i3-msg -t get_bar_config' to get the available configs.\n");
57         ELOG("Are you starting i3bar by hand? You should not:\n");
58         ELOG("Configure a 'bar' block in your i3 config and i3 will launch i3bar automatically.\n");
59         exit(EXIT_FAILURE);
60     }
61
62     return 1;
63 }
64
65 /*
66  * Parse a string
67  *
68  */
69 static int config_string_cb(void *params_, const unsigned char *val, size_t _len) {
70     int len = (int)_len;
71     /* The id and socket_path are ignored, we already know them. */
72     if (!strcmp(cur_key, "id") || !strcmp(cur_key, "socket_path"))
73         return 1;
74
75     if (parsing_mouse_commands) {
76         int button = atoi(cur_key + sizeof("button") - 1);
77
78         mouse_command_t *current;
79         TAILQ_FOREACH(current, &(config.mouse_commands), commands) {
80             if (current->button == button) {
81                 FREE(current->command);
82                 sasprintf(&(current->command), "%.*s", len, val);
83                 return 1;
84             }
85         }
86
87         mouse_command_t *command = scalloc(sizeof(mouse_command_t));
88         command->button = button;
89         sasprintf(&(command->command), "%.*s", len, val);
90         TAILQ_INSERT_TAIL(&(config.mouse_commands), command, commands);
91         return 1;
92     }
93
94     if (!strcmp(cur_key, "mode")) {
95         DLOG("mode = %.*s, len = %d\n", len, val, len);
96         config.hide_on_modifier = (len == 4 && !strncmp((const char *)val, "dock", strlen("dock")) ? M_DOCK
97                                                                                                    : (len == 4 && !strncmp((const char *)val, "hide", strlen("hide")) ? M_HIDE
98                                                                                                                                                                       : M_INVISIBLE));
99         return 1;
100     }
101
102     if (!strcmp(cur_key, "hidden_state")) {
103         DLOG("hidden_state = %.*s, len = %d\n", len, val, len);
104         config.hidden_state = (len == 4 && !strncmp((const char *)val, "hide", strlen("hide")) ? S_HIDE : S_SHOW);
105         return 1;
106     }
107
108     if (!strcmp(cur_key, "modifier")) {
109         DLOG("modifier = %.*s\n", len, val);
110         if (len == 5 && !strncmp((const char *)val, "shift", strlen("shift"))) {
111             config.modifier = ShiftMask;
112             return 1;
113         }
114         if (len == 4 && !strncmp((const char *)val, "ctrl", strlen("ctrl"))) {
115             config.modifier = ControlMask;
116             return 1;
117         }
118         if (len == 4 && !strncmp((const char *)val, "Mod", strlen("Mod"))) {
119             switch (val[3]) {
120                 case '1':
121                     config.modifier = Mod1Mask;
122                     return 1;
123                 case '2':
124                     config.modifier = Mod2Mask;
125                     return 1;
126                 case '3':
127                     config.modifier = Mod3Mask;
128                     return 1;
129                 /*
130                 case '4':
131                     config.modifier = Mod4Mask;
132                     return 1;
133                 */
134                 case '5':
135                     config.modifier = Mod5Mask;
136                     return 1;
137             }
138         }
139         config.modifier = Mod4Mask;
140         return 1;
141     }
142
143     /* This key was sent in <= 4.10.2. We keep it around to avoid breakage for
144      * users updating from that version and restarting i3bar before i3. */
145     if (!strcmp(cur_key, "wheel_up_cmd")) {
146         DLOG("wheel_up_cmd = %.*s\n", len, val);
147         binding_t *binding = scalloc(sizeof(binding_t));
148         binding->input_code = 4;
149         sasprintf(&(binding->command), "%.*s", len, val);
150         TAILQ_INSERT_TAIL(&(config.bindings), binding, bindings);
151         return 1;
152     }
153
154     /* This key was sent in <= 4.10.2. We keep it around to avoid breakage for
155      * users updating from that version and restarting i3bar before i3. */
156     if (!strcmp(cur_key, "wheel_down_cmd")) {
157         DLOG("wheel_down_cmd = %.*s\n", len, val);
158         binding_t *binding = scalloc(sizeof(binding_t));
159         binding->input_code = 5;
160         sasprintf(&(binding->command), "%.*s", len, val);
161         TAILQ_INSERT_TAIL(&(config.bindings), binding, bindings);
162         return 1;
163     }
164
165     if (!strcmp(cur_key, "position")) {
166         DLOG("position = %.*s\n", len, val);
167         config.position = (len == 3 && !strncmp((const char *)val, "top", strlen("top")) ? POS_TOP : POS_BOT);
168         return 1;
169     }
170
171     if (!strcmp(cur_key, "status_command")) {
172         DLOG("command = %.*s\n", len, val);
173         sasprintf(&config.command, "%.*s", len, val);
174         return 1;
175     }
176
177     if (!strcmp(cur_key, "font")) {
178         DLOG("font = %.*s\n", len, val);
179         sasprintf(&config.fontname, "%.*s", len, val);
180         return 1;
181     }
182
183     if (!strcmp(cur_key, "separator_symbol")) {
184         DLOG("separator = %.*s\n", len, val);
185         I3STRING_FREE(config.separator_symbol);
186         config.separator_symbol = i3string_from_utf8_with_length((const char *)val, len);
187         return 1;
188     }
189
190     if (!strcmp(cur_key, "outputs")) {
191         DLOG("+output %.*s\n", len, val);
192         int new_num_outputs = config.num_outputs + 1;
193         config.outputs = srealloc(config.outputs, sizeof(char *) * new_num_outputs);
194         sasprintf(&config.outputs[config.num_outputs], "%.*s", len, val);
195         config.num_outputs = new_num_outputs;
196         return 1;
197     }
198
199     if (!strcmp(cur_key, "tray_output")) {
200         DLOG("tray_output %.*s\n", len, val);
201         FREE(config.tray_output);
202         sasprintf(&config.tray_output, "%.*s", len, val);
203         return 1;
204     }
205
206 #define COLOR(json_name, struct_name)                                  \
207     do {                                                               \
208         if (!strcmp(cur_key, #json_name)) {                            \
209             DLOG(#json_name " = " #struct_name " = %.*s\n", len, val); \
210             sasprintf(&(config.colors.struct_name), "%.*s", len, val); \
211             return 1;                                                  \
212         }                                                              \
213     } while (0)
214
215     COLOR(statusline, bar_fg);
216     COLOR(background, bar_bg);
217     COLOR(separator, sep_fg);
218     COLOR(focused_workspace_border, focus_ws_border);
219     COLOR(focused_workspace_bg, focus_ws_bg);
220     COLOR(focused_workspace_text, focus_ws_fg);
221     COLOR(active_workspace_border, active_ws_border);
222     COLOR(active_workspace_bg, active_ws_bg);
223     COLOR(active_workspace_text, active_ws_fg);
224     COLOR(inactive_workspace_border, inactive_ws_border);
225     COLOR(inactive_workspace_bg, inactive_ws_bg);
226     COLOR(inactive_workspace_text, inactive_ws_fg);
227     COLOR(urgent_workspace_border, urgent_ws_border);
228     COLOR(urgent_workspace_bg, urgent_ws_bg);
229     COLOR(urgent_workspace_text, urgent_ws_fg);
230     COLOR(binding_mode_border, binding_mode_border);
231     COLOR(binding_mode_bg, binding_mode_bg);
232     COLOR(binding_mode_text, binding_mode_fg);
233
234     printf("got unexpected string %.*s for cur_key = %s\n", len, val, cur_key);
235
236     return 0;
237 }
238
239 /*
240  * Parse a boolean value
241  *
242  */
243 static int config_boolean_cb(void *params_, int val) {
244     if (!strcmp(cur_key, "binding_mode_indicator")) {
245         DLOG("binding_mode_indicator = %d\n", val);
246         config.disable_binding_mode_indicator = !val;
247         return 1;
248     }
249
250     if (!strcmp(cur_key, "workspace_buttons")) {
251         DLOG("workspace_buttons = %d\n", val);
252         config.disable_ws = !val;
253         return 1;
254     }
255
256     if (!strcmp(cur_key, "strip_workspace_numbers")) {
257         DLOG("strip_workspace_numbers = %d\n", val);
258         config.strip_ws_numbers = val;
259         return 1;
260     }
261
262     if (!strcmp(cur_key, "verbose")) {
263         DLOG("verbose = %d\n", val);
264         config.verbose = val;
265         return 1;
266     }
267
268     return 0;
269 }
270
271 /* A datastructure to pass all these callbacks to yajl */
272 static yajl_callbacks outputs_callbacks = {
273     .yajl_null = config_null_cb,
274     .yajl_boolean = config_boolean_cb,
275     .yajl_string = config_string_cb,
276     .yajl_end_map = config_end_map_cb,
277     .yajl_map_key = config_map_key_cb,
278 };
279
280 /*
281  * Start parsing the received bar configuration JSON string
282  *
283  */
284 void parse_config_json(char *json) {
285     yajl_handle handle;
286     yajl_status state;
287     handle = yajl_alloc(&outputs_callbacks, NULL, NULL);
288
289     TAILQ_INIT(&(config.mouse_commands));
290
291     state = yajl_parse(handle, (const unsigned char *)json, strlen(json));
292
293     /* FIXME: Proper error handling for JSON parsing */
294     switch (state) {
295         case yajl_status_ok:
296             break;
297         case yajl_status_client_canceled:
298         case yajl_status_error:
299             ELOG("Could not parse config reply!\n");
300             exit(EXIT_FAILURE);
301             break;
302     }
303
304     yajl_free(handle);
305 }
306
307 /*
308  * free()s the color strings as soon as they are not needed anymore.
309  *
310  */
311 void free_colors(struct xcb_color_strings_t *colors) {
312 #define FREE_COLOR(x)    \
313     do {                 \
314         FREE(colors->x); \
315     } while (0)
316     FREE_COLOR(bar_fg);
317     FREE_COLOR(bar_bg);
318     FREE_COLOR(sep_fg);
319     FREE_COLOR(active_ws_fg);
320     FREE_COLOR(active_ws_bg);
321     FREE_COLOR(active_ws_border);
322     FREE_COLOR(inactive_ws_fg);
323     FREE_COLOR(inactive_ws_bg);
324     FREE_COLOR(inactive_ws_border);
325     FREE_COLOR(urgent_ws_fg);
326     FREE_COLOR(urgent_ws_bg);
327     FREE_COLOR(urgent_ws_border);
328     FREE_COLOR(focus_ws_fg);
329     FREE_COLOR(focus_ws_bg);
330     FREE_COLOR(focus_ws_border);
331     FREE_COLOR(binding_mode_fg);
332     FREE_COLOR(binding_mode_bg);
333     FREE_COLOR(binding_mode_border);
334 #undef FREE_COLOR
335 }