diff --git a/kittens/tui/loop.py b/kittens/tui/loop.py index 705e5ef1a..a9dc85f7c 100644 --- a/kittens/tui/loop.py +++ b/kittens/tui/loop.py @@ -25,10 +25,12 @@ from kitty.key_encoding import ( enter_key ) from kitty.typing import ImageManagerType, KeyEventType, Protocol -from kitty.utils import ScreenSizeGetter, screen_size_function, write_all +from kitty.utils import ( + ScreenSize, ScreenSizeGetter, screen_size_function, write_all +) from .handler import Handler -from .operations import init_state, reset_state, MouseTracking +from .operations import MouseTracking, init_state, reset_state class BinaryWrite(Protocol): @@ -116,14 +118,21 @@ CTRL_INDICATOR = 1 << 4 class MouseEvent(NamedTuple): - x: int - y: int + cell_x: int + cell_y: int + pixel_x: int + pixel_y: int type: int buttons: int mods: int -def decode_sgr_mouse(text: str) -> MouseEvent: +def pixel_to_cell(px: int, length: int, cell_length: int) -> int: + px = max(0, min(px, length - 1)) + return px // cell_length + + +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_)) @@ -142,7 +151,10 @@ def decode_sgr_mouse(text: str) -> MouseEvent: mods |= ALT if cb & CTRL_INDICATOR: mods |= CTRL - return MouseEvent(x, y, typ, buttons, mods) + return MouseEvent( + pixel_to_cell(x, screen_size.width, screen_size.cell_width), pixel_to_cell(y, screen_size.height, screen_size.cell_height), + x, y, typ, buttons, mods + ) class UnhandledException(Handler): @@ -285,7 +297,7 @@ class Loop: if csi.startswith('<'): # SGR mouse event try: - ev = decode_sgr_mouse(csi[1:]) + ev = decode_sgr_mouse(csi[1:], self.handler.screen_size) except Exception: pass else: diff --git a/kittens/tui/operations.py b/kittens/tui/operations.py index bc131f96d..08133e941 100644 --- a/kittens/tui/operations.py +++ b/kittens/tui/operations.py @@ -43,6 +43,7 @@ class Mode(Enum): MOUSE_UTF8_MODE = 1005, '?' MOUSE_SGR_MODE = 1006, '?' MOUSE_URXVT_MODE = 1015, '?' + MOUSE_SGR_PIXEL_MODE = 1016, '?' ALTERNATE_SCREEN = 1049, '?' BRACKETED_PASTE = 2004, '?' PENDING_UPDATE = 2026, '?' @@ -304,7 +305,7 @@ def init_state(alternate_screen: bool = True, mouse_tracking: MouseTracking = Mo ans += set_mode(Mode.ALTERNATE_SCREEN) + reset_mode(Mode.DECOM) ans += clear_screen() if mouse_tracking is not MouseTracking.none: - ans += set_mode(Mode.MOUSE_SGR_MODE) + ans += set_mode(Mode.MOUSE_SGR_PIXEL_MODE) if mouse_tracking is MouseTracking.buttons_only: ans += set_mode(Mode.MOUSE_BUTTON_TRACKING) elif mouse_tracking is MouseTracking.buttons_and_drag: