From 5d6eaf606b5713365a232f188c922454943f2c09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 7 Apr 2022 12:42:44 +0200 Subject: [PATCH] input: improve XTerm compatibility when modifyOtherKeys=2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before this, foot always emitted a CSI if _any_ modifier was active. XTerm doesn’t behave quite like this. Instead, it appears to special-case shift somewhat: If the generated symbol (e.g ‘A’) is the upper case version of the pressed key’s base symbol (‘a’), and is in the range a-z, emit a CSI. If not emit a plain symbol: Examples (Swedish layout): * shift-a (generated symbol is ‘A’) emits a CSI * shift-, (generated symbol is ‘;’) emits ‘;’ * shift-alt-, (generated symbol is ‘;’) emits a CSI Closes #1009 --- input.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/input.c b/input.c index 1bf1e14b..ba45a59d 100644 --- a/input.c +++ b/input.c @@ -1096,7 +1096,56 @@ legacy_kbd_protocol(struct seat *seat, struct terminal *term, bool ctrl_is_in_effect = (keymap_mods & MOD_CTRL) != 0; bool ctrl_seq = is_control_key(sym) || (count == 1 && IS_CTRL(utf8[0])); - if (keymap_mods != MOD_NONE && (term->modify_other_keys_2 || + bool modify_other_keys2_in_effect = false; + + if (term->modify_other_keys_2) { + /* + * Try to mimic XTerm’s behavior, when holding shift: + * + * - if other modifiers are pressed (e.g. Alt), emit a CSI escape + * - upper-case symbols A-Z are encoded as an CSI escape + * - other upper-case symbols (e.g ‘Ö’) or emitted as is + * - non-upper cased symbols are _mostly_ emitted as is (foot + * always emits as is) + * + * Examples (assuming Swedish layout): + * - Shift-a (‘A’) emits a CSI + * - Shift-, (‘;’) emits ‘;’ + * - Shift-Alt-, (Alt-;) emits a CSI + * - Shift-ö (‘Ö’) emits ‘Ö’ + */ + + /* Any modifiers, besides shift active? */ + const xkb_mod_mask_t shift_mask = 1 << seat->kbd.mod_shift; + if ((ctx->mods & ~shift_mask & seat->kbd.bind_significant) != 0) + modify_other_keys2_in_effect = true; + + else { + const xkb_layout_index_t layout_idx = xkb_state_key_get_layout( + seat->kbd.xkb_state, ctx->key); + + /* + * Get pressed key’s base symbol. + * - for ‘A’ (shift-a), that’s ‘a’ + * - for ‘;’ (shift-,), that’s ‘,’ + */ + const xkb_keysym_t *base_syms = NULL; + size_t base_count = xkb_keymap_key_get_syms_by_level( + seat->kbd.xkb_keymap, ctx->key, layout_idx, 0, &base_syms); + + /* Check if base symbol(s) is a-z. If so, emit CSI */ + const xkb_keysym_t lower_cased_sym = xkb_keysym_to_lower(ctx->sym); + for (size_t i = 0; i < base_count; i++) { + const xkb_keysym_t s = base_syms[i]; + if (lower_cased_sym == s && s >= XKB_KEY_a && s <= XKB_KEY_z) { + modify_other_keys2_in_effect = true; + break; + } + } + } + } + + if (keymap_mods != MOD_NONE && (modify_other_keys2_in_effect || (ctrl_is_in_effect && !ctrl_seq))) { static const int mod_param_map[32] = {