Refactor mouse event region detection

This commit is contained in:
Kovid Goyal 2026-03-05 11:38:05 +05:30
parent 50bc3f465e
commit be83cf3a4a
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
3 changed files with 67 additions and 64 deletions

View file

@ -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, &central, &tab_bar);
const bool in_central = mouse_in_region(&central);
*in_tab_bar = false;
const OSWindow* w = global_state.callback_os_window;
os_window_regions(w, &central, &tab_bar);
const bool in_central = mouse_in_region(&central);
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;

View file

@ -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);

View file

@ -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);