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
|
||||
which are undesirable for other reasons
|
||||
* 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``
|
||||
(shift+tab is not a separate key from tab)
|
||||
* No support for the :kbd:`super` modifier.
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
from kitty.cli import parse_args
|
||||
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.remote_control import create_basic_command, encode_send
|
||||
from kitty.typing import KeyEventType, ScreenSize
|
||||
@ -59,15 +59,15 @@ def on_eot(self) -> None:
|
||||
def on_key(self, key_event: KeyEventType) -> None:
|
||||
if self.line_edit.on_key(key_event):
|
||||
self.commit_line()
|
||||
if key_event.type is not RELEASE and not key_event.mods:
|
||||
if key_event.key is K['ENTER']:
|
||||
self.write_broadcast_text('\r')
|
||||
self.print('')
|
||||
self.line_edit.clear()
|
||||
self.write(SAVE_CURSOR)
|
||||
return
|
||||
if key_event.matches('enter'):
|
||||
self.write_broadcast_text('\r')
|
||||
self.print('')
|
||||
self.line_edit.clear()
|
||||
self.write(SAVE_CURSOR)
|
||||
return
|
||||
|
||||
ek = encode_key_event(key_event)
|
||||
ek = standard_b64encode(ek.encode('utf-8')).decode('ascii')
|
||||
self.write_broadcast_data('kitty-key:' + ek)
|
||||
|
||||
def write_broadcast_text(self, text: str) -> None:
|
||||
|
@ -22,7 +22,7 @@
|
||||
from kitty.conf.utils import KittensKeyAction
|
||||
from kitty.constants import appname
|
||||
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.utils import ScreenSize
|
||||
|
||||
@ -57,7 +57,6 @@ def highlight_collection(collection: 'Collection', aliases: Optional[Dict[str, s
|
||||
|
||||
|
||||
INITIALIZING, COLLECTED, DIFFED, COMMAND, MESSAGE = range(5)
|
||||
ESCAPE = K['ESCAPE']
|
||||
|
||||
|
||||
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.cmd.bell()
|
||||
|
||||
def on_text(self, text: str, in_bracketed_paste: bool = False) -> None:
|
||||
if self.state is COMMAND:
|
||||
self.line_edit.on_text(text, in_bracketed_paste)
|
||||
self.draw_status_line()
|
||||
return
|
||||
if self.state is MESSAGE:
|
||||
self.state = DIFFED
|
||||
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:
|
||||
def on_key_event(self, key_event: KeyEvent, in_bracketed_paste: bool = False) -> None:
|
||||
if key_event.text:
|
||||
if self.state is COMMAND:
|
||||
self.line_edit.on_text(key_event.text, in_bracketed_paste)
|
||||
self.draw_status_line()
|
||||
return
|
||||
if self.state is MESSAGE:
|
||||
self.state = DIFFED
|
||||
self.draw_status_line()
|
||||
return
|
||||
if self.state is COMMAND:
|
||||
if self.line_edit.on_key(key_event):
|
||||
if not self.line_edit.current_input:
|
||||
return
|
||||
else:
|
||||
if self.state is MESSAGE:
|
||||
if key_event.type is not EventType.RELEASE:
|
||||
self.state = DIFFED
|
||||
self.draw_status_line()
|
||||
self.draw_status_line()
|
||||
return
|
||||
if key_event.type is RELEASE:
|
||||
return
|
||||
if self.state is COMMAND:
|
||||
if key_event.key is ESCAPE:
|
||||
self.state = DIFFED
|
||||
self.draw_status_line()
|
||||
if self.state is COMMAND:
|
||||
if self.line_edit.on_key(key_event):
|
||||
if not self.line_edit.current_input:
|
||||
self.state = DIFFED
|
||||
self.draw_status_line()
|
||||
return
|
||||
if key_event.type is EventType.RELEASE:
|
||||
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)
|
||||
if action is not None:
|
||||
return self.perform_action(action)
|
||||
|
@ -17,9 +17,7 @@
|
||||
from kitty.cli import parse_args
|
||||
from kitty.cli_stub import HintsCLIOptions
|
||||
from kitty.fast_data_types import set_clipboard_string
|
||||
from kitty.key_encoding import (
|
||||
KeyEvent, backspace_key, enter_key, key_defs as K
|
||||
)
|
||||
from kitty.key_encoding import KeyEvent
|
||||
from kitty.typing import BossType, KittyCommonOpts
|
||||
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_REGEX = r'(?m)^\s*(.+)\s*$'
|
||||
ESCAPE = K['ESCAPE']
|
||||
|
||||
|
||||
class Mark:
|
||||
@ -177,11 +174,11 @@ def on_text(self, text: str, in_bracketed_paste: bool = False) -> None:
|
||||
self.draw_screen()
|
||||
|
||||
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_text = None
|
||||
self.draw_screen()
|
||||
elif key_event is enter_key and self.current_input:
|
||||
elif key_event.matches('enter') and self.current_input:
|
||||
try:
|
||||
idx = decode_hint(self.current_input, self.alphabet)
|
||||
self.chosen.append(self.index_map[idx])
|
||||
@ -196,7 +193,7 @@ def on_key(self, key_event: KeyEvent) -> None:
|
||||
self.draw_screen()
|
||||
else:
|
||||
self.quit_loop(0)
|
||||
elif key_event.key is ESCAPE:
|
||||
elif key_event.matches('esc'):
|
||||
self.quit_loop(0 if self.multiple else 1)
|
||||
|
||||
def on_interrupt(self) -> None:
|
||||
@ -288,6 +285,7 @@ def quotes(text: str, s: int, e: int) -> Tuple[int, int]:
|
||||
@postprocessor
|
||||
def ip(text: str, s: int, e: int) -> Tuple[int, int]:
|
||||
from ipaddress import ip_address
|
||||
|
||||
# Check validity of IPs (or raise InvalidMatch)
|
||||
ip = text[s:e]
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
Any, Callable, ContextManager, Dict, Optional, Sequence, Type, Union
|
||||
)
|
||||
|
||||
from kitty.types import ParsedShortcut
|
||||
from kitty.typing import (
|
||||
AbstractEventLoop, BossType, Debug, ImageManagerType, KeyEventType,
|
||||
KittensKeyActionType, LoopType, MouseEvent, ScreenSize, TermManagerType
|
||||
@ -35,6 +36,7 @@ def _initialize(
|
||||
self.debug = debug
|
||||
self.cmd = commander(self)
|
||||
self._image_manager = image_manager
|
||||
self._key_shortcuts: Dict[ParsedShortcut, KittensKeyActionType] = {}
|
||||
|
||||
@property
|
||||
def image_manager(self) -> ImageManagerType:
|
||||
@ -45,18 +47,16 @@ def image_manager(self) -> ImageManagerType:
|
||||
def asyncio_loop(self) -> AbstractEventLoop:
|
||||
return self._tui_loop.asycio_loop
|
||||
|
||||
def add_shortcut(self, action: KittensKeyActionType, key: str, mods: Optional[int] = None, is_text: Optional[bool] = False) -> None:
|
||||
if not hasattr(self, '_text_shortcuts'):
|
||||
self._text_shortcuts, self._key_shortcuts = {}, {}
|
||||
if is_text:
|
||||
self._text_shortcuts[key] = action
|
||||
else:
|
||||
self._key_shortcuts[(key, mods or 0)] = action
|
||||
def add_shortcut(self, action: KittensKeyActionType, spec: Union[str, ParsedShortcut]) -> None:
|
||||
if isinstance(spec, str):
|
||||
from kitty.key_encoding import parse_shortcut
|
||||
spec = parse_shortcut(spec)
|
||||
self._key_shortcuts[spec] = action
|
||||
|
||||
def shortcut_action(self, key_event_or_text: Union[str, KeyEventType]) -> Optional[KittensKeyActionType]:
|
||||
if isinstance(key_event_or_text, str):
|
||||
return self._text_shortcuts.get(key_event_or_text)
|
||||
return self._key_shortcuts.get((key_event_or_text.key, key_event_or_text.mods))
|
||||
def shortcut_action(self, key_event: KeyEventType) -> Optional[KittensKeyActionType]:
|
||||
for sc, action in self._key_shortcuts.items():
|
||||
if key_event.matches(sc):
|
||||
return action
|
||||
|
||||
def __enter__(self) -> 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:
|
||||
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:
|
||||
pass
|
||||
|
||||
|
@ -5,17 +5,10 @@
|
||||
from typing import Callable, Tuple
|
||||
|
||||
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
|
||||
|
||||
HOME = K['HOME']
|
||||
END = K['END']
|
||||
BACKSPACE = K['BACKSPACE']
|
||||
DELETE = K['DELETE']
|
||||
LEFT = K['LEFT']
|
||||
RIGHT = K['RIGHT']
|
||||
|
||||
|
||||
class LineEdit:
|
||||
|
||||
@ -137,22 +130,22 @@ def end(self) -> bool:
|
||||
return self.cursor_pos != orig
|
||||
|
||||
def on_key(self, key_event: KeyEvent) -> bool:
|
||||
if key_event.type is RELEASE:
|
||||
if key_event.type is EventType.RELEASE:
|
||||
return False
|
||||
elif key_event.key is HOME:
|
||||
if key_event.matches('home'):
|
||||
return self.home()
|
||||
elif key_event.key is END:
|
||||
if key_event.matches('end'):
|
||||
return self.end()
|
||||
elif key_event.key is BACKSPACE:
|
||||
if key_event.matches('backspace'):
|
||||
self.backspace()
|
||||
return True
|
||||
elif key_event.key is DELETE:
|
||||
if key_event.matches('delete'):
|
||||
self.delete()
|
||||
return True
|
||||
elif key_event.key is LEFT:
|
||||
if key_event.matches('left'):
|
||||
self.left()
|
||||
return True
|
||||
elif key_event.key is RIGHT:
|
||||
if key_event.matches('right'):
|
||||
self.right()
|
||||
return True
|
||||
return False
|
||||
|
@ -21,7 +21,7 @@
|
||||
)
|
||||
from kitty.key_encoding import (
|
||||
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.utils import ScreenSizeGetter, screen_size_function, write_all
|
||||
@ -29,8 +29,6 @@
|
||||
from .handler import Handler
|
||||
from .operations import init_state, reset_state
|
||||
|
||||
C, D = K['C'], K['D']
|
||||
|
||||
|
||||
class BinaryWrite(Protocol):
|
||||
|
||||
@ -149,7 +147,7 @@ def initialize(self) -> None:
|
||||
self.write('Press the Enter key to quit')
|
||||
|
||||
def on_key(self, key_event: KeyEventType) -> None:
|
||||
if key_event is enter_key:
|
||||
if key_event.key == 'ENTER':
|
||||
self.quit_loop(1)
|
||||
|
||||
def on_interrupt(self) -> None:
|
||||
@ -282,8 +280,23 @@ def _on_csi(self, csi: str) -> None:
|
||||
elif q == '~':
|
||||
if csi == '200~':
|
||||
self.in_bracketed_paste = True
|
||||
return
|
||||
elif csi == '201~':
|
||||
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:
|
||||
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)
|
||||
|
||||
def _on_apc(self, apc: str) -> None:
|
||||
if apc.startswith('K'):
|
||||
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 apc.startswith('G'):
|
||||
if self.handler.image_manager is not None:
|
||||
self.handler.image_manager.handle_response(apc)
|
||||
# }}}
|
||||
|
@ -40,7 +40,6 @@
|
||||
MOUSE_URXVT_MODE=(1015, '?'),
|
||||
ALTERNATE_SCREEN=(1049, '?'),
|
||||
BRACKETED_PASTE=(2004, '?'),
|
||||
EXTENDED_KEYBOARD=(2017, '?'),
|
||||
)
|
||||
|
||||
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('FOCUS_TRACKING') + reset_mode('MOUSE_UTF8_MODE') +
|
||||
reset_mode('MOUSE_SGR_MODE') + reset_mode('MOUSE_UTF8_MODE') +
|
||||
set_mode('BRACKETED_PASTE') + set_mode('EXTENDED_KEYBOARD') +
|
||||
SAVE_COLORS +
|
||||
set_mode('BRACKETED_PASTE') + SAVE_COLORS +
|
||||
'\033[>31u' + # extended keyboard mode
|
||||
'\033[*x' # reset DECSACE to default region select
|
||||
)
|
||||
if alternate_screen:
|
||||
@ -287,6 +286,7 @@ def reset_state(normal_screen: bool = True) -> str:
|
||||
ans += RESTORE_PRIVATE_MODE_VALUES
|
||||
ans += RESTORE_CURSOR
|
||||
ans += RESTORE_COLORS
|
||||
ans += '\033[<u' # restore keyboard mode
|
||||
return ans
|
||||
|
||||
|
||||
|
@ -25,8 +25,7 @@
|
||||
)
|
||||
from .config_data import MINIMUM_FONT_SIZE
|
||||
from .constants import (
|
||||
SingleKey, appname, config_dir, is_macos, kitty_exe,
|
||||
supports_primary_selection
|
||||
appname, config_dir, is_macos, kitty_exe, supports_primary_selection
|
||||
)
|
||||
from .fast_data_types import (
|
||||
CLOSE_BEING_CONFIRMED, IMPERATIVE_CLOSE_REQUESTED, NO_CLOSE_REQUESTED,
|
||||
@ -49,6 +48,7 @@
|
||||
from .tabs import (
|
||||
SpecialWindow, SpecialWindowInstance, Tab, TabDict, TabManager
|
||||
)
|
||||
from .types import SingleKey
|
||||
from .typing import PopenType, TypedDict
|
||||
from .utils import (
|
||||
func_name, get_editor, get_primary_selection, is_path_in_temp_dir,
|
||||
|
@ -14,8 +14,9 @@
|
||||
from .cli_stub import CLIOptions
|
||||
from .conf.utils import resolve_config
|
||||
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 .types import SingleKey
|
||||
from .typing import BadLineType, SequenceMap, TypedDict
|
||||
|
||||
|
||||
@ -759,7 +760,10 @@ def parse_args(
|
||||
|
||||
|
||||
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}
|
||||
keys = []
|
||||
for key_spec in key_sequence:
|
||||
|
@ -11,7 +11,8 @@
|
||||
)
|
||||
|
||||
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+(.+)$')
|
||||
T = TypeVar('T')
|
||||
@ -301,38 +302,6 @@ def w(f: Callable) -> Callable:
|
||||
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, ...]]
|
||||
|
||||
|
||||
@ -362,15 +331,12 @@ def parse_kittens_func_args(action: str, args_funcs: Dict[str, Callable]) -> Kit
|
||||
return func, tuple(args)
|
||||
|
||||
|
||||
KittensKey = Tuple[str, Optional[int], bool]
|
||||
|
||||
|
||||
def parse_kittens_key(
|
||||
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]
|
||||
if not sc or not action:
|
||||
return None
|
||||
mods, key, is_text = parse_kittens_shortcut(sc)
|
||||
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
|
||||
)
|
||||
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 .options_stub import Options as OptionsStub
|
||||
from .types import SingleKey
|
||||
from .typing import TypedDict
|
||||
from .utils import expandvars, log_error
|
||||
|
||||
|
@ -16,13 +16,15 @@
|
||||
choices, positive_float, positive_int, to_bool, to_cmdline as tc, to_color,
|
||||
to_color_or_none, unit_float
|
||||
)
|
||||
from .constants import (
|
||||
FloatEdges, SingleKey, config_dir, is_macos
|
||||
)
|
||||
from .constants import config_dir, is_macos
|
||||
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 .rgb import Color, color_as_int, color_as_sharp, color_from_int
|
||||
from .types import FloatEdges, SingleKey
|
||||
from .utils import log_error
|
||||
|
||||
|
||||
@ -60,6 +62,8 @@ def to_modifiers(val: str) -> int:
|
||||
|
||||
|
||||
def parse_shortcut(sc: str) -> SingleKey:
|
||||
if sc.endswith('+') and len(sc) > 1:
|
||||
sc = sc[:-1] + 'plus'
|
||||
parts = sc.split('+')
|
||||
mods = 0
|
||||
if len(parts) > 1:
|
||||
@ -67,6 +71,7 @@ def parse_shortcut(sc: str) -> SingleKey:
|
||||
if not mods:
|
||||
raise InvalidMods('Invalid shortcut')
|
||||
q = parts[-1]
|
||||
q = character_key_name_aliases.get(q.upper(), q)
|
||||
is_native = False
|
||||
if q.startswith('0x'):
|
||||
try:
|
||||
@ -80,7 +85,7 @@ def parse_shortcut(sc: str) -> SingleKey:
|
||||
key = ord(q)
|
||||
except Exception:
|
||||
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)
|
||||
if x is None:
|
||||
lf = get_key_name_lookup()
|
||||
|
@ -27,45 +27,6 @@ class Version(NamedTuple):
|
||||
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)
|
||||
def kitty_exe() -> str:
|
||||
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
|
||||
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
import string
|
||||
from typing import Dict, NamedTuple, Optional
|
||||
from enum import IntEnum
|
||||
from functools import lru_cache
|
||||
from typing import Dict, NamedTuple, Optional, Tuple, Union
|
||||
|
||||
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 {{{
|
||||
ENCODING = {
|
||||
'0': 'G',
|
||||
'1': 'H',
|
||||
'2': 'I',
|
||||
'3': 'J',
|
||||
'4': 'K',
|
||||
'5': 'L',
|
||||
'6': 'M',
|
||||
'7': 'N',
|
||||
'8': 'O',
|
||||
'9': 'P',
|
||||
'A': 'S',
|
||||
'APOSTROPHE': 'B',
|
||||
'B': 'T',
|
||||
'BACKSLASH': 't',
|
||||
'BACKSPACE': '1',
|
||||
'C': 'U',
|
||||
'CAPS LOCK': ':',
|
||||
'COMMA': 'C',
|
||||
'D': 'V',
|
||||
'DELETE': '3',
|
||||
'DOWN': '6',
|
||||
'E': 'W',
|
||||
'END': '-',
|
||||
'ENTER': 'z',
|
||||
'EQUAL': 'R',
|
||||
'ESCAPE': 'y',
|
||||
'F': 'X',
|
||||
'F1': '/',
|
||||
'F10': ']',
|
||||
'F11': '{',
|
||||
'F12': '}',
|
||||
'F13': '@',
|
||||
'F14': '%',
|
||||
'F15': '$',
|
||||
'F16': '#',
|
||||
'F17': 'BA',
|
||||
'F18': 'BB',
|
||||
'F19': 'BC',
|
||||
'F2': '*',
|
||||
'F20': 'BD',
|
||||
'F21': 'BE',
|
||||
'F22': 'BF',
|
||||
'F23': 'BG',
|
||||
'F24': 'BH',
|
||||
'F25': 'BI',
|
||||
'F3': '?',
|
||||
'F4': '&',
|
||||
'F5': '<',
|
||||
'F6': '>',
|
||||
'F7': '(',
|
||||
'F8': ')',
|
||||
'F9': '[',
|
||||
'G': 'Y',
|
||||
'GRAVE ACCENT': 'v',
|
||||
'H': 'Z',
|
||||
'HOME': '.',
|
||||
'I': 'a',
|
||||
'INSERT': '2',
|
||||
'J': 'b',
|
||||
'K': 'c',
|
||||
'KP 0': 'BJ',
|
||||
'KP 1': 'BK',
|
||||
'KP 2': 'BL',
|
||||
'KP 3': 'BM',
|
||||
'KP 4': 'BN',
|
||||
'KP 5': 'BO',
|
||||
'KP 6': 'BP',
|
||||
'KP 7': 'BQ',
|
||||
'KP 8': 'BR',
|
||||
'KP 9': 'BS',
|
||||
'KP ADD': 'BX',
|
||||
'KP DECIMAL': 'BT',
|
||||
'KP DIVIDE': 'BU',
|
||||
'KP ENTER': 'BY',
|
||||
'KP EQUAL': 'BZ',
|
||||
'KP MULTIPLY': 'BV',
|
||||
'KP SUBTRACT': 'BW',
|
||||
'L': 'd',
|
||||
'LEFT': '5',
|
||||
'LEFT ALT': 'Bc',
|
||||
'LEFT BRACKET': 's',
|
||||
'LEFT CONTROL': 'Bb',
|
||||
'LEFT SHIFT': 'Ba',
|
||||
'LEFT SUPER': 'Bd',
|
||||
'M': 'e',
|
||||
'MINUS': 'D',
|
||||
'N': 'f',
|
||||
'NUM LOCK': '=',
|
||||
'O': 'g',
|
||||
'P': 'h',
|
||||
'PAGE DOWN': '9',
|
||||
'PAGE UP': '8',
|
||||
'PAUSE': '!',
|
||||
'PERIOD': 'E',
|
||||
'PRINT SCREEN': '^',
|
||||
'Q': 'i',
|
||||
'R': 'j',
|
||||
'RIGHT': '4',
|
||||
'RIGHT ALT': 'Bg',
|
||||
'RIGHT BRACKET': 'u',
|
||||
'RIGHT CONTROL': 'Bf',
|
||||
'RIGHT SHIFT': 'Be',
|
||||
'RIGHT SUPER': 'Bh',
|
||||
'S': 'k',
|
||||
'SCROLL LOCK': '+',
|
||||
'SEMICOLON': 'Q',
|
||||
'SLASH': 'F',
|
||||
'SPACE': 'A',
|
||||
'T': 'l',
|
||||
'TAB': '0',
|
||||
'U': 'm',
|
||||
'UP': '7',
|
||||
'V': 'n',
|
||||
'W': 'o',
|
||||
'WORLD 1': 'w',
|
||||
'WORLD 2': 'x',
|
||||
'X': 'p',
|
||||
'Y': 'q',
|
||||
'Z': 'r',
|
||||
'PLUS': 'Bi',
|
||||
'UNDERSCORE': 'Bj',
|
||||
'MENU': 'Bk',
|
||||
'EXCLAM': 'Bl',
|
||||
'DOUBLE QUOTE': 'Bm',
|
||||
'NUMBER SIGN': 'Bn',
|
||||
'DOLLAR': 'Bo',
|
||||
'AMPERSAND': 'Bp',
|
||||
'PARENTHESIS LEFT': 'Bq',
|
||||
'PARENTHESIS RIGHT': 'Br',
|
||||
'COLON': 'Bs',
|
||||
'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 +
|
||||
'`~!@#$%^&*()_-+=[{]}\\|<,>./?;:\'" '
|
||||
'ÄäÖöÜüߧºàåæçèéìñòøùабвгдежзийклмнопрстуфхцчшщъыьэюяё'
|
||||
)
|
||||
# number name mappings {{{
|
||||
# start csi mapping (auto generated by gen-key-constants.py do not edit)
|
||||
functional_key_number_to_name_map = {
|
||||
57344: 'ESCAPE',
|
||||
57345: 'ENTER',
|
||||
57346: 'TAB',
|
||||
57347: 'BACKSPACE',
|
||||
57348: 'INSERT',
|
||||
57349: 'DELETE',
|
||||
57350: 'LEFT',
|
||||
57351: 'RIGHT',
|
||||
57352: 'UP',
|
||||
57353: 'DOWN',
|
||||
57354: 'PAGE_UP',
|
||||
57355: 'PAGE_DOWN',
|
||||
57356: 'HOME',
|
||||
57357: 'END',
|
||||
57358: 'CAPS_LOCK',
|
||||
57359: 'SCROLL_LOCK',
|
||||
57360: 'NUM_LOCK',
|
||||
57361: 'PRINT_SCREEN',
|
||||
57362: 'PAUSE',
|
||||
57363: 'MENU',
|
||||
57364: 'F1',
|
||||
57365: 'F2',
|
||||
57366: 'F3',
|
||||
57367: 'F4',
|
||||
57368: 'F5',
|
||||
57369: 'F6',
|
||||
57370: 'F7',
|
||||
57371: 'F8',
|
||||
57372: 'F9',
|
||||
57373: 'F10',
|
||||
57374: 'F11',
|
||||
57375: 'F12',
|
||||
57376: 'F13',
|
||||
57377: 'F14',
|
||||
57378: 'F15',
|
||||
57379: 'F16',
|
||||
57380: 'F17',
|
||||
57381: 'F18',
|
||||
57382: 'F19',
|
||||
57383: 'F20',
|
||||
57384: 'F21',
|
||||
57385: 'F22',
|
||||
57386: 'F23',
|
||||
57387: 'F24',
|
||||
57388: 'F25',
|
||||
57389: 'F26',
|
||||
57390: 'F27',
|
||||
57391: 'F28',
|
||||
57392: 'F29',
|
||||
57393: 'F30',
|
||||
57394: 'F31',
|
||||
57395: 'F32',
|
||||
57396: 'F33',
|
||||
57397: 'F34',
|
||||
57398: 'F35',
|
||||
57399: 'KP_0',
|
||||
57400: 'KP_1',
|
||||
57401: 'KP_2',
|
||||
57402: 'KP_3',
|
||||
57403: 'KP_4',
|
||||
57404: 'KP_5',
|
||||
57405: 'KP_6',
|
||||
57406: 'KP_7',
|
||||
57407: 'KP_8',
|
||||
57408: 'KP_9',
|
||||
57409: 'KP_DECIMAL',
|
||||
57410: 'KP_DIVIDE',
|
||||
57411: 'KP_MULTIPLY',
|
||||
57412: 'KP_SUBTRACT',
|
||||
57413: 'KP_ADD',
|
||||
57414: 'KP_ENTER',
|
||||
57415: 'KP_EQUAL',
|
||||
57416: 'KP_SEPARATOR',
|
||||
57417: 'KP_LEFT',
|
||||
57418: 'KP_RIGHT',
|
||||
57419: 'KP_UP',
|
||||
57420: 'KP_DOWN',
|
||||
57421: 'KP_PAGE_UP',
|
||||
57422: 'KP_PAGE_DOWN',
|
||||
57423: 'KP_HOME',
|
||||
57424: 'KP_END',
|
||||
57425: 'KP_INSERT',
|
||||
57426: 'KP_DELETE',
|
||||
57427: 'LEFT_SHIFT',
|
||||
57428: 'LEFT_CONTROL',
|
||||
57429: 'LEFT_ALT',
|
||||
57430: 'LEFT_SUPER',
|
||||
57431: 'RIGHT_SHIFT',
|
||||
57432: 'RIGHT_CONTROL',
|
||||
57433: 'RIGHT_ALT',
|
||||
57434: 'RIGHT_SUPER',
|
||||
57435: 'MEDIA_PLAY',
|
||||
57436: 'MEDIA_PAUSE',
|
||||
57437: 'MEDIA_PLAY_PAUSE',
|
||||
57438: 'MEDIA_REVERSE',
|
||||
57439: 'MEDIA_STOP',
|
||||
57440: 'MEDIA_FAST_FORWARD',
|
||||
57441: 'MEDIA_REWIND',
|
||||
57442: 'MEDIA_TRACK_NEXT',
|
||||
57443: 'MEDIA_TRACK_PREVIOUS',
|
||||
57444: 'MEDIA_RECORD',
|
||||
57445: 'LOWER_VOLUME',
|
||||
57446: 'RAISE_VOLUME',
|
||||
57447: 'MUTE_VOLUME'}
|
||||
csi_number_to_functional_number_map = {
|
||||
2: 57348,
|
||||
3: 57349,
|
||||
5: 57354,
|
||||
6: 57355,
|
||||
7: 57356,
|
||||
8: 57357,
|
||||
9: 57346,
|
||||
11: 57364,
|
||||
12: 57365,
|
||||
13: 57345,
|
||||
14: 57367,
|
||||
15: 57368,
|
||||
17: 57369,
|
||||
18: 57370,
|
||||
19: 57371,
|
||||
20: 57372,
|
||||
21: 57373,
|
||||
23: 57374,
|
||||
24: 57375,
|
||||
27: 57344,
|
||||
127: 57347}
|
||||
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}
|
||||
tilde_trailers = {57348, 57349, 57354, 57355, 57368, 57369, 57370, 57371, 57372, 57373, 57374, 57375}
|
||||
# end csi mapping
|
||||
# }}}
|
||||
|
||||
|
||||
def text_match(key: str) -> Optional[str]:
|
||||
if key.upper() == 'SPACE':
|
||||
return ' '
|
||||
if key not in text_keys:
|
||||
return None
|
||||
return key
|
||||
@lru_cache(2)
|
||||
def get_name_to_functional_number_map() -> Dict[str, int]:
|
||||
return {v: k for k, v in functional_key_number_to_name_map.items()}
|
||||
|
||||
|
||||
def encode(
|
||||
integer: int,
|
||||
chars: str = string.ascii_uppercase + string.ascii_lowercase + string.digits +
|
||||
'.-:+=^!/*?&<>()[]{}@%$#'
|
||||
) -> str:
|
||||
ans = ''
|
||||
d = len(chars)
|
||||
while True:
|
||||
integer, remainder = divmod(integer, d)
|
||||
ans = chars[remainder] + ans
|
||||
if integer == 0:
|
||||
break
|
||||
return ans
|
||||
@lru_cache(2)
|
||||
def get_functional_to_csi_number_map() -> Dict[int, int]:
|
||||
return {v: k for k, v in csi_number_to_functional_number_map.items()}
|
||||
|
||||
|
||||
def symbolic_name(glfw_name: str) -> str:
|
||||
return glfw_name[9:].replace('_', ' ')
|
||||
|
||||
|
||||
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
|
||||
@lru_cache(2)
|
||||
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()}
|
||||
|
||||
|
||||
PRESS: int = 1
|
||||
REPEAT: int = 2
|
||||
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
|
||||
type_map = {'p': PRESS, 't': REPEAT, 'r': RELEASE}
|
||||
rtype_map = {v: k for k, v in type_map.items()}
|
||||
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 = {}
|
||||
enter_key = KeyEvent(key='ENTER')
|
||||
backspace_key = KeyEvent(key='BACKSPACE')
|
||||
config_mod_map = {
|
||||
'SHIFT': SHIFT,
|
||||
'ALT': ALT,
|
||||
@ -483,56 +261,102 @@ class KeyEvent(NamedTuple):
|
||||
'CTRL': 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:
|
||||
typ = type_map[text[0]]
|
||||
mods = mod_map[text[1]]
|
||||
key = key_rmap[text[2:4]]
|
||||
return KeyEvent(typ, mods, key)
|
||||
def decode_key_event(csi: str, csi_type: str) -> KeyEvent:
|
||||
parts = csi.split(';')
|
||||
|
||||
def get_sub_sections(x: str, missing: int = 0) -> Tuple[int, ...]:
|
||||
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:
|
||||
typ = rtype_map[key_event.type]
|
||||
mods = rmod_map[key_event.mods]
|
||||
key = ENCODING[key_event.key.replace('_', ' ')]
|
||||
return typ + mods + key
|
||||
|
||||
|
||||
class WindowSystemKeyEvent(NamedTuple):
|
||||
code: int
|
||||
mods: int
|
||||
action: int
|
||||
key = csi_number_for_name(key_event.key)
|
||||
shifted_key = csi_number_for_name(key_event.shifted_key)
|
||||
alternate_key = csi_number_for_name(key_event.alternate_key)
|
||||
lt = get_csi_number_to_letter_trailer_map()
|
||||
trailer = lt.get(key, 'u')
|
||||
if trailer != 'u':
|
||||
key = 1
|
||||
mods = key_event.mods
|
||||
text = key_event.text
|
||||
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]:
|
||||
k = decode_key_event(text)
|
||||
glfw_name = glfw_key_name(k.key)
|
||||
glfw_code = getattr(defines, glfw_name, None)
|
||||
if glfw_code is None:
|
||||
csi, trailer = text[2:-1], text[-1]
|
||||
try:
|
||||
k = decode_key_event(csi, trailer)
|
||||
except Exception:
|
||||
return None
|
||||
action = defines.GLFW_PRESS
|
||||
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)
|
||||
return k.as_window_system_event()
|
||||
|
@ -9,9 +9,7 @@
|
||||
from .constants import is_macos
|
||||
|
||||
|
||||
key_name_aliases = {
|
||||
'SPACE': ' ',
|
||||
'SPC': ' ',
|
||||
functional_key_name_aliases = {
|
||||
'ESC': 'ESCAPE',
|
||||
'PGUP': 'PAGE_UP',
|
||||
'PAGEUP': 'PAGE_UP',
|
||||
@ -21,7 +19,17 @@
|
||||
'ARROWUP': 'UP',
|
||||
'ARROWDOWN': 'DOWN',
|
||||
'ARROWRIGHT': 'RIGHT',
|
||||
'ARROWLEFT': 'LEFT'
|
||||
'ARROWLEFT': 'LEFT',
|
||||
'DEL': 'DELETE',
|
||||
}
|
||||
|
||||
|
||||
character_key_name_aliases = {
|
||||
'SPC': ' ',
|
||||
'SPACE': ' ',
|
||||
'PLUS': '+',
|
||||
'MINUS': '-',
|
||||
'HYPHEN': '-',
|
||||
}
|
||||
|
||||
LookupFunc = Callable[[str, bool], Optional[int]]
|
||||
|
@ -5,8 +5,8 @@
|
||||
from typing import Optional, Union
|
||||
|
||||
from .config import KeyAction, KeyMap, SequenceMap, SubSequenceMap
|
||||
from .constants import SingleKey
|
||||
from .fast_data_types import KeyEvent
|
||||
from .types import SingleKey
|
||||
from .typing import ScreenType
|
||||
|
||||
|
||||
|
@ -9,14 +9,14 @@
|
||||
Tuple
|
||||
)
|
||||
|
||||
from kitty.constants import Edges, WindowGeometry
|
||||
from kitty.borders import BorderColor
|
||||
from kitty.fast_data_types import (
|
||||
Region, set_active_window, viewport_for_window
|
||||
)
|
||||
from kitty.options_stub import Options
|
||||
from kitty.types import Edges, WindowGeometry
|
||||
from kitty.typing import TypedDict, WindowType
|
||||
from kitty.window_list import WindowGroup, WindowList
|
||||
from kitty.borders import BorderColor
|
||||
|
||||
|
||||
class BorderLine(NamedTuple):
|
||||
|
@ -10,7 +10,7 @@
|
||||
)
|
||||
|
||||
from kitty.borders import BorderColor
|
||||
from kitty.constants import Edges
|
||||
from kitty.types import Edges
|
||||
from kitty.typing import WindowType
|
||||
from kitty.window_list import WindowGroup, WindowList
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
)
|
||||
|
||||
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.window_list import WindowGroup, WindowList
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
from kitty.borders import BorderColor
|
||||
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.window_list import WindowGroup, WindowList
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
from typing import Dict, Generator, Iterable, List, Tuple
|
||||
|
||||
from kitty.borders import BorderColor
|
||||
from kitty.constants import Edges
|
||||
from kitty.types import Edges
|
||||
from kitty.typing import WindowType
|
||||
from kitty.window_list import WindowGroup, WindowList
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
from .conf.utils import BadLine
|
||||
from .config import cached_values_for
|
||||
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
|
||||
)
|
||||
from .fast_data_types import (
|
||||
@ -30,6 +30,7 @@
|
||||
from .options_stub import Options as OptionsStub
|
||||
from .os_window_size import initial_window_size_func
|
||||
from .session import get_os_window_sizing_data
|
||||
from .types import SingleKey
|
||||
from .utils import (
|
||||
detach, expandvars, find_exe, log_error, read_shell_environment,
|
||||
single_instance, startup_notification_handler, unix_socket_paths
|
||||
|
@ -34,10 +34,11 @@ def generate_stub():
|
||||
all_options,
|
||||
class_name='DiffOptions',
|
||||
preamble_lines=(
|
||||
'from kitty.conf.utils import KittensKey, KittensKeyAction',
|
||||
'from kitty.conf.utils import KittensKeyAction',
|
||||
'from kitty.types import ParsedShortcut',
|
||||
),
|
||||
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 .constants import FloatEdges, is_macos
|
||||
from .constants import is_macos
|
||||
from .types import FloatEdges
|
||||
from .typing import EdgeLiteral
|
||||
from .utils import log_error
|
||||
|
||||
|
@ -8,9 +8,9 @@
|
||||
from typing import TYPE_CHECKING, Dict, Generator, List, Optional
|
||||
|
||||
from kitty.config import parse_send_text_bytes
|
||||
from kitty.key_encoding import (
|
||||
WindowSystemKeyEvent, decode_key_event_as_window_system_key
|
||||
)
|
||||
from kitty.key_encoding import decode_key_event_as_window_system_key
|
||||
from kitty.fast_data_types import KeyEvent as WindowSystemKeyEvent
|
||||
|
||||
from .base import (
|
||||
MATCH_TAB_OPTION, MATCH_WINDOW_OPTION, ArgsType, Boss, MatchError,
|
||||
PayloadGetType, PayloadType, RCOptions, RemoteCommand, ResponseType,
|
||||
@ -145,7 +145,8 @@ def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get:
|
||||
elif encoding == 'base64':
|
||||
data = base64.standard_b64decode(q)
|
||||
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:
|
||||
raise TypeError(f'Invalid encoding for send-text data: {encoding}')
|
||||
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 not exclude_active or window is not boss.active_window:
|
||||
if isinstance(data, WindowSystemKeyEvent):
|
||||
kdata = window.encoded_key(data.code, mods=data.mods, action=data.action)
|
||||
kdata = window.encoded_key(data)
|
||||
if kdata:
|
||||
window.write_to_child(kdata)
|
||||
else:
|
||||
|
@ -6,7 +6,7 @@
|
||||
from typing import Any, Dict, NamedTuple, Optional, Sequence, Tuple
|
||||
|
||||
from .config import build_ansi_color_table
|
||||
from .constants import WindowGeometry
|
||||
from .types import WindowGeometry
|
||||
from .fast_data_types import (
|
||||
DECAWM, Screen, cell_size_for_window, pt_to_px, set_tab_bar_render_data,
|
||||
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 .cli_stub import CLIOptions
|
||||
from .config import build_ansi_color_table
|
||||
from .constants import ScreenGeometry, WindowGeometry, appname, wakeup
|
||||
from .constants import 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, GLFW_MOD_CONTROL,
|
||||
GLFW_PRESS, 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,
|
||||
GRAPHICS_ALPHA_MASK_PROGRAM, GRAPHICS_PREMULT_PROGRAM, GRAPHICS_PROGRAM,
|
||||
MARK, MARK_MASK, OSC, REVERSE, SCROLL_FULL, SCROLL_LINE, SCROLL_PAGE,
|
||||
STRIKETHROUGH, TINT_PROGRAM, KeyEvent, Screen, add_timer, add_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,
|
||||
@ -36,6 +36,7 @@
|
||||
from .options_stub import Options
|
||||
from .rgb import to_color
|
||||
from .terminfo import get_capabilities
|
||||
from .types import ScreenGeometry, WindowGeometry
|
||||
from .typing import BossType, ChildType, EdgeLiteral, TabType, TypedDict
|
||||
from .utils import (
|
||||
color_as_int, get_primary_selection, load_shaders, open_cmd, open_url,
|
||||
@ -881,18 +882,20 @@ def copy_to_clipboard(self) -> None:
|
||||
if 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(
|
||||
key=key, mods=mods, key_encoding_flags=self.screen.current_key_encoding_flags(),
|
||||
cursor_key_mode=self.screen.cursor_key_mode, action=action
|
||||
).encode('ascii')
|
||||
key=key_event.key, shifted_key=key_event.shifted_key, alternate_key=key_event.alternate_key,
|
||||
mods=key_event.mods, action=key_event.action, text=key_event.text,
|
||||
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:
|
||||
text = self.text_for_selection()
|
||||
if text:
|
||||
set_clipboard_string(text)
|
||||
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:
|
||||
self.copy_or_interrupt()
|
||||
|
@ -10,7 +10,7 @@
|
||||
Any, Deque, Dict, Generator, Iterator, List, Optional, Tuple, Union
|
||||
)
|
||||
|
||||
from .constants import WindowGeometry
|
||||
from .types import WindowGeometry
|
||||
from .typing import EdgeLiteral, TabType, WindowType
|
||||
|
||||
WindowOrId = Union[WindowType, int]
|
||||
|
Loading…
Reference in New Issue
Block a user