i3 - improved tiling WM


Ignore KeyRelease events without KeyPress

Patch status: rejected

Patch by Ari Edelkind

Long description:

When switching modes, i3 completely reconfigures the key bindings.
There's a bug, such that if a mode is set up with the likes of:

mode "new" {
    bindsym --release $mykey mode "busted"
}
bindsym $mykey mode "new"

then i3 would switch to mode "new" when $mykey is pressed, and then to
"busted" when $mykey is released.

This patch fixes the bug by ensuring that, before a KeyRelease event
will be acknowledged, a key has been pressed since the key bindings have
been reconfigured.

To apply this patch, use:
curl http://cr.i3wm.org/patch/530/raw.patch | git am

b/src/bindings.c

27
@@ -15,6 +15,12 @@
28
 const char *DEFAULT_BINDING_MODE = "default";
29
 
30
 /*
31
+ * Whether a key has been pressed since reconfiguring keybindings
32
+ *
33
+ */
34
+static bool key_pressed_in_bind_config;
35
+
36
+/*
37
  * Returns the mode specified by `name` or creates a new mode and adds it to
38
  * the list of modes.
39
  *
40
@@ -104,6 +110,7 @@ static void grab_keycode_for_binding(xcb_connection_t *conn, Binding *bind, uint
41
  */
42
 void grab_all_keys(xcb_connection_t *conn, bool bind_mode_switch) {
43
     Binding *bind;
44
+    key_pressed_in_bind_config = 0;
45
     TAILQ_FOREACH(bind, bindings, bindings) {
46
         if (bind->input_type != B_KEYBOARD ||
47
                 (bind_mode_switch && (bind->mods & BIND_MODE_SWITCH) == 0) ||
48
@@ -131,6 +138,7 @@ Binding *get_keyboard_binding(uint16_t modifiers, bool key_release, xcb_keycode_
49
     Binding *bind;
50
 
51
     if (!key_release) {
52
+        key_pressed_in_bind_config = 1;
53
         /* On a KeyPress event, we first reset all
54
          * B_UPON_KEYRELEASE_IGNORE_MODS bindings back to B_UPON_KEYRELEASE */
55
         TAILQ_FOREACH(bind, bindings, bindings) {
56
@@ -181,7 +189,21 @@ Binding *get_keyboard_binding(uint16_t modifiers, bool key_release, xcb_keycode_
57
         break;
58
     }
59
 
60
-    return (bind == TAILQ_END(bindings) ? NULL : bind);
61
+    if (bind == TAILQ_END(bindings))
62
+        return NULL;
63
+
64
+    if (key_release) {
65
+        /* If we see a KeyRelease event without a KeyPress event, that means
66
+         * we've reconfigured our keybindings (e.g. switched modes), and a new
67
+         * keybinding was created with the same key combination as the one that
68
+         * triggered the change. */
69
+        if (!key_pressed_in_bind_config) {
70
+            DLOG("Ignored KeyRelease for key not pressed in this binding configuration\n");
71
+            return NULL;
72
+        }
73
+    }
74
+
75
+    return bind;
76
 }
77
 
78
 /*