]> git.sur5r.net Git - i3/i3/blob - src/config.c
e8596c7331281e0c5dd5ab1327054c71e6d44125
[i3/i3] / src / config.c
1 #undef I3__FILE__
2 #define I3__FILE__ "config.c"
3 /*
4  * vim:ts=4:sw=4:expandtab
5  *
6  * i3 - an improved dynamic tiling window manager
7  * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
8  *
9  * config.c: Configuration file (calling the parser (src/config_parser.c) with
10  *           the correct path, switching key bindings mode).
11  *
12  */
13 #include "all.h"
14
15 /* We need Xlib for XStringToKeysym */
16 #include <X11/Xlib.h>
17
18 char *current_configpath = NULL;
19 Config config;
20 struct modes_head modes;
21 struct barconfig_head barconfigs = TAILQ_HEAD_INITIALIZER(barconfigs);
22
23 /**
24  * Ungrabs all keys, to be called before re-grabbing the keys because of a
25  * mapping_notify event or a configuration file reload
26  *
27  */
28 void ungrab_all_keys(xcb_connection_t *conn) {
29     DLOG("Ungrabbing all keys\n");
30     xcb_ungrab_key(conn, XCB_GRAB_ANY, root, XCB_BUTTON_MASK_ANY);
31 }
32
33 /*
34  * Returns a pointer to the Binding with the specified modifiers and keycode
35  * or NULL if no such binding exists.
36  *
37  */
38 Binding *get_binding(uint16_t modifiers, bool key_release, xcb_keycode_t keycode) {
39     Binding *bind;
40
41     if (!key_release) {
42         /* On a KeyPress event, we first reset all
43          * B_UPON_KEYRELEASE_IGNORE_MODS bindings back to B_UPON_KEYRELEASE */
44         TAILQ_FOREACH(bind, bindings, bindings) {
45             if (bind->release == B_UPON_KEYRELEASE_IGNORE_MODS)
46                 bind->release = B_UPON_KEYRELEASE;
47         }
48     }
49
50     TAILQ_FOREACH(bind, bindings, bindings) {
51         /* First compare the modifiers (unless this is a
52          * B_UPON_KEYRELEASE_IGNORE_MODS binding and this is a KeyRelease
53          * event) */
54         if (bind->mods != modifiers &&
55             (bind->release != B_UPON_KEYRELEASE_IGNORE_MODS ||
56              !key_release))
57             continue;
58
59         /* If a symbol was specified by the user, we need to look in
60          * the array of translated keycodes for the event’s keycode */
61         if (bind->symbol != NULL) {
62             if (memmem(bind->translated_to,
63                        bind->number_keycodes * sizeof(xcb_keycode_t),
64                        &keycode, sizeof(xcb_keycode_t)) == NULL)
65                 continue;
66         } else {
67             /* This case is easier: The user specified a keycode */
68             if (bind->keycode != keycode)
69                 continue;
70         }
71
72         /* If this keybinding is a KeyRelease binding, it matches the key which
73          * the user pressed. We therefore mark it as
74          * B_UPON_KEYRELEASE_IGNORE_MODS for later, so that the user can
75          * release the modifiers before the actual key and the KeyRelease will
76          * still be matched. */
77         if (bind->release == B_UPON_KEYRELEASE && !key_release)
78             bind->release = B_UPON_KEYRELEASE_IGNORE_MODS;
79
80         /* Check if the binding is for a KeyPress or a KeyRelease event */
81         if ((bind->release == B_UPON_KEYPRESS && key_release) ||
82             (bind->release >= B_UPON_KEYRELEASE && !key_release))
83             continue;
84
85         break;
86     }
87
88     return (bind == TAILQ_END(bindings) ? NULL : bind);
89 }
90
91 /*
92  * Translates keysymbols to keycodes for all bindings which use keysyms.
93  *
94  */
95 void translate_keysyms(void) {
96     Binding *bind;
97     xcb_keysym_t keysym;
98     int col;
99     xcb_keycode_t i,
100                   min_keycode = xcb_get_setup(conn)->min_keycode,
101                   max_keycode = xcb_get_setup(conn)->max_keycode;
102
103     TAILQ_FOREACH(bind, bindings, bindings) {
104         if (bind->keycode > 0)
105             continue;
106
107         /* We need to translate the symbol to a keycode */
108         keysym = XStringToKeysym(bind->symbol);
109         if (keysym == NoSymbol) {
110             ELOG("Could not translate string to key symbol: \"%s\"\n",
111                  bind->symbol);
112             continue;
113         }
114
115         /* Base column we use for looking up key symbols. We always consider
116          * the base column and the corresponding shift column, so without
117          * mode_switch, we look in 0 and 1, with mode_switch we look in 2 and
118          * 3. */
119         col = (bind->mods & BIND_MODE_SWITCH ? 2 : 0);
120
121         FREE(bind->translated_to);
122         bind->number_keycodes = 0;
123
124         for (i = min_keycode; i && i <= max_keycode; i++) {
125             if ((xcb_key_symbols_get_keysym(keysyms, i, col) != keysym) &&
126                 (xcb_key_symbols_get_keysym(keysyms, i, col+1) != keysym))
127                 continue;
128             bind->number_keycodes++;
129             bind->translated_to = srealloc(bind->translated_to,
130                                            (sizeof(xcb_keycode_t) *
131                                             bind->number_keycodes));
132             bind->translated_to[bind->number_keycodes-1] = i;
133         }
134
135         DLOG("Translated symbol \"%s\" to %d keycode\n", bind->symbol,
136              bind->number_keycodes);
137     }
138 }
139
140 /*
141  * Switches the key bindings to the given mode, if the mode exists
142  *
143  */
144 void switch_mode(const char *new_mode) {
145     struct Mode *mode;
146
147     LOG("Switching to mode %s\n", new_mode);
148
149     SLIST_FOREACH(mode, &modes, modes) {
150         if (strcasecmp(mode->name, new_mode) != 0)
151             continue;
152
153         ungrab_all_keys(conn);
154         bindings = mode->bindings;
155         translate_keysyms();
156         grab_all_keys(conn, false);
157
158         char *event_msg;
159         sasprintf(&event_msg, "{\"change\":\"%s\"}", mode->name);
160
161         ipc_send_event("mode", I3_IPC_EVENT_MODE, event_msg);
162         FREE(event_msg);
163
164         return;
165     }
166
167     ELOG("ERROR: Mode not found\n");
168 }
169
170 /*
171  * Sends the current bar configuration as an event to all barconfig_update listeners.
172  * This update mechnism currently only includes the hidden_state and the mode in the config.
173  *
174  */
175 void update_barconfig() {
176     Barconfig *current;
177     TAILQ_FOREACH(current, &barconfigs, configs) {
178         /* Build json message */
179         char *hidden_state;
180         switch (current->hidden_state) {
181             case S_SHOW:
182                 hidden_state ="show";
183                 break;
184             case S_HIDE:
185             default:
186                 hidden_state = "hide";
187                 break;
188         }
189
190         char *mode;
191         switch (current->mode) {
192             case M_HIDE:
193                 mode ="hide";
194                 break;
195             case M_INVISIBLE:
196                 mode ="invisible";
197                 break;
198             case M_DOCK:
199             default:
200                 mode = "dock";
201                 break;
202         }
203
204         /* Send an event to all barconfig listeners*/
205         char *event_msg;
206         sasprintf(&event_msg, "{ \"id\":\"%s\", \"hidden_state\":\"%s\", \"mode\":\"%s\" }", current->id, hidden_state, mode);
207
208         ipc_send_event("barconfig_update", I3_IPC_EVENT_BARCONFIG_UPDATE, event_msg);
209         FREE(event_msg);
210     }
211 }
212
213 /*
214  * Get the path of the first configuration file found. If override_configpath
215  * is specified, that path is returned and saved for further calls. Otherwise,
216  * checks the home directory first, then the system directory first, always
217  * taking into account the XDG Base Directory Specification ($XDG_CONFIG_HOME,
218  * $XDG_CONFIG_DIRS)
219  *
220  */
221 static char *get_config_path(const char *override_configpath) {
222     char *xdg_config_home, *xdg_config_dirs, *config_path;
223
224     static const char *saved_configpath = NULL;
225
226     if (override_configpath != NULL) {
227         saved_configpath = override_configpath;
228         return sstrdup(saved_configpath);
229     }
230
231     if (saved_configpath != NULL)
232         return sstrdup(saved_configpath);
233
234     /* 1: check the traditional path under the home directory */
235     config_path = resolve_tilde("~/.i3/config");
236     if (path_exists(config_path))
237         return config_path;
238     free(config_path);
239
240     /* 2: check for $XDG_CONFIG_HOME/i3/config */
241     if ((xdg_config_home = getenv("XDG_CONFIG_HOME")) == NULL)
242         xdg_config_home = "~/.config";
243
244     xdg_config_home = resolve_tilde(xdg_config_home);
245     sasprintf(&config_path, "%s/i3/config", xdg_config_home);
246     free(xdg_config_home);
247
248     if (path_exists(config_path))
249         return config_path;
250     free(config_path);
251
252     /* 3: check the traditional path under /etc */
253     config_path = SYSCONFDIR "/i3/config";
254     if (path_exists(config_path))
255         return sstrdup(config_path);
256
257     /* 4: check for $XDG_CONFIG_DIRS/i3/config */
258     if ((xdg_config_dirs = getenv("XDG_CONFIG_DIRS")) == NULL)
259         xdg_config_dirs = "/etc/xdg";
260
261     char *buf = sstrdup(xdg_config_dirs);
262     char *tok = strtok(buf, ":");
263     while (tok != NULL) {
264         tok = resolve_tilde(tok);
265         sasprintf(&config_path, "%s/i3/config", tok);
266         free(tok);
267         if (path_exists(config_path)) {
268             free(buf);
269             return config_path;
270         }
271         free(config_path);
272         tok = strtok(NULL, ":");
273     }
274     free(buf);
275
276     die("Unable to find the configuration file (looked at "
277             "~/.i3/config, $XDG_CONFIG_HOME/i3/config, "
278             SYSCONFDIR "/i3/config and $XDG_CONFIG_DIRS/i3/config)");
279 }
280
281 /*
282  * Finds the configuration file to use (either the one specified by
283  * override_configpath), the user’s one or the system default) and calls
284  * parse_file().
285  *
286  */
287 static void parse_configuration(const char *override_configpath) {
288     char *path = get_config_path(override_configpath);
289     LOG("Parsing configfile %s\n", path);
290     FREE(current_configpath);
291     current_configpath = path;
292     parse_file(path);
293 }
294
295 /*
296  * (Re-)loads the configuration file (sets useful defaults before).
297  *
298  */
299 void load_configuration(xcb_connection_t *conn, const char *override_configpath, bool reload) {
300     if (reload) {
301         /* First ungrab the keys */
302         ungrab_all_keys(conn);
303
304         struct Mode *mode;
305         Binding *bind;
306         while (!SLIST_EMPTY(&modes)) {
307             mode = SLIST_FIRST(&modes);
308             FREE(mode->name);
309
310             /* Clear the old binding list */
311             bindings = mode->bindings;
312             while (!TAILQ_EMPTY(bindings)) {
313                 bind = TAILQ_FIRST(bindings);
314                 TAILQ_REMOVE(bindings, bind, bindings);
315                 FREE(bind->translated_to);
316                 FREE(bind->command);
317                 FREE(bind);
318             }
319             FREE(bindings);
320             SLIST_REMOVE(&modes, mode, Mode, modes);
321         }
322
323         struct Assignment *assign;
324         while (!TAILQ_EMPTY(&assignments)) {
325             assign = TAILQ_FIRST(&assignments);
326             if (assign->type == A_TO_WORKSPACE)
327                 FREE(assign->dest.workspace);
328             else if (assign->type == A_TO_OUTPUT)
329                 FREE(assign->dest.output);
330             else if (assign->type == A_COMMAND)
331                 FREE(assign->dest.command);
332             match_free(&(assign->match));
333             TAILQ_REMOVE(&assignments, assign, assignments);
334             FREE(assign);
335         }
336
337         /* Clear bar configs */
338         Barconfig *barconfig;
339         while (!TAILQ_EMPTY(&barconfigs)) {
340             barconfig = TAILQ_FIRST(&barconfigs);
341             FREE(barconfig->id);
342             for (int c = 0; c < barconfig->num_outputs; c++)
343                 free(barconfig->outputs[c]);
344             FREE(barconfig->outputs);
345             FREE(barconfig->tray_output);
346             FREE(barconfig->socket_path);
347             FREE(barconfig->status_command);
348             FREE(barconfig->i3bar_command);
349             FREE(barconfig->font);
350             FREE(barconfig->colors.background);
351             FREE(barconfig->colors.statusline);
352             FREE(barconfig->colors.focused_workspace_border);
353             FREE(barconfig->colors.focused_workspace_bg);
354             FREE(barconfig->colors.focused_workspace_text);
355             FREE(barconfig->colors.active_workspace_border);
356             FREE(barconfig->colors.active_workspace_bg);
357             FREE(barconfig->colors.active_workspace_text);
358             FREE(barconfig->colors.inactive_workspace_border);
359             FREE(barconfig->colors.inactive_workspace_bg);
360             FREE(barconfig->colors.inactive_workspace_text);
361             FREE(barconfig->colors.urgent_workspace_border);
362             FREE(barconfig->colors.urgent_workspace_bg);
363             FREE(barconfig->colors.urgent_workspace_text);
364             TAILQ_REMOVE(&barconfigs, barconfig, configs);
365             FREE(barconfig);
366         }
367
368         /* Clear workspace names */
369 #if 0
370         Workspace *ws;
371         TAILQ_FOREACH(ws, workspaces, workspaces)
372             workspace_set_name(ws, NULL);
373 #endif
374
375         /* Invalidate pixmap caches in case font or colors changed */
376         Con *con;
377         TAILQ_FOREACH(con, &all_cons, all_cons)
378             FREE(con->deco_render_params);
379
380         /* Get rid of the current font */
381         free_font();
382     }
383
384     SLIST_INIT(&modes);
385
386     struct Mode *default_mode = scalloc(sizeof(struct Mode));
387     default_mode->name = sstrdup("default");
388     default_mode->bindings = scalloc(sizeof(struct bindings_head));
389     TAILQ_INIT(default_mode->bindings);
390     SLIST_INSERT_HEAD(&modes, default_mode, modes);
391
392     bindings = default_mode->bindings;
393
394 #define REQUIRED_OPTION(name) \
395     if (config.name == NULL) \
396         die("You did not specify required configuration option " #name "\n");
397
398     /* Clear the old config or initialize the data structure */
399     memset(&config, 0, sizeof(config));
400
401     /* Initialize default colors */
402 #define INIT_COLOR(x, cborder, cbackground, ctext, cindicator) \
403     do { \
404         x.border = get_colorpixel(cborder); \
405         x.background = get_colorpixel(cbackground); \
406         x.text = get_colorpixel(ctext); \
407         x.indicator = get_colorpixel(cindicator); \
408     } while (0)
409
410     config.client.background = get_colorpixel("#000000");
411     INIT_COLOR(config.client.focused, "#4c7899", "#285577", "#ffffff", "#2e9ef4");
412     INIT_COLOR(config.client.focused_inactive, "#333333", "#5f676a", "#ffffff", "#484e50");
413     INIT_COLOR(config.client.unfocused, "#333333", "#222222", "#888888", "#292d2e");
414     INIT_COLOR(config.client.urgent, "#2f343a", "#900000", "#ffffff", "#900000");
415
416     /* border and indicator color are ignored for placeholder contents */
417     INIT_COLOR(config.client.placeholder, "#000000", "#0c0c0c", "#ffffff", "#000000");
418
419     /* the last argument (indicator color) is ignored for bar colors */
420     INIT_COLOR(config.bar.focused, "#4c7899", "#285577", "#ffffff", "#000000");
421     INIT_COLOR(config.bar.unfocused, "#333333", "#222222", "#888888", "#000000");
422     INIT_COLOR(config.bar.urgent, "#2f343a", "#900000", "#ffffff", "#000000");
423
424     config.default_border = BS_NORMAL;
425     config.default_floating_border = BS_NORMAL;
426     config.default_border_width = 2;
427     /* Set default_orientation to NO_ORIENTATION for auto orientation. */
428     config.default_orientation = NO_ORIENTATION;
429
430     /* Set default urgency reset delay to 500ms */
431     if (config.workspace_urgency_timer == 0)
432         config.workspace_urgency_timer = 0.5;
433
434     parse_configuration(override_configpath);
435
436     if (reload) {
437         translate_keysyms();
438         grab_all_keys(conn, false);
439     }
440
441     if (config.font.type == FONT_TYPE_NONE) {
442         ELOG("You did not specify required configuration option \"font\"\n");
443         config.font = load_font("fixed", true);
444         set_font(&config.font);
445     }
446
447     /* Redraw the currently visible decorations on reload, so that
448      * the possibly new drawing parameters changed. */
449     if (reload) {
450         x_deco_recurse(croot);
451         xcb_flush(conn);
452     }
453
454 #if 0
455     /* Set an empty name for every workspace which got no name */
456     Workspace *ws;
457     TAILQ_FOREACH(ws, workspaces, workspaces) {
458             if (ws->name != NULL) {
459                     /* If the font was not specified when the workspace name
460                      * was loaded, we need to predict the text width now */
461                     if (ws->text_width == 0)
462                             ws->text_width = predict_text_width(global_conn,
463                                             config.font, ws->name, ws->name_len);
464                     continue;
465             }
466
467             workspace_set_name(ws, NULL);
468     }
469 #endif
470 }