diff --git a/include/config.h b/include/config.h index c0105cee..6bb53209 100644 --- a/include/config.h +++ b/include/config.h @@ -21,6 +21,7 @@ typedef struct Config Config; extern Config config; extern bool config_use_lexer; +extern SLIST_HEAD(modes_head, Mode) modes; /** * Part of the struct Config. It makes sense to group colors for background, @@ -46,6 +47,19 @@ struct Variable { SLIST_ENTRY(Variable) variables; }; +/** + * The configuration file can contain multiple sets of bindings. Apart from the + * default set (name == "default"), you can specify other sets and change the + * currently active set of bindings by using the "mode " command. + * + */ +struct Mode { + char *name; + struct bindings_head *bindings; + + SLIST_ENTRY(Mode) modes; +}; + /** * Holds part of the configuration (the part which is not already in dedicated * structures in include/data.h). @@ -97,4 +111,10 @@ void ungrab_all_keys(xcb_connection_t *conn); */ void grab_all_keys(xcb_connection_t *conn); +/** + * Switches the key bindings to the given mode, if the mode exists + * + */ +void switch_mode(xcb_connection_t *conn, const char *new_mode); + #endif diff --git a/include/i3.h b/include/i3.h index 935d8141..e34c5da3 100644 --- a/include/i3.h +++ b/include/i3.h @@ -27,7 +27,7 @@ extern xcb_connection_t *global_conn; extern xcb_key_symbols_t *keysyms; extern char **start_argv; extern Display *xkbdpy; -extern TAILQ_HEAD(bindings_head, Binding) bindings; +extern TAILQ_HEAD(bindings_head, Binding) *bindings; extern TAILQ_HEAD(autostarts_head, Autostart) autostarts; extern TAILQ_HEAD(assignments_head, Assignment) assignments; extern SLIST_HEAD(stack_wins_head, Stack_Window) stack_wins; diff --git a/src/cfgparse.y b/src/cfgparse.y index 15f0df8b..7c0bf900 100644 --- a/src/cfgparse.y +++ b/src/cfgparse.y @@ -222,7 +222,7 @@ command: bindline: binding { - TAILQ_INSERT_TAIL(&bindings, $1, bindings); + TAILQ_INSERT_TAIL(bindings, $1, bindings); } ; @@ -262,8 +262,23 @@ bindsym: mode: TOKMODE WHITESPACE QUOTEDSTRING WHITESPACE '{' modelines '}' { + if (strcasecmp($3, "default") == 0) { + printf("You cannot use the name \"default\" for your mode\n"); + exit(1); + } printf("\t now in mode %s\n", $3); printf("\t current bindings = %p\n", current_bindings); + Binding *binding; + TAILQ_FOREACH(binding, current_bindings, bindings) { + printf("got binding on mods %d, keycode %d, symbol %s, command %s\n", + binding->mods, binding->keycode, binding->symbol, binding->command); + } + + struct Mode *mode = scalloc(sizeof(struct Mode)); + mode->name = strdup($3); + mode->bindings = current_bindings; + current_bindings = NULL; + SLIST_INSERT_HEAD(&modes, mode, modes); } ; diff --git a/src/commands.c b/src/commands.c index 0517a075..539a2402 100644 --- a/src/commands.c +++ b/src/commands.c @@ -948,6 +948,12 @@ void parse_command(xcb_connection_t *conn, const char *command) { return; } + if (STARTS_WITH(command, "mode ")) { + const char *rest = command + strlen("mode "); + switch_mode(conn, rest); + return; + } + /* Is it an ? */ if (STARTS_WITH(command, "exit")) { LOG("User issued exit-command, exiting without error.\n"); diff --git a/src/config.c b/src/config.c index fe6fc5f2..52cf55b2 100644 --- a/src/config.c +++ b/src/config.c @@ -31,6 +31,7 @@ void parse_file(const char *f); Config config; +struct modes_head modes; bool config_use_lexer = false; @@ -99,7 +100,7 @@ static void grab_keycode_for_binding(xcb_connection_t *conn, Binding *bind, uint */ void grab_all_keys(xcb_connection_t *conn) { Binding *bind; - TAILQ_FOREACH(bind, &bindings, bindings) { + TAILQ_FOREACH(bind, bindings, bindings) { /* The easy case: the user specified a keycode directly. */ if (bind->keycode > 0) { grab_keycode_for_binding(conn, bind, bind->keycode); @@ -137,6 +138,28 @@ void grab_all_keys(xcb_connection_t *conn) { } } +/* + * Switches the key bindings to the given mode, if the mode exists + * + */ +void switch_mode(xcb_connection_t *conn, const char *new_mode) { + struct Mode *mode; + + LOG("Switching to mode %s\n", new_mode); + + SLIST_FOREACH(mode, &modes, modes) { + if (strcasecmp(mode->name, new_mode) != 0) + continue; + + ungrab_all_keys(conn); + bindings = mode->bindings; + grab_all_keys(conn); + return; + } + + LOG("ERROR: Mode not found\n"); +} + /* * Reads the configuration from ~/.i3/config or /etc/i3/config if not found. * @@ -149,13 +172,22 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath, /* First ungrab the keys */ ungrab_all_keys(conn); - /* Clear the old binding and assignment lists */ + struct Mode *mode; Binding *bind; - while (!TAILQ_EMPTY(&bindings)) { - bind = TAILQ_FIRST(&bindings); - TAILQ_REMOVE(&bindings, bind, bindings); - FREE(bind->command); - FREE(bind); + while (!SLIST_EMPTY(&modes)) { + mode = SLIST_FIRST(&modes); + FREE(mode->name); + + /* Clear the old binding list */ + bindings = mode->bindings; + while (!TAILQ_EMPTY(bindings)) { + bind = TAILQ_FIRST(bindings); + TAILQ_REMOVE(bindings, bind, bindings); + FREE(bind->command); + FREE(bind); + } + FREE(bindings); + SLIST_REMOVE(&modes, mode, Mode, modes); } struct Assignment *assign; @@ -167,6 +199,16 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath, } } + SLIST_INIT(&modes); + + struct Mode *default_mode = scalloc(sizeof(struct Mode)); + default_mode->name = sstrdup("default"); + default_mode->bindings = scalloc(sizeof(struct bindings_head)); + TAILQ_INIT(default_mode->bindings); + SLIST_INSERT_HEAD(&modes, default_mode, modes); + + bindings = default_mode->bindings; + SLIST_HEAD(variables_head, Variable) variables; #define OPTION_STRING(name) \ @@ -364,7 +406,7 @@ void load_configuration(xcb_connection_t *conn, const char *override_configpath, LOG("keycode = %d, symbol = %s, modifiers = %d, command = *%s*\n", new->keycode, new->symbol, modifiers, rest); new->mods = modifiers; new->command = sstrdup(rest); - TAILQ_INSERT_TAIL(&bindings, new, bindings); + TAILQ_INSERT_TAIL(bindings, new, bindings); continue; } diff --git a/src/handlers.c b/src/handlers.c index f3b7bfa4..cf28b906 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -116,7 +116,7 @@ int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press_event_ /* Find the binding */ Binding *bind; - TAILQ_FOREACH(bind, &bindings, bindings) { + TAILQ_FOREACH(bind, bindings, bindings) { /* First compare the modifiers */ if (bind->mods != state_filtered) continue; @@ -137,7 +137,7 @@ int handle_key_press(void *ignored, xcb_connection_t *conn, xcb_key_press_event_ /* No match? Then it was an actively grabbed key, that is with Mode_switch, and the user did not press Mode_switch, so just pass it… */ - if (bind == TAILQ_END(&bindings)) { + if (bind == TAILQ_END(bindings)) { xcb_allow_events(conn, ReplayKeyboard, event->time); xcb_flush(conn); return 1; diff --git a/src/mainx.c b/src/mainx.c index f8d12432..5ed63f17 100644 --- a/src/mainx.c +++ b/src/mainx.c @@ -61,7 +61,7 @@ Display *xkbdpy; xcb_key_symbols_t *keysyms; /* The list of key bindings */ -struct bindings_head bindings = TAILQ_HEAD_INITIALIZER(bindings); +struct bindings_head *bindings; /* The list of exec-lines */ struct autostarts_head autostarts = TAILQ_HEAD_INITIALIZER(autostarts);