diff --git a/include/data.h b/include/data.h index 4b886ae8..e8df78c7 100644 --- a/include/data.h +++ b/include/data.h @@ -202,7 +202,17 @@ struct regex { struct Binding { /** If true, the binding should be executed upon a KeyRelease event, not a * KeyPress (the default). */ - bool release; + enum { + /* This binding will only be executed upon KeyPress events */ + B_UPON_KEYPRESS = 0, + /* This binding will be executed either upon a KeyRelease event, or… */ + B_UPON_KEYRELEASE = 1, + /* …upon a KeyRelease event, even if the modifiers don’t match. This + * state is triggered from get_binding() when the corresponding + * KeyPress (!) happens, so that users can release the modifier keys + * before releasing the actual key. */ + B_UPON_KEYRELEASE_IGNORE_MODS = 2, + } release; /** Symbol the user specified in configfile, if any. This needs to be * stored with the binding to be able to re-convert it into a keycode diff --git a/src/cfgparse.y b/src/cfgparse.y index 5c166778..f175c720 100644 --- a/src/cfgparse.y +++ b/src/cfgparse.y @@ -913,8 +913,8 @@ bindsym: ; optional_release: - /* empty */ { $$ = false; } - | TOK_RELEASE { $$ = true; } + /* empty */ { $$ = B_UPON_KEYPRESS; } + | TOK_RELEASE { $$ = B_UPON_KEYRELEASE; } ; for_window: diff --git a/src/config.c b/src/config.c index 41491d86..fcf3841e 100644 --- a/src/config.c +++ b/src/config.c @@ -57,13 +57,35 @@ static void grab_keycode_for_binding(xcb_connection_t *conn, Binding *bind, uint Binding *get_binding(uint16_t modifiers, bool key_release, xcb_keycode_t keycode) { Binding *bind; + if (!key_release) { + /* On a KeyPress event, we first reset all + * B_UPON_KEYRELEASE_IGNORE_MODS bindings back to B_UPON_KEYRELEASE */ + TAILQ_FOREACH(bind, bindings, bindings) { + if (bind->release == B_UPON_KEYRELEASE_IGNORE_MODS) + bind->release = B_UPON_KEYRELEASE; + } + + /* Then we transition the KeyRelease bindings into a state where the + * modifiers no longer matter for the KeyRelease event so that users + * can release the modifier key before releasing the actual key. */ + TAILQ_FOREACH(bind, bindings, bindings) { + if (bind->release == B_UPON_KEYRELEASE && !key_release) + bind->release = B_UPON_KEYRELEASE_IGNORE_MODS; + } + } + TAILQ_FOREACH(bind, bindings, bindings) { - /* First compare the modifiers */ - if (bind->mods != modifiers) + /* First compare the modifiers (unless this is a + * B_UPON_KEYRELEASE_IGNORE_MODS binding and this is a KeyRelease + * event) */ + if (bind->mods != modifiers && + (bind->release != B_UPON_KEYRELEASE_IGNORE_MODS || + !key_release)) continue; /* Check if the binding is for a KeyPress or a KeyRelease event */ - if (bind->release != key_release) + if ((bind->release == B_UPON_KEYPRESS && key_release) || + (bind->release >= B_UPON_KEYRELEASE && !key_release)) continue; /* If a symbol was specified by the user, we need to look in