i3
src/config.c
Go to the documentation of this file.
00001 /*
00002  * vim:ts=4:sw=4:expandtab
00003  *
00004  * i3 - an improved dynamic tiling window manager
00005  * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
00006  *
00007  * config.c: Configuration file (calling the parser (src/cfgparse.y) with the
00008  *           correct path, switching key bindings mode).
00009  *
00010  */
00011 #include "all.h"
00012 
00013 /* We need Xlib for XStringToKeysym */
00014 #include <X11/Xlib.h>
00015 
00016 char *current_configpath = NULL;
00017 Config config;
00018 struct modes_head modes;
00019 struct barconfig_head barconfigs = TAILQ_HEAD_INITIALIZER(barconfigs);
00020 
00026 void ungrab_all_keys(xcb_connection_t *conn) {
00027         DLOG("Ungrabbing all keys\n");
00028         xcb_ungrab_key(conn, XCB_GRAB_ANY, root, XCB_BUTTON_MASK_ANY);
00029 }
00030 
00031 static void grab_keycode_for_binding(xcb_connection_t *conn, Binding *bind, uint32_t keycode) {
00032         DLOG("Grabbing %d\n", keycode);
00033         /* Grab the key in all combinations */
00034         #define GRAB_KEY(modifier) \
00035                 do { \
00036                         xcb_grab_key(conn, 0, root, modifier, keycode, \
00037                                      XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC); \
00038                 } while (0)
00039         int mods = bind->mods;
00040         if ((bind->mods & BIND_MODE_SWITCH) != 0) {
00041                 mods &= ~BIND_MODE_SWITCH;
00042                 if (mods == 0)
00043                         mods = XCB_MOD_MASK_ANY;
00044         }
00045         GRAB_KEY(mods);
00046         GRAB_KEY(mods | xcb_numlock_mask);
00047         GRAB_KEY(mods | xcb_numlock_mask | XCB_MOD_MASK_LOCK);
00048 }
00049 
00050 /*
00051  * Returns a pointer to the Binding with the specified modifiers and keycode
00052  * or NULL if no such binding exists.
00053  *
00054  */
00055 Binding *get_binding(uint16_t modifiers, xcb_keycode_t keycode) {
00056         Binding *bind;
00057 
00058         TAILQ_FOREACH(bind, bindings, bindings) {
00059                 /* First compare the modifiers */
00060                 if (bind->mods != modifiers)
00061                         continue;
00062 
00063                 /* If a symbol was specified by the user, we need to look in
00064                  * the array of translated keycodes for the event’s keycode */
00065                 if (bind->symbol != NULL) {
00066                         if (memmem(bind->translated_to,
00067                                    bind->number_keycodes * sizeof(xcb_keycode_t),
00068                                    &keycode, sizeof(xcb_keycode_t)) != NULL)
00069                                 break;
00070                 } else {
00071                         /* This case is easier: The user specified a keycode */
00072                         if (bind->keycode == keycode)
00073                                 break;
00074                 }
00075         }
00076 
00077         return (bind == TAILQ_END(bindings) ? NULL : bind);
00078 }
00079 
00080 /*
00081  * Translates keysymbols to keycodes for all bindings which use keysyms.
00082  *
00083  */
00084 void translate_keysyms(void) {
00085     Binding *bind;
00086     xcb_keysym_t keysym;
00087     int col;
00088     xcb_keycode_t i,
00089                   min_keycode = xcb_get_setup(conn)->min_keycode,
00090                   max_keycode = xcb_get_setup(conn)->max_keycode;
00091 
00092     TAILQ_FOREACH(bind, bindings, bindings) {
00093         if (bind->keycode > 0)
00094             continue;
00095 
00096         /* We need to translate the symbol to a keycode */
00097         keysym = XStringToKeysym(bind->symbol);
00098         if (keysym == NoSymbol) {
00099             ELOG("Could not translate string to key symbol: \"%s\"\n",
00100                  bind->symbol);
00101             continue;
00102         }
00103 
00104         /* Base column we use for looking up key symbols. We always consider
00105          * the base column and the corresponding shift column, so without
00106          * mode_switch, we look in 0 and 1, with mode_switch we look in 2 and
00107          * 3. */
00108         col = (bind->mods & BIND_MODE_SWITCH ? 2 : 0);
00109 
00110         FREE(bind->translated_to);
00111         bind->number_keycodes = 0;
00112 
00113         for (i = min_keycode; i && i <= max_keycode; i++) {
00114             if ((xcb_key_symbols_get_keysym(keysyms, i, col) != keysym) &&
00115                 (xcb_key_symbols_get_keysym(keysyms, i, col+1) != keysym))
00116                 continue;
00117             bind->number_keycodes++;
00118             bind->translated_to = srealloc(bind->translated_to,
00119                                            (sizeof(xcb_keycode_t) *
00120                                             bind->number_keycodes));
00121             bind->translated_to[bind->number_keycodes-1] = i;
00122         }
00123 
00124         DLOG("Translated symbol \"%s\" to %d keycode\n", bind->symbol,
00125              bind->number_keycodes);
00126     }
00127 }
00128 
00129 /*
00130  * Grab the bound keys (tell X to send us keypress events for those keycodes)
00131  *
00132  */
00133 void grab_all_keys(xcb_connection_t *conn, bool bind_mode_switch) {
00134         Binding *bind;
00135         TAILQ_FOREACH(bind, bindings, bindings) {
00136                 if ((bind_mode_switch && (bind->mods & BIND_MODE_SWITCH) == 0) ||
00137                     (!bind_mode_switch && (bind->mods & BIND_MODE_SWITCH) != 0))
00138                         continue;
00139 
00140                 /* The easy case: the user specified a keycode directly. */
00141                 if (bind->keycode > 0) {
00142                         grab_keycode_for_binding(conn, bind, bind->keycode);
00143                         continue;
00144                 }
00145 
00146                 xcb_keycode_t *walk = bind->translated_to;
00147                 for (int i = 0; i < bind->number_keycodes; i++)
00148                         grab_keycode_for_binding(conn, bind, *walk++);
00149         }
00150 }
00151 
00152 /*
00153  * Switches the key bindings to the given mode, if the mode exists
00154  *
00155  */
00156 void switch_mode(const char *new_mode) {
00157         struct Mode *mode;
00158 
00159         LOG("Switching to mode %s\n", new_mode);
00160 
00161         SLIST_FOREACH(mode, &modes, modes) {
00162                 if (strcasecmp(mode->name, new_mode) != 0)
00163                         continue;
00164 
00165                 ungrab_all_keys(conn);
00166                 bindings = mode->bindings;
00167                 translate_keysyms();
00168                 grab_all_keys(conn, false);
00169                 return;
00170         }
00171 
00172         ELOG("ERROR: Mode not found\n");
00173 }
00174 
00175 /*
00176  * Get the path of the first configuration file found. If override_configpath
00177  * is specified, that path is returned and saved for further calls. Otherwise,
00178  * checks the home directory first, then the system directory first, always
00179  * taking into account the XDG Base Directory Specification ($XDG_CONFIG_HOME,
00180  * $XDG_CONFIG_DIRS)
00181  *
00182  */
00183 static char *get_config_path(const char *override_configpath) {
00184     char *xdg_config_home, *xdg_config_dirs, *config_path;
00185 
00186     static const char *saved_configpath = NULL;
00187 
00188     if (override_configpath != NULL) {
00189         saved_configpath = override_configpath;
00190         return sstrdup(saved_configpath);
00191     }
00192 
00193     if (saved_configpath != NULL)
00194         return sstrdup(saved_configpath);
00195 
00196     /* 1: check the traditional path under the home directory */
00197     config_path = resolve_tilde("~/.i3/config");
00198     if (path_exists(config_path))
00199         return config_path;
00200     free(config_path);
00201 
00202     /* 2: check for $XDG_CONFIG_HOME/i3/config */
00203     if ((xdg_config_home = getenv("XDG_CONFIG_HOME")) == NULL)
00204         xdg_config_home = "~/.config";
00205 
00206     xdg_config_home = resolve_tilde(xdg_config_home);
00207     sasprintf(&config_path, "%s/i3/config", xdg_config_home);
00208     free(xdg_config_home);
00209 
00210     if (path_exists(config_path))
00211         return config_path;
00212     free(config_path);
00213 
00214     /* 3: check the traditional path under /etc */
00215     config_path = SYSCONFDIR "/i3/config";
00216     if (path_exists(config_path))
00217         return sstrdup(config_path);
00218 
00219     /* 4: check for $XDG_CONFIG_DIRS/i3/config */
00220     if ((xdg_config_dirs = getenv("XDG_CONFIG_DIRS")) == NULL)
00221         xdg_config_dirs = "/etc/xdg";
00222 
00223     char *buf = sstrdup(xdg_config_dirs);
00224     char *tok = strtok(buf, ":");
00225     while (tok != NULL) {
00226         tok = resolve_tilde(tok);
00227         sasprintf(&config_path, "%s/i3/config", tok);
00228         free(tok);
00229         if (path_exists(config_path)) {
00230             free(buf);
00231             return config_path;
00232         }
00233         free(config_path);
00234         tok = strtok(NULL, ":");
00235     }
00236     free(buf);
00237 
00238     die("Unable to find the configuration file (looked at "
00239             "~/.i3/config, $XDG_CONFIG_HOME/i3/config, "
00240             SYSCONFDIR "/i3/config and $XDG_CONFIG_DIRS/i3/config)");
00241 }
00242 
00243 /*
00244  * Finds the configuration file to use (either the one specified by
00245  * override_configpath), the user’s one or the system default) and calls
00246  * parse_file().
00247  *
00248  */
00249 static void parse_configuration(const char *override_configpath) {
00250     char *path = get_config_path(override_configpath);
00251     LOG("Parsing configfile %s\n", path);
00252     FREE(current_configpath);
00253     current_configpath = path;
00254     parse_file(path);
00255 }
00256 
00257 /*
00258  * (Re-)loads the configuration file (sets useful defaults before).
00259  *
00260  */
00261 void load_configuration(xcb_connection_t *conn, const char *override_configpath, bool reload) {
00262     if (reload) {
00263         /* First ungrab the keys */
00264         ungrab_all_keys(conn);
00265 
00266         struct Mode *mode;
00267         Binding *bind;
00268         while (!SLIST_EMPTY(&modes)) {
00269             mode = SLIST_FIRST(&modes);
00270             FREE(mode->name);
00271 
00272             /* Clear the old binding list */
00273             bindings = mode->bindings;
00274             while (!TAILQ_EMPTY(bindings)) {
00275                 bind = TAILQ_FIRST(bindings);
00276                 TAILQ_REMOVE(bindings, bind, bindings);
00277                 FREE(bind->translated_to);
00278                 FREE(bind->command);
00279                 FREE(bind);
00280             }
00281             FREE(bindings);
00282             SLIST_REMOVE(&modes, mode, Mode, modes);
00283         }
00284 
00285         struct Assignment *assign;
00286         while (!TAILQ_EMPTY(&assignments)) {
00287             assign = TAILQ_FIRST(&assignments);
00288             if (assign->type == A_TO_WORKSPACE)
00289                 FREE(assign->dest.workspace);
00290             else if (assign->type == A_TO_OUTPUT)
00291                 FREE(assign->dest.output);
00292             else if (assign->type == A_COMMAND)
00293                 FREE(assign->dest.command);
00294             match_free(&(assign->match));
00295             TAILQ_REMOVE(&assignments, assign, assignments);
00296             FREE(assign);
00297         }
00298 
00299         /* Clear bar configs */
00300         Barconfig *barconfig;
00301         while (!TAILQ_EMPTY(&barconfigs)) {
00302             barconfig = TAILQ_FIRST(&barconfigs);
00303             FREE(barconfig->id);
00304             for (int c = 0; c < barconfig->num_outputs; c++)
00305                 free(barconfig->outputs[c]);
00306             FREE(barconfig->outputs);
00307             FREE(barconfig->tray_output);
00308             FREE(barconfig->socket_path);
00309             FREE(barconfig->status_command);
00310             FREE(barconfig->i3bar_command);
00311             FREE(barconfig->font);
00312             FREE(barconfig->colors.background);
00313             FREE(barconfig->colors.statusline);
00314             FREE(barconfig->colors.focused_workspace_border);
00315             FREE(barconfig->colors.focused_workspace_bg);
00316             FREE(barconfig->colors.focused_workspace_text);
00317             FREE(barconfig->colors.active_workspace_border);
00318             FREE(barconfig->colors.active_workspace_bg);
00319             FREE(barconfig->colors.active_workspace_text);
00320             FREE(barconfig->colors.inactive_workspace_border);
00321             FREE(barconfig->colors.inactive_workspace_bg);
00322             FREE(barconfig->colors.inactive_workspace_text);
00323             FREE(barconfig->colors.urgent_workspace_border);
00324             FREE(barconfig->colors.urgent_workspace_bg);
00325             FREE(barconfig->colors.urgent_workspace_text);
00326             TAILQ_REMOVE(&barconfigs, barconfig, configs);
00327             FREE(barconfig);
00328         }
00329 
00330         /* Clear workspace names */
00331 #if 0
00332         Workspace *ws;
00333         TAILQ_FOREACH(ws, workspaces, workspaces)
00334             workspace_set_name(ws, NULL);
00335 #endif
00336 
00337         /* Invalidate pixmap caches in case font or colors changed */
00338         Con *con;
00339         TAILQ_FOREACH(con, &all_cons, all_cons)
00340             FREE(con->deco_render_params);
00341 
00342         /* Get rid of the current font */
00343         free_font();
00344     }
00345 
00346     SLIST_INIT(&modes);
00347 
00348     struct Mode *default_mode = scalloc(sizeof(struct Mode));
00349     default_mode->name = sstrdup("default");
00350     default_mode->bindings = scalloc(sizeof(struct bindings_head));
00351     TAILQ_INIT(default_mode->bindings);
00352     SLIST_INSERT_HEAD(&modes, default_mode, modes);
00353 
00354     bindings = default_mode->bindings;
00355 
00356 #define REQUIRED_OPTION(name) \
00357     if (config.name == NULL) \
00358         die("You did not specify required configuration option " #name "\n");
00359 
00360     /* Clear the old config or initialize the data structure */
00361     memset(&config, 0, sizeof(config));
00362 
00363     /* Initialize default colors */
00364 #define INIT_COLOR(x, cborder, cbackground, ctext, cindicator) \
00365     do { \
00366         x.border = get_colorpixel(cborder); \
00367         x.background = get_colorpixel(cbackground); \
00368         x.text = get_colorpixel(ctext); \
00369         x.indicator = get_colorpixel(cindicator); \
00370     } while (0)
00371 
00372     config.client.background = get_colorpixel("#000000");
00373     INIT_COLOR(config.client.focused, "#4c7899", "#285577", "#ffffff", "#2e9ef4");
00374     INIT_COLOR(config.client.focused_inactive, "#333333", "#5f676a", "#ffffff", "#484e50");
00375     INIT_COLOR(config.client.unfocused, "#333333", "#222222", "#888888", "#292d2e");
00376     INIT_COLOR(config.client.urgent, "#2f343a", "#900000", "#ffffff", "#900000");
00377 
00378     /* the last argument (indicator color) is ignored for bar colors */
00379     INIT_COLOR(config.bar.focused, "#4c7899", "#285577", "#ffffff", "#000000");
00380     INIT_COLOR(config.bar.unfocused, "#333333", "#222222", "#888888", "#000000");
00381     INIT_COLOR(config.bar.urgent, "#2f343a", "#900000", "#ffffff", "#000000");
00382 
00383     config.default_border = BS_NORMAL;
00384     config.default_floating_border = BS_NORMAL;
00385     /* Set default_orientation to NO_ORIENTATION for auto orientation. */
00386     config.default_orientation = NO_ORIENTATION;
00387 
00388     parse_configuration(override_configpath);
00389 
00390     if (reload) {
00391         translate_keysyms();
00392         grab_all_keys(conn, false);
00393     }
00394 
00395     if (config.font.id == 0) {
00396         ELOG("You did not specify required configuration option \"font\"\n");
00397         config.font = load_font("fixed", true);
00398         set_font(&config.font);
00399     }
00400 
00401     /* Redraw the currently visible decorations on reload, so that
00402      * the possibly new drawing parameters changed. */
00403     if (reload) {
00404         x_deco_recurse(croot);
00405         xcb_flush(conn);
00406     }
00407 
00408 #if 0
00409     /* Set an empty name for every workspace which got no name */
00410     Workspace *ws;
00411     TAILQ_FOREACH(ws, workspaces, workspaces) {
00412             if (ws->name != NULL) {
00413                     /* If the font was not specified when the workspace name
00414                      * was loaded, we need to predict the text width now */
00415                     if (ws->text_width == 0)
00416                             ws->text_width = predict_text_width(global_conn,
00417                                             config.font, ws->name, ws->name_len);
00418                     continue;
00419             }
00420 
00421             workspace_set_name(ws, NULL);
00422     }
00423 #endif
00424 }