i3/src/config.c

265 lines
9.4 KiB
C
Raw Normal View History

#undef I3__FILE__
#define I3__FILE__ "config.c"
2009-02-24 18:50:30 -05:00
/*
* vim:ts=4:sw=4:expandtab
2009-02-24 18:50:30 -05:00
*
* i3 - an improved dynamic tiling window manager
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
2009-02-24 18:50:30 -05:00
*
* config.c: Configuration file (calling the parser (src/config_parser.c) with
* the correct path, switching key bindings mode).
*
2009-02-24 18:50:30 -05:00
*/
#include "all.h"
#include <xkbcommon/xkbcommon.h>
char *current_configpath = NULL;
2009-02-24 18:50:30 -05:00
Config config;
struct modes_head modes;
2011-10-18 17:11:27 -04:00
struct barconfig_head barconfigs = TAILQ_HEAD_INITIALIZER(barconfigs);
2009-02-24 18:50:30 -05:00
/**
* Ungrabs all keys, to be called before re-grabbing the keys because of a
* mapping_notify event or a configuration file reload
*
*/
2009-07-23 12:14:24 -04:00
void ungrab_all_keys(xcb_connection_t *conn) {
2012-09-06 08:49:14 -04:00
DLOG("Ungrabbing all keys\n");
xcb_ungrab_key(conn, XCB_GRAB_ANY, root, XCB_BUTTON_MASK_ANY);
}
introduced i3 command for changing the hidden state and the mode of i3bar The hidden_state and mode of each i3bar instance can now be controlled from within i3. Therefore, two new i3 command were introduced: _ bar hidden_state show|hide|toggle [<bar_id>] show: always show the bar hide: normal hide mode toggle: toggle between show and hide (individually for each bar) _ bar mode dock|hide|invisible|toggle [<bar_id>] hide,dock: like before invisible: always keep the bar hidden toggle: toggle between dock and hide (individually for each bar) This patch introduces a hidden_state ("hidden_state hide|show") in the barconfig, which indicates the current hidden_state of each i3bar instance. It only affects the bar when in hide mode. Additionally, a new invisible mode was introduced. In order to change the hidden_state or mode of the bar from i3, a barconfig-update event was introduced, for which a bar can subscribe and the bar then gets notified about the currently set hidden_state and mode in its barconfig. For convenience, an id field ("id <bar_id>") was added to the barconfig, where one can set the desired id for the corresponding bar. If the id is not specified, i3 will deterministically choose an id; otherwise, with the previous random approach for finding a new id, which is actually not shared with i3bar, as it would determine its id on startup, the event-subscription would be destroyed on reload. Still, this issue remains when manually changing the bar_id in the config and then reloading. fixes #833, #651
2013-05-25 08:30:00 -04:00
/*
* Sends the current bar configuration as an event to all barconfig_update listeners.
*
*/
void update_barconfig() {
Barconfig *current;
TAILQ_FOREACH(current, &barconfigs, configs) {
ipc_send_barconfig_update_event(current);
introduced i3 command for changing the hidden state and the mode of i3bar The hidden_state and mode of each i3bar instance can now be controlled from within i3. Therefore, two new i3 command were introduced: _ bar hidden_state show|hide|toggle [<bar_id>] show: always show the bar hide: normal hide mode toggle: toggle between show and hide (individually for each bar) _ bar mode dock|hide|invisible|toggle [<bar_id>] hide,dock: like before invisible: always keep the bar hidden toggle: toggle between dock and hide (individually for each bar) This patch introduces a hidden_state ("hidden_state hide|show") in the barconfig, which indicates the current hidden_state of each i3bar instance. It only affects the bar when in hide mode. Additionally, a new invisible mode was introduced. In order to change the hidden_state or mode of the bar from i3, a barconfig-update event was introduced, for which a bar can subscribe and the bar then gets notified about the currently set hidden_state and mode in its barconfig. For convenience, an id field ("id <bar_id>") was added to the barconfig, where one can set the desired id for the corresponding bar. If the id is not specified, i3 will deterministically choose an id; otherwise, with the previous random approach for finding a new id, which is actually not shared with i3bar, as it would determine its id on startup, the event-subscription would be destroyed on reload. Still, this issue remains when manually changing the bar_id in the config and then reloading. fixes #833, #651
2013-05-25 08:30:00 -04:00
}
}
/*
* Finds the configuration file to use (either the one specified by
* override_configpath), the users one or the system default) and calls
* parse_file().
*
*/
bool parse_configuration(const char *override_configpath, bool use_nagbar) {
char *path = get_config_path(override_configpath, true);
if (path == NULL) {
die("Unable to find the configuration file (looked at "
"~/.i3/config, $XDG_CONFIG_HOME/i3/config, " SYSCONFDIR "/i3/config and $XDG_CONFIG_DIRS/i3/config)");
}
LOG("Parsing configfile %s\n", path);
FREE(current_configpath);
current_configpath = path;
/* initialize default bindings if we're just validating the config file */
if (!use_nagbar && bindings == NULL) {
bindings = scalloc(1, sizeof(struct bindings_head));
TAILQ_INIT(bindings);
}
return parse_file(path, use_nagbar);
}
/*
* (Re-)loads the configuration file (sets useful defaults before).
*
*/
void load_configuration(xcb_connection_t *conn, const char *override_configpath, bool reload) {
2011-09-11 18:31:06 -04:00
if (reload) {
/* First ungrab the keys */
ungrab_all_keys(conn);
2011-09-11 18:31:06 -04:00
struct Mode *mode;
while (!SLIST_EMPTY(&modes)) {
mode = SLIST_FIRST(&modes);
FREE(mode->name);
/* Clear the old binding list */
while (!TAILQ_EMPTY(mode->bindings)) {
Binding *bind = TAILQ_FIRST(mode->bindings);
TAILQ_REMOVE(mode->bindings, bind, bindings);
binding_free(bind);
2011-09-11 18:31:06 -04:00
}
FREE(mode->bindings);
2011-09-11 18:31:06 -04:00
SLIST_REMOVE(&modes, mode, Mode, modes);
FREE(mode);
2011-09-11 18:31:06 -04:00
}
2009-07-23 12:14:24 -04:00
2011-09-11 18:31:06 -04:00
struct Assignment *assign;
while (!TAILQ_EMPTY(&assignments)) {
assign = TAILQ_FIRST(&assignments);
if (assign->type == A_TO_WORKSPACE)
FREE(assign->dest.workspace);
else if (assign->type == A_COMMAND)
FREE(assign->dest.command);
match_free(&(assign->match));
2011-09-11 18:31:06 -04:00
TAILQ_REMOVE(&assignments, assign, assignments);
FREE(assign);
}
/* Clear bar configs */
Barconfig *barconfig;
while (!TAILQ_EMPTY(&barconfigs)) {
barconfig = TAILQ_FIRST(&barconfigs);
FREE(barconfig->id);
for (int c = 0; c < barconfig->num_outputs; c++)
free(barconfig->outputs[c]);
while (!TAILQ_EMPTY(&(barconfig->bar_bindings))) {
struct Barbinding *binding = TAILQ_FIRST(&(barconfig->bar_bindings));
FREE(binding->command);
TAILQ_REMOVE(&(barconfig->bar_bindings), binding, bindings);
FREE(binding);
}
while (!TAILQ_EMPTY(&(barconfig->tray_outputs))) {
struct tray_output_t *tray_output = TAILQ_FIRST(&(barconfig->tray_outputs));
FREE(tray_output->output);
TAILQ_REMOVE(&(barconfig->tray_outputs), tray_output, tray_outputs);
FREE(tray_output);
}
FREE(barconfig->outputs);
FREE(barconfig->socket_path);
FREE(barconfig->status_command);
FREE(barconfig->i3bar_command);
FREE(barconfig->font);
FREE(barconfig->colors.background);
FREE(barconfig->colors.statusline);
FREE(barconfig->colors.separator);
FREE(barconfig->colors.focused_background);
FREE(barconfig->colors.focused_statusline);
FREE(barconfig->colors.focused_separator);
FREE(barconfig->colors.focused_workspace_border);
FREE(barconfig->colors.focused_workspace_bg);
FREE(barconfig->colors.focused_workspace_text);
FREE(barconfig->colors.active_workspace_border);
FREE(barconfig->colors.active_workspace_bg);
FREE(barconfig->colors.active_workspace_text);
FREE(barconfig->colors.inactive_workspace_border);
FREE(barconfig->colors.inactive_workspace_bg);
FREE(barconfig->colors.inactive_workspace_text);
FREE(barconfig->colors.urgent_workspace_border);
FREE(barconfig->colors.urgent_workspace_bg);
FREE(barconfig->colors.urgent_workspace_text);
FREE(barconfig->colors.binding_mode_border);
FREE(barconfig->colors.binding_mode_bg);
FREE(barconfig->colors.binding_mode_text);
TAILQ_REMOVE(&barconfigs, barconfig, configs);
FREE(barconfig);
}
/* Clear workspace names */
#if 0
2011-09-11 18:31:06 -04:00
Workspace *ws;
TAILQ_FOREACH(ws, workspaces, workspaces)
workspace_set_name(ws, NULL);
#endif
/* Invalidate pixmap caches in case font or colors changed */
Con *con;
TAILQ_FOREACH(con, &all_cons, all_cons)
FREE(con->deco_render_params);
/* Get rid of the current font */
free_font();
2011-09-11 18:31:06 -04:00
}
2009-07-23 12:14:24 -04:00
2011-09-11 18:31:06 -04:00
SLIST_INIT(&modes);
struct Mode *default_mode = scalloc(1, sizeof(struct Mode));
2011-09-11 18:31:06 -04:00
default_mode->name = sstrdup("default");
default_mode->bindings = scalloc(1, sizeof(struct bindings_head));
2011-09-11 18:31:06 -04:00
TAILQ_INIT(default_mode->bindings);
SLIST_INSERT_HEAD(&modes, default_mode, modes);
2011-09-11 18:31:06 -04:00
bindings = default_mode->bindings;
2009-02-24 18:50:30 -05:00
#define REQUIRED_OPTION(name) \
if (config.name == NULL) \
2011-09-11 18:31:06 -04:00
die("You did not specify required configuration option " #name "\n");
2009-02-24 18:50:30 -05:00
2011-09-11 18:31:06 -04:00
/* Clear the old config or initialize the data structure */
memset(&config, 0, sizeof(config));
2009-02-24 18:50:30 -05:00
/* Initialize default colors */
#define INIT_COLOR(x, cborder, cbackground, ctext, cindicator) \
do { \
x.border = get_colorpixel(cborder); \
x.background = get_colorpixel(cbackground); \
x.text = get_colorpixel(ctext); \
x.indicator = get_colorpixel(cindicator); \
2011-09-11 18:31:06 -04:00
} while (0)
config.client.background = get_colorpixel("#000000");
INIT_COLOR(config.client.focused, "#4c7899", "#285577", "#ffffff", "#2e9ef4");
INIT_COLOR(config.client.focused_inactive, "#333333", "#5f676a", "#ffffff", "#484e50");
INIT_COLOR(config.client.unfocused, "#333333", "#222222", "#888888", "#292d2e");
INIT_COLOR(config.client.urgent, "#2f343a", "#900000", "#ffffff", "#900000");
2013-12-15 05:42:40 -05:00
/* border and indicator color are ignored for placeholder contents */
INIT_COLOR(config.client.placeholder, "#000000", "#0c0c0c", "#ffffff", "#000000");
/* the last argument (indicator color) is ignored for bar colors */
INIT_COLOR(config.bar.focused, "#4c7899", "#285577", "#ffffff", "#000000");
INIT_COLOR(config.bar.unfocused, "#333333", "#222222", "#888888", "#000000");
INIT_COLOR(config.bar.urgent, "#2f343a", "#900000", "#ffffff", "#000000");
2011-09-11 18:31:06 -04:00
config.show_marks = true;
2011-09-11 18:31:06 -04:00
config.default_border = BS_NORMAL;
config.default_floating_border = BS_NORMAL;
config.default_border_width = logical_px(2);
config.default_floating_border_width = logical_px(2);
2011-09-11 18:31:06 -04:00
/* Set default_orientation to NO_ORIENTATION for auto orientation. */
config.default_orientation = NO_ORIENTATION;
/* Set default urgency reset delay to 500ms */
if (config.workspace_urgency_timer == 0)
config.workspace_urgency_timer = 0.5;
parse_configuration(override_configpath, true);
2011-09-11 18:31:06 -04:00
if (reload) {
translate_keysyms();
Use libxkbcommon for translating keysyms, support all XKB groups. fixes #1835 This commit improves the translation of keysyms to keycodes by loading keymaps using libxkbcommon-x11 and using libxkbcommon for figuring out the keymap, depending on each keybinding’s modifiers. This way, the upper layers of complex layouts are now usable with i3’s bindsym directive, such as de_neo’s layer 3 and higher. Furthermore, the commit generalizes the handling of different XKB groups. We formerly had support only for two separate groups, the default group 1, and group 2. While Mode_switch is only one way to switch to group 2, we called the binding option Mode_switch. With this commit, the new names Group1, Group2 (an alias for Mode_switch), Group3 and Group4 are introduced for configuring bindings. This is only useful for advanced keyboard layouts, such as people loading two keyboard layouts and switching between them (us, ru seems to be a popular combination). When grabbing keys, one can only specify the modifier mask, but not an XKB state mask (or value), so we still dynamically unbind and re-bind keys whenever the XKB group changes. The commit was manually tested using the following i3 config: bindsym Group4+n nop heya from group 4 bindsym Group3+n nop heya from group 3 bindsym Group2+n nop heya from group 2 bindsym n nop heya bindsym shift+N nop explicit shift binding bindsym shift+r nop implicit shift binding bindcode Group2+38 nop fallback overwritten in group 2 only bindcode 38 nop fallback …with the following layout: setxkbmap -layout "us,ua,ru,de" -variant ",winkeys,,neo" \ -option "grp:shift_caps_toggle,grp_led:scroll" \ -model pc104 -rules evdev By default (xkb group 1, us layout), pressing “n” will result in the “heya” message appearing. Pressing “a” will result in the “fallback” message appearing. “j” is not triggered. By pressing Shift+CapsLock you switch to the next group (xkb group 2, ua layout). Pressing “a” will result in the “fallback overwritten in group 2 only” message, pressing “n” will still result in “heya”. “j” is not triggered. In the next group (xkb group 3, ru layout), pressing “a” will result in the “fallback” message again, pressing “n” will result in “heya”, “j” is not triggered. In the last group (xkb group 4, de_neo layout), pressing “a” will still result in “fallback”, pressing “n” will result in “heya”, pressing “j” will result in “heya from group 4”. Pressing shift+n results in “explicit shift binding”, pressing shift+r results in “implicit shift binding”. This ensures that keysym translation falls back to looking at non-shift keys (“r” can be used instead of ”R”) and that the order of keybindings doesn’t play a role (“bindsym n” does not override “bindsym shift+n”, even though it’s specified earlier in the config). The fallback behavior ensures use-cases such as ticket #1775 are still covered. Only binding keys when the X server is in the corresponding XKB group ensures use-cases such as ticket #585 are still covered.
2015-08-23 16:49:32 -04:00
grab_all_keys(conn);
regrab_all_buttons(conn);
2011-09-11 18:31:06 -04:00
}
if (config.font.type == FONT_TYPE_NONE) {
2011-09-11 18:31:06 -04:00
ELOG("You did not specify required configuration option \"font\"\n");
config.font = load_font("fixed", true);
set_font(&config.font);
2011-09-11 18:31:06 -04:00
}
/* Redraw the currently visible decorations on reload, so that
* the possibly new drawing parameters changed. */
if (reload) {
x_deco_recurse(croot);
xcb_flush(conn);
}
#if 0
2011-09-11 18:31:06 -04:00
/* Set an empty name for every workspace which got no name */
Workspace *ws;
TAILQ_FOREACH(ws, workspaces, workspaces) {
if (ws->name != NULL) {
/* If the font was not specified when the workspace name
* was loaded, we need to predict the text width now */
if (ws->text_width == 0)
ws->text_width = predict_text_width(global_conn,
config.font, ws->name, ws->name_len);
continue;
}
workspace_set_name(ws, NULL);
}
#endif
2009-02-24 18:50:30 -05:00
}