From be83cf3a4af2faac6a8ca52b8ad2790c6acea090 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 5 Mar 2026 11:38:05 +0530 Subject: [PATCH] Refactor mouse event region detection --- kitty/mouse.c | 123 ++++++++++++++++++++++++++------------------------ kitty/state.c | 6 +-- kitty/state.h | 2 +- 3 files changed, 67 insertions(+), 64 deletions(-) diff --git a/kitty/mouse.c b/kitty/mouse.c index 36615915f..899b2c7e8 100644 --- a/kitty/mouse.c +++ b/kitty/mouse.c @@ -915,23 +915,29 @@ num_visible_windows(Tab *t) { return ans; } -static Window* -window_for_event(unsigned int *window_idx, bool *in_tab_bar, Edge *window_border) { +typedef struct MouseRegion { + unsigned window_idx; + bool in_tab_bar; + Edge window_border; + Window *window; +} MouseRegion; + +static MouseRegion +mouse_region(bool detect_borders) { + MouseRegion ans = {0}; Region central, tab_bar; - os_window_regions(global_state.callback_os_window, ¢ral, &tab_bar); - const bool in_central = mouse_in_region(¢ral); - *in_tab_bar = false; const OSWindow* w = global_state.callback_os_window; + os_window_regions(w, ¢ral, &tab_bar); + const bool in_central = mouse_in_region(¢ral); if (!in_central) { if ( (tab_bar.top < central.top && w->mouse_y < central.top) || (tab_bar.bottom > central.bottom && w->mouse_y >= central.bottom) - ) *in_tab_bar = true; + ) ans.in_tab_bar = true; } if (in_central && w->num_tabs > 0) { Tab *t = global_state.callback_os_window->tabs + global_state.callback_os_window->active_tab; - if (window_border && num_visible_windows(t) > 1) { - *window_border = 0; + if (detect_borders && num_visible_windows(t) > 1) { id_type window_id = 0; double dpi = (w->fonts_data->logical_dpi_x + w->fonts_data->logical_dpi_y) / 2.; double tolerance = ((long)round((OPT(window_drag_tolerance) * (dpi / 72.0)))); @@ -943,7 +949,7 @@ window_for_event(unsigned int *window_idx, bool *in_tab_bar, Edge *window_border if (!br->border_type) continue; Edge edges = 0; if (border_contains_mouse(br, 0, &edges)) { - *window_border |= edges; + ans.window_border |= edges; is_within_border_without_tolerance = true; if (edges & (LEFT_EDGE | RIGHT_EDGE)) { closest_vert_dist = -1; closest_vert = NULL; } else { closest_horiz_dist = -1; closest_horiz = NULL; } @@ -960,26 +966,30 @@ window_for_event(unsigned int *window_idx, bool *in_tab_bar, Edge *window_border } } if (!is_within_border_without_tolerance) { - if (closest_vert && border_contains_mouse(closest_vert, tolerance, window_border) && !window_id) + if (closest_vert && border_contains_mouse(closest_vert, tolerance, &ans.window_border) && !window_id) window_id = closest_vert->border_type < 0 ? -closest_vert->border_type : closest_vert->border_type; - if (closest_horiz && border_contains_mouse(closest_horiz, tolerance, window_border) && !window_id) + if (closest_horiz && border_contains_mouse(closest_horiz, tolerance, &ans.window_border) && !window_id) window_id = closest_horiz->border_type < 0 ? -closest_horiz->border_type : closest_horiz->border_type; } - if (*window_border) { - if (!window_id) return NULL; - for (unsigned int i = 0; i < t->num_windows; i++) - if (t->windows[i].id == window_id) return t->windows + i; - return NULL; + if (ans.window_border) { + if (window_id) { + for (unsigned int i = 0; i < t->num_windows; i++) + if (t->windows[i].id == window_id) { + ans.window = t->windows + i; + ans.window_idx = i; + break; + } + } + return ans; } } for (unsigned int i = 0; i < t->num_windows; i++) { if (contains_mouse(t->windows + i) && t->windows[i].render_data.screen) { - *window_idx = i; - return t->windows + i; + ans.window_idx = i; ans.window = t->windows + i; } } } - return NULL; + return ans; } static Window* @@ -1002,13 +1012,11 @@ closest_window_for_event(unsigned int *window_idx) { void focus_in_event(void) { // Ensure that no URL is highlighted and the mouse cursor is in default shape - bool in_tab_bar; - unsigned int window_idx = 0; mouse_cursor_shape = TEXT_POINTER; - Window *w = window_for_event(&window_idx, &in_tab_bar, NULL); - if (w && w->render_data.screen) { - screen_mark_url(w->render_data.screen, 0, 0, 0, 0); - set_mouse_cursor_for_screen(w->render_data.screen); + MouseRegion r = mouse_region(false); + if (r.window && r.window->render_data.screen) { + screen_mark_url(r.window->render_data.screen, 0, 0, 0, 0); + set_mouse_cursor_for_screen(r.window->render_data.screen); } set_mouse_cursor(mouse_cursor_shape); } @@ -1016,17 +1024,15 @@ focus_in_event(void) { void update_mouse_pointer_shape(void) { mouse_cursor_shape = TEXT_POINTER; - bool in_tab_bar; - unsigned int window_idx = 0; - Window *w = window_for_event(&window_idx, &in_tab_bar, NULL); - if (in_tab_bar) { + MouseRegion r = mouse_region(false); + if (r.in_tab_bar) { mouse_cursor_shape = POINTER_POINTER; - } else if (w) { - if (handle_scrollbar_mouse(w, -1, MOVE, 0)) { + } else if (r.window) { + if (handle_scrollbar_mouse(r.window, -1, MOVE, 0)) { mouse_cursor_shape = scrollbar_drag_mouse_cursor; - } else if (w->render_data.screen) { - screen_mark_url(w->render_data.screen, 0, 0, 0, 0); - set_mouse_cursor_for_screen(w->render_data.screen); + } else if (r.window->render_data.screen) { + screen_mark_url(r.window->render_data.screen, 0, 0, 0, 0); + set_mouse_cursor_for_screen(r.window->render_data.screen); } } set_mouse_cursor(mouse_cursor_shape); @@ -1055,10 +1061,10 @@ enter_event(int modifiers) { // If the mouse is grabbed send a move event to update the cursor position // since the last report. if (global_state.redirect_mouse_handling || global_state.active_drag_in_window || global_state.tracked_drag_in_window) return; - unsigned window_idx; bool in_tab_bar; - Window *w = window_for_event(&window_idx, &in_tab_bar, NULL); + MouseRegion r = mouse_region(false); + Window *w = r.window; set_currently_hovered_window(w ? w->id : 0, modifiers); - if (!w || in_tab_bar) return; + if (!w || r.in_tab_bar) return; if (handle_scrollbar_mouse(w, -1, MOVE, modifiers)) return; @@ -1148,7 +1154,6 @@ border_name(int edges) { void mouse_event(const int button, int modifiers, int action) { MouseShape old_cursor = mouse_cursor_shape; - bool in_tab_bar; unsigned int window_idx = 0; Window *w = NULL; @@ -1159,9 +1164,9 @@ mouse_event(const int button, int modifiers, int action) { else { debug("%s mouse_button: %d %s", action == GLFW_RELEASE ? "\x1b[32mRelease\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, NULL); + MouseRegion r= mouse_region(false); w = r.window; call_boss(mouse_event, "OK iiii dd", - (in_tab_bar ? Py_True : Py_False), (w ? w->id : 0), + (r.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 ); @@ -1236,32 +1241,31 @@ mouse_event(const int button, int modifiers, int action) { } return; } - Edge window_border = 0; - w = window_for_event(&window_idx, &in_tab_bar, &window_border); + MouseRegion r = mouse_region(true); set_currently_hovered_window(w ? w->id : 0, modifiers); - if (in_tab_bar || global_state.tab_being_dragged.id) { + if (r.in_tab_bar || global_state.tab_being_dragged.id) { mouse_cursor_shape = POINTER_POINTER; handle_tab_bar_mouse(button, modifiers, action); debug("handled by tab bar\n"); - } else if (window_border) { - debug("window border: %s window id: %llu\n", border_name(window_border), w ? w->id : 0); - if (window_border & LEFT_EDGE) { - if (window_border & TOP_EDGE) mouse_cursor_shape = NWSE_RESIZE_POINTER; - else if (window_border & BOTTOM_EDGE) mouse_cursor_shape = NESW_RESIZE_POINTER; + } else if (r.window_border) { + debug("window border: %s window id: %llu\n", border_name(r.window_border), w ? w->id : 0); + if (r.window_border & LEFT_EDGE) { + if (r.window_border & TOP_EDGE) mouse_cursor_shape = NWSE_RESIZE_POINTER; + else if (r.window_border & BOTTOM_EDGE) mouse_cursor_shape = NESW_RESIZE_POINTER; else mouse_cursor_shape = EW_RESIZE_POINTER; - } else if (window_border & RIGHT_EDGE) { - if (window_border & TOP_EDGE) mouse_cursor_shape = NESW_RESIZE_POINTER; - else if (window_border & BOTTOM_EDGE) mouse_cursor_shape = NWSE_RESIZE_POINTER; + } else if (r.window_border & RIGHT_EDGE) { + if (r.window_border & TOP_EDGE) mouse_cursor_shape = NESW_RESIZE_POINTER; + else if (r.window_border & BOTTOM_EDGE) mouse_cursor_shape = NWSE_RESIZE_POINTER; else mouse_cursor_shape = EW_RESIZE_POINTER; - } else if (window_border & (TOP_EDGE | BOTTOM_EDGE)) mouse_cursor_shape = NS_RESIZE_POINTER; + } else if (r.window_border & (TOP_EDGE | BOTTOM_EDGE)) mouse_cursor_shape = NS_RESIZE_POINTER; if (w && button == GLFW_MOUSE_BUTTON_LEFT && w->render_data.screen) { - RAII_PyObject(r, PyObject_CallMethod( - global_state.boss, "drag_resize_start", "iddKII", window_border, + RAII_PyObject(retval, PyObject_CallMethod( + global_state.boss, "drag_resize_start", "iddKII", r.window_border, osw->mouse_x, osw->mouse_y, w->id, w->render_data.screen->cell_size.width, w->render_data.screen->cell_size.height)); - if (r == NULL) { PyErr_Print(); return; } - if (PyObject_IsTrue(r)) global_state.active_drag_resize = w->id; + if (retval == NULL) { PyErr_Print(); return; } + if (PyObject_IsTrue(retval)) global_state.active_drag_resize = w->id; } } else if (w) { debug("grabbed: %d\n", w->render_data.screen->modes.mouse_tracking_mode != 0); @@ -1361,10 +1365,8 @@ pixel_scroll_enabled_for_screen(const Screen *screen) { void scroll_event(const GLFWScrollEvent *ev) { debug("\x1b[36mScroll\x1b[m %s x: %f y: %f momentum: %s modifiers: %s\n", scroll_offset_type(ev->offset_type), ev->x_offset, ev->y_offset, scroll_phase(ev->momentum_type), format_mods(ev->keyboard_modifiers)); - bool in_tab_bar; static id_type window_for_momentum_scroll = 0; static bool main_screen_for_momentum_scroll = false; - unsigned int window_idx = 0; // allow scroll events even if window is not currently focused (in // which case on some platforms such as macOS the mouse location is zeroed so // window_for_event() does not work). @@ -1375,8 +1377,9 @@ scroll_event(const GLFWScrollEvent *ev) { osw->mouse_x = mouse_x * osw->viewport_x_ratio; osw->mouse_y = mouse_y * osw->viewport_y_ratio; } - Window *w = window_for_event(&window_idx, &in_tab_bar, NULL); - if (!w && !in_tab_bar) { + MouseRegion r = mouse_region(false); + Window *w = r.window; + if (!w && !r.in_tab_bar) { // fallback to last active window Tab *t = osw->tabs + osw->active_tab; if (t) w = t->windows + t->active_window; diff --git a/kitty/state.c b/kitty/state.c index b46ad48f4..90a90baa5 100644 --- a/kitty/state.c +++ b/kitty/state.c @@ -63,7 +63,7 @@ GlobalState global_state = {{0}}; }} static double -dpi_for_os_window(OSWindow *os_window) { +dpi_for_os_window(const OSWindow *os_window) { double dpi = (os_window->fonts_data->logical_dpi_x + os_window->fonts_data->logical_dpi_y) / 2.; if (dpi == 0) dpi = (global_state.default_dpi.x + global_state.default_dpi.y) / 2.; return dpi; @@ -84,7 +84,7 @@ dpi_for_os_window_id(id_type os_window_id) { } static long -pt_to_px_for_os_window(double pt, OSWindow *w) { +pt_to_px_for_os_window(double pt, const OSWindow *w) { const double dpi = dpi_for_os_window(w); return ((long)round((pt * (dpi / 72.0)))); } @@ -638,7 +638,7 @@ pyset_borders_rects(PyObject *self UNUSED, PyObject *args) { void -os_window_regions(OSWindow *os_window, Region *central, Region *tab_bar) { +os_window_regions(const OSWindow *os_window, Region *central, Region *tab_bar) { if (!OPT(tab_bar_hidden) && os_window->num_tabs && !os_window->has_too_few_tabs) { long margin_outer = pt_to_px_for_os_window(OPT(tab_bar_margin_height.outer), os_window); long margin_inner = pt_to_px_for_os_window(OPT(tab_bar_margin_height.inner), os_window); diff --git a/kitty/state.h b/kitty/state.h index 261263221..04f045251 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -451,7 +451,7 @@ OSWindow* os_window_for_kitty_window(id_type); OSWindow* os_window_for_id(id_type); OSWindow* add_os_window(void); OSWindow* current_os_window(void); -void os_window_regions(OSWindow*, Region *main, Region *tab_bar); +void os_window_regions(const OSWindow*, Region *main, Region *tab_bar); bool drag_scroll(Window *, OSWindow*); void draw_borders(ssize_t vao_idx, unsigned int num_border_rects, BorderRect *rect_buf, bool rect_data_is_dirty, color_type, unsigned int, bool, OSWindow *w); ssize_t create_cell_vao(void);