A kitten to demo mouse handling

This commit is contained in:
Kovid Goyal 2021-09-28 13:43:23 +05:30
parent 769cd9be3f
commit 066dfc71aa
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 72 additions and 9 deletions

View File

View File

@ -0,0 +1,54 @@
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: GPLv3 Copyright: 2021, Kovid Goyal <kovid at kovidgoyal.net>
import sys
from typing import List, Optional
from ..show_key.kitty_mode import format_mods
from ..tui.handler import Handler
from ..tui.loop import Loop, MouseEvent
from ..tui.operations import MouseTracking
class Mouse(Handler):
mouse_tracking = MouseTracking.full
def __init__(self) -> None:
self.current_mouse_event: Optional[MouseEvent] = None
def initialize(self) -> None:
self.cmd.set_cursor_visible(False)
self.draw_screen()
def finalize(self) -> None:
self.cmd.set_cursor_visible(True)
def on_mouse(self, ev: MouseEvent) -> None:
self.current_mouse_event = ev
self.draw_screen()
@Handler.atomic_update
def draw_screen(self) -> None:
self.cmd.clear_screen()
ev = self.current_mouse_event
if ev is None:
self.print('Move the mouse or click to see mouse events')
return
self.print(f'Position: {ev.pixel_x}, {ev.pixel_y}')
self.print(f'Cell: {ev.cell_x}, {ev.cell_y}')
self.print(f'{ev.type}')
if ev.buttons:
self.print(ev.buttons)
if ev.mods:
self.print(f'Modifiers: {format_mods(ev.mods)}')
def main(args: List[str]) -> None:
loop = Loop()
handler = Mouse()
loop.loop(handler)
if __name__ == '__main__':
main(sys.argv)

View File

@ -12,6 +12,7 @@
import sys
import termios
from contextlib import contextmanager
from enum import Enum, IntFlag, auto
from functools import partial
from typing import Any, Callable, Dict, Generator, List, NamedTuple, Optional
@ -21,8 +22,7 @@
parse_input_from_terminal, raw_tty
)
from kitty.key_encoding import (
ALT, CTRL, PRESS, RELEASE, REPEAT, SHIFT, backspace_key, decode_key_event,
enter_key
ALT, CTRL, SHIFT, backspace_key, decode_key_event, enter_key
)
from kitty.typing import ImageManagerType, KeyEventType, Protocol
from kitty.utils import (
@ -78,11 +78,12 @@ def __init__(
self.extra_finalize: Optional[str] = None
self.optional_actions = optional_actions
self.use_alternate_screen = use_alternate_screen
self.mouse_tracking = mouse_tracking
def set_state_for_loop(self, set_raw: bool = True) -> None:
if set_raw:
raw_tty(self.tty_fd, self.original_termios)
write_all(self.tty_fd, init_state(self.use_alternate_screen))
write_all(self.tty_fd, init_state(self.use_alternate_screen, self.mouse_tracking))
def reset_state_to_original(self) -> None:
normal_tty(self.tty_fd, self.original_termios)
@ -107,9 +108,11 @@ def __exit__(self, *a: object) -> None:
del self.tty_fd, self.original_termios
LEFT, MIDDLE, RIGHT, FOURTH, FIFTH = 1, 2, 4, 8, 16
DRAG = REPEAT
bmap = {0: LEFT, 1: MIDDLE, 2: RIGHT}
class MouseButton(IntFlag):
LEFT, MIDDLE, RIGHT, FOURTH, FIFTH = 1, 2, 4, 8, 16
bmap = {0: MouseButton.LEFT, 1: MouseButton.MIDDLE, 2: MouseButton.RIGHT}
MOTION_INDICATOR = 1 << 5
EXTRA_BUTTON_INDICATOR = 1 << 6
SHIFT_INDICATOR = 1 << 2
@ -117,12 +120,18 @@ def __exit__(self, *a: object) -> None:
CTRL_INDICATOR = 1 << 4
class EventType(Enum):
PRESS = auto()
RELEASE = auto()
MOVE = auto()
class MouseEvent(NamedTuple):
cell_x: int
cell_y: int
pixel_x: int
pixel_y: int
type: int
type: EventType
buttons: int
mods: int
@ -136,12 +145,12 @@ def decode_sgr_mouse(text: str, screen_size: ScreenSize) -> MouseEvent:
cb_, x_, y_ = text.split(';')
m, y_ = y_[-1], y_[:-1]
cb, x, y = map(int, (cb_, x_, y_))
typ = RELEASE if m == 'm' else (DRAG if cb & MOTION_INDICATOR else PRESS)
typ = EventType.RELEASE if m == 'm' else (EventType.MOVE if cb & MOTION_INDICATOR else EventType.PRESS)
buttons = 0
cb3 = cb & 3
if cb3 != 3:
if cb & EXTRA_BUTTON_INDICATOR:
buttons |= FIFTH if cb3 & 1 else FOURTH
buttons |= MouseButton.FIFTH if cb3 & 1 else MouseButton.FOURTH
else:
buttons |= bmap[cb3]
mods = 0