mirror of
https://gitlab.freedesktop.org/libinput/libinput.git
synced 2025-12-20 18:30:06 +01:00
plugin/wheel: tighten the wheel debouncing code
The previous approach had a fixed threshold of 60 (half a detent) below which scroll events were ignored. Reduce this threshold to have a threshold of one device-specific delta. That threshold adjusts over time to the device's individual minimum delta so after a few scroll event it should settle on the lowest value possible. The result is that fine-grained scrolling is possible on those devices and only the very first scroll event is held back/swallowed, two events in the same direction release scrolling. Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1258>
This commit is contained in:
parent
b347cc8691
commit
ca6b82841c
2 changed files with 81 additions and 28 deletions
|
|
@ -36,7 +36,7 @@
|
||||||
#include "libinput-plugin.h"
|
#include "libinput-plugin.h"
|
||||||
#include "libinput-util.h"
|
#include "libinput-util.h"
|
||||||
|
|
||||||
#define ACC_V120_THRESHOLD 60
|
#define ACC_V120_THRESHOLD 59
|
||||||
#define WHEEL_SCROLL_TIMEOUT ms2us(500)
|
#define WHEEL_SCROLL_TIMEOUT ms2us(500)
|
||||||
|
|
||||||
enum wheel_state {
|
enum wheel_state {
|
||||||
|
|
@ -72,6 +72,7 @@ struct plugin_device {
|
||||||
struct libinput_plugin_timer *scroll_timer;
|
struct libinput_plugin_timer *scroll_timer;
|
||||||
enum wheel_direction dir;
|
enum wheel_direction dir;
|
||||||
bool ignore_small_hi_res_movements;
|
bool ignore_small_hi_res_movements;
|
||||||
|
int min_movement;
|
||||||
|
|
||||||
struct ratelimit hires_warning_limit;
|
struct ratelimit hires_warning_limit;
|
||||||
};
|
};
|
||||||
|
|
@ -293,8 +294,8 @@ wheel_handle_state_accumulating_scroll(struct plugin_device *pd,
|
||||||
{
|
{
|
||||||
wheel_remove_scroll_events(frame);
|
wheel_remove_scroll_events(frame);
|
||||||
|
|
||||||
if (abs(pd->hi_res.x) >= ACC_V120_THRESHOLD ||
|
if (abs(pd->hi_res.x) > pd->min_movement ||
|
||||||
abs(pd->hi_res.y) >= ACC_V120_THRESHOLD) {
|
abs(pd->hi_res.y) > pd->min_movement) {
|
||||||
wheel_handle_event(pd, WHEEL_EVENT_SCROLL_ACCUMULATED, time);
|
wheel_handle_event(pd, WHEEL_EVENT_SCROLL_ACCUMULATED, time);
|
||||||
wheel_queue_scroll_events(pd, frame);
|
wheel_queue_scroll_events(pd, frame);
|
||||||
}
|
}
|
||||||
|
|
@ -348,12 +349,14 @@ wheel_process_relative(struct plugin_device *pd, struct evdev_event *e, uint64_t
|
||||||
case EVDEV_REL_WHEEL_HI_RES:
|
case EVDEV_REL_WHEEL_HI_RES:
|
||||||
pd->hi_res.y += e->value;
|
pd->hi_res.y += e->value;
|
||||||
pd->hi_res_event_received = true;
|
pd->hi_res_event_received = true;
|
||||||
|
pd->min_movement = min(pd->min_movement, abs(e->value));
|
||||||
wheel_handle_direction_change(pd, e, time);
|
wheel_handle_direction_change(pd, e, time);
|
||||||
wheel_handle_event(pd, WHEEL_EVENT_SCROLL, time);
|
wheel_handle_event(pd, WHEEL_EVENT_SCROLL, time);
|
||||||
break;
|
break;
|
||||||
case EVDEV_REL_HWHEEL_HI_RES:
|
case EVDEV_REL_HWHEEL_HI_RES:
|
||||||
pd->hi_res.x += e->value;
|
pd->hi_res.x += e->value;
|
||||||
pd->hi_res_event_received = true;
|
pd->hi_res_event_received = true;
|
||||||
|
pd->min_movement = min(pd->min_movement, abs(e->value));
|
||||||
wheel_handle_direction_change(pd, e, time);
|
wheel_handle_direction_change(pd, e, time);
|
||||||
wheel_handle_event(pd, WHEEL_EVENT_SCROLL, time);
|
wheel_handle_event(pd, WHEEL_EVENT_SCROLL, time);
|
||||||
break;
|
break;
|
||||||
|
|
@ -412,6 +415,7 @@ wheel_plugin_device_create(struct libinput_plugin *libinput_plugin,
|
||||||
pd->state = WHEEL_STATE_NONE;
|
pd->state = WHEEL_STATE_NONE;
|
||||||
pd->dir = WHEEL_DIR_UNKNOW;
|
pd->dir = WHEEL_DIR_UNKNOW;
|
||||||
pd->ignore_small_hi_res_movements = !evdev_device_is_virtual(evdev);
|
pd->ignore_small_hi_res_movements = !evdev_device_is_virtual(evdev);
|
||||||
|
pd->min_movement = ACC_V120_THRESHOLD;
|
||||||
ratelimit_init(&pd->hires_warning_limit, s2us(24 * 60 * 60), 1);
|
ratelimit_init(&pd->hires_warning_limit, s2us(24 * 60 * 60), 1);
|
||||||
|
|
||||||
if (pd->ignore_small_hi_res_movements) {
|
if (pd->ignore_small_hi_res_movements) {
|
||||||
|
|
|
||||||
|
|
@ -843,6 +843,7 @@ START_TEST(pointer_scroll_wheel_inhibit_small_deltas)
|
||||||
{
|
{
|
||||||
struct litest_device *dev = litest_current_device();
|
struct litest_device *dev = litest_current_device();
|
||||||
struct libinput *li = dev->libinput;
|
struct libinput *li = dev->libinput;
|
||||||
|
uint32_t delta = litest_test_param_get_u32(test_env->params, "hires-delta");
|
||||||
|
|
||||||
if (!libevdev_has_event_code(dev->evdev, EV_REL, REL_WHEEL_HI_RES) ||
|
if (!libevdev_has_event_code(dev->evdev, EV_REL, REL_WHEEL_HI_RES) ||
|
||||||
!libevdev_has_event_code(dev->evdev, EV_REL, REL_HWHEEL_HI_RES))
|
!libevdev_has_event_code(dev->evdev, EV_REL, REL_HWHEEL_HI_RES))
|
||||||
|
|
@ -850,49 +851,94 @@ START_TEST(pointer_scroll_wheel_inhibit_small_deltas)
|
||||||
|
|
||||||
litest_drain_events(dev->libinput);
|
litest_drain_events(dev->libinput);
|
||||||
|
|
||||||
/* Scroll deltas below the threshold (60) must be ignored */
|
/* A single delta (below the hardcoded threshold 60) is ignored */
|
||||||
litest_event(dev, EV_REL, REL_WHEEL_HI_RES, 15);
|
litest_event(dev, EV_REL, REL_WHEEL_HI_RES, delta);
|
||||||
litest_event(dev, EV_SYN, SYN_REPORT, 0);
|
|
||||||
litest_event(dev, EV_REL, REL_WHEEL_HI_RES, 15);
|
|
||||||
litest_event(dev, EV_SYN, SYN_REPORT, 0);
|
litest_event(dev, EV_SYN, SYN_REPORT, 0);
|
||||||
litest_dispatch(li);
|
litest_dispatch(li);
|
||||||
litest_assert_empty_queue(li);
|
litest_assert_empty_queue(li);
|
||||||
|
|
||||||
/* The accumulated scroll is 30, add 30 to trigger scroll */
|
/* Once we get two events in the same direction trigger scroll */
|
||||||
litest_event(dev, EV_REL, REL_WHEEL_HI_RES, 30);
|
litest_event(dev, EV_REL, REL_WHEEL_HI_RES, delta);
|
||||||
litest_event(dev, EV_SYN, SYN_REPORT, 0);
|
litest_event(dev, EV_SYN, SYN_REPORT, 0);
|
||||||
litest_dispatch(li);
|
litest_dispatch(li);
|
||||||
test_high_and_low_wheel_events_value(dev, REL_WHEEL_HI_RES, -60);
|
|
||||||
|
|
||||||
/* Once the threshold is reached, small scroll deltas are reported */
|
test_high_and_low_wheel_events_value(dev, REL_WHEEL_HI_RES, -2 * delta);
|
||||||
litest_event(dev, EV_REL, REL_WHEEL_HI_RES, 5);
|
|
||||||
|
/* Once the threshold is reached, every scroll deltas are reported */
|
||||||
|
litest_event(dev, EV_REL, REL_WHEEL_HI_RES, delta);
|
||||||
litest_event(dev, EV_SYN, SYN_REPORT, 0);
|
litest_event(dev, EV_SYN, SYN_REPORT, 0);
|
||||||
litest_dispatch(li);
|
litest_dispatch(li);
|
||||||
test_high_and_low_wheel_events_value(dev, REL_WHEEL_HI_RES, -5);
|
test_high_and_low_wheel_events_value(dev, REL_WHEEL_HI_RES, -delta);
|
||||||
|
|
||||||
/* When the scroll timeout is triggered, ignore small deltas again */
|
/* When the scroll timeout is triggered, ignore small deltas again */
|
||||||
litest_timeout_wheel_scroll(li);
|
litest_timeout_wheel_scroll(li);
|
||||||
|
|
||||||
litest_event(dev, EV_REL, REL_WHEEL_HI_RES, -15);
|
litest_event(dev, EV_REL, REL_WHEEL_HI_RES, -delta);
|
||||||
litest_event(dev, EV_SYN, SYN_REPORT, 0);
|
|
||||||
litest_event(dev, EV_REL, REL_WHEEL_HI_RES, -15);
|
|
||||||
litest_event(dev, EV_SYN, SYN_REPORT, 0);
|
litest_event(dev, EV_SYN, SYN_REPORT, 0);
|
||||||
litest_dispatch(li);
|
litest_dispatch(li);
|
||||||
litest_assert_empty_queue(li);
|
litest_assert_empty_queue(li);
|
||||||
|
|
||||||
litest_event(dev, EV_REL, REL_HWHEEL_HI_RES, 15);
|
litest_event(dev, EV_REL, REL_HWHEEL_HI_RES, delta);
|
||||||
litest_event(dev, EV_SYN, SYN_REPORT, 0);
|
|
||||||
litest_event(dev, EV_REL, REL_HWHEEL_HI_RES, 15);
|
|
||||||
litest_event(dev, EV_SYN, SYN_REPORT, 0);
|
litest_event(dev, EV_SYN, SYN_REPORT, 0);
|
||||||
litest_dispatch(li);
|
litest_dispatch(li);
|
||||||
litest_assert_empty_queue(li);
|
litest_assert_empty_queue(li);
|
||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
START_TEST(pointer_scroll_wheel_inhibit_small_deltas_reduce_delta)
|
||||||
|
{
|
||||||
|
struct litest_device *dev = litest_current_device();
|
||||||
|
struct libinput *li = dev->libinput;
|
||||||
|
|
||||||
|
if (!libevdev_has_event_code(dev->evdev, EV_REL, REL_WHEEL_HI_RES) ||
|
||||||
|
!libevdev_has_event_code(dev->evdev, EV_REL, REL_HWHEEL_HI_RES))
|
||||||
|
return LITEST_NOT_APPLICABLE;
|
||||||
|
|
||||||
|
litest_drain_events(dev->libinput);
|
||||||
|
|
||||||
|
/* A single delta (below the hardcoded threshold 60) is ignored */
|
||||||
|
litest_event(dev, EV_REL, REL_WHEEL_HI_RES, 50);
|
||||||
|
litest_event(dev, EV_SYN, SYN_REPORT, 0);
|
||||||
|
litest_dispatch(li);
|
||||||
|
litest_assert_empty_queue(li);
|
||||||
|
|
||||||
|
/* A second smaller delta changes the internal threshold */
|
||||||
|
litest_event(dev, EV_REL, REL_WHEEL_HI_RES, 5);
|
||||||
|
litest_event(dev, EV_SYN, SYN_REPORT, 0);
|
||||||
|
litest_dispatch(li);
|
||||||
|
|
||||||
|
test_high_and_low_wheel_events_value(dev, REL_WHEEL_HI_RES, -55);
|
||||||
|
|
||||||
|
litest_timeout_wheel_scroll(li);
|
||||||
|
|
||||||
|
/* Internal threshold is now 5 so two deltas of 5 trigger */
|
||||||
|
litest_log_group("Internal threshold is now 5 so two deltas of 5 trigger") {
|
||||||
|
litest_event(dev, EV_REL, REL_WHEEL_HI_RES, 5);
|
||||||
|
litest_event(dev, EV_SYN, SYN_REPORT, 0);
|
||||||
|
litest_dispatch(li);
|
||||||
|
litest_assert_empty_queue(li);
|
||||||
|
litest_event(dev, EV_REL, REL_WHEEL_HI_RES, 5);
|
||||||
|
litest_event(dev, EV_SYN, SYN_REPORT, 0);
|
||||||
|
litest_dispatch(li);
|
||||||
|
test_high_and_low_wheel_events_value(dev, REL_WHEEL_HI_RES, -10);
|
||||||
|
}
|
||||||
|
|
||||||
|
litest_timeout_wheel_scroll(li);
|
||||||
|
|
||||||
|
litest_log_group("Internal threshold is now 5 so one delta of 10 trigger") {
|
||||||
|
litest_event(dev, EV_REL, REL_WHEEL_HI_RES, 10);
|
||||||
|
litest_event(dev, EV_SYN, SYN_REPORT, 0);
|
||||||
|
litest_dispatch(li);
|
||||||
|
test_high_and_low_wheel_events_value(dev, REL_WHEEL_HI_RES, -10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
START_TEST(pointer_scroll_wheel_inhibit_dir_change)
|
START_TEST(pointer_scroll_wheel_inhibit_dir_change)
|
||||||
{
|
{
|
||||||
struct litest_device *dev = litest_current_device();
|
struct litest_device *dev = litest_current_device();
|
||||||
struct libinput *li = dev->libinput;
|
struct libinput *li = dev->libinput;
|
||||||
|
uint32_t delta = litest_test_param_get_u32(test_env->params, "hires-delta");
|
||||||
|
|
||||||
if (!libevdev_has_event_code(dev->evdev, EV_REL, REL_WHEEL_HI_RES))
|
if (!libevdev_has_event_code(dev->evdev, EV_REL, REL_WHEEL_HI_RES))
|
||||||
return LITEST_NOT_APPLICABLE;
|
return LITEST_NOT_APPLICABLE;
|
||||||
|
|
@ -900,28 +946,28 @@ START_TEST(pointer_scroll_wheel_inhibit_dir_change)
|
||||||
litest_drain_events(dev->libinput);
|
litest_drain_events(dev->libinput);
|
||||||
|
|
||||||
/* Scroll one detent and a bit */
|
/* Scroll one detent and a bit */
|
||||||
litest_event(dev, EV_REL, REL_WHEEL_HI_RES, 150);
|
litest_event(dev, EV_REL, REL_WHEEL_HI_RES, 120 + delta);
|
||||||
litest_event(dev, EV_SYN, SYN_REPORT, 0);
|
litest_event(dev, EV_SYN, SYN_REPORT, 0);
|
||||||
litest_dispatch(li);
|
litest_dispatch(li);
|
||||||
test_high_and_low_wheel_events_value(dev, REL_WHEEL_HI_RES, -150);
|
test_high_and_low_wheel_events_value(dev, REL_WHEEL_HI_RES, -120 - delta);
|
||||||
|
|
||||||
/* Scroll below the threshold in the oposite direction should be ignored */
|
/* Scroll below the threshold in the oposite direction should be ignored */
|
||||||
litest_event(dev, EV_REL, REL_WHEEL_HI_RES, -30);
|
litest_event(dev, EV_REL, REL_WHEEL_HI_RES, -delta);
|
||||||
litest_event(dev, EV_SYN, SYN_REPORT, 0);
|
litest_event(dev, EV_SYN, SYN_REPORT, 0);
|
||||||
litest_dispatch(li);
|
litest_dispatch(li);
|
||||||
litest_assert_empty_queue(li);
|
litest_assert_empty_queue(li);
|
||||||
|
|
||||||
/* But should be triggered if the scroll continues in the same direction */
|
/* But should be triggered if the scroll continues in the same direction */
|
||||||
litest_event(dev, EV_REL, REL_WHEEL_HI_RES, -120);
|
litest_event(dev, EV_REL, REL_WHEEL_HI_RES, -2 * delta);
|
||||||
litest_event(dev, EV_SYN, SYN_REPORT, 0);
|
litest_event(dev, EV_SYN, SYN_REPORT, 0);
|
||||||
litest_dispatch(li);
|
litest_dispatch(li);
|
||||||
test_high_and_low_wheel_events_value(dev, REL_WHEEL_HI_RES, 150);
|
test_high_and_low_wheel_events_value(dev, REL_WHEEL_HI_RES, 3 * delta);
|
||||||
|
|
||||||
/* Scroll above the threshold in the same dir should be triggered */
|
/* Scroll above the threshold in the same dir should be triggered */
|
||||||
litest_event(dev, EV_REL, REL_WHEEL_HI_RES, 80);
|
litest_event(dev, EV_REL, REL_WHEEL_HI_RES, 2 * delta);
|
||||||
litest_event(dev, EV_SYN, SYN_REPORT, 0);
|
litest_event(dev, EV_SYN, SYN_REPORT, 0);
|
||||||
litest_dispatch(li);
|
litest_dispatch(li);
|
||||||
test_high_and_low_wheel_events_value(dev, REL_WHEEL_HI_RES, -80);
|
test_high_and_low_wheel_events_value(dev, REL_WHEEL_HI_RES, -2 * delta);
|
||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
|
@ -3646,8 +3692,11 @@ TEST_COLLECTION(pointer)
|
||||||
litest_named_i32(LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, "horizontal")) {
|
litest_named_i32(LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL, "horizontal")) {
|
||||||
litest_add_parametrized(pointer_scroll_wheel_hires_send_only_lores, LITEST_WHEEL, LITEST_TABLET, params);
|
litest_add_parametrized(pointer_scroll_wheel_hires_send_only_lores, LITEST_WHEEL, LITEST_TABLET, params);
|
||||||
}
|
}
|
||||||
litest_add(pointer_scroll_wheel_inhibit_small_deltas, LITEST_WHEEL, LITEST_TABLET);
|
litest_with_parameters(params, "hires-delta", 'u', 3, 5, 15, 20) {
|
||||||
litest_add(pointer_scroll_wheel_inhibit_dir_change, LITEST_WHEEL, LITEST_TABLET);
|
litest_add_parametrized(pointer_scroll_wheel_inhibit_small_deltas, LITEST_WHEEL, LITEST_TABLET, params);
|
||||||
|
litest_add_parametrized(pointer_scroll_wheel_inhibit_dir_change, LITEST_WHEEL, LITEST_TABLET, params);
|
||||||
|
}
|
||||||
|
litest_add(pointer_scroll_wheel_inhibit_small_deltas_reduce_delta, LITEST_WHEEL, LITEST_TABLET);
|
||||||
litest_add_for_device(pointer_scroll_wheel_no_inhibit_small_deltas_when_virtual, LITEST_MOUSE_VIRTUAL);
|
litest_add_for_device(pointer_scroll_wheel_no_inhibit_small_deltas_when_virtual, LITEST_MOUSE_VIRTUAL);
|
||||||
litest_add_for_device(pointer_scroll_wheel_lenovo_scrollpoint, LITEST_LENOVO_SCROLLPOINT);
|
litest_add_for_device(pointer_scroll_wheel_lenovo_scrollpoint, LITEST_LENOVO_SCROLLPOINT);
|
||||||
litest_add(pointer_scroll_button, LITEST_RELATIVE|LITEST_BUTTON, LITEST_ANY);
|
litest_add(pointer_scroll_button, LITEST_RELATIVE|LITEST_BUTTON, LITEST_ANY);
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue