i3
|
00001 /* 00002 * vim:ts=4:sw=4:expandtab 00003 * 00004 * i3 - an improved dynamic tiling window manager 00005 * © 2009-2011 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() { 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 DLOG("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->font); 00311 FREE(barconfig->colors.background); 00312 FREE(barconfig->colors.statusline); 00313 FREE(barconfig->colors.focused_workspace_text); 00314 FREE(barconfig->colors.focused_workspace_bg); 00315 FREE(barconfig->colors.active_workspace_text); 00316 FREE(barconfig->colors.active_workspace_bg); 00317 FREE(barconfig->colors.inactive_workspace_text); 00318 FREE(barconfig->colors.inactive_workspace_bg); 00319 FREE(barconfig->colors.urgent_workspace_text); 00320 FREE(barconfig->colors.urgent_workspace_bg); 00321 TAILQ_REMOVE(&barconfigs, barconfig, configs); 00322 FREE(barconfig); 00323 } 00324 00325 /* Clear workspace names */ 00326 #if 0 00327 Workspace *ws; 00328 TAILQ_FOREACH(ws, workspaces, workspaces) 00329 workspace_set_name(ws, NULL); 00330 #endif 00331 } 00332 00333 SLIST_INIT(&modes); 00334 00335 struct Mode *default_mode = scalloc(sizeof(struct Mode)); 00336 default_mode->name = sstrdup("default"); 00337 default_mode->bindings = scalloc(sizeof(struct bindings_head)); 00338 TAILQ_INIT(default_mode->bindings); 00339 SLIST_INSERT_HEAD(&modes, default_mode, modes); 00340 00341 bindings = default_mode->bindings; 00342 00343 #define REQUIRED_OPTION(name) \ 00344 if (config.name == NULL) \ 00345 die("You did not specify required configuration option " #name "\n"); 00346 00347 /* Clear the old config or initialize the data structure */ 00348 memset(&config, 0, sizeof(config)); 00349 00350 /* Initialize default colors */ 00351 #define INIT_COLOR(x, cborder, cbackground, ctext) \ 00352 do { \ 00353 x.border = get_colorpixel(cborder); \ 00354 x.background = get_colorpixel(cbackground); \ 00355 x.text = get_colorpixel(ctext); \ 00356 } while (0) 00357 00358 config.client.background = get_colorpixel("#000000"); 00359 INIT_COLOR(config.client.focused, "#4c7899", "#285577", "#ffffff"); 00360 INIT_COLOR(config.client.focused_inactive, "#333333", "#5f676a", "#ffffff"); 00361 INIT_COLOR(config.client.unfocused, "#333333", "#222222", "#888888"); 00362 INIT_COLOR(config.client.urgent, "#2f343a", "#900000", "#ffffff"); 00363 INIT_COLOR(config.bar.focused, "#4c7899", "#285577", "#ffffff"); 00364 INIT_COLOR(config.bar.unfocused, "#333333", "#222222", "#888888"); 00365 INIT_COLOR(config.bar.urgent, "#2f343a", "#900000", "#ffffff"); 00366 00367 config.default_border = BS_NORMAL; 00368 config.default_floating_border = BS_NORMAL; 00369 /* Set default_orientation to NO_ORIENTATION for auto orientation. */ 00370 config.default_orientation = NO_ORIENTATION; 00371 00372 parse_configuration(override_configpath); 00373 00374 if (reload) { 00375 translate_keysyms(); 00376 grab_all_keys(conn, false); 00377 } 00378 00379 if (config.font.id == 0) { 00380 ELOG("You did not specify required configuration option \"font\"\n"); 00381 config.font = load_font("fixed", true); 00382 } 00383 00384 #if 0 00385 /* Set an empty name for every workspace which got no name */ 00386 Workspace *ws; 00387 TAILQ_FOREACH(ws, workspaces, workspaces) { 00388 if (ws->name != NULL) { 00389 /* If the font was not specified when the workspace name 00390 * was loaded, we need to predict the text width now */ 00391 if (ws->text_width == 0) 00392 ws->text_width = predict_text_width(global_conn, 00393 config.font, ws->name, ws->name_len); 00394 continue; 00395 } 00396 00397 workspace_set_name(ws, NULL); 00398 } 00399 #endif 00400 }