diff --git a/docs/keyboard-protocol.rst b/docs/keyboard-protocol.rst index b2d9e6832..73c87accf 100644 --- a/docs/keyboard-protocol.rst +++ b/docs/keyboard-protocol.rst @@ -26,6 +26,12 @@ advanced usages. The protocol is based on initial work in `fixterms issues in that proposal, listed at the :ref:`bottom of this document `. +You can see this protocol with all enhancements in action by running:: + + kitty +kitten key_demo + +inside the kitty terminal to report key events. + .. versionadded:: 0.20.0 Quickstart @@ -46,7 +52,7 @@ text, or using these escape codes, for keys that do not produce text (``CSI`` is the bytes ``0x1b 0x5b``:: CSI number ; modifiers [u~] - CSI 1; modifiers [ABCDFHPQRSZ] + CSI 1; modifiers [ABCDFHPQRS] 0x0d - for the Enter key 0x7f or 0x08 - for Backspace 0x09 - for Tab @@ -58,9 +64,9 @@ modifiers pressed for the key event. The encoding is described in the :ref:`modifiers` section. The second form is used for a few functional keys, such as the :kbd:`Home, End, -Tab, Arrow keys and F1-F4`, they are enumerated in the :ref:`functional` table below. +Arrow keys and F1-F4`, they are enumerated in the :ref:`functional` table below. Note that if no modifiers are present the parameters are omitted entirely -giving an escape code of the form ``CSI [ABCDFHPQRSZ]``. +giving an escape code of the form ``CSI [ABCDFHPQRS]``. If you want support for more advanced features such as repeat and release events, alternate keys for shortcut matching et cetera, these can be turned on @@ -274,7 +280,7 @@ With this flag turned on, all key events that do not generate text are represented in one of the following two forms:: CSI number; modifier u - CSI 1; modifier [~ABCDFHPQRSZ] + CSI 1; modifier [~ABCDFHPQRS] This makes it very easy to parse key events in an application. In particular, :kbd:`ctrl+c` will no longer generate the ``SIGINT`` signal, but instead be @@ -420,8 +426,8 @@ For legacy compatibility, the keys following algorithm: #. If the :kbd:`alt` key is pressed output the byte for ``ESC (0x1b)`` -#. If the :kbd:`ctrl` modifier is pressed mask the seventh bit ``(0b1000000)`` - in the key's ASCII code number and output that +#. If the :kbd:`ctrl` modifier is pressed map the key using the table + in :ref:`ctrl_mapping`. #. Otherwise, if the :kbd:`shift` modifier is pressed, output the shifted key, for example, ``A`` for ``a`` and ``$`` for ``4``. #. Otherwise, output the key unmodified @@ -572,6 +578,45 @@ compatibility reasons. Note that the escape codes above of the form ``CSI 1 letter`` will omit the ``1`` if there are no modifiers, since ``1`` is the default value. +.. _ctrl_mapping: + +Legacy :kbd:`ctrl` mapping of ASCII keys +------------------------------------------ + +When the :kbd:`ctrl` key and another key are pressed on the keyboard, terminals +map the result *for some keys* to a *C0 control code* i.e. an value from ``0 - +31``. This mapping was historically dependent on the layout of hardware +terminal keyboards and is not specified anywhere, completely. The best known +reference is `Tabe 3-5 here `_. + +The table below provides a mapping that is a commonly used superset of the table above. +Any ASCII keys not in the table must be left untouched by :kbd:`ctrl`. + +.. {{{ +.. start ctrl mapping (auto generated by gen-key-constants.py do not edit) +.. csv-table:: Emitted bytes when :kbd:`ctrl` is held down and a key is pressed + :header: "Key", "Byte", "Key", "Byte", "Key", "Byte" + + " ", "0", "/", "31", "0", "48" + "1", "49", "2", "0", "3", "27" + "4", "28", "5", "29", "6", "30" + "7", "31", "8", "127", "9", "57" + "?", "127", "@", "0", "[", "27" + "\", "28", "]", "29", "^", "30" + "_", "31", "a", "1", "b", "2" + "c", "3", "d", "4", "e", "5" + "f", "6", "g", "7", "h", "8" + "i", "9", "j", "10", "k", "11" + "l", "12", "m", "13", "n", "14" + "o", "15", "p", "16", "q", "17" + "r", "18", "s", "19", "t", "20" + "u", "21", "v", "22", "w", "23" + "x", "24", "y", "25", "z", "26" + "~", "30" + +.. end ctrl mapping +.. }}} + .. _fixterms_bugs: Bugs in fixterms @@ -590,7 +635,7 @@ specification. * No support for the :kbd:`super` modifier. * Makes no mention of cursor key mode and how it changes encodings * Incorrectly encoding shifted keys when shift modifier is used, for - instance, for :kbd:`ctrl+shift+I`. + instance, for :kbd:`ctrl+shift+i` is encoded as :kbd:`ctrl+I`. * No way to have non-conflicting escape codes for :kbd:`alt+letter, ctrl+letter, ctrl+alt+letter` key presses * No way to specify both shifted and unshifted keys for robust shortcut @@ -602,3 +647,12 @@ specification. * No way to report key events for presses that generate text, useful for gaming. Think of using the :kbd:`WASD` keys to control movement. * Only a small subset of all possible functional keys are assigned numbers. + * Claims the ``CSI u`` escape code has no fixed meaning, but has been used + for decades as ``SCORC`` for instance by xterm and ansi.sys and + `DECSMBV import string -from typing import Dict, List +from typing import Dict, List, Any +from pprint import pformat functional_key_defs = '''# {{{ # kitty XKB macOS @@ -141,6 +142,14 @@ for line in functional_key_defs.splitlines(): if parts[1] != '-': name_to_xkb[name] = parts[1] last_code = start_code + len(functional_key_names) - 1 +ctrl_mapping = { + ' ': 0, '@': 0, 'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6, 'g': 7, + 'h': 8, 'i': 9, 'j': 10, 'k': 11, 'l': 12, 'm': 13, 'n': 14, 'o': 15, 'p': 16, + 'q': 17, 'r': 18, 's': 19, 't': 20, 'u': 21, 'v': 22, 'w': 23, 'x': 24, + 'y': 25, 'z': 26, '[': 27, '\\': 28, ']': 29, '^': 30, '~': 30, '/': 31, + '_': 31, '?': 127, '0': 48, '1': 49, '2': 0, '3': 27, '4': 28, + '5': 29, '6': 30, '7': 31, '8': 127, '9': 57 +} def patch_file(path: str, what: str, text: str, start_marker: str = '/* ', end_marker: str = ' */') -> None: @@ -167,6 +176,10 @@ def patch_file(path: str, what: str, text: str, start_marker: str = '/* ', end_m f.write(raw) +def serialize_dict(x: dict) -> str: + return pformat(x, indent=4).replace('{', '{\n ', 1) + + def generate_glfw_header() -> None: lines = [ 'typedef enum {', @@ -205,10 +218,13 @@ def generate_functional_table() -> None: '' ] enc_lines = [] + tilde_trailers = set() for name, code in name_to_code.items(): if name in functional_encoding_overrides or name in different_trailer_functionals: - code = oc = functional_encoding_overrides.get(name, code) trailer = different_trailer_functionals.get(name, '~') + if trailer == '~': + tilde_trailers.add(code) + code = oc = functional_encoding_overrides.get(name, code) code = code if trailer in '~u' else 1 enc_lines.append((' ' * 8) + f"case GLFW_FKEY_{name.upper()}: S({code}, '{trailer}');") if code == 1 and name not in ('up', 'down', 'left', 'right'): @@ -220,6 +236,16 @@ def generate_functional_table() -> None: lines.append('') patch_file('docs/keyboard-protocol.rst', 'functional key table', '\n'.join(lines), start_marker='.. ', end_marker='') patch_file('kitty/key_encoding.c', 'special numbers', '\n'.join(enc_lines)) + code_to_name = {v: k.upper() for k, v in name_to_code.items()} + csi_map = {v: name_to_code[k] for k, v in functional_encoding_overrides.items()} + letter_trailer_codes = { + v: functional_encoding_overrides.get(k, name_to_code.get(k)) + for k, v in different_trailer_functionals.items() if v in 'ABCDHFPQRSZ'} + text = f'functional_key_number_to_name_map = {serialize_dict(code_to_name)}' + text += f'\ncsi_number_to_functional_number_map = {serialize_dict(csi_map)}' + text += f'\nletter_trailer_to_csi_number_map = {letter_trailer_codes!r}' + text += f'\ntilde_trailers = {tilde_trailers!r}' + patch_file('kitty/key_encoding.py', 'csi mapping', text, start_marker='# ', end_marker='') def generate_legacy_text_key_maps() -> None: @@ -236,7 +262,7 @@ def generate_legacy_text_key_maps() -> None: def simple(c: str) -> None: shifted = shift_map.get(c, c) - ctrled = chr(ord(c) & 0b111111) + ctrled = chr(ctrl_mapping.get(c, ord(c))) for m in range(16): if m == 0: tests.append(f'{tp}ae(enc(ord({c!r})), {c!r})') @@ -257,11 +283,41 @@ def generate_legacy_text_key_maps() -> None: patch_file('kitty_tests/keys.py', 'legacy letter tests', '\n'.join(tests), start_marker='# ', end_marker='') +def chunks(lst: List, n: int) -> Any: + """Yield successive n-sized chunks from lst.""" + for i in range(0, len(lst), n): + yield lst[i:i + n] + + +def generate_ctrl_mapping() -> None: + lines = [ + '.. csv-table:: Emitted bytes when :kbd:`ctrl` is held down and a key is pressed', + ' :header: "Key", "Byte", "Key", "Byte", "Key", "Byte"', + '' + ] + items = [] + mi = [] + for k in sorted(ctrl_mapping): + items.append(k) + val = str(ctrl_mapping[k]) + items.append(val) + if k in "\\'": + k = '\\' + k + mi.append(f" case '{k}': return {val};") + + for line_items in chunks(items, 6): + lines.append(' ' + ', '.join(f'"{x}"' for x in line_items)) + lines.append('') + patch_file('docs/keyboard-protocol.rst', 'ctrl mapping', '\n'.join(lines), start_marker='.. ', end_marker='') + patch_file('kitty/key_encoding.c', 'ctrl mapping', '\n'.join(mi)) + + def main() -> None: generate_glfw_header() generate_xkb_mapping() generate_functional_table() generate_legacy_text_key_maps() + generate_ctrl_mapping() if __name__ == '__main__': diff --git a/glfw/glfw3.h b/glfw/glfw3.h index 1c0471b6f..89d338a56 100644 --- a/glfw/glfw3.h +++ b/glfw/glfw3.h @@ -472,12 +472,12 @@ typedef enum { * * If this bit is set one or more Control keys were held down. */ -#define GLFW_MOD_CONTROL 0x0002 +#define GLFW_MOD_ALT 0x0002 /*! @brief If this bit is set one or more Alt keys were held down. * * If this bit is set one or more Alt keys were held down. */ -#define GLFW_MOD_ALT 0x0004 +#define GLFW_MOD_CONTROL 0x0004 /*! @brief If this bit is set one or more Super keys were held down. * * If this bit is set one or more Super keys were held down. diff --git a/kitty/fast_data_types.pyi b/kitty/fast_data_types.pyi index 758157f59..b2ebdabaf 100644 --- a/kitty/fast_data_types.pyi +++ b/kitty/fast_data_types.pyi @@ -1083,6 +1083,12 @@ class ChildMonitor: class KeyEvent: + + def __init__( + self, key: int, shifted_key: int = 0, alternate_key: int = 0, mods: int = 0, action: int = 1, native_key: int = 1, ime_state: int = 0, text: str = '' + ): + pass + @property def key(self) -> int: pass diff --git a/kitty/glfw-wrapper.h b/kitty/glfw-wrapper.h index 7c5e90e7e..75075fb30 100644 --- a/kitty/glfw-wrapper.h +++ b/kitty/glfw-wrapper.h @@ -210,12 +210,12 @@ typedef enum { * * If this bit is set one or more Control keys were held down. */ -#define GLFW_MOD_CONTROL 0x0002 +#define GLFW_MOD_ALT 0x0002 /*! @brief If this bit is set one or more Alt keys were held down. * * If this bit is set one or more Alt keys were held down. */ -#define GLFW_MOD_ALT 0x0004 +#define GLFW_MOD_CONTROL 0x0004 /*! @brief If this bit is set one or more Super keys were held down. * * If this bit is set one or more Super keys were held down. diff --git a/kitty/key_encoding.c b/kitty/key_encoding.c index a30726fc2..92ca43147 100644 --- a/kitty/key_encoding.c +++ b/kitty/key_encoding.c @@ -206,7 +206,7 @@ encode_function_key(const KeyEvent *ev, char *output) { } static inline char -shifted_ascii_key(const uint32_t key) { +shifted_ascii_key(const uint32_t key) { // {{{ switch(key) { /* start shifted key map (auto generated by gen-key-constants.py do not edit) */ case '`': return '~'; @@ -258,25 +258,80 @@ shifted_ascii_key(const uint32_t key) { case 'z': return 'Z'; /* end shifted key map */ default: - return 0; + return key; } -} +} // }}} + +static char +ctrled_key(const uint32_t key) { // {{{ + switch(key) { + /* start ctrl mapping (auto generated by gen-key-constants.py do not edit) */ + case ' ': return 0; + case '/': return 31; + case '0': return 48; + case '1': return 49; + case '2': return 0; + case '3': return 27; + case '4': return 28; + case '5': return 29; + case '6': return 30; + case '7': return 31; + case '8': return 127; + case '9': return 57; + case '?': return 127; + case '@': return 0; + case '[': return 27; + case '\\': return 28; + case ']': return 29; + case '^': return 30; + case '_': return 31; + case 'a': return 1; + case 'b': return 2; + case 'c': return 3; + case 'd': return 4; + case 'e': return 5; + case 'f': return 6; + case 'g': return 7; + case 'h': return 8; + case 'i': return 9; + case 'j': return 10; + case 'k': return 11; + case 'l': return 12; + case 'm': return 13; + case 'n': return 14; + case 'o': return 15; + case 'p': return 16; + case 'q': return 17; + case 'r': return 18; + case 's': return 19; + case 't': return 20; + case 'u': return 21; + case 'v': return 22; + case 'w': return 23; + case 'x': return 24; + case 'y': return 25; + case 'z': return 26; + case '~': return 30; +/* end ctrl mapping */ + default: + return key; + } +} // }}} static int encode_printable_ascii_key_legacy(const KeyEvent *ev, char *output) { if (!ev->mods.value) return snprintf(output, KEY_BUFFER_SIZE, "%c", (char)ev->key); if (ev->disambiguate) return 0; - char shifted_key = shifted_ascii_key(ev->key); - shifted_key = (shifted_key && ev->mods.shift) ? shifted_key : (char)ev->key; + char shifted_key = (ev->mods.shift) ? shifted_ascii_key(ev->key) : (char)ev->key; if (ev->mods.value == SHIFT) return snprintf(output, KEY_BUFFER_SIZE, "%c", shifted_key); if ((ev->mods.value == ALT || ev->mods.value == (SHIFT | ALT))) return snprintf(output, KEY_BUFFER_SIZE, "\x1b%c", shifted_key); if (ev->mods.value == CTRL) - return snprintf(output, KEY_BUFFER_SIZE, "%c", ev->key & 0x3f); + return snprintf(output, KEY_BUFFER_SIZE, "%c", ctrled_key(ev->key)); if (ev->mods.value == (CTRL | ALT)) - return snprintf(output, KEY_BUFFER_SIZE, "\x1b%c", ev->key & 0x3f); + return snprintf(output, KEY_BUFFER_SIZE, "\x1b%c", ctrled_key(ev->key)); return 0; } diff --git a/kitty/keys.c b/kitty/keys.c index c3909128d..dce8a606c 100644 --- a/kitty/keys.c +++ b/kitty/keys.c @@ -20,10 +20,14 @@ typedef struct { PyObject *text; } PyKeyEvent; +static inline PyObject* convert_glfw_key_event_to_python(const GLFWkeyevent *ev); + static PyObject* -new(PyTypeObject *type UNUSED, PyObject UNUSED *args, PyObject UNUSED *kwds) { - PyErr_SetString(PyExc_TypeError, "Direct creation of KeyEvent objects is not supported"); - return NULL; +new(PyTypeObject *type UNUSED, PyObject *args, PyObject *kw) { + static char *kwds[] = {"key", "shifted_key", "alternate_key", "mods", "action", "native_key", "ime_state", "text", NULL}; + GLFWkeyevent ev = {.action=GLFW_PRESS}; + if (!PyArg_ParseTupleAndKeywords(args, kw, "I|IIiiiiz", kwds, &ev.key, &ev.shifted_key, &ev.alternate_key, &ev.mods, &ev.action, &ev.native_key, &ev.ime_state, &ev.text)) return NULL; + return convert_glfw_key_event_to_python(&ev); } static void @@ -229,7 +233,7 @@ pyencode_key_for_tty(PyObject *self UNUSED, PyObject *args, PyObject *kw) { unsigned int key = 0, shifted_key = 0, alternate_key = 0, mods = 0, action = GLFW_PRESS, key_encoding_flags = 0; const char *text = NULL; int cursor_key_mode = 0; - if (!PyArg_ParseTupleAndKeywords(args, kw, "I|IIIIIsp", kwds, &key, &shifted_key, &alternate_key, &mods, &action, &key_encoding_flags, &text, &cursor_key_mode)) return NULL; + if (!PyArg_ParseTupleAndKeywords(args, kw, "I|IIIIIzp", kwds, &key, &shifted_key, &alternate_key, &mods, &action, &key_encoding_flags, &text, &cursor_key_mode)) return NULL; GLFWkeyevent ev = { .key = key, .shifted_key = shifted_key, .alternate_key = alternate_key, .text = text, .action = action, .mods = mods }; char output[KEY_BUFFER_SIZE+1] = {0}; int num = encode_glfw_key_event(&ev, cursor_key_mode, key_encoding_flags, output); diff --git a/kitty_tests/keys.py b/kitty_tests/keys.py index 5db6736df..7760eddfb 100644 --- a/kitty_tests/keys.py +++ b/kitty_tests/keys.py @@ -105,8 +105,8 @@ class TestKeys(BaseTest): ae(enc(ord('`'), mods=shift), '~') ae(enc(ord('`'), mods=alt), "\x1b" + '`') ae(enc(ord('`'), mods=shift | alt), "\x1b" + '~') - ae(enc(ord('`'), mods=ctrl), ' ') - ae(enc(ord('`'), mods=ctrl | alt), "\x1b" + ' ') + ae(enc(ord('`'), mods=ctrl), '`') + ae(enc(ord('`'), mods=ctrl | alt), "\x1b" + '`') ae(enc(ord('1')), '1') ae(enc(ord('1'), mods=shift), '!') ae(enc(ord('1'), mods=alt), "\x1b" + '1') @@ -117,44 +117,44 @@ class TestKeys(BaseTest): ae(enc(ord('2'), mods=shift), '@') ae(enc(ord('2'), mods=alt), "\x1b" + '2') ae(enc(ord('2'), mods=shift | alt), "\x1b" + '@') - ae(enc(ord('2'), mods=ctrl), '2') - ae(enc(ord('2'), mods=ctrl | alt), "\x1b" + '2') + ae(enc(ord('2'), mods=ctrl), '\x00') + ae(enc(ord('2'), mods=ctrl | alt), "\x1b" + '\x00') ae(enc(ord('3')), '3') ae(enc(ord('3'), mods=shift), '#') ae(enc(ord('3'), mods=alt), "\x1b" + '3') ae(enc(ord('3'), mods=shift | alt), "\x1b" + '#') - ae(enc(ord('3'), mods=ctrl), '3') - ae(enc(ord('3'), mods=ctrl | alt), "\x1b" + '3') + ae(enc(ord('3'), mods=ctrl), '\x1b') + ae(enc(ord('3'), mods=ctrl | alt), "\x1b" + '\x1b') ae(enc(ord('4')), '4') ae(enc(ord('4'), mods=shift), '$') ae(enc(ord('4'), mods=alt), "\x1b" + '4') ae(enc(ord('4'), mods=shift | alt), "\x1b" + '$') - ae(enc(ord('4'), mods=ctrl), '4') - ae(enc(ord('4'), mods=ctrl | alt), "\x1b" + '4') + ae(enc(ord('4'), mods=ctrl), '\x1c') + ae(enc(ord('4'), mods=ctrl | alt), "\x1b" + '\x1c') ae(enc(ord('5')), '5') ae(enc(ord('5'), mods=shift), '%') ae(enc(ord('5'), mods=alt), "\x1b" + '5') ae(enc(ord('5'), mods=shift | alt), "\x1b" + '%') - ae(enc(ord('5'), mods=ctrl), '5') - ae(enc(ord('5'), mods=ctrl | alt), "\x1b" + '5') + ae(enc(ord('5'), mods=ctrl), '\x1d') + ae(enc(ord('5'), mods=ctrl | alt), "\x1b" + '\x1d') ae(enc(ord('6')), '6') ae(enc(ord('6'), mods=shift), '^') ae(enc(ord('6'), mods=alt), "\x1b" + '6') ae(enc(ord('6'), mods=shift | alt), "\x1b" + '^') - ae(enc(ord('6'), mods=ctrl), '6') - ae(enc(ord('6'), mods=ctrl | alt), "\x1b" + '6') + ae(enc(ord('6'), mods=ctrl), '\x1e') + ae(enc(ord('6'), mods=ctrl | alt), "\x1b" + '\x1e') ae(enc(ord('7')), '7') ae(enc(ord('7'), mods=shift), '&') ae(enc(ord('7'), mods=alt), "\x1b" + '7') ae(enc(ord('7'), mods=shift | alt), "\x1b" + '&') - ae(enc(ord('7'), mods=ctrl), '7') - ae(enc(ord('7'), mods=ctrl | alt), "\x1b" + '7') + ae(enc(ord('7'), mods=ctrl), '\x1f') + ae(enc(ord('7'), mods=ctrl | alt), "\x1b" + '\x1f') ae(enc(ord('8')), '8') ae(enc(ord('8'), mods=shift), '*') ae(enc(ord('8'), mods=alt), "\x1b" + '8') ae(enc(ord('8'), mods=shift | alt), "\x1b" + '*') - ae(enc(ord('8'), mods=ctrl), '8') - ae(enc(ord('8'), mods=ctrl | alt), "\x1b" + '8') + ae(enc(ord('8'), mods=ctrl), '\x7f') + ae(enc(ord('8'), mods=ctrl | alt), "\x1b" + '\x7f') ae(enc(ord('9')), '9') ae(enc(ord('9'), mods=shift), '(') ae(enc(ord('9'), mods=alt), "\x1b" + '9') @@ -225,164 +225,164 @@ class TestKeys(BaseTest): ae(enc(ord('/'), mods=shift), '?') ae(enc(ord('/'), mods=alt), "\x1b" + '/') ae(enc(ord('/'), mods=shift | alt), "\x1b" + '?') - ae(enc(ord('/'), mods=ctrl), '/') - ae(enc(ord('/'), mods=ctrl | alt), "\x1b" + '/') + ae(enc(ord('/'), mods=ctrl), '\x1f') + ae(enc(ord('/'), mods=ctrl | alt), "\x1b" + '\x1f') ae(enc(ord('a')), 'a') ae(enc(ord('a'), mods=shift), 'A') ae(enc(ord('a'), mods=alt), "\x1b" + 'a') ae(enc(ord('a'), mods=shift | alt), "\x1b" + 'A') - ae(enc(ord('a'), mods=ctrl), '!') - ae(enc(ord('a'), mods=ctrl | alt), "\x1b" + '!') + ae(enc(ord('a'), mods=ctrl), '\x01') + ae(enc(ord('a'), mods=ctrl | alt), "\x1b" + '\x01') ae(enc(ord('b')), 'b') ae(enc(ord('b'), mods=shift), 'B') ae(enc(ord('b'), mods=alt), "\x1b" + 'b') ae(enc(ord('b'), mods=shift | alt), "\x1b" + 'B') - ae(enc(ord('b'), mods=ctrl), '"') - ae(enc(ord('b'), mods=ctrl | alt), "\x1b" + '"') + ae(enc(ord('b'), mods=ctrl), '\x02') + ae(enc(ord('b'), mods=ctrl | alt), "\x1b" + '\x02') ae(enc(ord('c')), 'c') ae(enc(ord('c'), mods=shift), 'C') ae(enc(ord('c'), mods=alt), "\x1b" + 'c') ae(enc(ord('c'), mods=shift | alt), "\x1b" + 'C') - ae(enc(ord('c'), mods=ctrl), '#') - ae(enc(ord('c'), mods=ctrl | alt), "\x1b" + '#') + ae(enc(ord('c'), mods=ctrl), '\x03') + ae(enc(ord('c'), mods=ctrl | alt), "\x1b" + '\x03') ae(enc(ord('d')), 'd') ae(enc(ord('d'), mods=shift), 'D') ae(enc(ord('d'), mods=alt), "\x1b" + 'd') ae(enc(ord('d'), mods=shift | alt), "\x1b" + 'D') - ae(enc(ord('d'), mods=ctrl), '$') - ae(enc(ord('d'), mods=ctrl | alt), "\x1b" + '$') + ae(enc(ord('d'), mods=ctrl), '\x04') + ae(enc(ord('d'), mods=ctrl | alt), "\x1b" + '\x04') ae(enc(ord('e')), 'e') ae(enc(ord('e'), mods=shift), 'E') ae(enc(ord('e'), mods=alt), "\x1b" + 'e') ae(enc(ord('e'), mods=shift | alt), "\x1b" + 'E') - ae(enc(ord('e'), mods=ctrl), '%') - ae(enc(ord('e'), mods=ctrl | alt), "\x1b" + '%') + ae(enc(ord('e'), mods=ctrl), '\x05') + ae(enc(ord('e'), mods=ctrl | alt), "\x1b" + '\x05') ae(enc(ord('f')), 'f') ae(enc(ord('f'), mods=shift), 'F') ae(enc(ord('f'), mods=alt), "\x1b" + 'f') ae(enc(ord('f'), mods=shift | alt), "\x1b" + 'F') - ae(enc(ord('f'), mods=ctrl), '&') - ae(enc(ord('f'), mods=ctrl | alt), "\x1b" + '&') + ae(enc(ord('f'), mods=ctrl), '\x06') + ae(enc(ord('f'), mods=ctrl | alt), "\x1b" + '\x06') ae(enc(ord('g')), 'g') ae(enc(ord('g'), mods=shift), 'G') ae(enc(ord('g'), mods=alt), "\x1b" + 'g') ae(enc(ord('g'), mods=shift | alt), "\x1b" + 'G') - ae(enc(ord('g'), mods=ctrl), "'") - ae(enc(ord('g'), mods=ctrl | alt), "\x1b" + "'") + ae(enc(ord('g'), mods=ctrl), '\x07') + ae(enc(ord('g'), mods=ctrl | alt), "\x1b" + '\x07') ae(enc(ord('h')), 'h') ae(enc(ord('h'), mods=shift), 'H') ae(enc(ord('h'), mods=alt), "\x1b" + 'h') ae(enc(ord('h'), mods=shift | alt), "\x1b" + 'H') - ae(enc(ord('h'), mods=ctrl), '(') - ae(enc(ord('h'), mods=ctrl | alt), "\x1b" + '(') + ae(enc(ord('h'), mods=ctrl), '\x08') + ae(enc(ord('h'), mods=ctrl | alt), "\x1b" + '\x08') ae(enc(ord('i')), 'i') ae(enc(ord('i'), mods=shift), 'I') ae(enc(ord('i'), mods=alt), "\x1b" + 'i') ae(enc(ord('i'), mods=shift | alt), "\x1b" + 'I') - ae(enc(ord('i'), mods=ctrl), ')') - ae(enc(ord('i'), mods=ctrl | alt), "\x1b" + ')') + ae(enc(ord('i'), mods=ctrl), '\t') + ae(enc(ord('i'), mods=ctrl | alt), "\x1b" + '\t') ae(enc(ord('j')), 'j') ae(enc(ord('j'), mods=shift), 'J') ae(enc(ord('j'), mods=alt), "\x1b" + 'j') ae(enc(ord('j'), mods=shift | alt), "\x1b" + 'J') - ae(enc(ord('j'), mods=ctrl), '*') - ae(enc(ord('j'), mods=ctrl | alt), "\x1b" + '*') + ae(enc(ord('j'), mods=ctrl), '\n') + ae(enc(ord('j'), mods=ctrl | alt), "\x1b" + '\n') ae(enc(ord('k')), 'k') ae(enc(ord('k'), mods=shift), 'K') ae(enc(ord('k'), mods=alt), "\x1b" + 'k') ae(enc(ord('k'), mods=shift | alt), "\x1b" + 'K') - ae(enc(ord('k'), mods=ctrl), '+') - ae(enc(ord('k'), mods=ctrl | alt), "\x1b" + '+') + ae(enc(ord('k'), mods=ctrl), '\x0b') + ae(enc(ord('k'), mods=ctrl | alt), "\x1b" + '\x0b') ae(enc(ord('l')), 'l') ae(enc(ord('l'), mods=shift), 'L') ae(enc(ord('l'), mods=alt), "\x1b" + 'l') ae(enc(ord('l'), mods=shift | alt), "\x1b" + 'L') - ae(enc(ord('l'), mods=ctrl), ',') - ae(enc(ord('l'), mods=ctrl | alt), "\x1b" + ',') + ae(enc(ord('l'), mods=ctrl), '\x0c') + ae(enc(ord('l'), mods=ctrl | alt), "\x1b" + '\x0c') ae(enc(ord('m')), 'm') ae(enc(ord('m'), mods=shift), 'M') ae(enc(ord('m'), mods=alt), "\x1b" + 'm') ae(enc(ord('m'), mods=shift | alt), "\x1b" + 'M') - ae(enc(ord('m'), mods=ctrl), '-') - ae(enc(ord('m'), mods=ctrl | alt), "\x1b" + '-') + ae(enc(ord('m'), mods=ctrl), '\r') + ae(enc(ord('m'), mods=ctrl | alt), "\x1b" + '\r') ae(enc(ord('n')), 'n') ae(enc(ord('n'), mods=shift), 'N') ae(enc(ord('n'), mods=alt), "\x1b" + 'n') ae(enc(ord('n'), mods=shift | alt), "\x1b" + 'N') - ae(enc(ord('n'), mods=ctrl), '.') - ae(enc(ord('n'), mods=ctrl | alt), "\x1b" + '.') + ae(enc(ord('n'), mods=ctrl), '\x0e') + ae(enc(ord('n'), mods=ctrl | alt), "\x1b" + '\x0e') ae(enc(ord('o')), 'o') ae(enc(ord('o'), mods=shift), 'O') ae(enc(ord('o'), mods=alt), "\x1b" + 'o') ae(enc(ord('o'), mods=shift | alt), "\x1b" + 'O') - ae(enc(ord('o'), mods=ctrl), '/') - ae(enc(ord('o'), mods=ctrl | alt), "\x1b" + '/') + ae(enc(ord('o'), mods=ctrl), '\x0f') + ae(enc(ord('o'), mods=ctrl | alt), "\x1b" + '\x0f') ae(enc(ord('p')), 'p') ae(enc(ord('p'), mods=shift), 'P') ae(enc(ord('p'), mods=alt), "\x1b" + 'p') ae(enc(ord('p'), mods=shift | alt), "\x1b" + 'P') - ae(enc(ord('p'), mods=ctrl), '0') - ae(enc(ord('p'), mods=ctrl | alt), "\x1b" + '0') + ae(enc(ord('p'), mods=ctrl), '\x10') + ae(enc(ord('p'), mods=ctrl | alt), "\x1b" + '\x10') ae(enc(ord('q')), 'q') ae(enc(ord('q'), mods=shift), 'Q') ae(enc(ord('q'), mods=alt), "\x1b" + 'q') ae(enc(ord('q'), mods=shift | alt), "\x1b" + 'Q') - ae(enc(ord('q'), mods=ctrl), '1') - ae(enc(ord('q'), mods=ctrl | alt), "\x1b" + '1') + ae(enc(ord('q'), mods=ctrl), '\x11') + ae(enc(ord('q'), mods=ctrl | alt), "\x1b" + '\x11') ae(enc(ord('r')), 'r') ae(enc(ord('r'), mods=shift), 'R') ae(enc(ord('r'), mods=alt), "\x1b" + 'r') ae(enc(ord('r'), mods=shift | alt), "\x1b" + 'R') - ae(enc(ord('r'), mods=ctrl), '2') - ae(enc(ord('r'), mods=ctrl | alt), "\x1b" + '2') + ae(enc(ord('r'), mods=ctrl), '\x12') + ae(enc(ord('r'), mods=ctrl | alt), "\x1b" + '\x12') ae(enc(ord('s')), 's') ae(enc(ord('s'), mods=shift), 'S') ae(enc(ord('s'), mods=alt), "\x1b" + 's') ae(enc(ord('s'), mods=shift | alt), "\x1b" + 'S') - ae(enc(ord('s'), mods=ctrl), '3') - ae(enc(ord('s'), mods=ctrl | alt), "\x1b" + '3') + ae(enc(ord('s'), mods=ctrl), '\x13') + ae(enc(ord('s'), mods=ctrl | alt), "\x1b" + '\x13') ae(enc(ord('t')), 't') ae(enc(ord('t'), mods=shift), 'T') ae(enc(ord('t'), mods=alt), "\x1b" + 't') ae(enc(ord('t'), mods=shift | alt), "\x1b" + 'T') - ae(enc(ord('t'), mods=ctrl), '4') - ae(enc(ord('t'), mods=ctrl | alt), "\x1b" + '4') + ae(enc(ord('t'), mods=ctrl), '\x14') + ae(enc(ord('t'), mods=ctrl | alt), "\x1b" + '\x14') ae(enc(ord('u')), 'u') ae(enc(ord('u'), mods=shift), 'U') ae(enc(ord('u'), mods=alt), "\x1b" + 'u') ae(enc(ord('u'), mods=shift | alt), "\x1b" + 'U') - ae(enc(ord('u'), mods=ctrl), '5') - ae(enc(ord('u'), mods=ctrl | alt), "\x1b" + '5') + ae(enc(ord('u'), mods=ctrl), '\x15') + ae(enc(ord('u'), mods=ctrl | alt), "\x1b" + '\x15') ae(enc(ord('v')), 'v') ae(enc(ord('v'), mods=shift), 'V') ae(enc(ord('v'), mods=alt), "\x1b" + 'v') ae(enc(ord('v'), mods=shift | alt), "\x1b" + 'V') - ae(enc(ord('v'), mods=ctrl), '6') - ae(enc(ord('v'), mods=ctrl | alt), "\x1b" + '6') + ae(enc(ord('v'), mods=ctrl), '\x16') + ae(enc(ord('v'), mods=ctrl | alt), "\x1b" + '\x16') ae(enc(ord('w')), 'w') ae(enc(ord('w'), mods=shift), 'W') ae(enc(ord('w'), mods=alt), "\x1b" + 'w') ae(enc(ord('w'), mods=shift | alt), "\x1b" + 'W') - ae(enc(ord('w'), mods=ctrl), '7') - ae(enc(ord('w'), mods=ctrl | alt), "\x1b" + '7') + ae(enc(ord('w'), mods=ctrl), '\x17') + ae(enc(ord('w'), mods=ctrl | alt), "\x1b" + '\x17') ae(enc(ord('x')), 'x') ae(enc(ord('x'), mods=shift), 'X') ae(enc(ord('x'), mods=alt), "\x1b" + 'x') ae(enc(ord('x'), mods=shift | alt), "\x1b" + 'X') - ae(enc(ord('x'), mods=ctrl), '8') - ae(enc(ord('x'), mods=ctrl | alt), "\x1b" + '8') + ae(enc(ord('x'), mods=ctrl), '\x18') + ae(enc(ord('x'), mods=ctrl | alt), "\x1b" + '\x18') ae(enc(ord('y')), 'y') ae(enc(ord('y'), mods=shift), 'Y') ae(enc(ord('y'), mods=alt), "\x1b" + 'y') ae(enc(ord('y'), mods=shift | alt), "\x1b" + 'Y') - ae(enc(ord('y'), mods=ctrl), '9') - ae(enc(ord('y'), mods=ctrl | alt), "\x1b" + '9') + ae(enc(ord('y'), mods=ctrl), '\x19') + ae(enc(ord('y'), mods=ctrl | alt), "\x1b" + '\x19') ae(enc(ord('z')), 'z') ae(enc(ord('z'), mods=shift), 'Z') ae(enc(ord('z'), mods=alt), "\x1b" + 'z') ae(enc(ord('z'), mods=shift | alt), "\x1b" + 'Z') - ae(enc(ord('z'), mods=ctrl), ':') - ae(enc(ord('z'), mods=ctrl | alt), "\x1b" + ':') + ae(enc(ord('z'), mods=ctrl), '\x1a') + ae(enc(ord('z'), mods=ctrl | alt), "\x1b" + '\x1a') # end legacy letter tests # }}} @@ -405,6 +405,7 @@ class TestKeys(BaseTest): ae(dq(defines.GLFW_FKEY_ENTER, mods=shift), csi(shift, 13)) ae(dq(defines.GLFW_FKEY_TAB), '\t') ae(dq(defines.GLFW_FKEY_BACKSPACE), '\x7f') + ae(dq(defines.GLFW_FKEY_TAB, mods=shift), csi(shift, 9)) for mods in (ctrl, alt, ctrl | shift, alt | shift): ae(dq(ord('a'), mods=mods), csi(mods, ord('a'))) ae(dq(ord(' '), mods=ctrl), csi(ctrl, ord(' ')))