]> git.sur5r.net Git - i3/i3/commitdiff
Implement modes. Modes allow you to use different keybindings and switch between...
authorMichael Stapelberg <michael@stapelberg.de>
Sun, 27 Sep 2009 16:45:39 +0000 (18:45 +0200)
committerMichael Stapelberg <michael@stapelberg.de>
Sun, 27 Sep 2009 16:45:39 +0000 (18:45 +0200)
For example, you can create a mode which will let you resize windows
with some easy to use keys. So, instead of binding a combination
of your homerow and modifiers to resize, like this:

bind Mod4+44 resize right +10
bind Mod4+45 resize right -10
...

You can instead define a new mode:

mode "resize" {
bind 44 resize right +10
bind 45 resize right -10
...
bind 36 mode default
}

bindsym Mod4+r mode resize

So, if you press Mod4+r now, your keybindings will be set to the ones
defined in your resize mode above. You can then use your homerow
(without any other modifier) to resize the current column/row and
press enter to go back to the default mode when you are done.

Note that using this option requires you to enable the new lexer/parser
by passing the -l flag to i3 when starting.

include/config.h
include/i3.h
src/cfgparse.y
src/commands.c
src/config.c
src/handlers.c
src/mainx.c

index c0105cee4507292bcc0384909074c39b671d8920..6bb53209bdf9268bab19421acc56237d2b9e244f 100644 (file)
@@ -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 <name>" 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
index 935d81410427beab78a564bcfe50224302d24856..e34c5da3904ee23f5d38b3789faf189a0f77af66 100644 (file)
@@ -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;
index 15f0df8b8b302d03771e731878de6d853d214197..7c0bf900c44ae9d25b997614702bff7714854e3f 100644 (file)
@@ -222,7 +222,7 @@ command:
 bindline:
         binding
         {
-                TAILQ_INSERT_TAIL(&bindings, $<binding>1, bindings);
+                TAILQ_INSERT_TAIL(bindings, $<binding>1, bindings);
         }
         ;
 
@@ -262,8 +262,23 @@ bindsym:
 mode:
         TOKMODE WHITESPACE QUOTEDSTRING WHITESPACE '{' modelines '}'
         {
+                if (strcasecmp($<string>3, "default") == 0) {
+                        printf("You cannot use the name \"default\" for your mode\n");
+                        exit(1);
+                }
                 printf("\t now in mode %s\n", $<string>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($<string>3);
+                mode->bindings = current_bindings;
+                current_bindings = NULL;
+                SLIST_INSERT_HEAD(&modes, mode, modes);
         }
         ;
 
index 0517a075d7f7b784472f856888815d6a11cd9fbc..539a2402a21030f79290baeb80dffa108e65be52 100644 (file)
@@ -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 <exit>? */
         if (STARTS_WITH(command, "exit")) {
                 LOG("User issued exit-command, exiting without error.\n");
index fe6fc5f238a88514a3b12ada964a847e13a66535..52cf55b26ca1c84e857b18725503ebdfa60cd792 100644 (file)
@@ -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;
                 }
 
index f3b7bfa47164575e1f5cc3ed59b92eb4902b807a..cf28b9064573ead8252baf4951e32d3d747300cb 100644 (file)
@@ -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;
index f8d12432930b7b875f346b968253e9b65cfca2c2..5ed63f17e20439cb38cc837601373caf533998a2 100644 (file)
@@ -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);