mirror of
https://github.com/kovidgoyal/kitty.git
synced 2026-10-28 18:15:15 +01:00
277 lines
6.9 KiB
Python
277 lines
6.9 KiB
Python
#!/usr/bin/env python
|
|
# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>
|
|
|
|
from collections.abc import Callable, Iterator, Mapping, Sequence
|
|
from enum import Enum
|
|
from functools import update_wrapper
|
|
from typing import TYPE_CHECKING, Any, Literal, NamedTuple, TypedDict, TypeVar, Union, cast
|
|
|
|
if TYPE_CHECKING:
|
|
from kitty.fast_data_types import SingleKey
|
|
|
|
|
|
class SingleInstanceData(TypedDict):
|
|
cmd: str
|
|
args: Sequence[str]
|
|
cmdline_args_for_open: Sequence[str]
|
|
cwd: str
|
|
session_data: str
|
|
session_arg: str
|
|
session_path: str
|
|
environ: Mapping[str, str]
|
|
notify_on_os_window_death: str | None
|
|
|
|
|
|
class OverlayType(Enum):
|
|
transient = 'transient'
|
|
main = 'main'
|
|
|
|
|
|
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 WindowGeometry(NamedTuple):
|
|
left: int
|
|
top: int
|
|
right: int
|
|
bottom: int
|
|
xnum: int
|
|
ynum: int
|
|
spaces: Edges = Edges()
|
|
# The part of spaces that comes from the window size not being an exact
|
|
# multiple of the cell size (as opposed to intentional margin/border/padding
|
|
# decoration). This is the innermost slice of the padding, adjacent to the
|
|
# cells, see layout_dimension().
|
|
compensatory: Edges = Edges()
|
|
|
|
|
|
class SignalInfo(NamedTuple):
|
|
si_signo: int
|
|
si_code: int
|
|
si_pid: int
|
|
si_uid: int
|
|
si_addr: int
|
|
si_status: int
|
|
sival_int: int
|
|
sival_ptr: int
|
|
|
|
|
|
class LayerShellConfig(NamedTuple):
|
|
type: int = 0
|
|
edge: int = 0
|
|
focus_policy: int = 0
|
|
output_name: str = ''
|
|
x_size_in_pixels: int = 0
|
|
y_size_in_pixels: int = 0
|
|
x_size_in_cells: int = 0
|
|
y_size_in_cells: int = 0
|
|
requested_top_margin: int = 0
|
|
requested_left_margin: int = 0
|
|
requested_bottom_margin: int = 0
|
|
requested_right_margin: int = 0
|
|
requested_exclusive_zone: int = -1
|
|
override_exclusive_zone: bool = False
|
|
hide_on_focus_loss: bool = False
|
|
|
|
|
|
def mod_to_names(mods: int, has_kitty_mod: bool = False, kitty_mod: int = 0) -> Iterator[str]:
|
|
if has_kitty_mod:
|
|
mods &= ~kitty_mod
|
|
yield 'kitty_mod'
|
|
for name, val in modmap().items():
|
|
if mods & val:
|
|
yield name
|
|
|
|
|
|
def human_repr_of_single_key(self: 'SingleKey', kitty_mod: int) -> str:
|
|
from .fast_data_types import glfw_get_key_name
|
|
|
|
names = []
|
|
names = list(mod_to_names(self.mods, self.defined_with_kitty_mod, kitty_mod))
|
|
if self.key > 0:
|
|
kname = (glfw_get_key_name(0, self.key) if self.is_native else glfw_get_key_name(self.key, 0)) or f'{self.key}'
|
|
kname = {' ': 'space'}.get(kname, kname)
|
|
names.append(kname)
|
|
return '+'.join(names)
|
|
|
|
|
|
class Shortcut(NamedTuple):
|
|
keys: tuple['SingleKey', ...]
|
|
|
|
def human_repr(self, kitty_mod: int = 0) -> str:
|
|
return ' > '.join(human_repr_of_single_key(k, kitty_mod) for k in self.keys)
|
|
|
|
|
|
class MouseEvent(NamedTuple):
|
|
button: int = 0
|
|
mods: int = 0
|
|
repeat_count: int = 1
|
|
grabbed: bool = False
|
|
|
|
def human_repr(self, kitty_mod: int = 0) -> str:
|
|
from .options.utils import mouse_button_map, mouse_trigger_count_map
|
|
|
|
def mouse_button_num_to_name(num: int) -> str:
|
|
button_map = {v: k for k, v in mouse_button_map.items()}
|
|
name = f'b{num + 1}'
|
|
return button_map.get(name, name)
|
|
|
|
def mouse_trigger_count_to_name(count: int) -> str:
|
|
trigger_count_map = {str(v): k for k, v in mouse_trigger_count_map.items()}
|
|
k = str(count)
|
|
return trigger_count_map.get(k, k)
|
|
|
|
names = list(mod_to_names(self.mods)) + [mouse_button_num_to_name(self.button)]
|
|
when = mouse_trigger_count_to_name(self.repeat_count)
|
|
grabbed = 'grabbed' if self.grabbed else 'ungrabbed'
|
|
return ' '.join(('+'.join(names), when, grabbed))
|
|
|
|
|
|
class WindowSystemMouseEvent(NamedTuple):
|
|
in_tab_bar: bool
|
|
window_id: int
|
|
action: int
|
|
modifiers: int
|
|
button: int
|
|
currently_pressed_button: int
|
|
x: float
|
|
y: float
|
|
|
|
|
|
ConvertibleToNumbers = Union[str, bytes, int, float]
|
|
|
|
|
|
class sentinel_type:
|
|
pass
|
|
|
|
|
|
sentinel = sentinel_type()
|
|
|
|
|
|
class AsyncResponse:
|
|
pass
|
|
|
|
|
|
class RunOnce[T]:
|
|
def __init__(self, f: Callable[[], T]) -> None:
|
|
self._override: T | sentinel_type = sentinel
|
|
self._cached_result: T | sentinel_type = sentinel
|
|
update_wrapper(self, f)
|
|
|
|
if TYPE_CHECKING:
|
|
|
|
def __call__(self) -> T:
|
|
return cast(T, 1)
|
|
else:
|
|
|
|
def __call__(self) -> T:
|
|
if self._override is not sentinel:
|
|
return self._override
|
|
if self._cached_result is sentinel:
|
|
self._cached_result = self.__wrapped__()
|
|
return self._cached_result
|
|
|
|
def clear_cached(self) -> None:
|
|
self._cached_result = sentinel
|
|
|
|
def set_override(self, val: T) -> None:
|
|
self._override = val
|
|
|
|
def clear_override(self) -> None:
|
|
self._override = sentinel
|
|
|
|
|
|
def run_once[T](f: Callable[[], T]) -> RunOnce[T]:
|
|
return RunOnce(f)
|
|
|
|
|
|
@run_once
|
|
def modmap() -> dict[str, int]:
|
|
from .constants import is_macos
|
|
from .fast_data_types import (
|
|
GLFW_MOD_ALT,
|
|
GLFW_MOD_CAPS_LOCK,
|
|
GLFW_MOD_CONTROL,
|
|
GLFW_MOD_HYPER,
|
|
GLFW_MOD_META,
|
|
GLFW_MOD_NUM_LOCK,
|
|
GLFW_MOD_SHIFT,
|
|
GLFW_MOD_SUPER,
|
|
)
|
|
|
|
return {
|
|
'ctrl': GLFW_MOD_CONTROL,
|
|
'shift': GLFW_MOD_SHIFT,
|
|
('opt' if is_macos else 'alt'): GLFW_MOD_ALT,
|
|
('cmd' if is_macos else 'super'): GLFW_MOD_SUPER,
|
|
'hyper': GLFW_MOD_HYPER,
|
|
'meta': GLFW_MOD_META,
|
|
'caps_lock': GLFW_MOD_CAPS_LOCK,
|
|
'num_lock': GLFW_MOD_NUM_LOCK,
|
|
}
|
|
|
|
|
|
ActionGroup = Literal['cp', 'sc', 'win', 'tab', 'fs', 'mouse', 'mk', 'lay', 'misc', 'debug', 'session']
|
|
|
|
|
|
class ActionSpec(NamedTuple):
|
|
group: str
|
|
doc: str
|
|
|
|
|
|
def ac[_T](group: ActionGroup, doc: str) -> Callable[[_T], _T]:
|
|
def w(f: _T) -> _T:
|
|
setattr(f, 'action_spec', ActionSpec(group, doc))
|
|
return f
|
|
|
|
return w
|
|
|
|
|
|
WindowMapper = Callable[[int], int | None]
|
|
DecoratedFunc = TypeVar('DecoratedFunc', bound=Callable[..., Any])
|
|
|
|
|
|
class NeighborsMap(TypedDict, total=False):
|
|
left: list[int]
|
|
top: list[int]
|
|
right: list[int]
|
|
bottom: list[int]
|
|
|
|
|
|
class WindowResizeDragData(NamedTuple):
|
|
horizontal_id: int | None = None
|
|
width_increases_rightwards: bool = True
|
|
vertical_id: int | None = None
|
|
height_increases_downwards: bool = True
|
|
|
|
|
|
class WindowResizeDrag(NamedTuple):
|
|
is_active: bool = False
|
|
cell_width: int = 0
|
|
cell_height: int = 0
|
|
initial_x: float = 0
|
|
initial_y: float = 0
|
|
last_step_x: float = 0
|
|
last_step_y: float = 0
|
|
tab_id: int = 0
|
|
data: WindowResizeDragData = WindowResizeDragData()
|
|
|
|
def __bool__(self) -> bool:
|
|
return self.is_active
|