input: rewrite of how we match foot’s own key bindings

Bindings are matched in one out of three ways:

* By translated (by XKB) symbols
* By untranslated symbols
* By raw key codes

A translated symbol is affected by pressed modifiers, some of which
can be “consumed”. Consumed modifiers to not partake in the comparison
with the binding’s modifiers. In this mode, ctrl+shift+2 maps to
ctrl+@ on a US layout.

Untranslated symbols, or un-shifted symbols refer to the “base” symbol
of the pressed key, i.e. it’s unaffected by modifiers. In this mode,
consumed modifiers *do* partake in the comparison with the binding’s
modifiers, and ctrl+shift+2 maps to ctrl+shift+2 on a US layout.

More examples: ctrl+shift+u maps to ctrl+U in the translated lookup,
while ctrl+shift+u maps to ctrl+shift+u in the untranslated lookup.

Finally, we also match raw key codes. This allows our bindings to work
using the same physical keys when the user switches between latin and
non-latin layouts.

This means key bindings in foot.ini *must* not include both +shift+
and a *shifted* key. I.e. ctrl+shift+U is not a valid combo as it
cannot be triggered. Unfortunately, this was how you were supposed to
write bindings up until now... so, we try to detect such bindings, log
a deprecation warning and then “fix” the binding for the user.

When specifying bindings in foot.ini, both ctrl+U and ctrl+shift+u are
valid, and will work. The latter is preferred though, since we cannot
detect the raw key code for the former variant. Personally, I also
prefer the latter one because it is more explicit; it’s more obvious
which keys are involved.

However, in some cases it makes more sense to use the other
variant. Typically for non-letter combos.
This commit is contained in:
Daniel Eklöf 2021-02-27 20:42:31 +01:00
parent decc655d48
commit 5e64e06a55
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
6 changed files with 117 additions and 42 deletions

50
input.c
View file

@ -350,20 +350,18 @@ key_codes_for_xkb_sym(struct xkb_keymap *keymap, xkb_keysym_t sym)
xkb_keycode_list_t key_codes = tll_init();
/*
* Find all key codes that map to the lower case
* version of the symbol.
* Find all key codes that map to this symbol.
*
* This allows us to match bindings in other layouts
* too.
*/
xkb_keysym_t lower_sym = xkb_keysym_to_lower(sym);
struct xkb_state *state = xkb_state_new(keymap);
for (xkb_keycode_t code = xkb_keymap_min_keycode(keymap);
code <= xkb_keymap_max_keycode(keymap);
code++)
{
if (xkb_state_key_get_one_sym(state, code) == lower_sym)
if (xkb_state_key_get_one_sym(state, code) == sym)
tll_push_back(key_codes, code);
}
@ -829,15 +827,24 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial,
mods &= significant;
consumed &= significant;
xkb_layout_index_t layout_idx =
xkb_state_key_get_layout(seat->kbd.xkb_state, key);
const xkb_keysym_t *raw_syms = NULL;
size_t raw_count = xkb_keymap_key_get_syms_by_level(
seat->kbd.xkb_keymap, key, layout_idx, 0, &raw_syms);
if (term->is_searching) {
if (should_repeat)
start_repeater(seat, key);
search_input(seat, term, key, sym, mods, serial);
search_input(
seat, term, key, sym, mods, consumed, raw_syms, raw_count, serial);
return;
} else if (urls_mode_is_active(term)) {
if (should_repeat)
start_repeater(seat, key);
urls_input(seat, term, key, sym, mods, serial);
urls_input(
seat, term, key, sym, mods, consumed, raw_syms, raw_count, serial);
return;
}
@ -863,20 +870,35 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial,
* User configurable bindings
*/
tll_foreach(seat->kbd.bindings.key, it) {
if (it->item.mods != mods)
const struct key_binding *bind = &it->item;
/* Match translated symbol */
if (bind->sym == sym &&
bind->mods == (mods & ~consumed) &&
execute_binding(
seat, term, bind->action, bind->pipe_argv, serial))
{
goto maybe_repeat;
}
if (bind->mods != mods)
continue;
/* Match symbol */
if (it->item.sym == sym) {
if (execute_binding(seat, term, it->item.action, it->item.pipe_argv, serial))
/* Match untranslated symbols */
for (size_t i = 0; i < raw_count; i++) {
if (bind->sym == raw_syms[i] && execute_binding(
seat, term, bind->action, bind->pipe_argv, serial))
{
goto maybe_repeat;
}
}
/* Match raw key code */
tll_foreach(it->item.key_codes, code) {
if (code->item == key) {
if (execute_binding(seat, term, it->item.action, it->item.pipe_argv, serial))
goto maybe_repeat;
tll_foreach(bind->key_codes, code) {
if (code->item == key && execute_binding(
seat, term, bind->action, bind->pipe_argv, serial))
{
goto maybe_repeat;
}
}
}