mirror of
https://github.com/kovidgoyal/kitty.git
synced 2024-10-26 15:13:22 +03:00
Work on porting kittens to use new key infrastructure
Also move type definitions into their own module
This commit is contained in:
parent
0714fd376b
commit
027c5a57f1
@ -629,7 +629,8 @@ specification.
|
|||||||
* No way to disambiguate :kbd:`Esc` keypresses, other than using 8-bit controls
|
* No way to disambiguate :kbd:`Esc` keypresses, other than using 8-bit controls
|
||||||
which are undesirable for other reasons
|
which are undesirable for other reasons
|
||||||
* Incorrectly claims special keys are sometimes encoded using ``CSI letter`` encodings when it
|
* Incorrectly claims special keys are sometimes encoded using ``CSI letter`` encodings when it
|
||||||
is actually ``SS3 letter``.
|
is actually ``SS3 letter`` in all terminals newer than a VT-52, which is
|
||||||
|
pretty much everything.
|
||||||
* :kbd:`ctrl+shift+tab`` should be ``CSI 9 ; 6 u`` not ``CSI 1 ; 5 Z``
|
* :kbd:`ctrl+shift+tab`` should be ``CSI 9 ; 6 u`` not ``CSI 1 ; 5 Z``
|
||||||
(shift+tab is not a separate key from tab)
|
(shift+tab is not a separate key from tab)
|
||||||
* No support for the :kbd:`super` modifier.
|
* No support for the :kbd:`super` modifier.
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
from kitty.cli import parse_args
|
from kitty.cli import parse_args
|
||||||
from kitty.cli_stub import BroadcastCLIOptions
|
from kitty.cli_stub import BroadcastCLIOptions
|
||||||
from kitty.key_encoding import RELEASE, encode_key_event, key_defs as K
|
from kitty.key_encoding import encode_key_event
|
||||||
from kitty.rc.base import MATCH_TAB_OPTION, MATCH_WINDOW_OPTION
|
from kitty.rc.base import MATCH_TAB_OPTION, MATCH_WINDOW_OPTION
|
||||||
from kitty.remote_control import create_basic_command, encode_send
|
from kitty.remote_control import create_basic_command, encode_send
|
||||||
from kitty.typing import KeyEventType, ScreenSize
|
from kitty.typing import KeyEventType, ScreenSize
|
||||||
@ -59,15 +59,15 @@ def on_eot(self) -> None:
|
|||||||
def on_key(self, key_event: KeyEventType) -> None:
|
def on_key(self, key_event: KeyEventType) -> None:
|
||||||
if self.line_edit.on_key(key_event):
|
if self.line_edit.on_key(key_event):
|
||||||
self.commit_line()
|
self.commit_line()
|
||||||
if key_event.type is not RELEASE and not key_event.mods:
|
if key_event.matches('enter'):
|
||||||
if key_event.key is K['ENTER']:
|
self.write_broadcast_text('\r')
|
||||||
self.write_broadcast_text('\r')
|
self.print('')
|
||||||
self.print('')
|
self.line_edit.clear()
|
||||||
self.line_edit.clear()
|
self.write(SAVE_CURSOR)
|
||||||
self.write(SAVE_CURSOR)
|
return
|
||||||
return
|
|
||||||
|
|
||||||
ek = encode_key_event(key_event)
|
ek = encode_key_event(key_event)
|
||||||
|
ek = standard_b64encode(ek.encode('utf-8')).decode('ascii')
|
||||||
self.write_broadcast_data('kitty-key:' + ek)
|
self.write_broadcast_data('kitty-key:' + ek)
|
||||||
|
|
||||||
def write_broadcast_text(self, text: str) -> None:
|
def write_broadcast_text(self, text: str) -> None:
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
from kitty.conf.utils import KittensKeyAction
|
from kitty.conf.utils import KittensKeyAction
|
||||||
from kitty.constants import appname
|
from kitty.constants import appname
|
||||||
from kitty.fast_data_types import wcswidth
|
from kitty.fast_data_types import wcswidth
|
||||||
from kitty.key_encoding import RELEASE, KeyEvent, enter_key, key_defs as K
|
from kitty.key_encoding import KeyEvent, EventType
|
||||||
from kitty.options_stub import DiffOptions
|
from kitty.options_stub import DiffOptions
|
||||||
from kitty.utils import ScreenSize
|
from kitty.utils import ScreenSize
|
||||||
|
|
||||||
@ -57,7 +57,6 @@ def highlight_collection(collection: 'Collection', aliases: Optional[Dict[str, s
|
|||||||
|
|
||||||
|
|
||||||
INITIALIZING, COLLECTED, DIFFED, COMMAND, MESSAGE = range(5)
|
INITIALIZING, COLLECTED, DIFFED, COMMAND, MESSAGE = range(5)
|
||||||
ESCAPE = K['ESCAPE']
|
|
||||||
|
|
||||||
|
|
||||||
def generate_diff(collection: Collection, context: int) -> Union[str, Dict[str, Patch]]:
|
def generate_diff(collection: Collection, context: int) -> Union[str, Dict[str, Patch]]:
|
||||||
@ -483,48 +482,30 @@ def do_search(self) -> None:
|
|||||||
self.message = sanitize(_('No matches found'))
|
self.message = sanitize(_('No matches found'))
|
||||||
self.cmd.bell()
|
self.cmd.bell()
|
||||||
|
|
||||||
def on_text(self, text: str, in_bracketed_paste: bool = False) -> None:
|
def on_key_event(self, key_event: KeyEvent, in_bracketed_paste: bool = False) -> None:
|
||||||
if self.state is COMMAND:
|
if key_event.text:
|
||||||
self.line_edit.on_text(text, in_bracketed_paste)
|
if self.state is COMMAND:
|
||||||
self.draw_status_line()
|
self.line_edit.on_text(key_event.text, in_bracketed_paste)
|
||||||
return
|
self.draw_status_line()
|
||||||
if self.state is MESSAGE:
|
return
|
||||||
self.state = DIFFED
|
if self.state is MESSAGE:
|
||||||
self.draw_status_line()
|
|
||||||
return
|
|
||||||
action = self.shortcut_action(text)
|
|
||||||
if action is not None:
|
|
||||||
return self.perform_action(action)
|
|
||||||
|
|
||||||
def on_key(self, key_event: KeyEvent) -> None:
|
|
||||||
if self.state is MESSAGE:
|
|
||||||
if key_event.type is not RELEASE:
|
|
||||||
self.state = DIFFED
|
self.state = DIFFED
|
||||||
self.draw_status_line()
|
self.draw_status_line()
|
||||||
return
|
return
|
||||||
if self.state is COMMAND:
|
else:
|
||||||
if self.line_edit.on_key(key_event):
|
if self.state is MESSAGE:
|
||||||
if not self.line_edit.current_input:
|
if key_event.type is not EventType.RELEASE:
|
||||||
self.state = DIFFED
|
self.state = DIFFED
|
||||||
self.draw_status_line()
|
self.draw_status_line()
|
||||||
return
|
return
|
||||||
if key_event.type is RELEASE:
|
if self.state is COMMAND:
|
||||||
return
|
if self.line_edit.on_key(key_event):
|
||||||
if self.state is COMMAND:
|
if not self.line_edit.current_input:
|
||||||
if key_event.key is ESCAPE:
|
self.state = DIFFED
|
||||||
self.state = DIFFED
|
self.draw_status_line()
|
||||||
self.draw_status_line()
|
return
|
||||||
|
if key_event.type is EventType.RELEASE:
|
||||||
return
|
return
|
||||||
if key_event is enter_key:
|
|
||||||
self.state = DIFFED
|
|
||||||
self.do_search()
|
|
||||||
self.line_edit.clear()
|
|
||||||
self.draw_screen()
|
|
||||||
return
|
|
||||||
if self.state >= DIFFED and self.current_search is not None and key_event.key is ESCAPE:
|
|
||||||
self.current_search = None
|
|
||||||
self.draw_screen()
|
|
||||||
return
|
|
||||||
action = self.shortcut_action(key_event)
|
action = self.shortcut_action(key_event)
|
||||||
if action is not None:
|
if action is not None:
|
||||||
return self.perform_action(action)
|
return self.perform_action(action)
|
||||||
|
@ -17,9 +17,7 @@
|
|||||||
from kitty.cli import parse_args
|
from kitty.cli import parse_args
|
||||||
from kitty.cli_stub import HintsCLIOptions
|
from kitty.cli_stub import HintsCLIOptions
|
||||||
from kitty.fast_data_types import set_clipboard_string
|
from kitty.fast_data_types import set_clipboard_string
|
||||||
from kitty.key_encoding import (
|
from kitty.key_encoding import KeyEvent
|
||||||
KeyEvent, backspace_key, enter_key, key_defs as K
|
|
||||||
)
|
|
||||||
from kitty.typing import BossType, KittyCommonOpts
|
from kitty.typing import BossType, KittyCommonOpts
|
||||||
from kitty.utils import ScreenSize, screen_size_function, set_primary_selection
|
from kitty.utils import ScreenSize, screen_size_function, set_primary_selection
|
||||||
|
|
||||||
@ -40,7 +38,6 @@ def kitty_common_opts() -> KittyCommonOpts:
|
|||||||
|
|
||||||
DEFAULT_HINT_ALPHABET = string.digits + string.ascii_lowercase
|
DEFAULT_HINT_ALPHABET = string.digits + string.ascii_lowercase
|
||||||
DEFAULT_REGEX = r'(?m)^\s*(.+)\s*$'
|
DEFAULT_REGEX = r'(?m)^\s*(.+)\s*$'
|
||||||
ESCAPE = K['ESCAPE']
|
|
||||||
|
|
||||||
|
|
||||||
class Mark:
|
class Mark:
|
||||||
@ -177,11 +174,11 @@ def on_text(self, text: str, in_bracketed_paste: bool = False) -> None:
|
|||||||
self.draw_screen()
|
self.draw_screen()
|
||||||
|
|
||||||
def on_key(self, key_event: KeyEvent) -> None:
|
def on_key(self, key_event: KeyEvent) -> None:
|
||||||
if key_event is backspace_key:
|
if key_event.matches('backspace'):
|
||||||
self.current_input = self.current_input[:-1]
|
self.current_input = self.current_input[:-1]
|
||||||
self.current_text = None
|
self.current_text = None
|
||||||
self.draw_screen()
|
self.draw_screen()
|
||||||
elif key_event is enter_key and self.current_input:
|
elif key_event.matches('enter') and self.current_input:
|
||||||
try:
|
try:
|
||||||
idx = decode_hint(self.current_input, self.alphabet)
|
idx = decode_hint(self.current_input, self.alphabet)
|
||||||
self.chosen.append(self.index_map[idx])
|
self.chosen.append(self.index_map[idx])
|
||||||
@ -196,7 +193,7 @@ def on_key(self, key_event: KeyEvent) -> None:
|
|||||||
self.draw_screen()
|
self.draw_screen()
|
||||||
else:
|
else:
|
||||||
self.quit_loop(0)
|
self.quit_loop(0)
|
||||||
elif key_event.key is ESCAPE:
|
elif key_event.matches('esc'):
|
||||||
self.quit_loop(0 if self.multiple else 1)
|
self.quit_loop(0 if self.multiple else 1)
|
||||||
|
|
||||||
def on_interrupt(self) -> None:
|
def on_interrupt(self) -> None:
|
||||||
@ -288,6 +285,7 @@ def quotes(text: str, s: int, e: int) -> Tuple[int, int]:
|
|||||||
@postprocessor
|
@postprocessor
|
||||||
def ip(text: str, s: int, e: int) -> Tuple[int, int]:
|
def ip(text: str, s: int, e: int) -> Tuple[int, int]:
|
||||||
from ipaddress import ip_address
|
from ipaddress import ip_address
|
||||||
|
|
||||||
# Check validity of IPs (or raise InvalidMatch)
|
# Check validity of IPs (or raise InvalidMatch)
|
||||||
ip = text[s:e]
|
ip = text[s:e]
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
Any, Callable, ContextManager, Dict, Optional, Sequence, Type, Union
|
Any, Callable, ContextManager, Dict, Optional, Sequence, Type, Union
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from kitty.types import ParsedShortcut
|
||||||
from kitty.typing import (
|
from kitty.typing import (
|
||||||
AbstractEventLoop, BossType, Debug, ImageManagerType, KeyEventType,
|
AbstractEventLoop, BossType, Debug, ImageManagerType, KeyEventType,
|
||||||
KittensKeyActionType, LoopType, MouseEvent, ScreenSize, TermManagerType
|
KittensKeyActionType, LoopType, MouseEvent, ScreenSize, TermManagerType
|
||||||
@ -35,6 +36,7 @@ def _initialize(
|
|||||||
self.debug = debug
|
self.debug = debug
|
||||||
self.cmd = commander(self)
|
self.cmd = commander(self)
|
||||||
self._image_manager = image_manager
|
self._image_manager = image_manager
|
||||||
|
self._key_shortcuts: Dict[ParsedShortcut, KittensKeyActionType] = {}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def image_manager(self) -> ImageManagerType:
|
def image_manager(self) -> ImageManagerType:
|
||||||
@ -45,18 +47,16 @@ def image_manager(self) -> ImageManagerType:
|
|||||||
def asyncio_loop(self) -> AbstractEventLoop:
|
def asyncio_loop(self) -> AbstractEventLoop:
|
||||||
return self._tui_loop.asycio_loop
|
return self._tui_loop.asycio_loop
|
||||||
|
|
||||||
def add_shortcut(self, action: KittensKeyActionType, key: str, mods: Optional[int] = None, is_text: Optional[bool] = False) -> None:
|
def add_shortcut(self, action: KittensKeyActionType, spec: Union[str, ParsedShortcut]) -> None:
|
||||||
if not hasattr(self, '_text_shortcuts'):
|
if isinstance(spec, str):
|
||||||
self._text_shortcuts, self._key_shortcuts = {}, {}
|
from kitty.key_encoding import parse_shortcut
|
||||||
if is_text:
|
spec = parse_shortcut(spec)
|
||||||
self._text_shortcuts[key] = action
|
self._key_shortcuts[spec] = action
|
||||||
else:
|
|
||||||
self._key_shortcuts[(key, mods or 0)] = action
|
|
||||||
|
|
||||||
def shortcut_action(self, key_event_or_text: Union[str, KeyEventType]) -> Optional[KittensKeyActionType]:
|
def shortcut_action(self, key_event: KeyEventType) -> Optional[KittensKeyActionType]:
|
||||||
if isinstance(key_event_or_text, str):
|
for sc, action in self._key_shortcuts.items():
|
||||||
return self._text_shortcuts.get(key_event_or_text)
|
if key_event.matches(sc):
|
||||||
return self._key_shortcuts.get((key_event_or_text.key, key_event_or_text.mods))
|
return action
|
||||||
|
|
||||||
def __enter__(self) -> None:
|
def __enter__(self) -> None:
|
||||||
if self._image_manager is not None:
|
if self._image_manager is not None:
|
||||||
@ -85,6 +85,12 @@ def quit_loop(self, return_code: Optional[int] = None) -> None:
|
|||||||
def on_term(self) -> None:
|
def on_term(self) -> None:
|
||||||
self._tui_loop.quit(1)
|
self._tui_loop.quit(1)
|
||||||
|
|
||||||
|
def on_key_event(self, key_event: KeyEventType, in_bracketed_paste: bool = False) -> None:
|
||||||
|
if key_event.text:
|
||||||
|
self.on_text(key_event.text, in_bracketed_paste)
|
||||||
|
else:
|
||||||
|
self.on_key(key_event)
|
||||||
|
|
||||||
def on_text(self, text: str, in_bracketed_paste: bool = False) -> None:
|
def on_text(self, text: str, in_bracketed_paste: bool = False) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -5,17 +5,10 @@
|
|||||||
from typing import Callable, Tuple
|
from typing import Callable, Tuple
|
||||||
|
|
||||||
from kitty.fast_data_types import truncate_point_for_length, wcswidth
|
from kitty.fast_data_types import truncate_point_for_length, wcswidth
|
||||||
from kitty.key_encoding import RELEASE, KeyEvent, key_defs as K
|
from kitty.key_encoding import EventType, KeyEvent
|
||||||
|
|
||||||
from .operations import RESTORE_CURSOR, SAVE_CURSOR, move_cursor_by
|
from .operations import RESTORE_CURSOR, SAVE_CURSOR, move_cursor_by
|
||||||
|
|
||||||
HOME = K['HOME']
|
|
||||||
END = K['END']
|
|
||||||
BACKSPACE = K['BACKSPACE']
|
|
||||||
DELETE = K['DELETE']
|
|
||||||
LEFT = K['LEFT']
|
|
||||||
RIGHT = K['RIGHT']
|
|
||||||
|
|
||||||
|
|
||||||
class LineEdit:
|
class LineEdit:
|
||||||
|
|
||||||
@ -137,22 +130,22 @@ def end(self) -> bool:
|
|||||||
return self.cursor_pos != orig
|
return self.cursor_pos != orig
|
||||||
|
|
||||||
def on_key(self, key_event: KeyEvent) -> bool:
|
def on_key(self, key_event: KeyEvent) -> bool:
|
||||||
if key_event.type is RELEASE:
|
if key_event.type is EventType.RELEASE:
|
||||||
return False
|
return False
|
||||||
elif key_event.key is HOME:
|
if key_event.matches('home'):
|
||||||
return self.home()
|
return self.home()
|
||||||
elif key_event.key is END:
|
if key_event.matches('end'):
|
||||||
return self.end()
|
return self.end()
|
||||||
elif key_event.key is BACKSPACE:
|
if key_event.matches('backspace'):
|
||||||
self.backspace()
|
self.backspace()
|
||||||
return True
|
return True
|
||||||
elif key_event.key is DELETE:
|
if key_event.matches('delete'):
|
||||||
self.delete()
|
self.delete()
|
||||||
return True
|
return True
|
||||||
elif key_event.key is LEFT:
|
if key_event.matches('left'):
|
||||||
self.left()
|
self.left()
|
||||||
return True
|
return True
|
||||||
elif key_event.key is RIGHT:
|
if key_event.matches('right'):
|
||||||
self.right()
|
self.right()
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
)
|
)
|
||||||
from kitty.key_encoding import (
|
from kitty.key_encoding import (
|
||||||
ALT, CTRL, PRESS, RELEASE, REPEAT, SHIFT, backspace_key, decode_key_event,
|
ALT, CTRL, PRESS, RELEASE, REPEAT, SHIFT, backspace_key, decode_key_event,
|
||||||
enter_key, key_defs as K
|
enter_key
|
||||||
)
|
)
|
||||||
from kitty.typing import ImageManagerType, KeyEventType, Protocol
|
from kitty.typing import ImageManagerType, KeyEventType, Protocol
|
||||||
from kitty.utils import ScreenSizeGetter, screen_size_function, write_all
|
from kitty.utils import ScreenSizeGetter, screen_size_function, write_all
|
||||||
@ -29,8 +29,6 @@
|
|||||||
from .handler import Handler
|
from .handler import Handler
|
||||||
from .operations import init_state, reset_state
|
from .operations import init_state, reset_state
|
||||||
|
|
||||||
C, D = K['C'], K['D']
|
|
||||||
|
|
||||||
|
|
||||||
class BinaryWrite(Protocol):
|
class BinaryWrite(Protocol):
|
||||||
|
|
||||||
@ -149,7 +147,7 @@ def initialize(self) -> None:
|
|||||||
self.write('Press the Enter key to quit')
|
self.write('Press the Enter key to quit')
|
||||||
|
|
||||||
def on_key(self, key_event: KeyEventType) -> None:
|
def on_key(self, key_event: KeyEventType) -> None:
|
||||||
if key_event is enter_key:
|
if key_event.key == 'ENTER':
|
||||||
self.quit_loop(1)
|
self.quit_loop(1)
|
||||||
|
|
||||||
def on_interrupt(self) -> None:
|
def on_interrupt(self) -> None:
|
||||||
@ -282,8 +280,23 @@ def _on_csi(self, csi: str) -> None:
|
|||||||
elif q == '~':
|
elif q == '~':
|
||||||
if csi == '200~':
|
if csi == '200~':
|
||||||
self.in_bracketed_paste = True
|
self.in_bracketed_paste = True
|
||||||
|
return
|
||||||
elif csi == '201~':
|
elif csi == '201~':
|
||||||
self.in_bracketed_paste = False
|
self.in_bracketed_paste = False
|
||||||
|
return
|
||||||
|
elif q in 'u~ABCDHFPQRS':
|
||||||
|
try:
|
||||||
|
k = decode_key_event(csi[:-1], q)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
if k.matches('ctrl+c'):
|
||||||
|
self.handler.on_interrupt()
|
||||||
|
return
|
||||||
|
if k.matches('ctrl+d'):
|
||||||
|
self.handler.on_eot()
|
||||||
|
return
|
||||||
|
self.handler.on_key_event(k)
|
||||||
|
|
||||||
def _on_pm(self, pm: str) -> None:
|
def _on_pm(self, pm: str) -> None:
|
||||||
pass
|
pass
|
||||||
@ -300,21 +313,7 @@ def _on_osc(self, osc: str) -> None:
|
|||||||
self.handler.on_clipboard_response(standard_b64decode(rest).decode('utf-8'), from_primary)
|
self.handler.on_clipboard_response(standard_b64decode(rest).decode('utf-8'), from_primary)
|
||||||
|
|
||||||
def _on_apc(self, apc: str) -> None:
|
def _on_apc(self, apc: str) -> None:
|
||||||
if apc.startswith('K'):
|
if apc.startswith('G'):
|
||||||
try:
|
|
||||||
k = decode_key_event(apc[1:])
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
if k.mods is CTRL and k.type is not RELEASE:
|
|
||||||
if k.key is C:
|
|
||||||
self.handler.on_interrupt()
|
|
||||||
return
|
|
||||||
if k.key is D:
|
|
||||||
self.handler.on_eot()
|
|
||||||
return
|
|
||||||
self.handler.on_key(k)
|
|
||||||
elif apc.startswith('G'):
|
|
||||||
if self.handler.image_manager is not None:
|
if self.handler.image_manager is not None:
|
||||||
self.handler.image_manager.handle_response(apc)
|
self.handler.image_manager.handle_response(apc)
|
||||||
# }}}
|
# }}}
|
||||||
|
@ -40,7 +40,6 @@
|
|||||||
MOUSE_URXVT_MODE=(1015, '?'),
|
MOUSE_URXVT_MODE=(1015, '?'),
|
||||||
ALTERNATE_SCREEN=(1049, '?'),
|
ALTERNATE_SCREEN=(1049, '?'),
|
||||||
BRACKETED_PASTE=(2004, '?'),
|
BRACKETED_PASTE=(2004, '?'),
|
||||||
EXTENDED_KEYBOARD=(2017, '?'),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
F = TypeVar('F')
|
F = TypeVar('F')
|
||||||
@ -270,8 +269,8 @@ def init_state(alternate_screen: bool = True) -> str:
|
|||||||
reset_mode('MOUSE_MOTION_TRACKING') + reset_mode('MOUSE_MOVE_TRACKING') +
|
reset_mode('MOUSE_MOTION_TRACKING') + reset_mode('MOUSE_MOVE_TRACKING') +
|
||||||
reset_mode('FOCUS_TRACKING') + reset_mode('MOUSE_UTF8_MODE') +
|
reset_mode('FOCUS_TRACKING') + reset_mode('MOUSE_UTF8_MODE') +
|
||||||
reset_mode('MOUSE_SGR_MODE') + reset_mode('MOUSE_UTF8_MODE') +
|
reset_mode('MOUSE_SGR_MODE') + reset_mode('MOUSE_UTF8_MODE') +
|
||||||
set_mode('BRACKETED_PASTE') + set_mode('EXTENDED_KEYBOARD') +
|
set_mode('BRACKETED_PASTE') + SAVE_COLORS +
|
||||||
SAVE_COLORS +
|
'\033[>31u' + # extended keyboard mode
|
||||||
'\033[*x' # reset DECSACE to default region select
|
'\033[*x' # reset DECSACE to default region select
|
||||||
)
|
)
|
||||||
if alternate_screen:
|
if alternate_screen:
|
||||||
@ -287,6 +286,7 @@ def reset_state(normal_screen: bool = True) -> str:
|
|||||||
ans += RESTORE_PRIVATE_MODE_VALUES
|
ans += RESTORE_PRIVATE_MODE_VALUES
|
||||||
ans += RESTORE_CURSOR
|
ans += RESTORE_CURSOR
|
||||||
ans += RESTORE_COLORS
|
ans += RESTORE_COLORS
|
||||||
|
ans += '\033[<u' # restore keyboard mode
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,8 +25,7 @@
|
|||||||
)
|
)
|
||||||
from .config_data import MINIMUM_FONT_SIZE
|
from .config_data import MINIMUM_FONT_SIZE
|
||||||
from .constants import (
|
from .constants import (
|
||||||
SingleKey, appname, config_dir, is_macos, kitty_exe,
|
appname, config_dir, is_macos, kitty_exe, supports_primary_selection
|
||||||
supports_primary_selection
|
|
||||||
)
|
)
|
||||||
from .fast_data_types import (
|
from .fast_data_types import (
|
||||||
CLOSE_BEING_CONFIRMED, IMPERATIVE_CLOSE_REQUESTED, NO_CLOSE_REQUESTED,
|
CLOSE_BEING_CONFIRMED, IMPERATIVE_CLOSE_REQUESTED, NO_CLOSE_REQUESTED,
|
||||||
@ -49,6 +48,7 @@
|
|||||||
from .tabs import (
|
from .tabs import (
|
||||||
SpecialWindow, SpecialWindowInstance, Tab, TabDict, TabManager
|
SpecialWindow, SpecialWindowInstance, Tab, TabDict, TabManager
|
||||||
)
|
)
|
||||||
|
from .types import SingleKey
|
||||||
from .typing import PopenType, TypedDict
|
from .typing import PopenType, TypedDict
|
||||||
from .utils import (
|
from .utils import (
|
||||||
func_name, get_editor, get_primary_selection, is_path_in_temp_dir,
|
func_name, get_editor, get_primary_selection, is_path_in_temp_dir,
|
||||||
|
@ -14,8 +14,9 @@
|
|||||||
from .cli_stub import CLIOptions
|
from .cli_stub import CLIOptions
|
||||||
from .conf.utils import resolve_config
|
from .conf.utils import resolve_config
|
||||||
from .config import KeyAction
|
from .config import KeyAction
|
||||||
from .constants import appname, defconf, is_macos, is_wayland, str_version, SingleKey
|
from .constants import appname, defconf, is_macos, is_wayland, str_version
|
||||||
from .options_stub import Options as OptionsStub
|
from .options_stub import Options as OptionsStub
|
||||||
|
from .types import SingleKey
|
||||||
from .typing import BadLineType, SequenceMap, TypedDict
|
from .typing import BadLineType, SequenceMap, TypedDict
|
||||||
|
|
||||||
|
|
||||||
@ -759,7 +760,10 @@ def parse_args(
|
|||||||
|
|
||||||
|
|
||||||
def print_shortcut(key_sequence: Iterable[SingleKey], action: KeyAction) -> None:
|
def print_shortcut(key_sequence: Iterable[SingleKey], action: KeyAction) -> None:
|
||||||
from .fast_data_types import glfw_get_key_name, GLFW_MOD_ALT, GLFW_MOD_SHIFT, GLFW_MOD_CONTROL, GLFW_MOD_SUPER
|
from .fast_data_types import (
|
||||||
|
GLFW_MOD_ALT, GLFW_MOD_CONTROL, GLFW_MOD_SHIFT, GLFW_MOD_SUPER,
|
||||||
|
glfw_get_key_name
|
||||||
|
)
|
||||||
mmap = {'shift': GLFW_MOD_SHIFT, 'alt': GLFW_MOD_ALT, 'ctrl': GLFW_MOD_CONTROL, ('cmd' if is_macos else 'super'): GLFW_MOD_SUPER}
|
mmap = {'shift': GLFW_MOD_SHIFT, 'alt': GLFW_MOD_ALT, 'ctrl': GLFW_MOD_CONTROL, ('cmd' if is_macos else 'super'): GLFW_MOD_SUPER}
|
||||||
keys = []
|
keys = []
|
||||||
for key_spec in key_sequence:
|
for key_spec in key_sequence:
|
||||||
|
@ -11,7 +11,8 @@
|
|||||||
)
|
)
|
||||||
|
|
||||||
from ..rgb import Color, to_color as as_color
|
from ..rgb import Color, to_color as as_color
|
||||||
from ..utils import log_error, expandvars
|
from ..types import ParsedShortcut
|
||||||
|
from ..utils import expandvars, log_error
|
||||||
|
|
||||||
key_pat = re.compile(r'([a-zA-Z][a-zA-Z0-9_-]*)\s+(.+)$')
|
key_pat = re.compile(r'([a-zA-Z][a-zA-Z0-9_-]*)\s+(.+)$')
|
||||||
T = TypeVar('T')
|
T = TypeVar('T')
|
||||||
@ -301,38 +302,6 @@ def w(f: Callable) -> Callable:
|
|||||||
return func_with_args, ans
|
return func_with_args, ans
|
||||||
|
|
||||||
|
|
||||||
def parse_kittens_shortcut(sc: str) -> Tuple[Optional[int], str, bool]:
|
|
||||||
from ..key_encoding import config_key_map, config_mod_map, text_match
|
|
||||||
if sc.endswith('+'):
|
|
||||||
parts = list(filter(None, sc.rstrip('+').split('+') + ['+']))
|
|
||||||
else:
|
|
||||||
parts = sc.split('+')
|
|
||||||
qmods = parts[:-1]
|
|
||||||
if qmods:
|
|
||||||
resolved_mods = 0
|
|
||||||
for mod in qmods:
|
|
||||||
m = config_mod_map.get(mod.upper())
|
|
||||||
if m is None:
|
|
||||||
raise ValueError('Unknown shortcut modifiers: {}'.format(sc))
|
|
||||||
resolved_mods |= m
|
|
||||||
mods: Optional[int] = resolved_mods
|
|
||||||
else:
|
|
||||||
mods = None
|
|
||||||
is_text = False
|
|
||||||
rkey = parts[-1]
|
|
||||||
tkey = text_match(rkey)
|
|
||||||
if tkey is None:
|
|
||||||
rkey = rkey.upper()
|
|
||||||
q = config_key_map.get(rkey)
|
|
||||||
if q is None:
|
|
||||||
raise ValueError('Unknown shortcut key: {}'.format(sc))
|
|
||||||
rkey = q
|
|
||||||
else:
|
|
||||||
is_text = True
|
|
||||||
rkey = tkey
|
|
||||||
return mods, rkey, is_text
|
|
||||||
|
|
||||||
|
|
||||||
KittensKeyAction = Tuple[str, Tuple[str, ...]]
|
KittensKeyAction = Tuple[str, Tuple[str, ...]]
|
||||||
|
|
||||||
|
|
||||||
@ -362,15 +331,12 @@ def parse_kittens_func_args(action: str, args_funcs: Dict[str, Callable]) -> Kit
|
|||||||
return func, tuple(args)
|
return func, tuple(args)
|
||||||
|
|
||||||
|
|
||||||
KittensKey = Tuple[str, Optional[int], bool]
|
|
||||||
|
|
||||||
|
|
||||||
def parse_kittens_key(
|
def parse_kittens_key(
|
||||||
val: str, funcs_with_args: Dict[str, Callable]
|
val: str, funcs_with_args: Dict[str, Callable]
|
||||||
) -> Optional[Tuple[KittensKeyAction, KittensKey]]:
|
) -> Optional[Tuple[KittensKeyAction, ParsedShortcut]]:
|
||||||
|
from ..key_encoding import parse_shortcut
|
||||||
sc, action = val.partition(' ')[::2]
|
sc, action = val.partition(' ')[::2]
|
||||||
if not sc or not action:
|
if not sc or not action:
|
||||||
return None
|
return None
|
||||||
mods, key, is_text = parse_kittens_shortcut(sc)
|
|
||||||
ans = parse_kittens_func_args(action, funcs_with_args)
|
ans = parse_kittens_func_args(action, funcs_with_args)
|
||||||
return ans, (key, mods, is_text)
|
return ans, parse_shortcut(sc)
|
||||||
|
@ -20,9 +20,10 @@
|
|||||||
parse_config_base, python_string, to_bool, to_cmdline
|
parse_config_base, python_string, to_bool, to_cmdline
|
||||||
)
|
)
|
||||||
from .config_data import InvalidMods, all_options, parse_shortcut, type_convert
|
from .config_data import InvalidMods, all_options, parse_shortcut, type_convert
|
||||||
from .constants import SingleKey, cache_dir, defconf, is_macos
|
from .constants import cache_dir, defconf, is_macos
|
||||||
from .fonts import FontFeature
|
from .fonts import FontFeature
|
||||||
from .options_stub import Options as OptionsStub
|
from .options_stub import Options as OptionsStub
|
||||||
|
from .types import SingleKey
|
||||||
from .typing import TypedDict
|
from .typing import TypedDict
|
||||||
from .utils import expandvars, log_error
|
from .utils import expandvars, log_error
|
||||||
|
|
||||||
|
@ -16,13 +16,15 @@
|
|||||||
choices, positive_float, positive_int, to_bool, to_cmdline as tc, to_color,
|
choices, positive_float, positive_int, to_bool, to_cmdline as tc, to_color,
|
||||||
to_color_or_none, unit_float
|
to_color_or_none, unit_float
|
||||||
)
|
)
|
||||||
from .constants import (
|
from .constants import config_dir, is_macos
|
||||||
FloatEdges, SingleKey, config_dir, is_macos
|
|
||||||
)
|
|
||||||
from .fast_data_types import CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE
|
from .fast_data_types import CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE
|
||||||
from .key_names import get_key_name_lookup, key_name_aliases
|
from .key_names import (
|
||||||
|
character_key_name_aliases, functional_key_name_aliases,
|
||||||
|
get_key_name_lookup
|
||||||
|
)
|
||||||
from .layout.interface import all_layouts
|
from .layout.interface import all_layouts
|
||||||
from .rgb import Color, color_as_int, color_as_sharp, color_from_int
|
from .rgb import Color, color_as_int, color_as_sharp, color_from_int
|
||||||
|
from .types import FloatEdges, SingleKey
|
||||||
from .utils import log_error
|
from .utils import log_error
|
||||||
|
|
||||||
|
|
||||||
@ -60,6 +62,8 @@ def to_modifiers(val: str) -> int:
|
|||||||
|
|
||||||
|
|
||||||
def parse_shortcut(sc: str) -> SingleKey:
|
def parse_shortcut(sc: str) -> SingleKey:
|
||||||
|
if sc.endswith('+') and len(sc) > 1:
|
||||||
|
sc = sc[:-1] + 'plus'
|
||||||
parts = sc.split('+')
|
parts = sc.split('+')
|
||||||
mods = 0
|
mods = 0
|
||||||
if len(parts) > 1:
|
if len(parts) > 1:
|
||||||
@ -67,6 +71,7 @@ def parse_shortcut(sc: str) -> SingleKey:
|
|||||||
if not mods:
|
if not mods:
|
||||||
raise InvalidMods('Invalid shortcut')
|
raise InvalidMods('Invalid shortcut')
|
||||||
q = parts[-1]
|
q = parts[-1]
|
||||||
|
q = character_key_name_aliases.get(q.upper(), q)
|
||||||
is_native = False
|
is_native = False
|
||||||
if q.startswith('0x'):
|
if q.startswith('0x'):
|
||||||
try:
|
try:
|
||||||
@ -80,7 +85,7 @@ def parse_shortcut(sc: str) -> SingleKey:
|
|||||||
key = ord(q)
|
key = ord(q)
|
||||||
except Exception:
|
except Exception:
|
||||||
uq = q.upper()
|
uq = q.upper()
|
||||||
uq = key_name_aliases.get(uq, uq)
|
uq = functional_key_name_aliases.get(uq, uq)
|
||||||
x: Optional[int] = getattr(defines, f'GLFW_FKEY_{uq}', None)
|
x: Optional[int] = getattr(defines, f'GLFW_FKEY_{uq}', None)
|
||||||
if x is None:
|
if x is None:
|
||||||
lf = get_key_name_lookup()
|
lf = get_key_name_lookup()
|
||||||
|
@ -27,45 +27,6 @@ class Version(NamedTuple):
|
|||||||
base = os.path.dirname(os.path.abspath(__file__))
|
base = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
|
||||||
class Edges(NamedTuple):
|
|
||||||
left: int = 0
|
|
||||||
top: int = 0
|
|
||||||
right: int = 0
|
|
||||||
bottom: int = 0
|
|
||||||
|
|
||||||
|
|
||||||
class FloatEdges(NamedTuple):
|
|
||||||
left: float = 0
|
|
||||||
top: float = 0
|
|
||||||
right: float = 0
|
|
||||||
bottom: float = 0
|
|
||||||
|
|
||||||
|
|
||||||
class ScreenGeometry(NamedTuple):
|
|
||||||
xstart: float
|
|
||||||
ystart: float
|
|
||||||
xnum: int
|
|
||||||
ynum: int
|
|
||||||
dx: float
|
|
||||||
dy: float
|
|
||||||
|
|
||||||
|
|
||||||
class WindowGeometry(NamedTuple):
|
|
||||||
left: int
|
|
||||||
top: int
|
|
||||||
right: int
|
|
||||||
bottom: int
|
|
||||||
xnum: int
|
|
||||||
ynum: int
|
|
||||||
spaces: Edges = Edges()
|
|
||||||
|
|
||||||
|
|
||||||
class SingleKey(NamedTuple):
|
|
||||||
mods: int = 0
|
|
||||||
is_native: bool = False
|
|
||||||
key: int = -1
|
|
||||||
|
|
||||||
|
|
||||||
@lru_cache(maxsize=2)
|
@lru_cache(maxsize=2)
|
||||||
def kitty_exe() -> str:
|
def kitty_exe() -> str:
|
||||||
rpath = sys._xoptions.get('bundle_exe_dir')
|
rpath = sys._xoptions.get('bundle_exe_dir')
|
||||||
|
822
kitty/key_encoding.py
generated
822
kitty/key_encoding.py
generated
@ -2,476 +2,254 @@
|
|||||||
# vim:fileencoding=utf-8
|
# vim:fileencoding=utf-8
|
||||||
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
import string
|
from enum import IntEnum
|
||||||
from typing import Dict, NamedTuple, Optional
|
from functools import lru_cache
|
||||||
|
from typing import Dict, NamedTuple, Optional, Tuple, Union
|
||||||
|
|
||||||
from . import fast_data_types as defines
|
from . import fast_data_types as defines
|
||||||
from .key_names import key_name_aliases
|
from .fast_data_types import KeyEvent as WindowSystemKeyEvent
|
||||||
|
from .key_names import character_key_name_aliases, functional_key_name_aliases
|
||||||
|
from .types import ParsedShortcut
|
||||||
|
|
||||||
# ENCODING {{{
|
# number name mappings {{{
|
||||||
ENCODING = {
|
# start csi mapping (auto generated by gen-key-constants.py do not edit)
|
||||||
'0': 'G',
|
functional_key_number_to_name_map = {
|
||||||
'1': 'H',
|
57344: 'ESCAPE',
|
||||||
'2': 'I',
|
57345: 'ENTER',
|
||||||
'3': 'J',
|
57346: 'TAB',
|
||||||
'4': 'K',
|
57347: 'BACKSPACE',
|
||||||
'5': 'L',
|
57348: 'INSERT',
|
||||||
'6': 'M',
|
57349: 'DELETE',
|
||||||
'7': 'N',
|
57350: 'LEFT',
|
||||||
'8': 'O',
|
57351: 'RIGHT',
|
||||||
'9': 'P',
|
57352: 'UP',
|
||||||
'A': 'S',
|
57353: 'DOWN',
|
||||||
'APOSTROPHE': 'B',
|
57354: 'PAGE_UP',
|
||||||
'B': 'T',
|
57355: 'PAGE_DOWN',
|
||||||
'BACKSLASH': 't',
|
57356: 'HOME',
|
||||||
'BACKSPACE': '1',
|
57357: 'END',
|
||||||
'C': 'U',
|
57358: 'CAPS_LOCK',
|
||||||
'CAPS LOCK': ':',
|
57359: 'SCROLL_LOCK',
|
||||||
'COMMA': 'C',
|
57360: 'NUM_LOCK',
|
||||||
'D': 'V',
|
57361: 'PRINT_SCREEN',
|
||||||
'DELETE': '3',
|
57362: 'PAUSE',
|
||||||
'DOWN': '6',
|
57363: 'MENU',
|
||||||
'E': 'W',
|
57364: 'F1',
|
||||||
'END': '-',
|
57365: 'F2',
|
||||||
'ENTER': 'z',
|
57366: 'F3',
|
||||||
'EQUAL': 'R',
|
57367: 'F4',
|
||||||
'ESCAPE': 'y',
|
57368: 'F5',
|
||||||
'F': 'X',
|
57369: 'F6',
|
||||||
'F1': '/',
|
57370: 'F7',
|
||||||
'F10': ']',
|
57371: 'F8',
|
||||||
'F11': '{',
|
57372: 'F9',
|
||||||
'F12': '}',
|
57373: 'F10',
|
||||||
'F13': '@',
|
57374: 'F11',
|
||||||
'F14': '%',
|
57375: 'F12',
|
||||||
'F15': '$',
|
57376: 'F13',
|
||||||
'F16': '#',
|
57377: 'F14',
|
||||||
'F17': 'BA',
|
57378: 'F15',
|
||||||
'F18': 'BB',
|
57379: 'F16',
|
||||||
'F19': 'BC',
|
57380: 'F17',
|
||||||
'F2': '*',
|
57381: 'F18',
|
||||||
'F20': 'BD',
|
57382: 'F19',
|
||||||
'F21': 'BE',
|
57383: 'F20',
|
||||||
'F22': 'BF',
|
57384: 'F21',
|
||||||
'F23': 'BG',
|
57385: 'F22',
|
||||||
'F24': 'BH',
|
57386: 'F23',
|
||||||
'F25': 'BI',
|
57387: 'F24',
|
||||||
'F3': '?',
|
57388: 'F25',
|
||||||
'F4': '&',
|
57389: 'F26',
|
||||||
'F5': '<',
|
57390: 'F27',
|
||||||
'F6': '>',
|
57391: 'F28',
|
||||||
'F7': '(',
|
57392: 'F29',
|
||||||
'F8': ')',
|
57393: 'F30',
|
||||||
'F9': '[',
|
57394: 'F31',
|
||||||
'G': 'Y',
|
57395: 'F32',
|
||||||
'GRAVE ACCENT': 'v',
|
57396: 'F33',
|
||||||
'H': 'Z',
|
57397: 'F34',
|
||||||
'HOME': '.',
|
57398: 'F35',
|
||||||
'I': 'a',
|
57399: 'KP_0',
|
||||||
'INSERT': '2',
|
57400: 'KP_1',
|
||||||
'J': 'b',
|
57401: 'KP_2',
|
||||||
'K': 'c',
|
57402: 'KP_3',
|
||||||
'KP 0': 'BJ',
|
57403: 'KP_4',
|
||||||
'KP 1': 'BK',
|
57404: 'KP_5',
|
||||||
'KP 2': 'BL',
|
57405: 'KP_6',
|
||||||
'KP 3': 'BM',
|
57406: 'KP_7',
|
||||||
'KP 4': 'BN',
|
57407: 'KP_8',
|
||||||
'KP 5': 'BO',
|
57408: 'KP_9',
|
||||||
'KP 6': 'BP',
|
57409: 'KP_DECIMAL',
|
||||||
'KP 7': 'BQ',
|
57410: 'KP_DIVIDE',
|
||||||
'KP 8': 'BR',
|
57411: 'KP_MULTIPLY',
|
||||||
'KP 9': 'BS',
|
57412: 'KP_SUBTRACT',
|
||||||
'KP ADD': 'BX',
|
57413: 'KP_ADD',
|
||||||
'KP DECIMAL': 'BT',
|
57414: 'KP_ENTER',
|
||||||
'KP DIVIDE': 'BU',
|
57415: 'KP_EQUAL',
|
||||||
'KP ENTER': 'BY',
|
57416: 'KP_SEPARATOR',
|
||||||
'KP EQUAL': 'BZ',
|
57417: 'KP_LEFT',
|
||||||
'KP MULTIPLY': 'BV',
|
57418: 'KP_RIGHT',
|
||||||
'KP SUBTRACT': 'BW',
|
57419: 'KP_UP',
|
||||||
'L': 'd',
|
57420: 'KP_DOWN',
|
||||||
'LEFT': '5',
|
57421: 'KP_PAGE_UP',
|
||||||
'LEFT ALT': 'Bc',
|
57422: 'KP_PAGE_DOWN',
|
||||||
'LEFT BRACKET': 's',
|
57423: 'KP_HOME',
|
||||||
'LEFT CONTROL': 'Bb',
|
57424: 'KP_END',
|
||||||
'LEFT SHIFT': 'Ba',
|
57425: 'KP_INSERT',
|
||||||
'LEFT SUPER': 'Bd',
|
57426: 'KP_DELETE',
|
||||||
'M': 'e',
|
57427: 'LEFT_SHIFT',
|
||||||
'MINUS': 'D',
|
57428: 'LEFT_CONTROL',
|
||||||
'N': 'f',
|
57429: 'LEFT_ALT',
|
||||||
'NUM LOCK': '=',
|
57430: 'LEFT_SUPER',
|
||||||
'O': 'g',
|
57431: 'RIGHT_SHIFT',
|
||||||
'P': 'h',
|
57432: 'RIGHT_CONTROL',
|
||||||
'PAGE DOWN': '9',
|
57433: 'RIGHT_ALT',
|
||||||
'PAGE UP': '8',
|
57434: 'RIGHT_SUPER',
|
||||||
'PAUSE': '!',
|
57435: 'MEDIA_PLAY',
|
||||||
'PERIOD': 'E',
|
57436: 'MEDIA_PAUSE',
|
||||||
'PRINT SCREEN': '^',
|
57437: 'MEDIA_PLAY_PAUSE',
|
||||||
'Q': 'i',
|
57438: 'MEDIA_REVERSE',
|
||||||
'R': 'j',
|
57439: 'MEDIA_STOP',
|
||||||
'RIGHT': '4',
|
57440: 'MEDIA_FAST_FORWARD',
|
||||||
'RIGHT ALT': 'Bg',
|
57441: 'MEDIA_REWIND',
|
||||||
'RIGHT BRACKET': 'u',
|
57442: 'MEDIA_TRACK_NEXT',
|
||||||
'RIGHT CONTROL': 'Bf',
|
57443: 'MEDIA_TRACK_PREVIOUS',
|
||||||
'RIGHT SHIFT': 'Be',
|
57444: 'MEDIA_RECORD',
|
||||||
'RIGHT SUPER': 'Bh',
|
57445: 'LOWER_VOLUME',
|
||||||
'S': 'k',
|
57446: 'RAISE_VOLUME',
|
||||||
'SCROLL LOCK': '+',
|
57447: 'MUTE_VOLUME'}
|
||||||
'SEMICOLON': 'Q',
|
csi_number_to_functional_number_map = {
|
||||||
'SLASH': 'F',
|
2: 57348,
|
||||||
'SPACE': 'A',
|
3: 57349,
|
||||||
'T': 'l',
|
5: 57354,
|
||||||
'TAB': '0',
|
6: 57355,
|
||||||
'U': 'm',
|
7: 57356,
|
||||||
'UP': '7',
|
8: 57357,
|
||||||
'V': 'n',
|
9: 57346,
|
||||||
'W': 'o',
|
11: 57364,
|
||||||
'WORLD 1': 'w',
|
12: 57365,
|
||||||
'WORLD 2': 'x',
|
13: 57345,
|
||||||
'X': 'p',
|
14: 57367,
|
||||||
'Y': 'q',
|
15: 57368,
|
||||||
'Z': 'r',
|
17: 57369,
|
||||||
'PLUS': 'Bi',
|
18: 57370,
|
||||||
'UNDERSCORE': 'Bj',
|
19: 57371,
|
||||||
'MENU': 'Bk',
|
20: 57372,
|
||||||
'EXCLAM': 'Bl',
|
21: 57373,
|
||||||
'DOUBLE QUOTE': 'Bm',
|
23: 57374,
|
||||||
'NUMBER SIGN': 'Bn',
|
24: 57375,
|
||||||
'DOLLAR': 'Bo',
|
27: 57344,
|
||||||
'AMPERSAND': 'Bp',
|
127: 57347}
|
||||||
'PARENTHESIS LEFT': 'Bq',
|
letter_trailer_to_csi_number_map = {'A': 57352, 'B': 57353, 'C': 57351, 'D': 57350, 'F': 8, 'H': 7, 'P': 11, 'Q': 12, 'R': 13, 'S': 14}
|
||||||
'PARENTHESIS RIGHT': 'Br',
|
tilde_trailers = {57348, 57349, 57354, 57355, 57368, 57369, 57370, 57371, 57372, 57373, 57374, 57375}
|
||||||
'COLON': 'Bs',
|
# end csi mapping
|
||||||
'LESS': 'Bt',
|
# }}}
|
||||||
'GREATER': 'Bu',
|
|
||||||
'AT': 'Bv',
|
|
||||||
'PARAGRAPH': 'Bw',
|
|
||||||
'MASCULINE': 'Bx',
|
|
||||||
'A GRAVE': 'By',
|
|
||||||
'A DIAERESIS': 'Bz',
|
|
||||||
'A RING': 'B0',
|
|
||||||
'AE': 'B1',
|
|
||||||
'C CEDILLA': 'B2',
|
|
||||||
'E GRAVE': 'B3',
|
|
||||||
'E ACUTE': 'B4',
|
|
||||||
'I GRAVE': 'B5',
|
|
||||||
'N TILDE': 'B6',
|
|
||||||
'O GRAVE': 'B7',
|
|
||||||
'O DIAERESIS': 'B8',
|
|
||||||
'O SLASH': 'B9',
|
|
||||||
'U GRAVE': 'B.',
|
|
||||||
'U DIAERESIS': 'B-',
|
|
||||||
'S SHARP': 'B:',
|
|
||||||
'CYRILLIC A': 'B+',
|
|
||||||
'CYRILLIC BE': 'B=',
|
|
||||||
'CYRILLIC VE': 'B^',
|
|
||||||
'CYRILLIC GHE': 'B!',
|
|
||||||
'CYRILLIC DE': 'B/',
|
|
||||||
'CYRILLIC IE': 'B*',
|
|
||||||
'CYRILLIC ZHE': 'B?',
|
|
||||||
'CYRILLIC ZE': 'B&',
|
|
||||||
'CYRILLIC I': 'B<',
|
|
||||||
'CYRILLIC SHORT I': 'B>',
|
|
||||||
'CYRILLIC KA': 'B(',
|
|
||||||
'CYRILLIC EL': 'B)',
|
|
||||||
'CYRILLIC EM': 'B[',
|
|
||||||
'CYRILLIC EN': 'B]',
|
|
||||||
'CYRILLIC O': 'B{',
|
|
||||||
'CYRILLIC PE': 'B}',
|
|
||||||
'CYRILLIC ER': 'B@',
|
|
||||||
'CYRILLIC ES': 'B%',
|
|
||||||
'CYRILLIC TE': 'B$',
|
|
||||||
'CYRILLIC U': 'B#',
|
|
||||||
'CYRILLIC EF': 'CA',
|
|
||||||
'CYRILLIC HA': 'CB',
|
|
||||||
'CYRILLIC TSE': 'CC',
|
|
||||||
'CYRILLIC CHE': 'CD',
|
|
||||||
'CYRILLIC SHA': 'CE',
|
|
||||||
'CYRILLIC SHCHA': 'CF',
|
|
||||||
'CYRILLIC HARD SIGN': 'CG',
|
|
||||||
'CYRILLIC YERU': 'CH',
|
|
||||||
'CYRILLIC SOFT SIGN': 'CI',
|
|
||||||
'CYRILLIC E': 'CJ',
|
|
||||||
'CYRILLIC YU': 'CK',
|
|
||||||
'CYRILLIC YA': 'CL',
|
|
||||||
'CYRILLIC IO': 'CM',
|
|
||||||
'CIRCUMFLEX': 'CN'
|
|
||||||
}
|
|
||||||
KEY_MAP = {
|
|
||||||
32: 'A',
|
|
||||||
33: 'Bl',
|
|
||||||
34: 'Bm',
|
|
||||||
35: 'Bn',
|
|
||||||
36: 'Bo',
|
|
||||||
38: 'Bp',
|
|
||||||
39: 'B',
|
|
||||||
40: 'Bq',
|
|
||||||
41: 'Br',
|
|
||||||
43: 'Bi',
|
|
||||||
44: 'C',
|
|
||||||
45: 'D',
|
|
||||||
46: 'E',
|
|
||||||
47: 'F',
|
|
||||||
48: 'G',
|
|
||||||
49: 'H',
|
|
||||||
50: 'I',
|
|
||||||
51: 'J',
|
|
||||||
52: 'K',
|
|
||||||
53: 'L',
|
|
||||||
54: 'M',
|
|
||||||
55: 'N',
|
|
||||||
56: 'O',
|
|
||||||
57: 'P',
|
|
||||||
58: 'Bs',
|
|
||||||
59: 'Q',
|
|
||||||
60: 'Bt',
|
|
||||||
61: 'R',
|
|
||||||
62: 'Bu',
|
|
||||||
64: 'Bv',
|
|
||||||
65: 'S',
|
|
||||||
66: 'T',
|
|
||||||
67: 'U',
|
|
||||||
68: 'V',
|
|
||||||
69: 'W',
|
|
||||||
70: 'X',
|
|
||||||
71: 'Y',
|
|
||||||
72: 'Z',
|
|
||||||
73: 'a',
|
|
||||||
74: 'b',
|
|
||||||
75: 'c',
|
|
||||||
76: 'd',
|
|
||||||
77: 'e',
|
|
||||||
78: 'f',
|
|
||||||
79: 'g',
|
|
||||||
80: 'h',
|
|
||||||
81: 'i',
|
|
||||||
82: 'j',
|
|
||||||
83: 'k',
|
|
||||||
84: 'l',
|
|
||||||
85: 'm',
|
|
||||||
86: 'n',
|
|
||||||
87: 'o',
|
|
||||||
88: 'p',
|
|
||||||
89: 'q',
|
|
||||||
90: 'r',
|
|
||||||
91: 's',
|
|
||||||
92: 't',
|
|
||||||
93: 'u',
|
|
||||||
94: 'CN',
|
|
||||||
95: 'Bj',
|
|
||||||
96: 'v',
|
|
||||||
161: 'w',
|
|
||||||
162: 'x',
|
|
||||||
167: 'Bw',
|
|
||||||
186: 'Bx',
|
|
||||||
192: 'By',
|
|
||||||
196: 'Bz',
|
|
||||||
197: 'B0',
|
|
||||||
198: 'B1',
|
|
||||||
199: 'B2',
|
|
||||||
200: 'B3',
|
|
||||||
201: 'B4',
|
|
||||||
204: 'B5',
|
|
||||||
209: 'B6',
|
|
||||||
210: 'B7',
|
|
||||||
214: 'B8',
|
|
||||||
216: 'B9',
|
|
||||||
217: 'B.',
|
|
||||||
220: 'B-',
|
|
||||||
222: 'B:',
|
|
||||||
223: 'B+',
|
|
||||||
224: 'B=',
|
|
||||||
225: 'B^',
|
|
||||||
226: 'B!',
|
|
||||||
227: 'B/',
|
|
||||||
228: 'B*',
|
|
||||||
229: 'B?',
|
|
||||||
230: 'B&',
|
|
||||||
231: 'B<',
|
|
||||||
232: 'B>',
|
|
||||||
233: 'B(',
|
|
||||||
234: 'B)',
|
|
||||||
235: 'B[',
|
|
||||||
236: 'B]',
|
|
||||||
237: 'B{',
|
|
||||||
238: 'B}',
|
|
||||||
239: 'B@',
|
|
||||||
240: 'B%',
|
|
||||||
241: 'B$',
|
|
||||||
242: 'B#',
|
|
||||||
243: 'CA',
|
|
||||||
244: 'CB',
|
|
||||||
245: 'CC',
|
|
||||||
246: 'CD',
|
|
||||||
247: 'CE',
|
|
||||||
248: 'CF',
|
|
||||||
249: 'CG',
|
|
||||||
250: 'CH',
|
|
||||||
251: 'CI',
|
|
||||||
252: 'CJ',
|
|
||||||
253: 'CK',
|
|
||||||
254: 'CL',
|
|
||||||
255: 'CM',
|
|
||||||
256: 'y',
|
|
||||||
257: 'z',
|
|
||||||
258: '0',
|
|
||||||
259: '1',
|
|
||||||
260: '2',
|
|
||||||
261: '3',
|
|
||||||
262: '4',
|
|
||||||
263: '5',
|
|
||||||
264: '6',
|
|
||||||
265: '7',
|
|
||||||
266: '8',
|
|
||||||
267: '9',
|
|
||||||
268: '.',
|
|
||||||
269: '-',
|
|
||||||
280: ':',
|
|
||||||
281: '+',
|
|
||||||
282: '=',
|
|
||||||
283: '^',
|
|
||||||
284: '!',
|
|
||||||
290: '/',
|
|
||||||
291: '*',
|
|
||||||
292: '?',
|
|
||||||
293: '&',
|
|
||||||
294: '<',
|
|
||||||
295: '>',
|
|
||||||
296: '(',
|
|
||||||
297: ')',
|
|
||||||
298: '[',
|
|
||||||
299: ']',
|
|
||||||
300: '{',
|
|
||||||
301: '}',
|
|
||||||
302: '@',
|
|
||||||
303: '%',
|
|
||||||
304: '$',
|
|
||||||
305: '#',
|
|
||||||
306: 'BA',
|
|
||||||
307: 'BB',
|
|
||||||
308: 'BC',
|
|
||||||
309: 'BD',
|
|
||||||
310: 'BE',
|
|
||||||
311: 'BF',
|
|
||||||
312: 'BG',
|
|
||||||
313: 'BH',
|
|
||||||
314: 'BI',
|
|
||||||
320: 'BJ',
|
|
||||||
321: 'BK',
|
|
||||||
322: 'BL',
|
|
||||||
323: 'BM',
|
|
||||||
324: 'BN',
|
|
||||||
325: 'BO',
|
|
||||||
326: 'BP',
|
|
||||||
327: 'BQ',
|
|
||||||
328: 'BR',
|
|
||||||
329: 'BS',
|
|
||||||
330: 'BT',
|
|
||||||
331: 'BU',
|
|
||||||
332: 'BV',
|
|
||||||
333: 'BW',
|
|
||||||
334: 'BX',
|
|
||||||
335: 'BY',
|
|
||||||
336: 'BZ',
|
|
||||||
340: 'Ba',
|
|
||||||
341: 'Bb',
|
|
||||||
342: 'Bc',
|
|
||||||
343: 'Bd',
|
|
||||||
344: 'Be',
|
|
||||||
345: 'Bf',
|
|
||||||
346: 'Bg',
|
|
||||||
347: 'Bh',
|
|
||||||
348: 'Bk'
|
|
||||||
}
|
|
||||||
# END_ENCODING }}}
|
|
||||||
|
|
||||||
text_keys = (
|
|
||||||
string.ascii_uppercase + string.ascii_lowercase + string.digits +
|
|
||||||
'`~!@#$%^&*()_-+=[{]}\\|<,>./?;:\'" '
|
|
||||||
'ÄäÖöÜüߧºàåæçèéìñòøùабвгдежзийклмнопрстуфхцчшщъыьэюяё'
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def text_match(key: str) -> Optional[str]:
|
@lru_cache(2)
|
||||||
if key.upper() == 'SPACE':
|
def get_name_to_functional_number_map() -> Dict[str, int]:
|
||||||
return ' '
|
return {v: k for k, v in functional_key_number_to_name_map.items()}
|
||||||
if key not in text_keys:
|
|
||||||
return None
|
|
||||||
return key
|
|
||||||
|
|
||||||
|
|
||||||
def encode(
|
@lru_cache(2)
|
||||||
integer: int,
|
def get_functional_to_csi_number_map() -> Dict[int, int]:
|
||||||
chars: str = string.ascii_uppercase + string.ascii_lowercase + string.digits +
|
return {v: k for k, v in csi_number_to_functional_number_map.items()}
|
||||||
'.-:+=^!/*?&<>()[]{}@%$#'
|
|
||||||
) -> str:
|
|
||||||
ans = ''
|
|
||||||
d = len(chars)
|
|
||||||
while True:
|
|
||||||
integer, remainder = divmod(integer, d)
|
|
||||||
ans = chars[remainder] + ans
|
|
||||||
if integer == 0:
|
|
||||||
break
|
|
||||||
return ans
|
|
||||||
|
|
||||||
|
|
||||||
def symbolic_name(glfw_name: str) -> str:
|
@lru_cache(2)
|
||||||
return glfw_name[9:].replace('_', ' ')
|
def get_csi_number_to_letter_trailer_map() -> Dict[int, str]:
|
||||||
|
return {v: k for k, v in letter_trailer_to_csi_number_map.items()}
|
||||||
|
|
||||||
def glfw_key_name(symbolic_name: str) -> str:
|
|
||||||
return 'GLFW_KEY_' + symbolic_name.replace(' ', '_')
|
|
||||||
|
|
||||||
|
|
||||||
def update_encoding() -> None:
|
|
||||||
import re
|
|
||||||
import subprocess
|
|
||||||
keys = {a for a in dir(defines) if a.startswith('GLFW_KEY_')}
|
|
||||||
ans = ENCODING
|
|
||||||
key_map = {}
|
|
||||||
i = len(ans)
|
|
||||||
for k in sorted(keys, key=lambda k: int(getattr(defines, k))):
|
|
||||||
if k in ('GLFW_KEY_LAST', 'GLFW_KEY_LAST_PRINTABLE'):
|
|
||||||
continue
|
|
||||||
val = getattr(defines, k)
|
|
||||||
name = symbolic_name(k)
|
|
||||||
if val <= defines.GLFW_KEY_LAST and val != defines.GLFW_KEY_UNKNOWN:
|
|
||||||
if name not in ans:
|
|
||||||
ans[name] = encode(i)
|
|
||||||
i += 1
|
|
||||||
key_map[val] = ans[name]
|
|
||||||
with open(__file__, 'r+') as f:
|
|
||||||
raw = f.read()
|
|
||||||
nraw = re.sub(
|
|
||||||
r'^ENCODING = {.+^# END_ENCODING',
|
|
||||||
'ENCODING = {!r}\nKEY_MAP={!r}\n# END_ENCODING'.format(
|
|
||||||
ans, key_map
|
|
||||||
),
|
|
||||||
raw,
|
|
||||||
flags=re.MULTILINE | re.DOTALL
|
|
||||||
)
|
|
||||||
if raw == nraw:
|
|
||||||
raise SystemExit('Failed to replace ENCODING dict')
|
|
||||||
f.seek(0), f.truncate()
|
|
||||||
f.write(nraw)
|
|
||||||
subprocess.check_call(['yapf', '-i', __file__])
|
|
||||||
|
|
||||||
|
|
||||||
class KeyEvent(NamedTuple):
|
|
||||||
type: int
|
|
||||||
mods: int
|
|
||||||
key: str
|
|
||||||
|
|
||||||
|
|
||||||
PRESS: int = 1
|
PRESS: int = 1
|
||||||
REPEAT: int = 2
|
REPEAT: int = 2
|
||||||
RELEASE: int = 4
|
RELEASE: int = 4
|
||||||
|
|
||||||
|
|
||||||
|
class EventType(IntEnum):
|
||||||
|
PRESS = PRESS
|
||||||
|
REPEAT = REPEAT
|
||||||
|
RELEASE = RELEASE
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache(maxsize=128)
|
||||||
|
def parse_shortcut(spec: str) -> ParsedShortcut:
|
||||||
|
if spec.endswith('+') and len(spec) > 1:
|
||||||
|
spec = spec[:-1] + 'plus'
|
||||||
|
parts = spec.split('+')
|
||||||
|
key_name = parts[-1]
|
||||||
|
key_name = functional_key_name_aliases.get(key_name.upper(), key_name)
|
||||||
|
is_functional_key = key_name.upper() in get_name_to_functional_number_map()
|
||||||
|
if is_functional_key:
|
||||||
|
key_name = key_name.upper()
|
||||||
|
else:
|
||||||
|
key_name = character_key_name_aliases.get(key_name.upper(), key_name)
|
||||||
|
mods = tuple(config_mod_map.get(x.upper(), SUPER << 8) for x in parts[:-1])
|
||||||
|
mod_val = 0
|
||||||
|
for x in mods:
|
||||||
|
mod_val |= x
|
||||||
|
return ParsedShortcut(mod_val, key_name)
|
||||||
|
|
||||||
|
|
||||||
|
class KeyEvent(NamedTuple):
|
||||||
|
type: EventType = EventType.PRESS
|
||||||
|
mods: int = 0
|
||||||
|
key: str = ''
|
||||||
|
text: str = ''
|
||||||
|
shifted_key: str = ''
|
||||||
|
alternate_key: str = ''
|
||||||
|
shift: bool = False
|
||||||
|
alt: bool = False
|
||||||
|
ctrl: bool = False
|
||||||
|
super: bool = False
|
||||||
|
|
||||||
|
def matches(self, spec: Union[str, ParsedShortcut], types: int = EventType.PRESS | EventType.REPEAT) -> bool:
|
||||||
|
if not self.type & types:
|
||||||
|
return False
|
||||||
|
q = self.mods
|
||||||
|
is_shifted = bool(self.shifted_key and self.shift)
|
||||||
|
if is_shifted:
|
||||||
|
q = self.mods & ~SHIFT
|
||||||
|
kq = self.shifted_key
|
||||||
|
else:
|
||||||
|
kq = self.key
|
||||||
|
if isinstance(spec, str):
|
||||||
|
spec = parse_shortcut(spec)
|
||||||
|
if q != spec.mods:
|
||||||
|
return False
|
||||||
|
return kq == spec.key_name
|
||||||
|
|
||||||
|
def as_window_system_event(self) -> WindowSystemKeyEvent:
|
||||||
|
action = defines.GLFW_PRESS
|
||||||
|
if self.type is EventType.REPEAT:
|
||||||
|
action = defines.GLFW_REPEAT
|
||||||
|
elif self.type is EventType.RELEASE:
|
||||||
|
action = defines.GLFW_RELEASE
|
||||||
|
mods = 0
|
||||||
|
if self.mods:
|
||||||
|
if self.shift:
|
||||||
|
mods |= defines.GLFW_MOD_SHIFT
|
||||||
|
if self.alt:
|
||||||
|
mods |= defines.GLFW_MOD_ALT
|
||||||
|
if self.ctrl:
|
||||||
|
mods |= defines.GLFW_MOD_CONTROL
|
||||||
|
if self.super:
|
||||||
|
mods |= defines.GLFW_MOD_SUPER
|
||||||
|
|
||||||
|
fnm = get_name_to_functional_number_map()
|
||||||
|
|
||||||
|
def as_num(key: str) -> int:
|
||||||
|
return (fnm.get(key) or ord(key)) if key else 0
|
||||||
|
|
||||||
|
return WindowSystemKeyEvent(
|
||||||
|
key=as_num(self.key), shifted_key=as_num(self.shifted_key),
|
||||||
|
alternate_key=as_num(self.alternate_key), mods=mods,
|
||||||
|
action=action, text=self.text)
|
||||||
|
|
||||||
|
|
||||||
SHIFT, ALT, CTRL, SUPER = 1, 2, 4, 8
|
SHIFT, ALT, CTRL, SUPER = 1, 2, 4, 8
|
||||||
type_map = {'p': PRESS, 't': REPEAT, 'r': RELEASE}
|
enter_key = KeyEvent(key='ENTER')
|
||||||
rtype_map = {v: k for k, v in type_map.items()}
|
backspace_key = KeyEvent(key='BACKSPACE')
|
||||||
mod_map = {c: i for i, c in enumerate('ABCDEFGHIJKLMNOP')}
|
|
||||||
rmod_map = {v: k for k, v in mod_map.items()}
|
|
||||||
key_rmap = {}
|
|
||||||
key_defs: Dict[str, str] = {}
|
|
||||||
config_key_map = {}
|
|
||||||
config_mod_map = {
|
config_mod_map = {
|
||||||
'SHIFT': SHIFT,
|
'SHIFT': SHIFT,
|
||||||
'ALT': ALT,
|
'ALT': ALT,
|
||||||
@ -483,56 +261,102 @@ class KeyEvent(NamedTuple):
|
|||||||
'CTRL': CTRL,
|
'CTRL': CTRL,
|
||||||
'CONTROL': CTRL
|
'CONTROL': CTRL
|
||||||
}
|
}
|
||||||
for key_name, enc in ENCODING.items():
|
|
||||||
key_name = key_name.replace(' ', '_')
|
|
||||||
key_defs[key_name] = config_key_map[key_name] = key_name
|
|
||||||
key_rmap[enc] = key_name
|
|
||||||
config_key_map.update({k: key_defs[v] for k, v in key_name_aliases.items() if v in key_defs})
|
|
||||||
|
|
||||||
enter_key = KeyEvent(PRESS, 0, key_defs['ENTER'])
|
|
||||||
backspace_key = KeyEvent(PRESS, 0, key_defs['BACKSPACE'])
|
|
||||||
globals().update(key_defs)
|
|
||||||
del key_name, enc
|
|
||||||
|
|
||||||
|
|
||||||
def decode_key_event(text: str) -> KeyEvent:
|
def decode_key_event(csi: str, csi_type: str) -> KeyEvent:
|
||||||
typ = type_map[text[0]]
|
parts = csi.split(';')
|
||||||
mods = mod_map[text[1]]
|
|
||||||
key = key_rmap[text[2:4]]
|
def get_sub_sections(x: str, missing: int = 0) -> Tuple[int, ...]:
|
||||||
return KeyEvent(typ, mods, key)
|
return tuple(int(y) if y else missing for y in x.split(':'))
|
||||||
|
|
||||||
|
first_section = get_sub_sections(parts[0])
|
||||||
|
second_section = get_sub_sections(parts[1], 1) if len(parts) > 1 else ()
|
||||||
|
third_section = get_sub_sections(parts[2]) if len(parts) > 2 else ()
|
||||||
|
mods = (second_section[0] - 1) if second_section else 0
|
||||||
|
action = second_section[1] if len(second_section) > 1 else 1
|
||||||
|
keynum = first_section[0]
|
||||||
|
if csi_type in 'ABCDHFPQRS':
|
||||||
|
keynum = letter_trailer_to_csi_number_map[csi_type]
|
||||||
|
|
||||||
|
def key_name(num: int) -> str:
|
||||||
|
if not num:
|
||||||
|
return ''
|
||||||
|
num = csi_number_to_functional_number_map.get(num, num)
|
||||||
|
ans = functional_key_number_to_name_map.get(num)
|
||||||
|
if ans is None:
|
||||||
|
ans = chr(num)
|
||||||
|
return ans
|
||||||
|
|
||||||
|
return KeyEvent(
|
||||||
|
mods=mods, shift=bool(mods & SHIFT), alt=bool(mods & ALT),
|
||||||
|
ctrl=bool(mods & CTRL), super=bool(mods & SUPER),
|
||||||
|
key=key_name(keynum),
|
||||||
|
shifted_key=key_name(first_section[1] if len(first_section) > 1 else 0),
|
||||||
|
alternate_key=key_name(first_section[2] if len(first_section) > 2 else 0),
|
||||||
|
type={1: EventType.PRESS, 2: EventType.REPEAT, 3: EventType.RELEASE}[action],
|
||||||
|
text=''.join(map(chr, third_section))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def csi_number_for_name(key_name: str) -> int:
|
||||||
|
if not key_name:
|
||||||
|
return 0
|
||||||
|
fn = get_name_to_functional_number_map().get(key_name)
|
||||||
|
if fn is None:
|
||||||
|
return ord(key_name)
|
||||||
|
return get_functional_to_csi_number_map().get(fn, fn)
|
||||||
|
|
||||||
|
|
||||||
def encode_key_event(key_event: KeyEvent) -> str:
|
def encode_key_event(key_event: KeyEvent) -> str:
|
||||||
typ = rtype_map[key_event.type]
|
key = csi_number_for_name(key_event.key)
|
||||||
mods = rmod_map[key_event.mods]
|
shifted_key = csi_number_for_name(key_event.shifted_key)
|
||||||
key = ENCODING[key_event.key.replace('_', ' ')]
|
alternate_key = csi_number_for_name(key_event.alternate_key)
|
||||||
return typ + mods + key
|
lt = get_csi_number_to_letter_trailer_map()
|
||||||
|
trailer = lt.get(key, 'u')
|
||||||
|
if trailer != 'u':
|
||||||
class WindowSystemKeyEvent(NamedTuple):
|
key = 1
|
||||||
code: int
|
mods = key_event.mods
|
||||||
mods: int
|
text = key_event.text
|
||||||
action: int
|
ans = '\033['
|
||||||
|
if key != 1 or mods or shifted_key or alternate_key or text:
|
||||||
|
ans += f'{key}'
|
||||||
|
if shifted_key or alternate_key:
|
||||||
|
ans += ':' + (f'{shifted_key}' if shifted_key else '')
|
||||||
|
if alternate_key:
|
||||||
|
ans += f':{alternate_key}'
|
||||||
|
action = 1
|
||||||
|
if key_event.type is EventType.REPEAT:
|
||||||
|
action = 2
|
||||||
|
elif key_event.type is EventType.RELEASE:
|
||||||
|
action = 3
|
||||||
|
if mods or action > 1 or text:
|
||||||
|
m = 0
|
||||||
|
if key_event.shift:
|
||||||
|
m |= 1
|
||||||
|
if key_event.alt:
|
||||||
|
m |= 2
|
||||||
|
if key_event.ctrl:
|
||||||
|
m |= 4
|
||||||
|
if key_event.super:
|
||||||
|
m |= 8
|
||||||
|
if action > 1 or m:
|
||||||
|
ans += f';{m+1}'
|
||||||
|
if action > 1:
|
||||||
|
ans += f':{action}'
|
||||||
|
elif text:
|
||||||
|
ans += ';'
|
||||||
|
if text:
|
||||||
|
ans += ';' + ':'.join(map(str, map(ord, text)))
|
||||||
|
fn = get_name_to_functional_number_map().get(key_event.key)
|
||||||
|
if fn is not None and fn in tilde_trailers:
|
||||||
|
trailer = '~'
|
||||||
|
return ans + trailer
|
||||||
|
|
||||||
|
|
||||||
def decode_key_event_as_window_system_key(text: str) -> Optional[WindowSystemKeyEvent]:
|
def decode_key_event_as_window_system_key(text: str) -> Optional[WindowSystemKeyEvent]:
|
||||||
k = decode_key_event(text)
|
csi, trailer = text[2:-1], text[-1]
|
||||||
glfw_name = glfw_key_name(k.key)
|
try:
|
||||||
glfw_code = getattr(defines, glfw_name, None)
|
k = decode_key_event(csi, trailer)
|
||||||
if glfw_code is None:
|
except Exception:
|
||||||
return None
|
return None
|
||||||
action = defines.GLFW_PRESS
|
return k.as_window_system_event()
|
||||||
if k.type is RELEASE:
|
|
||||||
action = defines.GLFW_RELEASE
|
|
||||||
elif k.type is REPEAT:
|
|
||||||
action = defines.GLFW_REPEAT
|
|
||||||
mods = 0
|
|
||||||
if k.mods & CTRL:
|
|
||||||
mods |= defines.GLFW_MOD_CONTROL
|
|
||||||
if k.mods & ALT:
|
|
||||||
mods |= defines.GLFW_MOD_ALT
|
|
||||||
if k.mods & SUPER:
|
|
||||||
mods |= defines.GLFW_MOD_SUPER
|
|
||||||
if k.mods & SHIFT:
|
|
||||||
mods |= defines.GLFW_MOD_SHIFT
|
|
||||||
return WindowSystemKeyEvent(glfw_code, mods, action)
|
|
||||||
|
@ -9,9 +9,7 @@
|
|||||||
from .constants import is_macos
|
from .constants import is_macos
|
||||||
|
|
||||||
|
|
||||||
key_name_aliases = {
|
functional_key_name_aliases = {
|
||||||
'SPACE': ' ',
|
|
||||||
'SPC': ' ',
|
|
||||||
'ESC': 'ESCAPE',
|
'ESC': 'ESCAPE',
|
||||||
'PGUP': 'PAGE_UP',
|
'PGUP': 'PAGE_UP',
|
||||||
'PAGEUP': 'PAGE_UP',
|
'PAGEUP': 'PAGE_UP',
|
||||||
@ -21,7 +19,17 @@
|
|||||||
'ARROWUP': 'UP',
|
'ARROWUP': 'UP',
|
||||||
'ARROWDOWN': 'DOWN',
|
'ARROWDOWN': 'DOWN',
|
||||||
'ARROWRIGHT': 'RIGHT',
|
'ARROWRIGHT': 'RIGHT',
|
||||||
'ARROWLEFT': 'LEFT'
|
'ARROWLEFT': 'LEFT',
|
||||||
|
'DEL': 'DELETE',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
character_key_name_aliases = {
|
||||||
|
'SPC': ' ',
|
||||||
|
'SPACE': ' ',
|
||||||
|
'PLUS': '+',
|
||||||
|
'MINUS': '-',
|
||||||
|
'HYPHEN': '-',
|
||||||
}
|
}
|
||||||
|
|
||||||
LookupFunc = Callable[[str, bool], Optional[int]]
|
LookupFunc = Callable[[str, bool], Optional[int]]
|
||||||
|
@ -5,8 +5,8 @@
|
|||||||
from typing import Optional, Union
|
from typing import Optional, Union
|
||||||
|
|
||||||
from .config import KeyAction, KeyMap, SequenceMap, SubSequenceMap
|
from .config import KeyAction, KeyMap, SequenceMap, SubSequenceMap
|
||||||
from .constants import SingleKey
|
|
||||||
from .fast_data_types import KeyEvent
|
from .fast_data_types import KeyEvent
|
||||||
|
from .types import SingleKey
|
||||||
from .typing import ScreenType
|
from .typing import ScreenType
|
||||||
|
|
||||||
|
|
||||||
|
@ -9,14 +9,14 @@
|
|||||||
Tuple
|
Tuple
|
||||||
)
|
)
|
||||||
|
|
||||||
from kitty.constants import Edges, WindowGeometry
|
from kitty.borders import BorderColor
|
||||||
from kitty.fast_data_types import (
|
from kitty.fast_data_types import (
|
||||||
Region, set_active_window, viewport_for_window
|
Region, set_active_window, viewport_for_window
|
||||||
)
|
)
|
||||||
from kitty.options_stub import Options
|
from kitty.options_stub import Options
|
||||||
|
from kitty.types import Edges, WindowGeometry
|
||||||
from kitty.typing import TypedDict, WindowType
|
from kitty.typing import TypedDict, WindowType
|
||||||
from kitty.window_list import WindowGroup, WindowList
|
from kitty.window_list import WindowGroup, WindowList
|
||||||
from kitty.borders import BorderColor
|
|
||||||
|
|
||||||
|
|
||||||
class BorderLine(NamedTuple):
|
class BorderLine(NamedTuple):
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
)
|
)
|
||||||
|
|
||||||
from kitty.borders import BorderColor
|
from kitty.borders import BorderColor
|
||||||
from kitty.constants import Edges
|
from kitty.types import Edges
|
||||||
from kitty.typing import WindowType
|
from kitty.typing import WindowType
|
||||||
from kitty.window_list import WindowGroup, WindowList
|
from kitty.window_list import WindowGroup, WindowList
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
)
|
)
|
||||||
|
|
||||||
from kitty.borders import BorderColor
|
from kitty.borders import BorderColor
|
||||||
from kitty.constants import Edges, WindowGeometry
|
from kitty.types import Edges, WindowGeometry
|
||||||
from kitty.typing import EdgeLiteral, WindowType
|
from kitty.typing import EdgeLiteral, WindowType
|
||||||
from kitty.window_list import WindowGroup, WindowList
|
from kitty.window_list import WindowGroup, WindowList
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
from kitty.borders import BorderColor
|
from kitty.borders import BorderColor
|
||||||
from kitty.conf.utils import to_bool
|
from kitty.conf.utils import to_bool
|
||||||
from kitty.constants import Edges
|
from kitty.types import Edges
|
||||||
from kitty.typing import EdgeLiteral, WindowType
|
from kitty.typing import EdgeLiteral, WindowType
|
||||||
from kitty.window_list import WindowGroup, WindowList
|
from kitty.window_list import WindowGroup, WindowList
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
from typing import Dict, Generator, Iterable, List, Tuple
|
from typing import Dict, Generator, Iterable, List, Tuple
|
||||||
|
|
||||||
from kitty.borders import BorderColor
|
from kitty.borders import BorderColor
|
||||||
from kitty.constants import Edges
|
from kitty.types import Edges
|
||||||
from kitty.typing import WindowType
|
from kitty.typing import WindowType
|
||||||
from kitty.window_list import WindowGroup, WindowList
|
from kitty.window_list import WindowGroup, WindowList
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
from .conf.utils import BadLine
|
from .conf.utils import BadLine
|
||||||
from .config import cached_values_for
|
from .config import cached_values_for
|
||||||
from .constants import (
|
from .constants import (
|
||||||
SingleKey, appname, beam_cursor_data_file, config_dir, glfw_path, is_macos,
|
appname, beam_cursor_data_file, config_dir, glfw_path, is_macos,
|
||||||
is_wayland, kitty_exe, logo_data_file, running_in_kitty
|
is_wayland, kitty_exe, logo_data_file, running_in_kitty
|
||||||
)
|
)
|
||||||
from .fast_data_types import (
|
from .fast_data_types import (
|
||||||
@ -30,6 +30,7 @@
|
|||||||
from .options_stub import Options as OptionsStub
|
from .options_stub import Options as OptionsStub
|
||||||
from .os_window_size import initial_window_size_func
|
from .os_window_size import initial_window_size_func
|
||||||
from .session import get_os_window_sizing_data
|
from .session import get_os_window_sizing_data
|
||||||
|
from .types import SingleKey
|
||||||
from .utils import (
|
from .utils import (
|
||||||
detach, expandvars, find_exe, log_error, read_shell_environment,
|
detach, expandvars, find_exe, log_error, read_shell_environment,
|
||||||
single_instance, startup_notification_handler, unix_socket_paths
|
single_instance, startup_notification_handler, unix_socket_paths
|
||||||
|
@ -34,10 +34,11 @@ def generate_stub():
|
|||||||
all_options,
|
all_options,
|
||||||
class_name='DiffOptions',
|
class_name='DiffOptions',
|
||||||
preamble_lines=(
|
preamble_lines=(
|
||||||
'from kitty.conf.utils import KittensKey, KittensKeyAction',
|
'from kitty.conf.utils import KittensKeyAction',
|
||||||
|
'from kitty.types import ParsedShortcut',
|
||||||
),
|
),
|
||||||
extra_fields=(
|
extra_fields=(
|
||||||
('key_definitions', 'typing.Dict[KittensKey, KittensKeyAction]'),
|
('key_definitions', 'typing.Dict[ParsedShortcut, KittensKeyAction]'),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -4,7 +4,8 @@
|
|||||||
|
|
||||||
from typing import Any, Callable, Dict, NamedTuple, Tuple
|
from typing import Any, Callable, Dict, NamedTuple, Tuple
|
||||||
|
|
||||||
from .constants import FloatEdges, is_macos
|
from .constants import is_macos
|
||||||
|
from .types import FloatEdges
|
||||||
from .typing import EdgeLiteral
|
from .typing import EdgeLiteral
|
||||||
from .utils import log_error
|
from .utils import log_error
|
||||||
|
|
||||||
|
@ -8,9 +8,9 @@
|
|||||||
from typing import TYPE_CHECKING, Dict, Generator, List, Optional
|
from typing import TYPE_CHECKING, Dict, Generator, List, Optional
|
||||||
|
|
||||||
from kitty.config import parse_send_text_bytes
|
from kitty.config import parse_send_text_bytes
|
||||||
from kitty.key_encoding import (
|
from kitty.key_encoding import decode_key_event_as_window_system_key
|
||||||
WindowSystemKeyEvent, decode_key_event_as_window_system_key
|
from kitty.fast_data_types import KeyEvent as WindowSystemKeyEvent
|
||||||
)
|
|
||||||
from .base import (
|
from .base import (
|
||||||
MATCH_TAB_OPTION, MATCH_WINDOW_OPTION, ArgsType, Boss, MatchError,
|
MATCH_TAB_OPTION, MATCH_WINDOW_OPTION, ArgsType, Boss, MatchError,
|
||||||
PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType,
|
PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType,
|
||||||
@ -145,7 +145,8 @@ def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get:
|
|||||||
elif encoding == 'base64':
|
elif encoding == 'base64':
|
||||||
data = base64.standard_b64decode(q)
|
data = base64.standard_b64decode(q)
|
||||||
elif encoding == 'kitty-key':
|
elif encoding == 'kitty-key':
|
||||||
data = decode_key_event_as_window_system_key(q)
|
data = base64.standard_b64decode(q)
|
||||||
|
data = decode_key_event_as_window_system_key(data)
|
||||||
else:
|
else:
|
||||||
raise TypeError(f'Invalid encoding for send-text data: {encoding}')
|
raise TypeError(f'Invalid encoding for send-text data: {encoding}')
|
||||||
exclude_active = payload_get('exclude_active')
|
exclude_active = payload_get('exclude_active')
|
||||||
@ -153,7 +154,7 @@ def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get:
|
|||||||
if window is not None:
|
if window is not None:
|
||||||
if not exclude_active or window is not boss.active_window:
|
if not exclude_active or window is not boss.active_window:
|
||||||
if isinstance(data, WindowSystemKeyEvent):
|
if isinstance(data, WindowSystemKeyEvent):
|
||||||
kdata = window.encoded_key(data.code, mods=data.mods, action=data.action)
|
kdata = window.encoded_key(data)
|
||||||
if kdata:
|
if kdata:
|
||||||
window.write_to_child(kdata)
|
window.write_to_child(kdata)
|
||||||
else:
|
else:
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
from typing import Any, Dict, NamedTuple, Optional, Sequence, Tuple
|
from typing import Any, Dict, NamedTuple, Optional, Sequence, Tuple
|
||||||
|
|
||||||
from .config import build_ansi_color_table
|
from .config import build_ansi_color_table
|
||||||
from .constants import WindowGeometry
|
from .types import WindowGeometry
|
||||||
from .fast_data_types import (
|
from .fast_data_types import (
|
||||||
DECAWM, Screen, cell_size_for_window, pt_to_px, set_tab_bar_render_data,
|
DECAWM, Screen, cell_size_for_window, pt_to_px, set_tab_bar_render_data,
|
||||||
viewport_for_window
|
viewport_for_window
|
||||||
|
49
kitty/types.py
Normal file
49
kitty/types.py
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
from typing import NamedTuple
|
||||||
|
|
||||||
|
|
||||||
|
class ParsedShortcut(NamedTuple):
|
||||||
|
mods: int
|
||||||
|
key_name: str
|
||||||
|
|
||||||
|
|
||||||
|
class Edges(NamedTuple):
|
||||||
|
left: int = 0
|
||||||
|
top: int = 0
|
||||||
|
right: int = 0
|
||||||
|
bottom: int = 0
|
||||||
|
|
||||||
|
|
||||||
|
class FloatEdges(NamedTuple):
|
||||||
|
left: float = 0
|
||||||
|
top: float = 0
|
||||||
|
right: float = 0
|
||||||
|
bottom: float = 0
|
||||||
|
|
||||||
|
|
||||||
|
class ScreenGeometry(NamedTuple):
|
||||||
|
xstart: float
|
||||||
|
ystart: float
|
||||||
|
xnum: int
|
||||||
|
ynum: int
|
||||||
|
dx: float
|
||||||
|
dy: float
|
||||||
|
|
||||||
|
|
||||||
|
class WindowGeometry(NamedTuple):
|
||||||
|
left: int
|
||||||
|
top: int
|
||||||
|
right: int
|
||||||
|
bottom: int
|
||||||
|
xnum: int
|
||||||
|
ynum: int
|
||||||
|
spaces: Edges = Edges()
|
||||||
|
|
||||||
|
|
||||||
|
class SingleKey(NamedTuple):
|
||||||
|
mods: int = 0
|
||||||
|
is_native: bool = False
|
||||||
|
key: int = -1
|
@ -19,13 +19,13 @@
|
|||||||
from .child import ProcessDesc
|
from .child import ProcessDesc
|
||||||
from .cli_stub import CLIOptions
|
from .cli_stub import CLIOptions
|
||||||
from .config import build_ansi_color_table
|
from .config import build_ansi_color_table
|
||||||
from .constants import ScreenGeometry, WindowGeometry, appname, wakeup
|
from .constants import appname, wakeup
|
||||||
from .fast_data_types import (
|
from .fast_data_types import (
|
||||||
BGIMAGE_PROGRAM, BLIT_PROGRAM, CELL_BG_PROGRAM, CELL_FG_PROGRAM,
|
BGIMAGE_PROGRAM, BLIT_PROGRAM, CELL_BG_PROGRAM, CELL_FG_PROGRAM,
|
||||||
CELL_PROGRAM, CELL_SPECIAL_PROGRAM, DCS, DECORATION, DIM, GLFW_MOD_CONTROL,
|
CELL_PROGRAM, CELL_SPECIAL_PROGRAM, DCS, DECORATION, DIM, GLFW_MOD_CONTROL,
|
||||||
GLFW_PRESS, GRAPHICS_ALPHA_MASK_PROGRAM, GRAPHICS_PREMULT_PROGRAM,
|
GRAPHICS_ALPHA_MASK_PROGRAM, GRAPHICS_PREMULT_PROGRAM, GRAPHICS_PROGRAM,
|
||||||
GRAPHICS_PROGRAM, MARK, MARK_MASK, OSC, REVERSE, SCROLL_FULL, SCROLL_LINE,
|
MARK, MARK_MASK, OSC, REVERSE, SCROLL_FULL, SCROLL_LINE, SCROLL_PAGE,
|
||||||
SCROLL_PAGE, STRIKETHROUGH, TINT_PROGRAM, Screen, add_timer, add_window,
|
STRIKETHROUGH, TINT_PROGRAM, KeyEvent, Screen, add_timer, add_window,
|
||||||
cell_size_for_window, compile_program, encode_key_for_tty, get_boss,
|
cell_size_for_window, compile_program, encode_key_for_tty, get_boss,
|
||||||
get_clipboard_string, init_cell_program, pt_to_px, set_clipboard_string,
|
get_clipboard_string, init_cell_program, pt_to_px, set_clipboard_string,
|
||||||
set_titlebar_color, set_window_padding, set_window_render_data,
|
set_titlebar_color, set_window_padding, set_window_render_data,
|
||||||
@ -36,6 +36,7 @@
|
|||||||
from .options_stub import Options
|
from .options_stub import Options
|
||||||
from .rgb import to_color
|
from .rgb import to_color
|
||||||
from .terminfo import get_capabilities
|
from .terminfo import get_capabilities
|
||||||
|
from .types import ScreenGeometry, WindowGeometry
|
||||||
from .typing import BossType, ChildType, EdgeLiteral, TabType, TypedDict
|
from .typing import BossType, ChildType, EdgeLiteral, TabType, TypedDict
|
||||||
from .utils import (
|
from .utils import (
|
||||||
color_as_int, get_primary_selection, load_shaders, open_cmd, open_url,
|
color_as_int, get_primary_selection, load_shaders, open_cmd, open_url,
|
||||||
@ -881,18 +882,20 @@ def copy_to_clipboard(self) -> None:
|
|||||||
if text:
|
if text:
|
||||||
set_clipboard_string(text)
|
set_clipboard_string(text)
|
||||||
|
|
||||||
def encoded_key(self, key: int, mods: int = 0, action: int = GLFW_PRESS) -> bytes:
|
def encoded_key(self, key_event: KeyEvent) -> bytes:
|
||||||
return encode_key_for_tty(
|
return encode_key_for_tty(
|
||||||
key=key, mods=mods, key_encoding_flags=self.screen.current_key_encoding_flags(),
|
key=key_event.key, shifted_key=key_event.shifted_key, alternate_key=key_event.alternate_key,
|
||||||
cursor_key_mode=self.screen.cursor_key_mode, action=action
|
mods=key_event.mods, action=key_event.action, text=key_event.text,
|
||||||
).encode('ascii')
|
key_encoding_flags=self.screen.current_key_encoding_flags(),
|
||||||
|
cursor_key_mode=self.screen.cursor_key_mode,
|
||||||
|
).encode('ascii')
|
||||||
|
|
||||||
def copy_or_interrupt(self) -> None:
|
def copy_or_interrupt(self) -> None:
|
||||||
text = self.text_for_selection()
|
text = self.text_for_selection()
|
||||||
if text:
|
if text:
|
||||||
set_clipboard_string(text)
|
set_clipboard_string(text)
|
||||||
else:
|
else:
|
||||||
self.write_to_child(self.encoded_key(ord('c'), mods=GLFW_MOD_CONTROL))
|
self.write_to_child(self.encoded_key(KeyEvent(key=ord('c'), mods=GLFW_MOD_CONTROL)))
|
||||||
|
|
||||||
def copy_and_clear_or_interrupt(self) -> None:
|
def copy_and_clear_or_interrupt(self) -> None:
|
||||||
self.copy_or_interrupt()
|
self.copy_or_interrupt()
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
Any, Deque, Dict, Generator, Iterator, List, Optional, Tuple, Union
|
Any, Deque, Dict, Generator, Iterator, List, Optional, Tuple, Union
|
||||||
)
|
)
|
||||||
|
|
||||||
from .constants import WindowGeometry
|
from .types import WindowGeometry
|
||||||
from .typing import EdgeLiteral, TabType, WindowType
|
from .typing import EdgeLiteral, TabType, WindowType
|
||||||
|
|
||||||
WindowOrId = Union[WindowType, int]
|
WindowOrId = Union[WindowType, int]
|
||||||
|
Loading…
Reference in New Issue
Block a user