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
This commit is contained in:
copilot-swe-agent[bot] 2026-03-19 09:49:15 +00:00 committed by Kovid Goyal
parent 7c8e797985
commit 36be8be951
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
3 changed files with 17 additions and 2 deletions

2
glfw/x11_init.c vendored
View file

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

2
glfw/x11_platform.h vendored
View file

@ -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 {

15
glfw/x11_window.c vendored
View file

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