mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-24 01:08:10 +02:00
Fix focus_follows_mouse switching active window on desktop/space return
When focus_follows_mouse is enabled, returning to a desktop/space fired an enter event that switched the active window to whichever one was under the cursor, even though the mouse had not crossed a window boundary. Distinguish genuine mouse motion into a window from a window reappearing under a stationary cursor by checking, in cursor_enter_callback, whether the cursor position actually changed. focus_follows_mouse now switches focus only when the cursor moved, so motion into a window still switches focus while a stationary reappearance does not.
This commit is contained in:
@@ -178,6 +178,8 @@ Detailed list of changes
|
|||||||
|
|
||||||
- macOS: Show a key symbol on the active tab if the macOS Secure Input feature is enabled
|
- macOS: Show a key symbol on the active tab if the macOS Secure Input feature is enabled
|
||||||
|
|
||||||
|
- Fix :opt:`focus_follows_mouse` switching the active window when returning to a desktop/space, even though the mouse did not move. Now the window under a stationary cursor is left alone, while moving the mouse across windows still switches focus as before.
|
||||||
|
|
||||||
0.47.2 [2026-06-07]
|
0.47.2 [2026-06-07]
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|||||||
@@ -329,7 +329,7 @@ bool colorprofile_pop_colors(ColorProfile*, unsigned int);
|
|||||||
void colorprofile_report_stack(ColorProfile*, unsigned int*, unsigned int*);
|
void colorprofile_report_stack(ColorProfile*, unsigned int*, unsigned int*);
|
||||||
|
|
||||||
void set_mouse_cursor(MouseShape);
|
void set_mouse_cursor(MouseShape);
|
||||||
void enter_event(int modifiers);
|
void enter_event(int modifiers, bool cursor_moved);
|
||||||
void leave_event(int modifiers);
|
void leave_event(int modifiers);
|
||||||
void mouse_event(const int, int, int);
|
void mouse_event(const int, int, int);
|
||||||
void focus_in_event(void);
|
void focus_in_event(void);
|
||||||
|
|||||||
14
kitty/glfw.c
14
kitty/glfw.c
@@ -539,12 +539,20 @@ cursor_enter_callback(GLFWwindow *w, int entered) {
|
|||||||
glfwGetCursorPos(w, &x, &y);
|
glfwGetCursorPos(w, &x, &y);
|
||||||
monotonic_t now = monotonic();
|
monotonic_t now = monotonic();
|
||||||
global_state.callback_os_window->last_mouse_activity_at = now;
|
global_state.callback_os_window->last_mouse_activity_at = now;
|
||||||
global_state.callback_os_window->mouse_x = x * global_state.callback_os_window->viewport_x_ratio;
|
double new_mouse_x = x * global_state.callback_os_window->viewport_x_ratio;
|
||||||
global_state.callback_os_window->mouse_y = y * global_state.callback_os_window->viewport_y_ratio;
|
double new_mouse_y = y * global_state.callback_os_window->viewport_y_ratio;
|
||||||
|
// focus_follows_mouse should react to the mouse moving, not to a window
|
||||||
|
// appearing under a stationary cursor (such as when returning to this
|
||||||
|
// desktop/space). Detect genuine motion by comparing against the last
|
||||||
|
// known cursor position so an enter caused by mouse motion still switches
|
||||||
|
// focus, while a stationary reappearance does not.
|
||||||
|
bool cursor_moved = new_mouse_x != global_state.callback_os_window->mouse_x || new_mouse_y != global_state.callback_os_window->mouse_y;
|
||||||
|
global_state.callback_os_window->mouse_x = new_mouse_x;
|
||||||
|
global_state.callback_os_window->mouse_y = new_mouse_y;
|
||||||
if (entered) {
|
if (entered) {
|
||||||
debug_input("Mouse cursor entered window: %llu at %fx%f\n", global_state.callback_os_window->id, x, y);
|
debug_input("Mouse cursor entered window: %llu at %fx%f\n", global_state.callback_os_window->id, x, y);
|
||||||
cursor_active_callback(now);
|
cursor_active_callback(now);
|
||||||
if (is_window_ready_for_callbacks()) enter_event(global_state.mods_at_last_key_or_button_event);
|
if (is_window_ready_for_callbacks()) enter_event(global_state.mods_at_last_key_or_button_event, cursor_moved);
|
||||||
} else {
|
} else {
|
||||||
debug_input("Mouse cursor left window: %llu\n", global_state.callback_os_window->id);
|
debug_input("Mouse cursor left window: %llu\n", global_state.callback_os_window->id);
|
||||||
if (is_window_ready_for_callbacks()) leave_event(global_state.mods_at_last_key_or_button_event);
|
if (is_window_ready_for_callbacks()) leave_event(global_state.mods_at_last_key_or_button_event);
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ update_scrollbar_hover_state(Window *w, bool hovering) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
set_currently_hovered_window(id_type window_id, int modifiers) {
|
set_currently_hovered_window(id_type window_id, int modifiers, bool focus_follows) {
|
||||||
if (global_state.mouse_hover_in_window != window_id) {
|
if (global_state.mouse_hover_in_window != window_id) {
|
||||||
Window *left_window = window_for_id(global_state.mouse_hover_in_window);
|
Window *left_window = window_for_id(global_state.mouse_hover_in_window);
|
||||||
global_state.mouse_hover_in_window = window_id;
|
global_state.mouse_hover_in_window = window_id;
|
||||||
@@ -196,7 +196,7 @@ set_currently_hovered_window(id_type window_id, int modifiers) {
|
|||||||
debug("Sent mouse leave event to window: %llu currently hovering: %llu\n", left_window->id, window_id);
|
debug("Sent mouse leave event to window: %llu currently hovering: %llu\n", left_window->id, window_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (window_id && OPT(focus_follows_mouse).on_cross && global_state.callback_os_window && global_state.callback_os_window->num_tabs) {
|
if (focus_follows && window_id && OPT(focus_follows_mouse).on_cross && global_state.callback_os_window && global_state.callback_os_window->num_tabs) {
|
||||||
Tab *t = global_state.callback_os_window->tabs + global_state.callback_os_window->active_tab;
|
Tab *t = global_state.callback_os_window->tabs + global_state.callback_os_window->active_tab;
|
||||||
for (unsigned i = 0; i < t->num_windows; i++) {
|
for (unsigned i = 0; i < t->num_windows; i++) {
|
||||||
if (t->windows[i].id == window_id) {
|
if (t->windows[i].id == window_id) {
|
||||||
@@ -934,7 +934,7 @@ currently_pressed_button(void) {
|
|||||||
HANDLER(handle_event) {
|
HANDLER(handle_event) {
|
||||||
modifiers &= ~GLFW_LOCK_MASK;
|
modifiers &= ~GLFW_LOCK_MASK;
|
||||||
set_mouse_cursor_for_screen(w->render_data.screen);
|
set_mouse_cursor_for_screen(w->render_data.screen);
|
||||||
set_currently_hovered_window(w->id, modifiers);
|
set_currently_hovered_window(w->id, modifiers, true);
|
||||||
if (button == -1) {
|
if (button == -1) {
|
||||||
button = currently_pressed_button();
|
button = currently_pressed_button();
|
||||||
handle_move_event(w, button, modifiers, window_idx);
|
handle_move_event(w, button, modifiers, window_idx);
|
||||||
@@ -955,7 +955,7 @@ handle_window_title_bar_mouse(Window *w, int button, int modifiers, int action)
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
handle_tab_bar_mouse(int button, int modifiers, int action) {
|
handle_tab_bar_mouse(int button, int modifiers, int action) {
|
||||||
set_currently_hovered_window(0, modifiers);
|
set_currently_hovered_window(0, modifiers, false);
|
||||||
OSWindow *w = global_state.callback_os_window;
|
OSWindow *w = global_state.callback_os_window;
|
||||||
// dont report motion events, as they are expensive and useless
|
// dont report motion events, as they are expensive and useless
|
||||||
if (w && (button > -1 || global_state.tab_being_dragged.id)) {
|
if (w && (button > -1 || global_state.tab_being_dragged.id)) {
|
||||||
@@ -1148,11 +1148,11 @@ update_mouse_pointer_shape(void) {
|
|||||||
void
|
void
|
||||||
leave_event(int modifiers) {
|
leave_event(int modifiers) {
|
||||||
if (global_state.redirect_mouse_handling || global_state.active_drag_in_window || global_state.tracked_drag_in_window) return;
|
if (global_state.redirect_mouse_handling || global_state.active_drag_in_window || global_state.tracked_drag_in_window) return;
|
||||||
set_currently_hovered_window(0, modifiers);
|
set_currently_hovered_window(0, modifiers, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
enter_event(int modifiers) {
|
enter_event(int modifiers, bool cursor_moved) {
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
// On cocoa there is no way to configure the window manager to
|
// On cocoa there is no way to configure the window manager to
|
||||||
// focus windows on mouse enter, so we do it ourselves
|
// focus windows on mouse enter, so we do it ourselves
|
||||||
@@ -1170,7 +1170,7 @@ enter_event(int modifiers) {
|
|||||||
if (global_state.redirect_mouse_handling || global_state.active_drag_in_window || global_state.tracked_drag_in_window) return;
|
if (global_state.redirect_mouse_handling || global_state.active_drag_in_window || global_state.tracked_drag_in_window) return;
|
||||||
MouseRegion r = mouse_region(false, false);
|
MouseRegion r = mouse_region(false, false);
|
||||||
Window *w = r.window;
|
Window *w = r.window;
|
||||||
set_currently_hovered_window(w ? w->id : 0, modifiers);
|
set_currently_hovered_window(w ? w->id : 0, modifiers, cursor_moved);
|
||||||
if (!w || r.in_tab_bar || r.in_title_bar) return;
|
if (!w || r.in_tab_bar || r.in_title_bar) return;
|
||||||
|
|
||||||
if (handle_scrollbar_mouse(w, -1, MOVE, modifiers)) return;
|
if (handle_scrollbar_mouse(w, -1, MOVE, modifiers)) return;
|
||||||
@@ -1359,7 +1359,7 @@ mouse_event(const int button, int modifiers, int action) {
|
|||||||
}
|
}
|
||||||
MouseRegion r = mouse_region(true, true);
|
MouseRegion r = mouse_region(true, true);
|
||||||
w = r.window; window_idx = r.window_idx;
|
w = r.window; window_idx = r.window_idx;
|
||||||
set_currently_hovered_window(w && !r.window_border && !r.in_title_bar ? w->id : 0, modifiers);
|
set_currently_hovered_window(w && !r.window_border && !r.in_title_bar ? w->id : 0, modifiers, true);
|
||||||
|
|
||||||
if (r.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;
|
mouse_cursor_shape = POINTER_POINTER;
|
||||||
|
|||||||
Reference in New Issue
Block a user