input: improve XTerm compatibility when modifyOtherKeys=2

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
This commit is contained in:
Daniel Eklöf 2022-04-07 12:42:44 +02:00
parent d1a072d67d
commit 5d6eaf606b
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F

51
input.c
View file

@ -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 XTerms 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 keys base symbol.
* - for A (shift-a), thats a
* - for ; (shift-,), thats ,
*/
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] = {