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;