diff --git a/glfw/x11_init.c b/glfw/x11_init.c index 0b518af51..5bea3c3fc 100644 --- a/glfw/x11_init.c +++ b/glfw/x11_init.c @@ -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; diff --git a/glfw/x11_platform.h b/glfw/x11_platform.h index a8bcdaf2f..33f32dfe8 100644 --- a/glfw/x11_platform.h +++ b/glfw/x11_platform.h @@ -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 { diff --git a/glfw/x11_window.c b/glfw/x11_window.c index f46365a75..7650fb90b 100644 --- a/glfw/x11_window.c +++ b/glfw/x11_window.c @@ -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();