diff --git a/docs/keyboard-protocol.rst b/docs/keyboard-protocol.rst index 3b2fc7950..a742f24db 100644 --- a/docs/keyboard-protocol.rst +++ b/docs/keyboard-protocol.rst @@ -19,26 +19,8 @@ To solve these issues and others, kitty has created a new keyboard protocol, that is backward compatible but allows applications to opt-in to support more advanced usages. The protocol is based on initial work in `fixterms `_, however, it corrects various -issues in that proposal, namely: - - * No way to disambiguate :kbd:`Esc` keypresses, other than using 8-bit controls - which are undesirable for other reasons - * Incorrectly claims special keys are sometimes encoded using ``CSI letter`` encodings when it - is actually ``ESC O letter``. - * Makes no mention of cursor key mode and how it changes encodings - * Incorrectly encoding shifted keys when shift modifier is used - * 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 - matching (think matching :kbd:`ctrl+shift+equal` and :kbd:`ctrl+plus`) - * No way to specify alternate layout key. This is useful for keyboard layouts - such as Cyrillic where you want the shortcut :kbd:`ctrl+c` to work when - pressing the :kbd:`ctrl+ц` on the keyboard. - * No way to report repeat and release key events, only key press events - * No way to report key events without text, useful for gaming. Think of using - the :kbd:`WASD` keys to control movement. - * A very small subset of all possible functional keys are specified. - +issues in that proposal, listed at the :ref:`bottom of this document +`. A basic overview ------------------ @@ -321,7 +303,7 @@ Functional key definitions ---------------------------- All numbers are in the Unicode Private Use Area (``57344 - 63743``) except -for a handful of keys that use numbers under 32 (C0 control codes) for legacy +for a handful of keys that use numbers under 32 and 127 (C0 control codes) for legacy compatibility reasons. .. {{{ @@ -331,9 +313,9 @@ compatibility reasons. :header: "Name", "CSI sequence" "ESCAPE", "CSI 57344 ... u" - "ENTER", "CSI 57345 ... u" - "TAB", "CSI 57346 ... u" - "BACKSPACE", "CSI 57347 ... u" + "ENTER", "CSI 13 ... u" + "TAB", "CSI 9 ... u" + "BACKSPACE", "CSI 127 ... u" "INSERT", "CSI 2 ... ~" "DELETE", "CSI 3 ... ~" "LEFT", "CSI 1 ... D" @@ -352,7 +334,7 @@ compatibility reasons. "MENU", "CSI 57363 ... u" "F1", "CSI 1 ... P or CSI 11 ... ~" "F2", "CSI 1 ... Q or CSI 12 ... ~" - "F3", "CSI 1 ... R or CSI 13 ... ~" + "F3", "CSI 1 ... R or CSI 57366 ... ~" "F4", "CSI 1 ... S or CSI 14 ... ~" "F5", "CSI 15 ... ~" "F6", "CSI 17 ... ~" @@ -437,3 +419,28 @@ compatibility reasons. .. end functional key table .. }}} + +.. _fixterms_bugs: + +Bugs in fixterms +------------------- + + * No way to disambiguate :kbd:`Esc` keypresses, other than using 8-bit controls + which are undesirable for other reasons + * Incorrectly claims special keys are sometimes encoded using ``CSI letter`` encodings when it + is actually ``ESC O letter``. + * ``Enter`` and ``F3`` are both assigned the number 13. + * 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`. + * 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 + matching (think matching :kbd:`ctrl+shift+equal` and :kbd:`ctrl+plus`) + * No way to specify alternate layout key. This is useful for keyboard layouts + such as Cyrillic where you want the shortcut :kbd:`ctrl+c` to work when + pressing the :kbd:`ctrl+ц` on the keyboard. + * No way to report repeat and release key events, only key press events + * 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. diff --git a/gen-key-constants.py b/gen-key-constants.py index b943eff6e..feec53a3e 100644 --- a/gen-key-constants.py +++ b/gen-key-constants.py @@ -115,13 +115,14 @@ mute_volume XF86AudioMute - functional_encoding_overrides = { 'insert': 2, 'delete': 3, 'page_up': 5, 'page_down': 6, - 'home': 7, 'end': 8, 'f1': 11, 'f2': 12, 'f3': 13, 'f4': 14, + 'home': 7, 'end': 8, 'tab': 9, 'f1': 11, 'f2': 12, 'enter': 13, 'f4': 14, 'f5': 15, 'f6': 17, 'f7': 18, 'f8': 19, 'f9': 20, 'f10': 21, - 'f11': 23, 'f12': 24 + 'f11': 23, 'f12': 24, 'backspace': 127 } different_trailer_functionals = { 'up': 'A', 'down': 'B', 'right': 'C', 'left': 'D', 'end': 'F', 'home': 'H', - 'f1': 'P', 'f2': 'Q', 'f3': 'R', 'f4': 'S' + 'f1': 'P', 'f2': 'Q', 'f3': 'R', 'f4': 'S', 'enter': 'u', 'tab': 'u', + 'backspace': 'u' } functional_key_names: List[str] = [] name_to_code: Dict[str, int] = {} @@ -188,11 +189,13 @@ def generate_functional_table() -> None: ' :header: "Name", "CSI sequence"', '' ] + enc_lines = [] 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, '~') - code = code if trailer == '~' else 1 + 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'): trailer += f' or CSI {oc} ... ~' else: @@ -201,6 +204,7 @@ def generate_functional_table() -> None: lines.append(f' {name} "CSI {code} ... {trailer}"') 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)) def main() -> None: diff --git a/kitty/key_encoding.c b/kitty/key_encoding.c index 9d00e5b9c..ab29a06e1 100644 --- a/kitty/key_encoding.c +++ b/kitty/key_encoding.c @@ -132,20 +132,24 @@ encode_function_key(const KeyEvent *ev, char *output) { #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) */ + case GLFW_FKEY_ENTER: S(13, 'u'); + case GLFW_FKEY_TAB: S(9, 'u'); + case GLFW_FKEY_BACKSPACE: S(127, 'u'); + case GLFW_FKEY_INSERT: S(2, '~'); + case GLFW_FKEY_DELETE: S(3, '~'); + case GLFW_FKEY_LEFT: S(1, 'D'); + case GLFW_FKEY_RIGHT: S(1, 'C'); case GLFW_FKEY_UP: S(1, 'A'); case GLFW_FKEY_DOWN: S(1, 'B'); - case GLFW_FKEY_LEFT: S(1, 'C'); - case GLFW_FKEY_RIGHT: S(1, 'D'); + case GLFW_FKEY_PAGE_UP: S(5, '~'); + case GLFW_FKEY_PAGE_DOWN: S(6, '~'); case GLFW_FKEY_HOME: S(1, 'H'); case GLFW_FKEY_END: S(1, 'F'); case GLFW_FKEY_F1: S(1, 'P'); case GLFW_FKEY_F2: S(1, 'Q'); case GLFW_FKEY_F3: S(1, 'R'); case GLFW_FKEY_F4: S(1, 'S'); - case GLFW_FKEY_INSERT: S(2, '~'); - case GLFW_FKEY_DELETE: S(3, '~'); - case GLFW_FKEY_PAGE_UP: S(5, '~'); - case GLFW_FKEY_PAGE_DOWN: S(6, '~'); case GLFW_FKEY_F5: S(15, '~'); case GLFW_FKEY_F6: S(17, '~'); case GLFW_FKEY_F7: S(18, '~'); @@ -154,6 +158,7 @@ encode_function_key(const KeyEvent *ev, char *output) { case GLFW_FKEY_F10: S(21, '~'); case GLFW_FKEY_F11: S(23, '~'); case GLFW_FKEY_F12: S(24, '~'); +/* end special numbers */ default: break; } #undef S diff --git a/kitty_tests/keys.py b/kitty_tests/keys.py index c0c3d24cc..bc654817e 100644 --- a/kitty_tests/keys.py +++ b/kitty_tests/keys.py @@ -8,6 +8,9 @@ from . import BaseTest class TestKeys(BaseTest): + def test_encode_key_event(self): + pass + def test_encode_mouse_event(self): NORMAL_PROTOCOL, UTF8_PROTOCOL, SGR_PROTOCOL, URXVT_PROTOCOL = range(4) L, M, R = 1, 2, 3