From a12dc6eba3ba870bd0f242b42403601eedf7aa70 Mon Sep 17 00:00:00 2001 From: Yinon Burgansky Date: Mon, 10 Nov 2025 14:57:24 +0200 Subject: [PATCH] filter: differentiate scroll wheel from button scrolling to fix wheel speed Commit 94b783645618 ("filter: support accelerating high-resolution scroll wheel events") introduced a regression where high-res scroll wheel events were incorrectly normalized by DPI. Mice with non-default DPI (e.g., Logitech G502 at 2400 DPI) had their scroll wheel speed reduced by the DPI ratio (1000/2400), resulting in 2-3x slower scrolling. The "noop" filter functions were actually performing DPI normalization or applying a constant acceleration factor, which is appropriate for button scrolling but incorrect for scroll wheels that have their own units. Add a filter_scroll_type enum (CONTINOUS, WHEEL, FINGER to match the public events) passed through the filter_scroll interface. Update all filter implementations to skip acceleration and normalization for wheel events while maintaining existing behavior for button scrolling and touchpad scrolling. The custom acceleration profile continues to accelerate high-res wheel events as designed. Fixes: 94b783645618 ("filter: support accelerating high-resolution scroll wheel events") Closes: #1212 Signed-off-by: Yinon Burgansky Part-of: --- src/evdev-fallback.c | 9 ++++++--- src/evdev-mt-touchpad.c | 6 +++++- src/filter-custom.c | 3 ++- src/filter-flat.c | 33 ++++++++++++++++++++++++++------ src/filter-low-dpi.c | 22 +++++++++++++++------ src/filter-mouse.c | 33 ++++++++++++++++++++++++++------ src/filter-private.h | 3 ++- src/filter-touchpad-flat.c | 37 ++++++++++++++++++++++++++++++------ src/filter-touchpad-x230.c | 23 +++++++++++++++++++++- src/filter-touchpad.c | 23 +++++++++++++++++++++- src/filter-trackpoint-flat.c | 33 ++++++++++++++++++++++++++------ src/filter-trackpoint.c | 36 +++++++++++++++++++++++++++++------ src/filter.c | 9 +++++++-- src/filter.h | 10 +++++++++- 14 files changed, 233 insertions(+), 47 deletions(-) diff --git a/src/evdev-fallback.c b/src/evdev-fallback.c index a5614348..54a4c479 100644 --- a/src/evdev-fallback.c +++ b/src/evdev-fallback.c @@ -117,7 +117,8 @@ post_button_scroll(struct evdev_device *device, filter_dispatch_scroll(device->pointer.filter, &raw, device, - time); + time, + FILTER_SCROLL_TYPE_CONTINUOUS); evdev_post_scroll(device, time, LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS, @@ -221,7 +222,8 @@ fallback_flush_wheels(struct fallback_dispatch *dispatch, filter_dispatch_scroll(device->pointer.filter, &raw, device, - time); + time, + FILTER_SCROLL_TYPE_WHEEL); evdev_post_scroll(device, time, LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS, @@ -245,7 +247,8 @@ fallback_flush_wheels(struct fallback_dispatch *dispatch, ? filter_dispatch_scroll(device->pointer.filter, &v120_unaccelerated, device, - time) + time, + FILTER_SCROLL_TYPE_WHEEL) : (const struct normalized_coords){ .x = v120_unaccelerated.x, .y = v120_unaccelerated.y diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 1ddfe409..bc03d6e5 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -107,7 +107,11 @@ tp_filter_scroll(struct tp_dispatch *tp, /* Convert to device units with x/y in the same resolution */ raw = tp_scale_to_xaxis(tp, *unaccelerated); - return filter_dispatch_scroll(tp->device->pointer.filter, &raw, tp, time); + return filter_dispatch_scroll(tp->device->pointer.filter, + &raw, + tp, + time, + FILTER_SCROLL_TYPE_FINGER); } static inline void diff --git a/src/filter-custom.c b/src/filter-custom.c index 7b0e2343..96ebae29 100644 --- a/src/filter-custom.c +++ b/src/filter-custom.c @@ -400,7 +400,8 @@ static struct normalized_coords custom_accelerator_filter_scroll(struct motion_filter *filter, const struct device_float_coords *unaccelerated, void *data, - uint64_t time) + uint64_t time, + enum filter_scroll_type type) { return custom_accelerator_filter(LIBINPUT_ACCEL_TYPE_SCROLL, filter, diff --git a/src/filter-flat.c b/src/filter-flat.c index 7c34a20c..7db9cd23 100644 --- a/src/filter-flat.c +++ b/src/filter-flat.c @@ -62,10 +62,10 @@ accelerator_filter_flat(struct motion_filter *filter, } static struct normalized_coords -accelerator_filter_noop_flat(struct motion_filter *filter, - const struct device_float_coords *unaccelerated, - void *data, - uint64_t time) +accelerator_filter_constant_flat(struct motion_filter *filter, + const struct device_float_coords *unaccelerated, + void *data, + uint64_t time) { /* We map the unaccelerated flat filter to have the same behavior as * the "accelerated" flat filter. @@ -81,6 +81,27 @@ accelerator_filter_noop_flat(struct motion_filter *filter, return accelerator_filter_flat(filter, unaccelerated, data, time); } +static struct normalized_coords +accelerator_filter_scroll_flat(struct motion_filter *filter, + const struct device_float_coords *unaccelerated, + void *data, + uint64_t time, + enum filter_scroll_type type) +{ + /* Scroll wheels were not historically accelerated and have different + * units than button scrolling. Maintain the status quo and do not + * accelerate wheel events. + */ + if (type == FILTER_SCROLL_TYPE_WHEEL) { + return (struct normalized_coords){ + .x = unaccelerated->x, + .y = unaccelerated->y, + }; + } + + return accelerator_filter_constant_flat(filter, unaccelerated, data, time); +} + static bool accelerator_set_speed_flat(struct motion_filter *filter, double speed_adjustment) { @@ -112,8 +133,8 @@ accelerator_destroy_flat(struct motion_filter *filter) static const struct motion_filter_interface accelerator_interface_flat = { .type = LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT, .filter = accelerator_filter_flat, - .filter_constant = accelerator_filter_noop_flat, - .filter_scroll = accelerator_filter_noop_flat, + .filter_constant = accelerator_filter_constant_flat, + .filter_scroll = accelerator_filter_scroll_flat, .restart = NULL, .destroy = accelerator_destroy_flat, .set_speed = accelerator_set_speed_flat, diff --git a/src/filter-low-dpi.c b/src/filter-low-dpi.c index 674b5164..2a08a202 100644 --- a/src/filter-low-dpi.c +++ b/src/filter-low-dpi.c @@ -145,10 +145,10 @@ accelerator_filter_low_dpi(struct motion_filter *filter, } static struct normalized_coords -accelerator_filter_noop(struct motion_filter *filter, - const struct device_float_coords *unaccelerated, - void *data, - uint64_t time) +accelerator_filter_constant(struct motion_filter *filter, + const struct device_float_coords *unaccelerated, + void *data, + uint64_t time) { const struct normalized_coords normalized = { .x = unaccelerated->x, @@ -157,6 +157,16 @@ accelerator_filter_noop(struct motion_filter *filter, return normalized; } +static struct normalized_coords +accelerator_filter_scroll(struct motion_filter *filter, + const struct device_float_coords *unaccelerated, + void *data, + uint64_t time, + enum filter_scroll_type type) +{ + return accelerator_filter_constant(filter, unaccelerated, data, time); +} + static void accelerator_restart(struct motion_filter *filter, void *data, uint64_t time) { @@ -205,8 +215,8 @@ accelerator_set_speed(struct motion_filter *filter, double speed_adjustment) static const struct motion_filter_interface accelerator_interface_low_dpi = { .type = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE, .filter = accelerator_filter_low_dpi, - .filter_constant = accelerator_filter_noop, - .filter_scroll = accelerator_filter_noop, + .filter_constant = accelerator_filter_constant, + .filter_scroll = accelerator_filter_scroll, .restart = accelerator_restart, .destroy = accelerator_destroy, .set_speed = accelerator_set_speed, diff --git a/src/filter-mouse.c b/src/filter-mouse.c index 38058744..4292635f 100644 --- a/src/filter-mouse.c +++ b/src/filter-mouse.c @@ -133,16 +133,37 @@ accelerator_filter_linear(struct motion_filter *filter, * motion */ static struct normalized_coords -accelerator_filter_noop(struct motion_filter *filter, - const struct device_float_coords *unaccelerated, - void *data, - uint64_t time) +accelerator_filter_constant(struct motion_filter *filter, + const struct device_float_coords *unaccelerated, + void *data, + uint64_t time) { struct pointer_accelerator *accel = (struct pointer_accelerator *)filter; return normalize_for_dpi(unaccelerated, accel->dpi); } +static struct normalized_coords +accelerator_filter_scroll(struct motion_filter *filter, + const struct device_float_coords *unaccelerated, + void *data, + uint64_t time, + enum filter_scroll_type type) +{ + /* Scroll wheels were not historically accelerated and have different + * units than button scrolling. Maintain the status quo and do not + * accelerate wheel events. + */ + if (type == FILTER_SCROLL_TYPE_WHEEL) { + return (struct normalized_coords){ + .x = unaccelerated->x, + .y = unaccelerated->y, + }; + } + + return accelerator_filter_constant(filter, unaccelerated, data, time); +} + static void accelerator_restart(struct motion_filter *filter, void *data, uint64_t time) { @@ -260,8 +281,8 @@ pointer_accel_profile_linear(struct motion_filter *filter, static const struct motion_filter_interface accelerator_interface = { .type = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE, .filter = accelerator_filter_linear, - .filter_constant = accelerator_filter_noop, - .filter_scroll = accelerator_filter_noop, + .filter_constant = accelerator_filter_constant, + .filter_scroll = accelerator_filter_scroll, .restart = accelerator_restart, .destroy = accelerator_destroy, .set_speed = accelerator_set_speed, diff --git a/src/filter-private.h b/src/filter-private.h index f20c53c8..844f499c 100644 --- a/src/filter-private.h +++ b/src/filter-private.h @@ -44,7 +44,8 @@ struct motion_filter_interface { struct motion_filter *filter, const struct device_float_coords *unaccelerated, void *data, - uint64_t time); + uint64_t time, + enum filter_scroll_type type); void (*restart)(struct motion_filter *filter, void *data, uint64_t time); void (*destroy)(struct motion_filter *filter); bool (*set_speed)(struct motion_filter *filter, double speed_adjustment); diff --git a/src/filter-touchpad-flat.c b/src/filter-touchpad-flat.c index 519f8a85..da4d3933 100644 --- a/src/filter-touchpad-flat.c +++ b/src/filter-touchpad-flat.c @@ -65,10 +65,11 @@ accelerator_filter_touchpad_flat(struct motion_filter *filter, } static struct normalized_coords -accelerator_filter_noop_touchpad_flat(struct motion_filter *filter, - const struct device_float_coords *unaccelerated, - void *data, - uint64_t time) +accelerator_filter_constant_touchpad_flat( + struct motion_filter *filter, + const struct device_float_coords *unaccelerated, + void *data, + uint64_t time) { /* We map the unaccelerated flat filter to have the same behavior as * the "accelerated" flat filter. @@ -84,6 +85,30 @@ accelerator_filter_noop_touchpad_flat(struct motion_filter *filter, return accelerator_filter_touchpad_flat(filter, unaccelerated, data, time); } +static struct normalized_coords +accelerator_filter_scroll_touchpad_flat(struct motion_filter *filter, + const struct device_float_coords *unaccelerated, + void *data, + uint64_t time, + enum filter_scroll_type type) +{ + /* Scroll wheels were not historically accelerated and have different + * units than button scrolling. Maintain the status quo and do not + * accelerate wheel events. + */ + if (type == FILTER_SCROLL_TYPE_WHEEL) { + return (struct normalized_coords){ + .x = unaccelerated->x, + .y = unaccelerated->y, + }; + } + + return accelerator_filter_constant_touchpad_flat(filter, + unaccelerated, + data, + time); +} + static bool accelerator_set_speed_touchpad_flat(struct motion_filter *filter, double speed_adjustment) @@ -111,8 +136,8 @@ accelerator_destroy_touchpad_flat(struct motion_filter *filter) static const struct motion_filter_interface accelerator_interface_touchpad_flat = { .type = LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT, .filter = accelerator_filter_touchpad_flat, - .filter_constant = accelerator_filter_noop_touchpad_flat, - .filter_scroll = accelerator_filter_noop_touchpad_flat, + .filter_constant = accelerator_filter_constant_touchpad_flat, + .filter_scroll = accelerator_filter_scroll_touchpad_flat, .restart = NULL, .destroy = accelerator_destroy_touchpad_flat, .set_speed = accelerator_set_speed_touchpad_flat, diff --git a/src/filter-touchpad-x230.c b/src/filter-touchpad-x230.c index 7ffd698b..95c6657d 100644 --- a/src/filter-touchpad-x230.c +++ b/src/filter-touchpad-x230.c @@ -185,6 +185,27 @@ accelerator_filter_constant_x230(struct motion_filter *filter, return normalized; } +static struct normalized_coords +accelerator_filter_scroll_x230(struct motion_filter *filter, + const struct device_float_coords *unaccelerated, + void *data, + uint64_t time, + enum filter_scroll_type type) +{ + /* Scroll wheels were not historically accelerated and have different + * units than button scrolling. Maintain the status quo and do not + * accelerate wheel events. + */ + if (type == FILTER_SCROLL_TYPE_WHEEL) { + return (struct normalized_coords){ + .x = unaccelerated->x, + .y = unaccelerated->y, + }; + } + + return accelerator_filter_constant_x230(filter, unaccelerated, data, time); +} + static void accelerator_restart_x230(struct motion_filter *filter, void *data, uint64_t time) { @@ -285,7 +306,7 @@ static const struct motion_filter_interface accelerator_interface_x230 = { .type = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE, .filter = accelerator_filter_x230, .filter_constant = accelerator_filter_constant_x230, - .filter_scroll = accelerator_filter_constant_x230, + .filter_scroll = accelerator_filter_scroll_x230, .restart = accelerator_restart_x230, .destroy = accelerator_destroy_x230, .set_speed = accelerator_set_speed_x230, diff --git a/src/filter-touchpad.c b/src/filter-touchpad.c index 92d69ba4..6630ebf4 100644 --- a/src/filter-touchpad.c +++ b/src/filter-touchpad.c @@ -162,6 +162,27 @@ touchpad_constant_filter(struct motion_filter *filter, return normalized; } +static struct normalized_coords +touchpad_scroll_filter(struct motion_filter *filter, + const struct device_float_coords *unaccelerated, + void *data, + uint64_t time, + enum filter_scroll_type type) +{ + /* Scroll wheels were not historically accelerated and have different + * units than button scrolling. Maintain the status quo and do not + * accelerate wheel events. + */ + if (type == FILTER_SCROLL_TYPE_WHEEL) { + return (struct normalized_coords){ + .x = unaccelerated->x, + .y = unaccelerated->y, + }; + } + + return touchpad_constant_filter(filter, unaccelerated, data, time); +} + static void touchpad_accelerator_restart(struct motion_filter *filter, void *data, uint64_t time) { @@ -272,7 +293,7 @@ static const struct motion_filter_interface accelerator_interface_touchpad = { .type = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE, .filter = accelerator_filter_touchpad, .filter_constant = touchpad_constant_filter, - .filter_scroll = touchpad_constant_filter, + .filter_scroll = touchpad_scroll_filter, .restart = touchpad_accelerator_restart, .destroy = touchpad_accelerator_destroy, .set_speed = touchpad_accelerator_set_speed, diff --git a/src/filter-trackpoint-flat.c b/src/filter-trackpoint-flat.c index b569803d..bacb4f64 100644 --- a/src/filter-trackpoint-flat.c +++ b/src/filter-trackpoint-flat.c @@ -60,10 +60,10 @@ trackpoint_flat_filter(struct motion_filter *filter, } static struct normalized_coords -trackpoint_flat_filter_noop(struct motion_filter *filter, - const struct device_float_coords *unaccelerated, - void *data, - uint64_t time) +trackpoint_flat_filter_constant(struct motion_filter *filter, + const struct device_float_coords *unaccelerated, + void *data, + uint64_t time) { /* We map the unaccelerated flat filter to have the same behavior as * the "accelerated" flat filter. @@ -79,6 +79,27 @@ trackpoint_flat_filter_noop(struct motion_filter *filter, return trackpoint_flat_filter(filter, unaccelerated, data, time); } +static struct normalized_coords +trackpoint_flat_filter_scroll(struct motion_filter *filter, + const struct device_float_coords *unaccelerated, + void *data, + uint64_t time, + enum filter_scroll_type type) +{ + /* Scroll wheels were not historically accelerated and have different + * units than button scrolling. Maintain the status quo and do not + * accelerate wheel events. + */ + if (type == FILTER_SCROLL_TYPE_WHEEL) { + return (struct normalized_coords){ + .x = unaccelerated->x, + .y = unaccelerated->y, + }; + } + + return trackpoint_flat_filter_constant(filter, unaccelerated, data, time); +} + /* Maps the [-1, 1] speed setting into a constant acceleration * range. This isn't a linear scale, we keep 0 as the 'optimized' * mid-point and scale down to 0 for setting -1 and up to 5 for @@ -128,8 +149,8 @@ trackpoint_flat_destroy(struct motion_filter *filter) static struct motion_filter_interface accelerator_interface_flat = { .type = LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT, .filter = trackpoint_flat_filter, - .filter_constant = trackpoint_flat_filter_noop, - .filter_scroll = trackpoint_flat_filter_noop, + .filter_constant = trackpoint_flat_filter_constant, + .filter_scroll = trackpoint_flat_filter_scroll, .restart = NULL, .destroy = trackpoint_flat_destroy, .set_speed = trackpoint_flat_set_speed, diff --git a/src/filter-trackpoint.c b/src/filter-trackpoint.c index 2237c652..b3fc698b 100644 --- a/src/filter-trackpoint.c +++ b/src/filter-trackpoint.c @@ -97,10 +97,10 @@ trackpoint_accelerator_filter(struct motion_filter *filter, } static struct normalized_coords -trackpoint_accelerator_filter_noop(struct motion_filter *filter, - const struct device_float_coords *unaccelerated, - void *data, - uint64_t time) +trackpoint_accelerator_filter_constant(struct motion_filter *filter, + const struct device_float_coords *unaccelerated, + void *data, + uint64_t time) { struct trackpoint_accelerator *accel_filter = (struct trackpoint_accelerator *)filter; @@ -112,6 +112,30 @@ trackpoint_accelerator_filter_noop(struct motion_filter *filter, return coords; } +static struct normalized_coords +trackpoint_accelerator_filter_scroll(struct motion_filter *filter, + const struct device_float_coords *unaccelerated, + void *data, + uint64_t time, + enum filter_scroll_type type) +{ + /* Scroll wheels were not historically accelerated and have different + * units than button scrolling. Maintain the status quo and do not + * accelerate wheel events. + */ + if (type == FILTER_SCROLL_TYPE_WHEEL) { + return (struct normalized_coords){ + .x = unaccelerated->x, + .y = unaccelerated->y, + }; + } + + return trackpoint_accelerator_filter_constant(filter, + unaccelerated, + data, + time); +} + /* Maps the [-1, 1] speed setting into a constant acceleration * range. This isn't a linear scale, we keep 0 as the 'optimized' * mid-point and scale down to 0 for setting -1 and up to 5 for @@ -170,8 +194,8 @@ trackpoint_accelerator_destroy(struct motion_filter *filter) static const struct motion_filter_interface accelerator_interface_trackpoint = { .type = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE, .filter = trackpoint_accelerator_filter, - .filter_constant = trackpoint_accelerator_filter_noop, - .filter_scroll = trackpoint_accelerator_filter_noop, + .filter_constant = trackpoint_accelerator_filter_constant, + .filter_scroll = trackpoint_accelerator_filter_scroll, .restart = trackpoint_accelerator_restart, .destroy = trackpoint_accelerator_destroy, .set_speed = trackpoint_accelerator_set_speed, diff --git a/src/filter.c b/src/filter.c index 977a206c..1f1bebc3 100644 --- a/src/filter.c +++ b/src/filter.c @@ -59,9 +59,14 @@ struct normalized_coords filter_dispatch_scroll(struct motion_filter *filter, const struct device_float_coords *unaccelerated, void *data, - uint64_t time) + uint64_t time, + enum filter_scroll_type type) { - return filter->interface->filter_scroll(filter, unaccelerated, data, time); + return filter->interface->filter_scroll(filter, + unaccelerated, + data, + time, + type); } void diff --git a/src/filter.h b/src/filter.h index 10ea6b28..c46846bc 100644 --- a/src/filter.h +++ b/src/filter.h @@ -34,6 +34,12 @@ struct motion_filter; +enum filter_scroll_type { + FILTER_SCROLL_TYPE_CONTINUOUS, + FILTER_SCROLL_TYPE_WHEEL, + FILTER_SCROLL_TYPE_FINGER, +}; + /** * Accelerate the given coordinates. * Takes a set of unaccelerated deltas and accelerates them based on the @@ -98,6 +104,7 @@ filter_dispatch_constant(struct motion_filter *filter, * originally provided resolution. * @param data Custom data * @param time The time of the delta + * @param type The type of scroll event * * @see filter_dispatch */ @@ -105,7 +112,8 @@ struct normalized_coords filter_dispatch_scroll(struct motion_filter *filter, const struct device_float_coords *unaccelerated, void *data, - uint64_t time); + uint64_t time, + enum filter_scroll_type type); void filter_restart(struct motion_filter *filter, void *data, uint64_t time);