Make XI2 scroll offset type detection more robust

Since we have to use heuristics, lets at least collect stats for a few
events before deciding.
This commit is contained in:
Kovid Goyal 2026-03-12 21:44:20 +05:30
parent 99639f1373
commit c0b549fee8
No known key found for this signature in database
GPG key ID: 06BC317B515ACE7C
3 changed files with 37 additions and 15 deletions

6
glfw/x11_init.c vendored
View file

@ -216,8 +216,12 @@ read_xi_scroll_devices(void) {
if (_glfw.x11.xi.num_scroll_devices >= arraysz(_glfw.x11.xi.scroll_devices)) continue;
d = &_glfw.x11.xi.scroll_devices[_glfw.x11.xi.num_scroll_devices++];
*d = (XIScrollDevice){
.is_highres=is_highres, .is_finger_based=is_finger_based, .deviceid=device->deviceid, .sourceid=scroll->sourceid,
.is_finger_based=is_finger_based, .deviceid=device->deviceid, .sourceid=scroll->sourceid,
};
if (is_highres) {
d->type_detected = true;
d->offset_type = GLFW_SCROLL_OFFEST_HIGHRES;
}
memcpy(d->name, device->name, MIN(sizeof(d->name)-1, strlen(device->name)));
}
if (d->num_valuators >= arraysz(d->valuators)) continue;

4
glfw/x11_platform.h vendored
View file

@ -239,12 +239,14 @@ typedef struct XIScrollValuator {
} XIScrollValuator;
typedef struct XIScrollDevice {
bool is_highres;
bool is_finger_based;
bool type_detected;
int deviceid, sourceid;
XIScrollValuator valuators[8];
unsigned num_valuators;
char name[32];
unsigned num_events;
GLFWOffsetType offset_type;
} XIScrollDevice;
typedef struct XdndSelectionRequest {

42
glfw/x11_window.c vendored
View file

@ -1415,6 +1415,11 @@ handle_mouse_move_event(_GLFWwindow *window, const int x, const int y) {
window->x11.lastCursorPosY = y;
}
static bool
number_has_fractional_part(double x) {
return fabs(x - round(x)) >= 1e-6;
}
static void
handle_xi_motion_event(_GLFWwindow *window, XIDeviceEvent *de) {
XIScrollDevice *d = NULL;
@ -1442,24 +1447,35 @@ handle_xi_motion_event(_GLFWwindow *window, XIDeviceEvent *de) {
scroll_valuator_found = true;
double delta = value - v->value;
v->value = value;
if (v->is_vertical) delta *= -1;
delta *= -1;
double *off = v->is_vertical ? &yOffset : &xOffset;
*off = delta;
if (!d->is_highres) {
if (v->increment == 120.) type = GLFW_SCROLL_OFFEST_V120;
else {
// XInput2 has no reliable way to distinguish high res scroll devices so we
// assume that if fractional values are seen it must be high res. Sigh, Linux, the
// land of truly wondrous wonders. See https://github.com/kovidgoyal/kitty/issues/9649
double int_part; bool delta_is_fractional = modf(delta, &int_part) != 0.;
if (delta_is_fractional) d->is_highres = true;
else {
type = GLFW_SCROLL_OFFSET_LINES;
if (v->increment != 0) *off /= v->increment;
d->num_events++;
if (!d->type_detected) {
if (v->increment == 120.) {
d->type_detected = true;
d->offset_type = GLFW_SCROLL_OFFEST_V120;
} else {
bool delta_is_fractional = number_has_fractional_part(delta);
if (delta_is_fractional) {
if (fabs(delta * 120 - round(delta * 120)) < 0.01) {
d->type_detected = d->num_events > 2;
d->offset_type = GLFW_SCROLL_OFFEST_V120;
} else {
d->type_detected = true;
d->offset_type = GLFW_SCROLL_OFFEST_HIGHRES;
}
} else {
d->type_detected = d->num_events > 2;
d->offset_type = GLFW_SCROLL_OFFSET_LINES;
}
}
}
if (d->offset_type == GLFW_SCROLL_OFFSET_LINES) {
if (v->increment != 0) *off /= v->increment;
}
}
type = d->offset_type;
if (xOffset != 0 || yOffset != 0) {
// Get keyboard modifiers
int mods = translateState(de->mods.effective);
@ -1473,7 +1489,7 @@ handle_xi_motion_event(_GLFWwindow *window, XIDeviceEvent *de) {
};
// For high-resolution, finger-based scrolling, use timer-based momentum scrolling
if (d->is_highres && d->is_finger_based && type == GLFW_SCROLL_OFFEST_HIGHRES) {
if (d->is_finger_based && type == GLFW_SCROLL_OFFEST_HIGHRES) {
// Reset the timer on each scroll event
x11_cancel_momentum_scroll_timer();