From 36be8be951426c526fbd2b5b6095967c62b337cf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 19 Mar 2026 09:49:15 +0000 Subject: [PATCH] Fix massive scroll on focus return by resetting X11 scroll valuators on focus loss When kitty loses focus and the user scrolls in another application, X11 XI scroll valuators accumulate position values. When the user returns to kitty and scrolls, delta (value - v->value) uses the stale pre-focus-loss value, causing a massive unexpected scroll jump. Fix: reset scroll valuators (mark them uninitialized) on FocusOut so the first scroll event after focus is regained sets the baseline without firing a scroll event. Fixes #9703 Fixes #9707 --- glfw/x11_init.c | 2 +- glfw/x11_platform.h | 2 +- glfw/x11_window.c | 15 +++++++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/glfw/x11_init.c b/glfw/x11_init.c index 5bea3c3fc..fd09fec56 100644 --- a/glfw/x11_init.c +++ b/glfw/x11_init.c @@ -247,7 +247,7 @@ read_xi_scroll_devices(void) { } if (!v) continue; v->value = vi->value; v->mode = vi->mode; v->resolution = vi->resolution; - v->min = vi->min; v->max = vi->max; + v->min = vi->min; v->max = vi->max; v->initialized = true; } } XIFreeDeviceInfo(devices); diff --git a/glfw/x11_platform.h b/glfw/x11_platform.h index 33f32dfe8..fe78d17c8 100644 --- a/glfw/x11_platform.h +++ b/glfw/x11_platform.h @@ -235,7 +235,7 @@ typedef struct AtomArray { } AtomArray; typedef struct XIScrollValuator { - double increment, value, min, max; int number, resolution, mode; bool is_vertical; + double increment, value, min, max; int number, resolution, mode; bool is_vertical, initialized; } XIScrollValuator; typedef struct XIScrollDevice { diff --git a/glfw/x11_window.c b/glfw/x11_window.c index e57aefe66..b83a891ac 100644 --- a/glfw/x11_window.c +++ b/glfw/x11_window.c @@ -553,6 +553,15 @@ static void enableSmoothScrolling(_GLFWwindow* window) XISelectEvents(_glfw.x11.display, window->x11.handle, &em, 1); } +static void +resetScrollValuators(void) { + for (unsigned i = 0; i < _glfw.x11.xi.num_scroll_devices; i++) { + XIScrollDevice *d = &_glfw.x11.xi.scroll_devices[i]; + for (unsigned k = 0; k < d->num_valuators; k++) + d->valuators[k].initialized = false; + } +} + // Apply disabled cursor mode to a focused window // static void disableCursor(_GLFWwindow* window) @@ -1445,6 +1454,11 @@ handle_xi_motion_event(_GLFWwindow *window, XIDeviceEvent *de) { } if (!v) continue; scroll_valuator_found = true; + if (!v->initialized) { + v->initialized = true; + v->value = value; + continue; + } double delta = value - v->value; v->value = value; delta *= -1; @@ -2344,6 +2358,7 @@ static void processEvent(XEvent *event) if (window->monitor && window->autoIconify) _glfwPlatformIconifyWindow(window); + resetScrollValuators(); _glfwInputWindowFocus(window, false); return; }