From 5e64e06a550c2499a23f03749986791136cbbcb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 27 Feb 2021 20:42:31 +0100 Subject: [PATCH] =?UTF-8?q?input:=20rewrite=20of=20how=20we=20match=20foot?= =?UTF-8?q?=E2=80=99s=20own=20key=20bindings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- config.c | 35 ++++++++++++++++++++++++++--------- input.c | 50 ++++++++++++++++++++++++++++++++++++-------------- search.c | 34 ++++++++++++++++++++++++++-------- search.h | 7 +++++-- url-mode.c | 29 +++++++++++++++++++++-------- url-mode.h | 4 +++- 6 files changed, 117 insertions(+), 42 deletions(-) diff --git a/config.c b/config.c index 13673123..b1653916 100644 --- a/config.c +++ b/config.c @@ -1168,6 +1168,7 @@ out: static bool parse_key_combos(struct config *conf, const char *combos, key_combo_list_t *key_combos, + const char *section, const char *option, const char *path, unsigned lineno) { xassert(tll_length(*key_combos) == 0); @@ -1179,7 +1180,7 @@ parse_key_combos(struct config *conf, const char *combos, key_combo_list_t *key_ combo = strtok_r(NULL, " ", &tok_ctx)) { struct config_key_modifiers modifiers = {0}; - const char *key = strrchr(combo, '+'); + char *key = strrchr(combo, '+'); if (key == NULL) { /* No modifiers */ @@ -1190,11 +1191,26 @@ parse_key_combos(struct config *conf, const char *combos, key_combo_list_t *key_ key++; /* Skip past the '+' */ } + if (modifiers.shift && strlen(key) == 1 && (*key >= 'A' && *key <= 'Z')) { + LOG_WARN( + "%s:%d: [%s]: %s: %s: " + "upper case keys not supported with explicit 'Shift' modifier", + path, lineno, section, option, combo); + user_notification_add( + &conf->notifications, USER_NOTIFICATION_DEPRECATED, + "%s:%d: [%s]: %s: \033[1m%s\033[m: " + "shifted keys not supported with explicit \033[1mShift\033[m " + "modifier", + path, lineno, section, option, combo); + *key = tolower(*key); + } + /* Translate key name to symbol */ xkb_keysym_t sym = xkb_keysym_from_name(key, 0); if (sym == XKB_KEY_NoSymbol) { - LOG_AND_NOTIFY_ERR("%s:%d: %s: key is not a valid XKB key name", - path, lineno, key); + LOG_AND_NOTIFY_ERR( + "%s:%d: [%s]: %s: ]%s: key is not a valid XKB key name", + path, lineno, section, option, key); goto err; } @@ -1373,7 +1389,8 @@ parse_key_binding_section( } key_combo_list_t key_combos = tll_init(); - if (!parse_key_combos(conf, value, &key_combos, path, lineno) || + if (!parse_key_combos( + conf, value, &key_combos, section, key, path, lineno) || has_key_binding_collisions( conf, action, binding_action_map, bindings, &key_combos, path, lineno)) @@ -2040,10 +2057,10 @@ add_default_key_bindings(struct config *conf) add_binding(BIND_ACTION_SCROLLBACK_UP_PAGE, shift, XKB_KEY_Page_Up); add_binding(BIND_ACTION_SCROLLBACK_DOWN_PAGE, shift, XKB_KEY_Page_Down); - add_binding(BIND_ACTION_CLIPBOARD_COPY, ctrl_shift, XKB_KEY_C); - add_binding(BIND_ACTION_CLIPBOARD_PASTE, ctrl_shift, XKB_KEY_V); + add_binding(BIND_ACTION_CLIPBOARD_COPY, ctrl_shift, XKB_KEY_c); + add_binding(BIND_ACTION_CLIPBOARD_PASTE, ctrl_shift, XKB_KEY_v); add_binding(BIND_ACTION_PRIMARY_PASTE, shift, XKB_KEY_Insert); - add_binding(BIND_ACTION_SEARCH_START, ctrl_shift, XKB_KEY_R); + add_binding(BIND_ACTION_SEARCH_START, ctrl_shift, XKB_KEY_r); add_binding(BIND_ACTION_FONT_SIZE_UP, ctrl, XKB_KEY_plus); add_binding(BIND_ACTION_FONT_SIZE_UP, ctrl, XKB_KEY_equal); add_binding(BIND_ACTION_FONT_SIZE_UP, ctrl, XKB_KEY_KP_Add); @@ -2051,8 +2068,8 @@ add_default_key_bindings(struct config *conf) add_binding(BIND_ACTION_FONT_SIZE_DOWN, ctrl, XKB_KEY_KP_Subtract); add_binding(BIND_ACTION_FONT_SIZE_RESET, ctrl, XKB_KEY_0); add_binding(BIND_ACTION_FONT_SIZE_RESET, ctrl, XKB_KEY_KP_0); - add_binding(BIND_ACTION_SPAWN_TERMINAL, ctrl_shift, XKB_KEY_N); - add_binding(BIND_ACTION_SHOW_URLS_LAUNCH, ctrl_shift, XKB_KEY_U); + add_binding(BIND_ACTION_SPAWN_TERMINAL, ctrl_shift, XKB_KEY_n); + add_binding(BIND_ACTION_SHOW_URLS_LAUNCH, ctrl_shift, XKB_KEY_u); #undef add_binding } diff --git a/input.c b/input.c index 907a99bb..db84c8e4 100644 --- a/input.c +++ b/input.c @@ -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; } } } diff --git a/search.c b/search.c index 5dfa7b58..30a50ae5 100644 --- a/search.c +++ b/search.c @@ -783,7 +783,9 @@ execute_binding(struct seat *seat, struct terminal *term, void search_input(struct seat *seat, struct terminal *term, uint32_t key, - xkb_keysym_t sym, xkb_mod_mask_t mods, uint32_t serial) + xkb_keysym_t sym, xkb_mod_mask_t mods, xkb_mod_mask_t consumed, + const xkb_keysym_t *raw_syms, size_t raw_count, + uint32_t serial) { LOG_DBG("search: input: sym=%d/0x%x, mods=0x%08x", sym, sym, mods); @@ -795,12 +797,13 @@ search_input(struct seat *seat, struct terminal *term, uint32_t key, /* Key bindings */ tll_foreach(seat->kbd.bindings.search, it) { - if (it->item.mods != mods) - continue; + const struct key_binding *bind = &it->item; - /* Match symbol */ - if (it->item.sym == sym) { - if (execute_binding(seat, term, it->item.action, serial, + /* Match translated symbol */ + if (bind->sym == sym && + bind->mods == (mods & ~consumed)) { + + if (execute_binding(seat, term, bind->action, serial, &update_search_result, &redraw)) { goto update_search; @@ -808,10 +811,25 @@ search_input(struct seat *seat, struct terminal *term, uint32_t key, return; } + if (bind->mods != mods) + continue; + + /* Match untranslated symbols */ + for (size_t i = 0; i < raw_count; i++) { + if (bind->sym == raw_syms[i]) { + if (execute_binding(seat, term, bind->action, serial, + &update_search_result, &redraw)) + { + goto update_search; + } + return; + } + } + /* Match raw key code */ - tll_foreach(it->item.key_codes, code) { + tll_foreach(bind->key_codes, code) { if (code->item == key) { - if (execute_binding(seat, term, it->item.action, serial, + if (execute_binding(seat, term, bind->action, serial, &update_search_result, &redraw)) { goto update_search; diff --git a/search.h b/search.h index ece0d68a..5db1610d 100644 --- a/search.h +++ b/search.h @@ -5,6 +5,9 @@ void search_begin(struct terminal *term); void search_cancel(struct terminal *term); -void search_input(struct seat *seat, struct terminal *term, uint32_t key, - xkb_keysym_t sym, xkb_mod_mask_t mods, uint32_t serial); +void search_input( + struct seat *seat, struct terminal *term, uint32_t key, + xkb_keysym_t sym, xkb_mod_mask_t mods, xkb_mod_mask_t consumed, + const xkb_keysym_t *raw_syms, size_t raw_count, + uint32_t serial); void search_add_chars(struct terminal *term, const char *text, size_t len); diff --git a/url-mode.c b/url-mode.c index 791b4ce0..34866fb8 100644 --- a/url-mode.c +++ b/url-mode.c @@ -115,23 +115,36 @@ activate_url(struct seat *seat, struct terminal *term, const struct url *url) void urls_input(struct seat *seat, struct terminal *term, uint32_t key, - xkb_keysym_t sym, xkb_mod_mask_t mods, uint32_t serial) + xkb_keysym_t sym, xkb_mod_mask_t mods, xkb_mod_mask_t consumed, + const xkb_keysym_t *raw_syms, size_t raw_count, + uint32_t serial) { /* Key bindings */ tll_foreach(seat->kbd.bindings.url, it) { - if (it->item.mods != mods) - continue; + const struct key_binding *bind = &it->item; - /* Match symbol */ - if (it->item.sym == sym) { - execute_binding(seat, term, it->item.action, serial); + /* Match translated symbol */ + if (bind->sym == sym && + bind->mods == (mods & ~consumed)) + { + execute_binding(seat, term, bind->action, serial); return; } + if (bind->mods != mods) + continue; + + for (size_t i = 0; i < raw_count; i++) { + if (bind->sym == raw_syms[i]) { + execute_binding(seat, term, bind->action, serial); + return; + } + } + /* Match raw key code */ - tll_foreach(it->item.key_codes, code) { + tll_foreach(bind->key_codes, code) { if (code->item == key) { - execute_binding(seat, term, it->item.action, serial); + execute_binding(seat, term, bind->action, serial); return; } } diff --git a/url-mode.h b/url-mode.h index 28abde18..b1314780 100644 --- a/url-mode.h +++ b/url-mode.h @@ -20,4 +20,6 @@ void urls_render(struct terminal *term); void urls_reset(struct terminal *term); void urls_input(struct seat *seat, struct terminal *term, uint32_t key, - xkb_keysym_t sym, xkb_mod_mask_t mods, uint32_t serial); + xkb_keysym_t sym, xkb_mod_mask_t mods, xkb_mod_mask_t consumed, + const xkb_keysym_t *raw_syms, size_t raw_count, + uint32_t serial);