From 6d30e7d15de49ba531cd8f8cd99c356d7f1ca0fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 18 Mar 2020 14:29:34 +0100 Subject: [PATCH] input: bind key bindings to raw key codes too Before, when looking for a matching user key binding, we only matched *symbols*. This means that the physical keys that generate a specific key binding is layout dependent. What's worse, it may not even be possible to generate the key binding at all. Russian is one such layout, where all the "normal" (us) symbols are replaced. By using raw key codes, we can get around this - the key code has a direct mapping to the physical key. However, matching raw key codes **only** doesn't always make sense either. For now, match **both** symbols _and_ key codes. Symbols take precedence. TODO: we might have to make this configurable _per binding_. Note: 'search' mode still uses mostly hardcoded shortcuts that still have this problem (i.e. ctrl+g doesn't work with a russian layout). --- input.c | 44 +++++++++++++++++++++++++++++++++++++++++++- search.c | 9 +++++++++ wayland.c | 6 ++++++ wayland.h | 3 +++ 4 files changed, 61 insertions(+), 1 deletion(-) diff --git a/input.c b/input.c index b9f1a7d0..8a51fe7a 100644 --- a/input.c +++ b/input.c @@ -125,6 +125,8 @@ input_parse_key_binding_for_action( combo != NULL; combo = strtok_r(NULL, " ", &save1)) { + xkb_keycode_list_t key_codes = tll_init(); + LOG_DBG("%s", combo); for (char *save2 = NULL, *key = strtok_r(combo, "+", &save2), *next_key = strtok_r(NULL, "+", &save2); @@ -152,19 +154,43 @@ input_parse_key_binding_for_action( key); break; } + + /* + * Find all key codes that map to the lower case + * version of the 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) + tll_push_back(key_codes, code); + } + + xkb_state_unref(state); } } LOG_DBG("action=%u: mods=0x%08x, sym=%d", action, mod_mask, sym); - if (sym == XKB_KEY_NoSymbol) + if (sym == XKB_KEY_NoSymbol) { + assert(tll_length(key_codes) == 0); + tll_free(key_codes); continue; + } assert(sym != 0); if (bindings != NULL) { const struct key_binding binding = { .mods = mod_mask, .sym = sym, + .key_codes = key_codes, .action = action, }; @@ -207,8 +233,15 @@ keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard, xkb_context_unref(wayl->kbd.xkb); wayl->kbd.xkb = NULL; } + + tll_foreach(wayl->kbd.bindings.key, it) + tll_free(it->item.key_codes); tll_free(wayl->kbd.bindings.key); + tll_foreach(wayl->kbd.bindings.search, it) + tll_free(it->item.key_codes); + tll_free(wayl->kbd.bindings.search); + wayl->kbd.xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS); wayl->kbd.xkb_keymap = xkb_keymap_new_from_string( wayl->kbd.xkb, map_str, XKB_KEYMAP_FORMAT_TEXT_V1, @@ -525,10 +558,19 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, * User configurable bindings */ tll_foreach(wayl->kbd.bindings.key, it) { + /* Match symbol */ if (it->item.mods == effective_mods && it->item.sym == sym) { input_execute_binding(term, it->item.action, serial); goto maybe_repeat; } + + /* Match raw key code */ + tll_foreach(it->item.key_codes, code) { + if (code->item == key) { + input_execute_binding(term, it->item.action, serial); + goto maybe_repeat; + } + } } /* diff --git a/search.c b/search.c index 98ad64eb..35d1be0f 100644 --- a/search.c +++ b/search.c @@ -430,10 +430,19 @@ search_input(struct terminal *term, uint32_t key, xkb_keysym_t sym, * User configurable bindings */ tll_foreach(term->wl->kbd.bindings.search, it) { + /* Match symbol */ if (it->item.mods == mods && it->item.sym == sym) { input_execute_binding(term, it->item.action, serial); return; } + + /* Match raw key code */ + tll_foreach(it->item.key_codes, code) { + if (code->item == key) { + input_execute_binding(term, it->item.action, serial); + return; + } + } } /* Cancel search */ diff --git a/wayland.c b/wayland.c index c9eedc60..e4907f96 100644 --- a/wayland.c +++ b/wayland.c @@ -923,8 +923,14 @@ wayl_destroy(struct wayland *wayl) if (wayl->presentation != NULL) wp_presentation_destroy(wayl->presentation); + tll_foreach(wayl->kbd.bindings.key, it) + tll_free(it->item.key_codes); tll_free(wayl->kbd.bindings.key); + + tll_foreach(wayl->kbd.bindings.search, it) + tll_free(it->item.key_codes); tll_free(wayl->kbd.bindings.search); + if (wayl->kbd.xkb_compose_state != NULL) xkb_compose_state_unref(wayl->kbd.xkb_compose_state); if (wayl->kbd.xkb_compose_table != NULL) diff --git a/wayland.h b/wayland.h index 70b21d4b..6ad46d83 100644 --- a/wayland.h +++ b/wayland.h @@ -84,9 +84,12 @@ enum binding_action { BIND_ACTION_COUNT, }; +typedef tll(xkb_keycode_t) xkb_keycode_list_t; + struct key_binding { xkb_mod_mask_t mods; xkb_keysym_t sym; + xkb_keycode_list_t key_codes; enum binding_action action; }; typedef tll(struct key_binding) key_binding_list_t;