From a16ffcdde2668cf294285e42ac7f81d1344f4d9e Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 15 Oct 2021 09:55:15 +0530 Subject: [PATCH] Handle mouse clicks when displaying focus window overlay --- kitty/boss.py | 44 ++++++++++++++++++++++++++++++++------- kitty/fast_data_types.pyi | 4 ++++ kitty/mouse.c | 10 +++++++++ kitty/state.c | 14 ++++++++++++- kitty/state.h | 1 + kitty/types.py | 11 ++++++++++ 6 files changed, 75 insertions(+), 9 deletions(-) diff --git a/kitty/boss.py b/kitty/boss.py index 26c3240ff..28330d3e6 100755 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -31,14 +31,14 @@ from .fast_data_types import ( GLFW_MOD_SUPER, IMPERATIVE_CLOSE_REQUESTED, NO_CLOSE_REQUESTED, ChildMonitor, KeyEvent, add_timer, apply_options_update, background_opacity_of, change_background_opacity, change_os_window_state, - cocoa_set_menubar_title, create_os_window, + cocoa_set_menubar_title, create_os_window, GLFW_PRESS, GLFW_MOUSE_BUTTON_LEFT, current_application_quit_request, current_os_window, destroy_global_data, focus_os_window, get_clipboard_string, get_options, get_os_window_size, global_font_size, mark_os_window_for_close, os_window_font_size, - patch_global_colors, ring_bell, safe_pipe, set_application_quit_request, - set_background_image, set_boss, set_clipboard_string, set_in_sequence_mode, - set_options, set_os_window_size, thread_write, toggle_fullscreen, - toggle_maximized + patch_global_colors, redirect_mouse_handling, ring_bell, safe_pipe, + set_application_quit_request, set_background_image, set_boss, + set_clipboard_string, set_in_sequence_mode, set_options, + set_os_window_size, thread_write, toggle_fullscreen, toggle_maximized ) from .key_encoding import get_name_to_functional_number_map from .keys import get_shortcut, shortcut_matches @@ -52,7 +52,7 @@ from .session import Session, create_sessions, get_os_window_sizing_data from .tabs import ( SpecialWindow, SpecialWindowInstance, Tab, TabDict, TabManager ) -from .types import _T, SingleKey, ac +from .types import _T, SingleKey, WindowSystemMouseEvent, ac from .typing import PopenType, TypedDict from .utils import ( func_name, get_editor, get_new_os_window_size, get_primary_selection, @@ -183,6 +183,7 @@ class Boss: self.args = args self.global_shortcuts_map = {v: KeyAction(k) for k, v in global_shortcuts.items()} self.global_shortcuts = global_shortcuts + self.mouse_handler: Optional[Callable[[WindowSystemMouseEvent], None]] = None self.update_keymap() if is_macos: from .fast_data_types import ( @@ -779,6 +780,10 @@ class Boss: elif isinstance(key_action, KeyAction): return self.dispatch_action(key_action) + def clear_pending_sequences(self) -> None: + self.pending_sequences = self.default_pending_action = None + set_in_sequence_mode(False) + def process_sequence(self, ev: KeyEvent) -> None: if not self.pending_sequences: set_in_sequence_mode(False) @@ -798,8 +803,7 @@ class Boss: self.pending_sequences = remaining else: matched_action = matched_action or self.default_pending_action - self.pending_sequences = self.default_pending_action = None - set_in_sequence_mode(False) + self.clear_pending_sequences() if matched_action is not None: self.dispatch_action(matched_action) @@ -825,6 +829,8 @@ class Boss: pending_sequences[(SingleKey(mods=mods, key=fmap[f'KP_{idx+1}']),)] = ac if count > 1: self.set_pending_sequences(pending_sequences, default_pending_action=KeyAction('focus_visible_window_trigger')) + redirect_mouse_handling(True) + self.mouse_handler = self.focus_visible_window_mouse_handler else: self.focus_visible_window_trigger() if get_options().enable_audio_bell: @@ -832,12 +838,34 @@ class Boss: def focus_visible_window_trigger(self, idx: int = -1) -> None: tab = self.active_tab + redirect_mouse_handling(False) if tab is not None: for window in tab: window.screen.set_window_number() if idx > -1: tab.nth_window(idx) + def focus_visible_window_mouse_handler(self, ev: WindowSystemMouseEvent) -> None: + if ev.button == GLFW_MOUSE_BUTTON_LEFT and ev.action == GLFW_PRESS and ev.window_id: + w = self.window_id_map.get(ev.window_id) + tab = self.active_tab + if w is not None and tab is not None and w in tab: + tab.set_active_window(w) + self.clear_pending_sequences() + self.focus_visible_window_trigger() + return + if ev.button > -1: + self.clear_pending_sequences() + self.focus_visible_window_trigger() + + def mouse_event( + self, in_tab_bar: bool, window_id: int, action: int, modifiers: int, button: int, + currently_pressed_button: int, x: float, y: float + ) -> None: + if self.mouse_handler is not None: + ev = WindowSystemMouseEvent(in_tab_bar, window_id, action, modifiers, button, currently_pressed_button, x, y) + self.mouse_handler(ev) + @ac('win', 'Interactively select a window in the current tab') def select_window_in_tab(self) -> None: tab = self.active_tab diff --git a/kitty/fast_data_types.pyi b/kitty/fast_data_types.pyi index 95b6909ed..ee2313c44 100644 --- a/kitty/fast_data_types.pyi +++ b/kitty/fast_data_types.pyi @@ -1246,3 +1246,7 @@ def get_all_processes() -> Tuple[int, ...]: def num_users() -> int: pass + + +def redirect_mouse_handling(yes: bool) -> None: + pass diff --git a/kitty/mouse.c b/kitty/mouse.c index 565b87183..b8ea84e44 100644 --- a/kitty/mouse.c +++ b/kitty/mouse.c @@ -669,6 +669,16 @@ mouse_event(const int button, int modifiers, int action) { unsigned int window_idx = 0; Window *w = NULL; debug("%s mouse_button: %d %s", action == GLFW_RELEASE ? "\x1b[32mRelease\x1b[m" : (button < 0 ? "\x1b[36mMove\x1b[m" : "\x1b[31mPress\x1b[m"), button, format_mods(modifiers)); + if (global_state.redirect_mouse_handling) { + w = window_for_event(&window_idx, &in_tab_bar); + call_boss(mouse_event, "OK iiii dd", + (in_tab_bar ? Py_True : Py_False), (w ? w->id : 0), + action, modifiers, button, currently_pressed_button(), + global_state.callback_os_window->mouse_x, global_state.callback_os_window->mouse_y + ); + debug("mouse handling redirected\n"); + return; + } if (global_state.active_drag_in_window) { if (button == -1) { // drag move w = window_for_id(global_state.active_drag_in_window); diff --git a/kitty/state.c b/kitty/state.c index 6547f6601..44f039d7b 100644 --- a/kitty/state.c +++ b/kitty/state.c @@ -1055,7 +1055,18 @@ PYWRAP1(click_mouse_url) { if (click_mouse_url(a, b, c)) { Py_RETURN_TRUE; } Py_RETURN_FALSE; } -PYWRAP1(move_cursor_to_mouse_if_in_prompt) { id_type a, b, c; PA("KKK", &a, &b, &c); if (move_cursor_to_mouse_if_in_prompt(a, b, c)) Py_RETURN_TRUE; Py_RETURN_FALSE; } + +PYWRAP1(move_cursor_to_mouse_if_in_prompt) { + id_type a, b, c; PA("KKK", &a, &b, &c); + if (move_cursor_to_mouse_if_in_prompt(a, b, c)) Py_RETURN_TRUE; + Py_RETURN_FALSE; +} + +PYWRAP1(redirect_mouse_handling) { + global_state.redirect_mouse_handling = PyObject_IsTrue(args) ? true : false; + Py_RETURN_NONE; +} + THREE_ID_OBJ(update_window_title) THREE_ID(remove_window) THREE_ID(detach_window) @@ -1080,6 +1091,7 @@ static PyMethodDef module_methods[] = { MW(get_options, METH_NOARGS), MW(click_mouse_url, METH_VARARGS), MW(move_cursor_to_mouse_if_in_prompt, METH_VARARGS), + MW(redirect_mouse_handling, METH_O), MW(mouse_selection, METH_VARARGS), MW(set_in_sequence_mode, METH_O), MW(resolve_key_mods, METH_VARARGS), diff --git a/kitty/state.h b/kitty/state.h index cc692e53e..2f074d5cf 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -217,6 +217,7 @@ typedef struct { id_type active_drag_in_window, tracked_drag_in_window; int active_drag_button; CloseRequest quit_request; + bool redirect_mouse_handling; } GlobalState; extern GlobalState global_state; diff --git a/kitty/types.py b/kitty/types.py index af6e667ae..bdd3c636a 100644 --- a/kitty/types.py +++ b/kitty/types.py @@ -61,6 +61,17 @@ class MouseEvent(NamedTuple): grabbed: bool = False +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]