More removal of GLFW_KEY_ constants

This commit is contained in:
Kovid Goyal 2021-01-12 07:46:21 +05:30
parent 774a6c8c8b
commit 1690718710
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
11 changed files with 186 additions and 551 deletions

View File

@ -430,6 +430,9 @@ Bugs in fixterms
* 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.
* :kbd:`ctrl+shift+tab`` should be ``CSI 9 ; 6 u`` not ``CSI 1 ; 5 Z``
(shift+tab is not a separate key from tab)
* 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`.

View File

@ -180,7 +180,7 @@ def generate_glfw_header() -> None:
lines.append('} GLFWFunctionKey;')
patch_file('glfw/glfw3.h', 'functional key names', '\n'.join(lines))
patch_file('kitty/glfw.c', 'glfw functional keys', '\n'.join(klines))
patch_file('kitty/fast_data_types.pyi', 'glfw functional keys', '\n'.join(klines), start_marker='# ', end_marker='')
patch_file('kitty/fast_data_types.pyi', 'glfw functional keys', '\n'.join(pyi), start_marker='# ', end_marker='')
patch_file('glfw/input.c', 'functional key names', '\n'.join(names))

View File

@ -394,7 +394,7 @@ def parse_key(val: str, key_definitions: List[KeyDefinition]) -> None:
mods, is_native, key = parse_shortcut(part)
except InvalidMods:
return
if key is defines.GLFW_KEY_UNKNOWN:
if key == 0:
if mods is not None:
log_error('Shortcut: {} has unknown key, ignoring'.format(sc))
return
@ -408,7 +408,7 @@ def parse_key(val: str, key_definitions: List[KeyDefinition]) -> None:
mods, is_native, key = parse_shortcut(sc)
except InvalidMods:
return
if key is defines.GLFW_KEY_UNKNOWN:
if key == 0:
if mods is not None:
log_error('Shortcut: {} has unknown key, ignoring'.format(sc))
return

View File

@ -78,7 +78,7 @@ def parse_shortcut(sc: str) -> SingleKey:
else:
key = get_key_name_lookup()(q, False)
is_native = key is not None
return SingleKey(mods, is_native, key or defines.GLFW_KEY_UNKNOWN)
return SingleKey(mods, is_native, key or 0)
T = TypeVar('T')

View File

@ -18,110 +18,110 @@ ERROR_PREFIX: str
GLSL_VERSION: int
GLFW_IBEAM_CURSOR: int
# start glfw functional keys (auto generated by gen-key-constants.py do not edit)
ADDC(GLFW_FKEY_ESCAPE);
ADDC(GLFW_FKEY_ENTER);
ADDC(GLFW_FKEY_TAB);
ADDC(GLFW_FKEY_BACKSPACE);
ADDC(GLFW_FKEY_INSERT);
ADDC(GLFW_FKEY_DELETE);
ADDC(GLFW_FKEY_LEFT);
ADDC(GLFW_FKEY_RIGHT);
ADDC(GLFW_FKEY_UP);
ADDC(GLFW_FKEY_DOWN);
ADDC(GLFW_FKEY_PAGE_UP);
ADDC(GLFW_FKEY_PAGE_DOWN);
ADDC(GLFW_FKEY_HOME);
ADDC(GLFW_FKEY_END);
ADDC(GLFW_FKEY_CAPS_LOCK);
ADDC(GLFW_FKEY_SCROLL_LOCK);
ADDC(GLFW_FKEY_NUM_LOCK);
ADDC(GLFW_FKEY_PRINT_SCREEN);
ADDC(GLFW_FKEY_PAUSE);
ADDC(GLFW_FKEY_MENU);
ADDC(GLFW_FKEY_F1);
ADDC(GLFW_FKEY_F2);
ADDC(GLFW_FKEY_F3);
ADDC(GLFW_FKEY_F4);
ADDC(GLFW_FKEY_F5);
ADDC(GLFW_FKEY_F6);
ADDC(GLFW_FKEY_F7);
ADDC(GLFW_FKEY_F8);
ADDC(GLFW_FKEY_F9);
ADDC(GLFW_FKEY_F10);
ADDC(GLFW_FKEY_F11);
ADDC(GLFW_FKEY_F12);
ADDC(GLFW_FKEY_F13);
ADDC(GLFW_FKEY_F14);
ADDC(GLFW_FKEY_F15);
ADDC(GLFW_FKEY_F16);
ADDC(GLFW_FKEY_F17);
ADDC(GLFW_FKEY_F18);
ADDC(GLFW_FKEY_F19);
ADDC(GLFW_FKEY_F20);
ADDC(GLFW_FKEY_F21);
ADDC(GLFW_FKEY_F22);
ADDC(GLFW_FKEY_F23);
ADDC(GLFW_FKEY_F24);
ADDC(GLFW_FKEY_F25);
ADDC(GLFW_FKEY_F26);
ADDC(GLFW_FKEY_F27);
ADDC(GLFW_FKEY_F28);
ADDC(GLFW_FKEY_F29);
ADDC(GLFW_FKEY_F30);
ADDC(GLFW_FKEY_F31);
ADDC(GLFW_FKEY_F32);
ADDC(GLFW_FKEY_F33);
ADDC(GLFW_FKEY_F34);
ADDC(GLFW_FKEY_F35);
ADDC(GLFW_FKEY_KP_0);
ADDC(GLFW_FKEY_KP_1);
ADDC(GLFW_FKEY_KP_2);
ADDC(GLFW_FKEY_KP_3);
ADDC(GLFW_FKEY_KP_4);
ADDC(GLFW_FKEY_KP_5);
ADDC(GLFW_FKEY_KP_6);
ADDC(GLFW_FKEY_KP_7);
ADDC(GLFW_FKEY_KP_8);
ADDC(GLFW_FKEY_KP_9);
ADDC(GLFW_FKEY_KP_DECIMAL);
ADDC(GLFW_FKEY_KP_DIVIDE);
ADDC(GLFW_FKEY_KP_MULTIPLY);
ADDC(GLFW_FKEY_KP_SUBTRACT);
ADDC(GLFW_FKEY_KP_ADD);
ADDC(GLFW_FKEY_KP_ENTER);
ADDC(GLFW_FKEY_KP_EQUAL);
ADDC(GLFW_FKEY_KP_SEPARATOR);
ADDC(GLFW_FKEY_KP_LEFT);
ADDC(GLFW_FKEY_KP_RIGHT);
ADDC(GLFW_FKEY_KP_UP);
ADDC(GLFW_FKEY_KP_DOWN);
ADDC(GLFW_FKEY_KP_PAGE_UP);
ADDC(GLFW_FKEY_KP_PAGE_DOWN);
ADDC(GLFW_FKEY_KP_HOME);
ADDC(GLFW_FKEY_KP_END);
ADDC(GLFW_FKEY_KP_INSERT);
ADDC(GLFW_FKEY_KP_DELETE);
ADDC(GLFW_FKEY_LEFT_SHIFT);
ADDC(GLFW_FKEY_LEFT_CONTROL);
ADDC(GLFW_FKEY_LEFT_ALT);
ADDC(GLFW_FKEY_LEFT_SUPER);
ADDC(GLFW_FKEY_RIGHT_SHIFT);
ADDC(GLFW_FKEY_RIGHT_CONTROL);
ADDC(GLFW_FKEY_RIGHT_ALT);
ADDC(GLFW_FKEY_RIGHT_SUPER);
ADDC(GLFW_FKEY_MEDIA_PLAY);
ADDC(GLFW_FKEY_MEDIA_PAUSE);
ADDC(GLFW_FKEY_MEDIA_PLAY_PAUSE);
ADDC(GLFW_FKEY_MEDIA_REVERSE);
ADDC(GLFW_FKEY_MEDIA_STOP);
ADDC(GLFW_FKEY_MEDIA_FAST_FORWARD);
ADDC(GLFW_FKEY_MEDIA_REWIND);
ADDC(GLFW_FKEY_MEDIA_TRACK_NEXT);
ADDC(GLFW_FKEY_MEDIA_TRACK_PREVIOUS);
ADDC(GLFW_FKEY_MEDIA_RECORD);
ADDC(GLFW_FKEY_LOWER_VOLUME);
ADDC(GLFW_FKEY_RAISE_VOLUME);
ADDC(GLFW_FKEY_MUTE_VOLUME);
GLFW_FKEY_ESCAPE: int
GLFW_FKEY_ENTER: int
GLFW_FKEY_TAB: int
GLFW_FKEY_BACKSPACE: int
GLFW_FKEY_INSERT: int
GLFW_FKEY_DELETE: int
GLFW_FKEY_LEFT: int
GLFW_FKEY_RIGHT: int
GLFW_FKEY_UP: int
GLFW_FKEY_DOWN: int
GLFW_FKEY_PAGE_UP: int
GLFW_FKEY_PAGE_DOWN: int
GLFW_FKEY_HOME: int
GLFW_FKEY_END: int
GLFW_FKEY_CAPS_LOCK: int
GLFW_FKEY_SCROLL_LOCK: int
GLFW_FKEY_NUM_LOCK: int
GLFW_FKEY_PRINT_SCREEN: int
GLFW_FKEY_PAUSE: int
GLFW_FKEY_MENU: int
GLFW_FKEY_F1: int
GLFW_FKEY_F2: int
GLFW_FKEY_F3: int
GLFW_FKEY_F4: int
GLFW_FKEY_F5: int
GLFW_FKEY_F6: int
GLFW_FKEY_F7: int
GLFW_FKEY_F8: int
GLFW_FKEY_F9: int
GLFW_FKEY_F10: int
GLFW_FKEY_F11: int
GLFW_FKEY_F12: int
GLFW_FKEY_F13: int
GLFW_FKEY_F14: int
GLFW_FKEY_F15: int
GLFW_FKEY_F16: int
GLFW_FKEY_F17: int
GLFW_FKEY_F18: int
GLFW_FKEY_F19: int
GLFW_FKEY_F20: int
GLFW_FKEY_F21: int
GLFW_FKEY_F22: int
GLFW_FKEY_F23: int
GLFW_FKEY_F24: int
GLFW_FKEY_F25: int
GLFW_FKEY_F26: int
GLFW_FKEY_F27: int
GLFW_FKEY_F28: int
GLFW_FKEY_F29: int
GLFW_FKEY_F30: int
GLFW_FKEY_F31: int
GLFW_FKEY_F32: int
GLFW_FKEY_F33: int
GLFW_FKEY_F34: int
GLFW_FKEY_F35: int
GLFW_FKEY_KP_0: int
GLFW_FKEY_KP_1: int
GLFW_FKEY_KP_2: int
GLFW_FKEY_KP_3: int
GLFW_FKEY_KP_4: int
GLFW_FKEY_KP_5: int
GLFW_FKEY_KP_6: int
GLFW_FKEY_KP_7: int
GLFW_FKEY_KP_8: int
GLFW_FKEY_KP_9: int
GLFW_FKEY_KP_DECIMAL: int
GLFW_FKEY_KP_DIVIDE: int
GLFW_FKEY_KP_MULTIPLY: int
GLFW_FKEY_KP_SUBTRACT: int
GLFW_FKEY_KP_ADD: int
GLFW_FKEY_KP_ENTER: int
GLFW_FKEY_KP_EQUAL: int
GLFW_FKEY_KP_SEPARATOR: int
GLFW_FKEY_KP_LEFT: int
GLFW_FKEY_KP_RIGHT: int
GLFW_FKEY_KP_UP: int
GLFW_FKEY_KP_DOWN: int
GLFW_FKEY_KP_PAGE_UP: int
GLFW_FKEY_KP_PAGE_DOWN: int
GLFW_FKEY_KP_HOME: int
GLFW_FKEY_KP_END: int
GLFW_FKEY_KP_INSERT: int
GLFW_FKEY_KP_DELETE: int
GLFW_FKEY_LEFT_SHIFT: int
GLFW_FKEY_LEFT_CONTROL: int
GLFW_FKEY_LEFT_ALT: int
GLFW_FKEY_LEFT_SUPER: int
GLFW_FKEY_RIGHT_SHIFT: int
GLFW_FKEY_RIGHT_CONTROL: int
GLFW_FKEY_RIGHT_ALT: int
GLFW_FKEY_RIGHT_SUPER: int
GLFW_FKEY_MEDIA_PLAY: int
GLFW_FKEY_MEDIA_PAUSE: int
GLFW_FKEY_MEDIA_PLAY_PAUSE: int
GLFW_FKEY_MEDIA_REVERSE: int
GLFW_FKEY_MEDIA_STOP: int
GLFW_FKEY_MEDIA_FAST_FORWARD: int
GLFW_FKEY_MEDIA_REWIND: int
GLFW_FKEY_MEDIA_TRACK_NEXT: int
GLFW_FKEY_MEDIA_TRACK_PREVIOUS: int
GLFW_FKEY_MEDIA_RECORD: int
GLFW_FKEY_LOWER_VOLUME: int
GLFW_FKEY_RAISE_VOLUME: int
GLFW_FKEY_MUTE_VOLUME: int
# end glfw functional keys
GLFW_MOD_SHIFT: int
GLFW_MOD_CONTROL: int
@ -257,6 +257,19 @@ MOVE: int
# }}}
def encode_key_for_tty(
key: int = 0,
shifted_key: int = 0,
alternate_key: int = 0,
mods: int = 0,
action: int = 1,
key_encoding_flags: int = 0,
text: str = "",
cursor_key_mode: bool = False
) -> str:
pass
def log_error_string(s: str) -> None:
pass
@ -908,7 +921,6 @@ class Screen:
scrolled_by: int
cursor: Cursor
disable_ligatures: int
extended_keyboard: bool
cursor_key_mode: bool
auto_repeat_enabled: bool
@ -922,6 +934,9 @@ class Screen:
):
pass
def current_key_encoding_flags(self) -> int:
pass
def line(self, num: int) -> Line:
pass
@ -1086,9 +1101,5 @@ def spawn(
pass
def key_to_bytes(glfw_key: int, smkx: bool, extended: bool, mods: int, action: int) -> bytes:
pass
def set_window_padding(os_window_id: int, tab_id: int, window_id: int, left: int, top: int, right: int, bottom: int) -> None:
pass

View File

@ -116,11 +116,6 @@ encode_function_key(const KeyEvent *ev, char *output) {
default: break;
}
}
if (key_number == GLFW_FKEY_TAB) {
if (ev->mods.value == SHIFT) return encode_csi_string('Z', "", output);
if (ev->mods.value == (CTRL | SHIFT)) return encode_csi_string('Z', "1;5", output);
if (ev->mods.value == ALT) SIMPLE("\x1b\t");
}
if (ev->mods.value == ALT) {
switch(key_number) {
case GLFW_FKEY_TAB: SIMPLE("\x1b\t");
@ -128,6 +123,7 @@ encode_function_key(const KeyEvent *ev, char *output) {
case GLFW_FKEY_BACKSPACE: SIMPLE("\x1b\x7f");
}
}
if (ev->mods.value == SHIFT && key_number == GLFW_FKEY_TAB) { SIMPLE("\x1b[Z"); }
#undef SIMPLE
#define S(number, trailer) key_number = number; csi_trailer = trailer; break
@ -185,7 +181,7 @@ encode_printable_ascii_key_legacy(const KeyEvent *ev, char *output) {
shifted_key = (shifted_key && ev->mods.shift) ? shifted_key : (char)ev->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)
if (ev->mods.value == CTRL && ev->key && 0x1f != ev->key)
return snprintf(output, KEY_BUFFER_SIZE, "%c", ev->key & 0x1f);
if (ev->mods.value == (CTRL | ALT))
return snprintf(output, KEY_BUFFER_SIZE, "\x1b%c", ev->key & 0x1f);

View File

@ -34,57 +34,6 @@ key_name_aliases = {
'^': 'CIRCUMFLEX',
'_': 'UNDERSCORE',
'`': 'GRAVE_ACCENT',
'§': 'PARAGRAPH',
'º': 'MASCULINE',
'À': 'A_GRAVE',
'Ä': 'A_DIAERESIS',
'Å': 'A_RING',
'Æ': 'AE',
'Ç': 'C_CEDILLA',
'È': 'E_GRAVE',
'É': 'E_ACUTE',
'Ì': 'I_GRAVE',
'Ñ': 'N_TILDE',
'Ò': 'O_GRAVE',
'Ö': 'O_DIAERESIS',
'Ø': 'O_SLASH',
'Ù': 'U_GRAVE',
'Ü': 'U_DIAERESIS',
'SS': 'S_SHARP', # 'ß'.upper() == 'SS'
'А': 'CYRILLIC_A',
'Б': 'CYRILLIC_BE',
'В': 'CYRILLIC_VE',
'Г': 'CYRILLIC_GHE',
'Д': 'CYRILLIC_DE',
'Е': 'CYRILLIC_IE',
'Ж': 'CYRILLIC_ZHE',
'З': 'CYRILLIC_ZE',
'И': 'CYRILLIC_I',
'Й': 'CYRILLIC_SHORT_I',
'К': 'CYRILLIC_KA',
'Л': 'CYRILLIC_EL',
'М': 'CYRILLIC_EM',
'Н': 'CYRILLIC_EN',
'О': 'CYRILLIC_O',
'П': 'CYRILLIC_PE',
'Р': 'CYRILLIC_ER',
'С': 'CYRILLIC_ES',
'Т': 'CYRILLIC_TE',
'У': 'CYRILLIC_U',
'Ф': 'CYRILLIC_EF',
'Х': 'CYRILLIC_HA',
'Ц': 'CYRILLIC_TSE',
'Ч': 'CYRILLIC_CHE',
'Ш': 'CYRILLIC_SHA',
'Щ': 'CYRILLIC_SHCHA',
'Ъ': 'CYRILLIC_HARD_SIGN',
'Ы': 'CYRILLIC_YERU',
'Ь': 'CYRILLIC_SOFT_SIGN',
'Э': 'CYRILLIC_E',
'Ю': 'CYRILLIC_YU',
'Я': 'CYRILLIC_YA',
'Ё': 'CYRILLIC_IO',
'ESC': 'ESCAPE',
'PGUP': 'PAGE_UP',
'PAGEUP': 'PAGE_UP',

View File

@ -2,281 +2,19 @@
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
import string
from typing import Any, Callable, Dict, Iterable, Optional, Tuple, Union
from typing import Optional, Union
from . import fast_data_types as defines
from .constants import SingleKey
from .config import KeyAction, KeyMap, SequenceMap, SubSequenceMap
from .key_encoding import KEY_MAP
from .terminfo import key_as_bytes, modify_key_bytes
from .typing import ScreenType, WindowType
from .utils import base64_encode
def modify_complex_key(name: Union[str, bytes], amt: int) -> bytes:
q = name if isinstance(name, bytes) else key_as_bytes(name)
return modify_key_bytes(q, amt)
control_codes: Dict[int, Union[bytes, Tuple[int, ...]]] = {
defines.GLFW_KEY_BACKSPACE: b'\x08'
}
smkx_key_map = {}
alt_codes = {
defines.GLFW_KEY_TAB: b'\033\t',
defines.GLFW_KEY_ENTER: b'\033\r',
defines.GLFW_KEY_ESCAPE: b'\033\033',
defines.GLFW_KEY_BACKSPACE: b'\033\177'
}
shift_alt_codes = alt_codes.copy()
shift_alt_codes[defines.GLFW_KEY_TAB] = key_as_bytes('kcbt')
alt_mods = (defines.GLFW_MOD_ALT, defines.GLFW_MOD_SHIFT | defines.GLFW_MOD_ALT)
ctrl_shift_mod = defines.GLFW_MOD_SHIFT | defines.GLFW_MOD_CONTROL
ctrl_alt_mod = defines.GLFW_MOD_ALT | defines.GLFW_MOD_CONTROL
ctrl_alt_shift_mod = ctrl_alt_mod | defines.GLFW_MOD_SHIFT
SHIFTED_KEYS = {
defines.GLFW_KEY_TAB: key_as_bytes('kcbt'),
defines.GLFW_KEY_HOME: key_as_bytes('kHOM'),
defines.GLFW_KEY_END: key_as_bytes('kEND'),
defines.GLFW_KEY_LEFT: key_as_bytes('kLFT'),
defines.GLFW_KEY_RIGHT: key_as_bytes('kRIT'),
defines.GLFW_KEY_UP: key_as_bytes('kri'),
defines.GLFW_KEY_DOWN: key_as_bytes('kind'),
defines.GLFW_KEY_PAGE_UP: modify_complex_key('kpp', 2),
defines.GLFW_KEY_PAGE_DOWN: modify_complex_key('knp', 2),
}
control_alt_codes = {
defines.GLFW_KEY_SPACE: b'\x1b\0',
}
control_alt_shift_codes: Dict[int, bytes] = {}
ASCII_C0_SHIFTED = {
# ^@
'2': b'\x00',
# ^^
'6': b'\x1e',
# ^_
'MINUS': b'\x1f',
# ^?
'SLASH': b'\x7f',
}
control_shift_keys = {getattr(defines, 'GLFW_KEY_' + k): v for k, v in ASCII_C0_SHIFTED.items()}
def create_modifier_variants(keycode: int, terminfo_name_or_bytes: Union[str, bytes], add_shifted_key: bool = True) -> None:
kn = terminfo_name_or_bytes
smkx_key_map[keycode] = kn if isinstance(kn, bytes) else key_as_bytes(kn)
if add_shifted_key:
SHIFTED_KEYS[keycode] = modify_complex_key(kn, 2)
alt_codes[keycode] = modify_complex_key(kn, 3)
shift_alt_codes[keycode] = modify_complex_key(kn, 4)
control_codes[keycode] = modify_complex_key(kn, 5)
control_shift_keys[keycode] = modify_complex_key(kn, 6)
control_alt_codes[keycode] = modify_complex_key(kn, 7)
control_alt_shift_codes[keycode] = modify_complex_key(kn, 8)
for kf, kn in {
defines.GLFW_KEY_UP: 'kcuu1',
defines.GLFW_KEY_DOWN: 'kcud1',
defines.GLFW_KEY_LEFT: 'kcub1',
defines.GLFW_KEY_RIGHT: 'kcuf1',
defines.GLFW_KEY_HOME: 'khome',
defines.GLFW_KEY_END: 'kend',
defines.GLFW_KEY_INSERT: 'kich1',
defines.GLFW_KEY_DELETE: 'kdch1',
defines.GLFW_KEY_PAGE_UP: 'kpp',
defines.GLFW_KEY_PAGE_DOWN: 'knp',
}.items():
create_modifier_variants(kf, kn, add_shifted_key=False)
for f in range(1, 13):
kf = getattr(defines, 'GLFW_KEY_F{}'.format(f))
kn = 'kf{}'.format(f)
create_modifier_variants(kf, kn)
for f in range(13, 26):
kf = getattr(defines, 'GLFW_KEY_F{}'.format(f))
kn = 'kf{}'.format(f)
smkx_key_map[kf] = key_as_bytes(kn)
create_modifier_variants(defines.GLFW_KEY_MENU, b'\x1b[29~')
f_ = {k: k for k in '0123456789'}
f_.update({
'COMMA': ',',
'PERIOD': '.',
'SEMICOLON': ';',
'APOSTROPHE': "'",
'MINUS': '-',
'EQUAL': '=',
})
for kf_, kn_ in f_.items():
control_codes[getattr(defines, 'GLFW_KEY_' + kf_)] = (ord(kn_),)
del f, f_, kf, kn, kf_, kn_
smkx_key_map[defines.GLFW_KEY_ESCAPE] = b'\033'
smkx_key_map[defines.GLFW_KEY_ENTER] = b'\r'
smkx_key_map[defines.GLFW_KEY_KP_ENTER] = b'\r'
smkx_key_map[defines.GLFW_KEY_BACKSPACE] = key_as_bytes('kbs')
smkx_key_map[defines.GLFW_KEY_TAB] = b'\t'
control_codes.update({
k: (1 + i, )
for i, k in
enumerate(range(defines.GLFW_KEY_A, defines.GLFW_KEY_RIGHT_BRACKET + 1))
})
control_codes[defines.GLFW_KEY_GRAVE_ACCENT] = control_codes[defines.GLFW_KEY_UNDERSCORE] = \
control_codes[defines.GLFW_KEY_SPACE] = control_codes[defines.GLFW_KEY_2] = (0,)
control_codes[defines.GLFW_KEY_3] = (27,)
control_codes[defines.GLFW_KEY_4] = (28,)
control_codes[defines.GLFW_KEY_5] = (29,)
control_codes[defines.GLFW_KEY_6] = control_codes[defines.GLFW_KEY_CIRCUMFLEX] = (30,)
control_codes[defines.GLFW_KEY_7] = control_codes[defines.GLFW_KEY_SLASH] = (31,)
control_codes[defines.GLFW_KEY_8] = (127,)
rmkx_key_map = smkx_key_map.copy()
rmkx_key_map.update({
defines.GLFW_KEY_UP: b'\033[A',
defines.GLFW_KEY_DOWN: b'\033[B',
defines.GLFW_KEY_LEFT: b'\033[D',
defines.GLFW_KEY_RIGHT: b'\033[C',
defines.GLFW_KEY_HOME: b'\033[H',
defines.GLFW_KEY_END: b'\033[F',
})
cursor_key_mode_map = {True: smkx_key_map, False: rmkx_key_map}
from .typing import ScreenType
def keyboard_mode_name(screen: ScreenType) -> str:
if screen.extended_keyboard:
if screen.current_key_encoding_flags() & 0b1000:
return 'kitty'
return 'application' if screen.cursor_key_mode else 'normal'
action_map = {
defines.GLFW_PRESS: 'p',
defines.GLFW_RELEASE: 'r',
defines.GLFW_REPEAT: 't'
}
def extended_key_event(key: int, mods: int, action: int) -> bytes:
if key >= defines.GLFW_KEY_LAST or key == defines.GLFW_KEY_UNKNOWN or (
# Shifted printable key should be handled by on_text_input()
mods <= defines.GLFW_MOD_SHIFT and defines.GLFW_KEY_SPACE <= key <= defines.GLFW_KEY_LAST_PRINTABLE
):
return b''
if mods == 0 and key in (
defines.GLFW_KEY_BACKSPACE, defines.GLFW_KEY_ENTER
):
if action == defines.GLFW_RELEASE:
return b''
return smkx_key_map[key]
if key in (defines.GLFW_KEY_LEFT_SHIFT, defines.GLFW_KEY_RIGHT_SHIFT):
return b''
name = KEY_MAP.get(key)
if name is None:
return b''
m = 0
if mods & defines.GLFW_MOD_SHIFT:
m |= 0x1
if mods & defines.GLFW_MOD_ALT:
m |= 0x2
if mods & defines.GLFW_MOD_CONTROL:
m |= 0x4
if mods & defines.GLFW_MOD_SUPER:
m |= 0x8
return 'K{}{}{}'.format(
action_map[action], base64_encode(m), name
).encode('ascii')
def pmap(names: str, r: Iterable[str]) -> Dict[int, bytes]:
snames = names.split()
b = [x.encode('ascii') for x in r]
if len(snames) != len(b):
raise ValueError('Incorrect mapping for {}'.format(names))
anames = [getattr(defines, 'GLFW_KEY_' + n) for n in snames]
return dict(zip(anames, b))
UN_SHIFTED_PRINTABLE = {
getattr(defines, 'GLFW_KEY_' + x): x.lower().encode('ascii')
for x in string.digits + string.ascii_uppercase
}
UN_SHIFTED_PRINTABLE.update(pmap(
'SPACE APOSTROPHE COMMA MINUS PERIOD SLASH SEMICOLON EQUAL',
" ',-./;="
))
UN_SHIFTED_PRINTABLE.update(pmap(
'LEFT_BRACKET BACKSLASH RIGHT_BRACKET GRAVE_ACCENT UNDERSCORE',
"[\\]`_"
))
SHIFTED_PRINTABLE = UN_SHIFTED_PRINTABLE.copy()
SHIFTED_PRINTABLE.update({
getattr(defines, 'GLFW_KEY_' + x): x.encode('ascii') for x in string.ascii_uppercase
})
SHIFTED_PRINTABLE.update(pmap(
'1 2 3 4 5 6 7 8 9 0',
'!@#$%^&*()'
))
SHIFTED_PRINTABLE.update(pmap(
'APOSTROPHE COMMA MINUS PERIOD SLASH SEMICOLON EQUAL',
'"<_>?:+'
))
SHIFTED_PRINTABLE.update(pmap(
'LEFT_BRACKET BACKSLASH RIGHT_BRACKET GRAVE_ACCENT',
"{|}~"
))
CTRL_ALT_KEYS = {getattr(defines, 'GLFW_KEY_' + k) for k in string.ascii_uppercase}
all_control_alt_keys = set(CTRL_ALT_KEYS) | set(control_alt_codes)
def key_to_bytes(key: int, smkx: bool, extended: bool, mods: int, action: int) -> bytes:
if extended:
return extended_key_event(key, mods, action)
data = bytearray()
if mods == defines.GLFW_MOD_CONTROL and key in control_codes:
# Map Ctrl-key to ascii control code
data.extend(control_codes[key])
elif mods == ctrl_shift_mod and key in control_shift_keys:
data.extend(control_shift_keys[key])
elif mods in alt_mods:
if key in alt_codes:
data.extend((alt_codes if mods == defines.GLFW_MOD_ALT else shift_alt_codes)[key])
elif key in UN_SHIFTED_PRINTABLE:
m = UN_SHIFTED_PRINTABLE if mods == defines.GLFW_MOD_ALT else SHIFTED_PRINTABLE
data.append(0o33)
data.extend(m[key])
elif mods == ctrl_alt_mod and key in all_control_alt_keys:
if key in CTRL_ALT_KEYS:
data.append(0x1b)
data.extend(control_codes[key])
else:
data.extend(control_alt_codes[key])
elif mods == ctrl_alt_shift_mod and key in control_alt_shift_codes:
data.extend(control_alt_shift_codes[key])
else:
key_map = cursor_key_mode_map[smkx]
x = key_map.get(key)
if x is not None:
if mods == defines.GLFW_MOD_SHIFT:
x = SHIFTED_KEYS.get(key, x)
assert x is not None
data.extend(x)
return bytes(data)
def interpret_key_event(key: int, native_key: int, mods: int, window: WindowType, action: int) -> bytes:
screen = window.screen
if (
action == defines.GLFW_PRESS or
(action == defines.GLFW_REPEAT and screen.auto_repeat_enabled) or
screen.extended_keyboard
):
return defines.key_to_bytes(key, screen.cursor_key_mode, screen.extended_keyboard, mods, action)
return b''
def get_shortcut(keymap: Union[KeyMap, SequenceMap], mods: int, key: int, native_key: int) -> Optional[Union[KeyAction, SubSequenceMap]]:
mods &= 0b1111
ans = keymap.get(SingleKey(mods, False, key))
@ -289,109 +27,3 @@ def shortcut_matches(s: SingleKey, mods: int, key: int, native_key: int) -> bool
mods &= 0b1111
q = native_key if s[1] else key
return bool(s[0] & 0b1111 == mods & 0b1111 and s[2] == q)
def generate_key_table_impl(w: Callable) -> None:
w('// auto-generated from keys.py, do not edit!')
w('#pragma once')
w('#include <stddef.h>')
w('#include <stdint.h>')
w('#include <stdbool.h>')
w('#include <limits.h>')
number_of_keys = defines.GLFW_KEY_LAST + 1
w('// map glfw key numbers to 7-bit numbers for compact data storage')
w('static const uint8_t key_map[%d] = {' % number_of_keys)
key_count = 0
def key_name(k: str) -> str:
return k[len('GLFW_KEY_'):]
keys = {v: k for k, v in vars(defines).items() if k.startswith('GLFW_KEY_') and k not in {'GLFW_KEY_LAST', 'GLFW_KEY_LAST_PRINTABLE', 'GLFW_KEY_UNKNOWN'}}
key_rmap = []
for i in range(number_of_keys):
k = keys.get(i)
if k is None:
w('UINT8_MAX,')
else:
w('%d, /* %s */' % (key_count, key_name(k)))
key_rmap.append(i)
key_count += 1
if key_count > 256:
raise OverflowError('Too many keys')
w('};\n')
w('static inline const char* key_name(int key) { switch(key) {')
for i in range(number_of_keys):
k = keys.get(i)
if k is not None:
w('case %d: return "%s";' % (i, key_name(k)))
w('default: return NULL; }}\n')
w('typedef enum { NORMAL, APPLICATION, EXTENDED } KeyboardMode;\n')
w('static inline const char*\nkey_lookup(uint8_t key, KeyboardMode mode, uint8_t mods, uint8_t action) {')
i = 1
def ind(*a: Any) -> None:
w((' ' * i)[:-1], *a)
ind('switch(mode) {')
mmap = [(False, False), (True, False), (False, True)]
for (smkx, extended), mode in zip(mmap, 'NORMAL APPLICATION EXTENDED'.split()):
i += 1
ind('case {}:'.format(mode))
i += 1
ind('switch(action & 3) { case 3: return NULL;')
for action in (defines.GLFW_RELEASE, defines.GLFW_PRESS, defines.GLFW_REPEAT):
i += 1
ind('case {}: // {}'.format(action, 'RELEASE PRESS REPEAT'.split()[action]))
i += 1
if action != defines.GLFW_RELEASE or mode == 'EXTENDED':
ind('switch (mods & 0xf) {')
i += 1
for mods in range(16):
key_bytes = {}
for key in range(key_count):
glfw_key = key_rmap[key]
data = key_to_bytes(glfw_key, smkx, extended, mods, action)
if data:
key_bytes[key] = data, glfw_key
i += 1
ind('case 0x{:x}:'.format(mods))
i += 1
if key_bytes:
ind('switch(key & 0xff) { default: return NULL;')
i += 1
for key, (data, glfw_key) in key_bytes.items():
ind('case {}: // {}'.format(key, key_name(keys[glfw_key])))
i += 1
items = bytearray(data)
items.insert(0, len(items))
ind('return "{}";'.format(''.join('\\x{:02x}'.format(x) for x in items)))
i -= 1
i -= 1
ind('} // end switch(key)')
else:
ind('return NULL;')
i -= 2
i -= 1
ind('} // end switch(mods)')
ind('break;\n')
i -= 1
else:
ind('return NULL;\n')
i -= 1
i -= 1
ind('}} // end switch(action) in mode {}'.format(mode))
ind('break;\n\n')
i -= 1
i -= 1
ind('}')
ind('return NULL;')
i -= 1
w('}')
def generate_key_table() -> None:
# To run this, use: ./kitty/launcher/kitty +runpy "from kitty.keys import *; generate_key_table()"
import os
from functools import partial
with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'keys.h'), 'w') as f:
w = partial(print, file=f)
generate_key_table_impl(w)

View File

@ -2324,6 +2324,7 @@ WRAP0(carriage_return)
WRAP2(resize, 1, 1)
WRAP2(set_margins, 1, 1)
WRAP0(rescale_images)
WRAP0(current_key_encoding_flags)
static PyObject*
start_selection(Screen *self, PyObject *args) {
@ -2886,6 +2887,7 @@ static PyMethodDef methods[] = {
MND(resize, METH_VARARGS)
MND(set_margins, METH_VARARGS)
MND(rescale_images, METH_NOARGS)
MND(current_key_encoding_flags, METH_NOARGS)
MND(text_for_selection, METH_NOARGS)
MND(is_rectangle_select, METH_NOARGS)
MND(scroll, METH_VARARGS)

View File

@ -22,16 +22,16 @@ from .config import build_ansi_color_table
from .constants import ScreenGeometry, WindowGeometry, appname, wakeup
from .fast_data_types import (
BGIMAGE_PROGRAM, BLIT_PROGRAM, CELL_BG_PROGRAM, CELL_FG_PROGRAM,
CELL_PROGRAM, CELL_SPECIAL_PROGRAM, DCS, DECORATION, DIM,
CELL_PROGRAM, CELL_SPECIAL_PROGRAM, DCS, DECORATION, DIM, GLFW_MOD_CONTROL,
GRAPHICS_ALPHA_MASK_PROGRAM, GRAPHICS_PREMULT_PROGRAM, GRAPHICS_PROGRAM,
MARK, MARK_MASK, OSC, REVERSE, SCROLL_FULL, SCROLL_LINE, SCROLL_PAGE,
STRIKETHROUGH, TINT_PROGRAM, Screen, add_timer, add_window,
cell_size_for_window, compile_program, get_boss, get_clipboard_string,
init_cell_program, pt_to_px, set_clipboard_string, set_titlebar_color,
set_window_padding, set_window_render_data, update_window_title,
update_window_visibility, viewport_for_window
cell_size_for_window, compile_program, encode_key_for_tty, get_boss,
get_clipboard_string, init_cell_program, pt_to_px, set_clipboard_string,
set_titlebar_color, set_window_padding, set_window_render_data,
update_window_title, update_window_visibility, viewport_for_window
)
from .keys import defines, extended_key_event, keyboard_mode_name
from .keys import keyboard_mode_name
from .notify import NotificationCommand, handle_notification_cmd
from .options_stub import Options
from .rgb import to_color
@ -886,9 +886,11 @@ class Window:
if text:
set_clipboard_string(text)
else:
mode = keyboard_mode_name(self.screen)
data = extended_key_event(defines.GLFW_KEY_C, defines.GLFW_MOD_CONTROL, defines.GLFW_PRESS) if mode == 'kitty' else b'\x03'
self.write_to_child(data)
data = encode_key_for_tty(
key=ord('c'), mods=GLFW_MOD_CONTROL, key_encoding_flags=self.screen.current_key_encoding_flags(),
cursor_key_mode=self.screen.cursor_key_mode
)
self.write_to_child(data.encode('ascii'))
def copy_and_clear_or_interrupt(self) -> None:
self.copy_or_interrupt()

View File

@ -2,6 +2,7 @@
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
from functools import partial
import kitty.fast_data_types as defines
from . import BaseTest
@ -9,7 +10,46 @@ from . import BaseTest
class TestKeys(BaseTest):
def test_encode_key_event(self):
pass
enc = defines.encode_key_for_tty
ae = self.assertEqual
shift, alt, ctrl, super = defines.GLFW_MOD_SHIFT, defines.GLFW_MOD_ALT, defines.GLFW_MOD_CONTROL, defines.GLFW_MOD_SUPER # noqa
press, repeat, release = defines.GLFW_PRESS, defines.GLFW_REPEAT, defines.GLFW_RELEASE # noqa
def csi(mods=0, num=1, trailer='u'):
ans = f'\033[{num}'
if mods:
m = 0
if mods & shift:
m |= 1
if mods & alt:
m |= 2
if mods & ctrl:
m |= 4
if mods & super:
m |= 8
ans += f';{m+1}'
return ans + trailer
def mods_test(key, plain, shift=None, ctrl=None, alt=None, calt=None, cshift=None, ashift=None, csi_num=None, trailer='u'):
c = partial(csi, num=csi_num or key, trailer=trailer)
e = partial(enc, key=key)
ae(e(), plain)
ae(e(mods=defines.GLFW_MOD_SHIFT), shift or c(defines.GLFW_MOD_SHIFT))
ae(e(mods=defines.GLFW_MOD_CONTROL), ctrl or c(defines.GLFW_MOD_CONTROL))
ae(e(mods=defines.GLFW_MOD_ALT | defines.GLFW_MOD_CONTROL), calt or c(defines.GLFW_MOD_ALT | defines.GLFW_MOD_CONTROL))
ae(e(mods=defines.GLFW_MOD_SHIFT | defines.GLFW_MOD_CONTROL), cshift or c(defines.GLFW_MOD_CONTROL | defines.GLFW_MOD_SHIFT))
ae(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_ESCAPE, '\x1b', alt='\033\033', csi_num=27)
mods_test(defines.GLFW_FKEY_BACKSPACE, '\x7f', alt='\033\x7f', csi_num=127)
mods_test(defines.GLFW_FKEY_TAB, '\t', alt='\033\t', shift='\x1b[Z', csi_num=ord('\t'))
q = partial(enc, key=ord('a'))
ae(q(), 'a')
ae(q(text='a'), 'a')
ae(q(action=repeat), 'a')
ae(q(action=release), '')
def test_encode_mouse_event(self):
NORMAL_PROTOCOL, UTF8_PROTOCOL, SGR_PROTOCOL, URXVT_PROTOCOL = range(4)