Key encoding: in legacy mode use legacy encoding for a few more combinations

Some legacy terminal applications get confused seeing CSI u escape
codes. Since it is relatively common to press ctrl or shift and
space/enter/tab/backspace, emit the same bytes as traditional terminals
do for these common keys.
This commit is contained in:
Kovid Goyal 2021-02-21 11:14:25 +05:30
parent de100ac67c
commit 56fcbb95ab
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 77 additions and 48 deletions

View File

@ -415,17 +415,18 @@ mode* (the ``smkx/rmkx`` terminfo capabilities). This form is used only in
"F11", "kf11", "CSI 23 ~"
"F12", "kf12", "CSI 24 ~"
There are a few more functional keys that have special cased legacy
encodings:
There are a few more functional keys that have special cased legacy encodings.
These are present because they are commonly used and for the sake of legacy
terminal applications that get confused when seeing CSI u escape codes:
.. csv-table:: C0 controls
:header: "Key", "Encodings"
:header: "Key", "No mods", "Ctrl", "Alt", "Shift", "Ctrl + Shift", "Alt + Shift", "Ctrl + Alt"
"Enter", "Plain - 0xd, alt+Enter - 0x1b 0x1d"
"Escape", "Plain - 0x1b, alt+Esc - 0x1b 0x1b"
"Backspace", "Plain - 0x7f, alt+Backspace - 0x1b 0x7f, ctrl+Backspace - 0x08"
"Space", "Plain - 0x20, ctrl+Space - 0x0, alt+space - 0x1b 0x20"
"Tab", "Plain - 0x09, shift+Tab - CSI Z"
"Enter", "0xd", "0xd", "0x1b 0xd", "0xd", "0xd", "0x1b 0xd", "0x1b 0xd"
"Escape", "0x1b", "0x1b", "0x1b 0x1b", "0x1b", "0x1b", "0x1b 0x1b", "0x1b 0x1b"
"Backspace", "0x7f", "0x8", "0x1b 0x7f", "0x7f", "0x8", "0x1b 0x7f", "0x1b 0x8"
"Tab", "0x9", "0x9", "0x1b 0x9", "CSI Z", "CSI Z", "0x1b CSI Z", "0x1b 0x9"
"Space", "0x20", "0x0", "0x1b 0x20", "0x20", "0x0", "0x1b 0x20", "0x1b 0x0"
Note that :kbd:`Backspace` and :kbd:`ctrl+Backspace` are swapped in some
terminals, this can be detected using the ``kbs`` terminfo property that

View File

@ -126,6 +126,34 @@ convert_kp_key_to_normal_key(uint32_t key_number) {
return key_number;
}
static int
legacy_functional_key_encoding_with_modifiers(uint32_t key_number, const KeyEvent *ev, char *output) {
const char *prefix = ev->mods.value & ALT ? "\x1b" : "";
const char *main = "";
switch (key_number) {
case GLFW_FKEY_ENTER:
main = "\x0d";
break;
case GLFW_FKEY_ESCAPE:
main = "\x1b";
break;
case GLFW_FKEY_BACKSPACE:
main = ev->mods.value & CTRL ? "\x08" : "\x7f";
break;
case GLFW_FKEY_TAB:
if (ev->mods.value & SHIFT) {
prefix = ev->mods.value & ALT ? "\x1b\x1b" : "\x1b";
main = "[Z";
} else {
main = "\t";
}
break;
default:
return -1;
}
return snprintf(output, KEY_BUFFER_SIZE, "%s%s", prefix, main);
}
static int
encode_function_key(const KeyEvent *ev, char *output) {
#define SIMPLE(val) return snprintf(output, KEY_BUFFER_SIZE, "%s", val);
@ -158,18 +186,11 @@ encode_function_key(const KeyEvent *ev, char *output) {
default: break;
}
}
} else if (legacy_mode) {
int num = legacy_functional_key_encoding_with_modifiers(key_number, ev, output);
if (num > -1) return num;
}
if (ev->mods.value == ALT && !ev->disambiguate) {
switch(key_number) {
case GLFW_FKEY_TAB: SIMPLE("\x1b\t");
case GLFW_FKEY_ENTER: SIMPLE("\x1b\r");
case GLFW_FKEY_BACKSPACE: SIMPLE("\x1b\x7f");
}
}
if (ev->mods.value == SHIFT && key_number == GLFW_FKEY_TAB && !ev->disambiguate) { SIMPLE("\x1b[Z"); }
if (ev->mods.value == CTRL && key_number == GLFW_FKEY_BACKSPACE && !ev->disambiguate) { SIMPLE("\x08"); }
#undef SIMPLE
#define S(number, trailer) key_number = number; csi_trailer = trailer; break
switch(key_number) {
/* start special numbers (auto generated by gen-key-constants.py do not edit) */
@ -288,6 +309,10 @@ encode_printable_ascii_key_legacy(const KeyEvent *ev, char *output) {
return snprintf(output, KEY_BUFFER_SIZE, "%c", ctrled_key(key));
if (mods == (CTRL | ALT))
return snprintf(output, KEY_BUFFER_SIZE, "\x1b%c", ctrled_key(key));
if (key == ' ') {
if (mods == (CTRL | SHIFT)) return snprintf(output, KEY_BUFFER_SIZE, "%c", ctrled_key(key));
if (mods == (ALT | SHIFT)) return snprintf(output, KEY_BUFFER_SIZE, "\x1b%c", key);
}
return 0;
}

View File

@ -60,29 +60,32 @@ def mods_test(key, plain=None, shift=None, ctrl=None, alt=None, calt=None, cshif
def a(a, b):
ae(a, b, f"{a.encode('ascii')} != {b.encode('ascii')}")
a(e(), plain or c())
a(e(mods=defines.GLFW_MOD_SHIFT), shift or c(defines.GLFW_MOD_SHIFT))
a(e(mods=defines.GLFW_MOD_CONTROL), ctrl or c(defines.GLFW_MOD_CONTROL))
a(e(mods=defines.GLFW_MOD_ALT | defines.GLFW_MOD_CONTROL), calt or c(defines.GLFW_MOD_ALT | defines.GLFW_MOD_CONTROL))
a(e(mods=defines.GLFW_MOD_SHIFT | defines.GLFW_MOD_CONTROL), cshift or c(defines.GLFW_MOD_CONTROL | defines.GLFW_MOD_SHIFT))
a(e(mods=defines.GLFW_MOD_SHIFT | defines.GLFW_MOD_ALT), ashift or c(defines.GLFW_MOD_ALT | defines.GLFW_MOD_SHIFT))
mods_test(defines.GLFW_FKEY_ENTER, '\x0d', alt='\033\x0d', csi_num=ord('\r'))
mods_test(defines.GLFW_FKEY_KP_ENTER, '\x0d', alt='\033\x0d', csi_num=ord('\r'))
mods_test(defines.GLFW_FKEY_ESCAPE, '\x1b', alt='\033\033', csi_num=27)
mods_test(defines.GLFW_FKEY_BACKSPACE, '\x7f', alt='\033\x7f', ctrl='\x08', csi_num=127)
mods_test(defines.GLFW_FKEY_TAB, '\t', alt='\033\t', shift='\x1b[Z', csi_num=ord('\t'))
mods_test(defines.GLFW_FKEY_INSERT, csi_num=2, trailer='~')
mods_test(defines.GLFW_FKEY_KP_INSERT, csi_num=2, trailer='~')
mods_test(defines.GLFW_FKEY_DELETE, csi_num=3, trailer='~')
mods_test(defines.GLFW_FKEY_KP_DELETE, csi_num=3, trailer='~')
mods_test(defines.GLFW_FKEY_PAGE_UP, csi_num=5, trailer='~')
mods_test(defines.GLFW_FKEY_KP_PAGE_UP, csi_num=5, trailer='~')
mods_test(defines.GLFW_FKEY_KP_PAGE_DOWN, csi_num=6, trailer='~')
mods_test(defines.GLFW_FKEY_HOME, csi_num=1, trailer='H')
mods_test(defines.GLFW_FKEY_KP_HOME, csi_num=1, trailer='H')
mods_test(defines.GLFW_FKEY_END, csi_num=1, trailer='F')
mods_test(defines.GLFW_FKEY_KP_END, csi_num=1, trailer='F')
def w(a, b):
return c(b) if a is None else a
a(e(), plain or c())
a(e(mods=defines.GLFW_MOD_SHIFT), w(shift, defines.GLFW_MOD_SHIFT))
a(e(mods=defines.GLFW_MOD_CONTROL), w(ctrl, defines.GLFW_MOD_CONTROL))
a(e(mods=defines.GLFW_MOD_ALT | defines.GLFW_MOD_CONTROL), w(calt, defines.GLFW_MOD_ALT | defines.GLFW_MOD_CONTROL))
a(e(mods=defines.GLFW_MOD_SHIFT | defines.GLFW_MOD_CONTROL), w(cshift, defines.GLFW_MOD_CONTROL | defines.GLFW_MOD_SHIFT))
a(e(mods=defines.GLFW_MOD_SHIFT | defines.GLFW_MOD_ALT), w(ashift, defines.GLFW_MOD_ALT | defines.GLFW_MOD_SHIFT))
def mkp(name, *a, **kw):
for x in (f'GLFW_FKEY_{name}', f'GLFW_FKEY_KP_{name}'):
k = getattr(defines, x)
mods_test(k, *a, **kw)
mkp('ENTER', '\x0d', alt='\033\x0d', ctrl='\x0d', shift='\x0d', ashift='\033\x0d', calt='\033\x0d', cshift='\x0d')
mods_test(defines.GLFW_FKEY_ESCAPE, '\x1b', alt='\033\033', ctrl='\x1b', shift='\x1b', calt='\x1b\x1b', cshift='\x1b', ashift='\x1b\x1b')
mods_test(defines.GLFW_FKEY_BACKSPACE, '\x7f', alt='\033\x7f', ctrl='\x08', shift='\x7f', ashift='\033\x7f', cshift='\x08', calt='\x1b\x08')
mods_test(defines.GLFW_FKEY_TAB, '\t', alt='\033\t', shift='\x1b[Z', ctrl='\t', ashift='\x1b\x1b[Z', cshift='\x1b[Z', calt='\x1b\t')
mkp('INSERT', csi_num=2, trailer='~')
mkp('DELETE', csi_num=3, trailer='~')
mkp('PAGE_UP', csi_num=5, trailer='~')
mkp('PAGE_DOWN', csi_num=6, trailer='~')
mkp('HOME', csi_num=1, trailer='H')
mkp('END', csi_num=1, trailer='F')
mods_test(defines.GLFW_FKEY_F1, csi_num=1, trailer='P')
mods_test(defines.GLFW_FKEY_F2, csi_num=1, trailer='Q')
mods_test(defines.GLFW_FKEY_F3, csi_num=1, trailer='R')
@ -95,14 +98,10 @@ def a(a, b):
mods_test(defines.GLFW_FKEY_F10, csi_num=21, trailer='~')
mods_test(defines.GLFW_FKEY_F11, csi_num=23, trailer='~')
mods_test(defines.GLFW_FKEY_F12, csi_num=24, trailer='~')
mods_test(defines.GLFW_FKEY_UP, csi_num=1, trailer='A')
mods_test(defines.GLFW_FKEY_KP_UP, csi_num=1, trailer='A')
mods_test(defines.GLFW_FKEY_DOWN, csi_num=1, trailer='B')
mods_test(defines.GLFW_FKEY_KP_DOWN, csi_num=1, trailer='B')
mods_test(defines.GLFW_FKEY_RIGHT, csi_num=1, trailer='C')
mods_test(defines.GLFW_FKEY_KP_RIGHT, csi_num=1, trailer='C')
mods_test(defines.GLFW_FKEY_LEFT, csi_num=1, trailer='D')
mods_test(defines.GLFW_FKEY_KP_LEFT, csi_num=1, trailer='D')
mkp('UP', csi_num=1, trailer='A')
mkp('DOWN', csi_num=1, trailer='B')
mkp('RIGHT', csi_num=1, trailer='C')
mkp('LEFT', csi_num=1, trailer='D')
# legacy key tests {{{
# start legacy letter tests (auto generated by gen-key-constants.py do not edit)
@ -397,6 +396,10 @@ def a(a, b):
ae(enc(key=ord(' ')), ' ')
ae(enc(key=ord(' '), mods=ctrl), '\0')
ae(enc(key=ord(' '), mods=alt), '\x1b ')
ae(enc(key=ord(' '), mods=shift), ' ')
ae(enc(key=ord(' '), mods=ctrl | alt), '\x1b\0')
ae(enc(key=ord(' '), mods=ctrl | shift), '\0')
ae(enc(key=ord(' '), mods=alt | shift), '\x1b ')
ae(enc(key=ord('i'), mods=ctrl | shift), csi(ctrl | shift, ord('i')))
ae(enc(key=defines.GLFW_FKEY_LEFT_SHIFT), '')
ae(enc(key=defines.GLFW_FKEY_CAPS_LOCK), '')