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