summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosé Expósito <jose.exposito89@gmail.com>2021-09-29 18:32:54 +0200
committerJosé Expósito <jose.exposito89@gmail.com>2021-11-08 18:00:46 +0100
commitb6a944bb80a9bb886bf38508e8a2b85be22dda57 (patch)
tree7d14f4fb03554544d66bda9cdc88f2f847025607
parent08245c778a8ff0e92b50266b9712174bbf4d9e32 (diff)
downloadlibinput-b6a944bb80a9bb886bf38508e8a2b85be22dda57.tar.gz
wheel: ignore initial small scroll deltas
Mice with high-resolution support can generate deltas when the finger is put on the wheel or when the user tries to click the wheel. To avoid sending involuntary scroll events, add an extra state the the wheel state machine to accumulate scroll deltas. While the accumulated scroll is lower than a certain threshold, ignore them until the threshold is reached. Since no finish event is sent by the mouse, reset the state machine after a period of scroll inactivity. Signed-off-by: José Expósito <jose.exposito89@gmail.com>
-rw-r--r--src/evdev-fallback.c2
-rw-r--r--src/evdev-fallback.h2
-rw-r--r--src/evdev-wheel.c101
-rw-r--r--test/litest.c6
-rw-r--r--test/litest.h3
-rw-r--r--test/test-pointer.c48
6 files changed, 161 insertions, 1 deletions
diff --git a/src/evdev-fallback.c b/src/evdev-fallback.c
index 19ae4a0a..28d5de99 100644
--- a/src/evdev-fallback.c
+++ b/src/evdev-fallback.c
@@ -1109,6 +1109,7 @@ fallback_interface_remove(struct evdev_dispatch *evdev_dispatch)
struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch);
struct evdev_paired_keyboard *kbd;
+ libinput_timer_cancel(&dispatch->wheel.scroll_timer);
libinput_timer_cancel(&dispatch->debounce.timer);
libinput_timer_cancel(&dispatch->debounce.timer_short);
libinput_timer_cancel(&dispatch->arbitration.arbitration_timer);
@@ -1222,6 +1223,7 @@ fallback_interface_destroy(struct evdev_dispatch *evdev_dispatch)
{
struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch);
+ libinput_timer_destroy(&dispatch->wheel.scroll_timer);
libinput_timer_destroy(&dispatch->arbitration.arbitration_timer);
libinput_timer_destroy(&dispatch->debounce.timer);
libinput_timer_destroy(&dispatch->debounce.timer_short);
diff --git a/src/evdev-fallback.h b/src/evdev-fallback.h
index 2338f4a7..ad41cd3b 100644
--- a/src/evdev-fallback.h
+++ b/src/evdev-fallback.h
@@ -62,6 +62,7 @@ enum palm_state {
enum wheel_state {
WHEEL_STATE_NONE,
WHEEL_STATE_PRESSED,
+ WHEEL_STATE_ACCUMULATING_SCROLL,
WHEEL_STATE_SCROLLING,
};
@@ -109,6 +110,7 @@ struct fallback_dispatch {
struct device_coords hi_res;
bool emulate_hi_res_wheel;
bool hi_res_event_received;
+ struct libinput_timer scroll_timer;
} wheel;
struct {
diff --git a/src/evdev-wheel.c b/src/evdev-wheel.c
index acdbf861..215cd2db 100644
--- a/src/evdev-wheel.c
+++ b/src/evdev-wheel.c
@@ -30,10 +30,15 @@
#include "evdev-fallback.h"
#include "util-input-event.h"
+#define ACC_V120_THRESHOLD 60
+#define WHEEL_SCROLL_TIMEOUT ms2us(500)
+
enum wheel_event {
WHEEL_EVENT_PRESS,
WHEEL_EVENT_RELEASE,
+ WHEEL_EVENT_SCROLL_ACCUMULATED,
WHEEL_EVENT_SCROLL,
+ WHEEL_EVENT_SCROLL_TIMEOUT,
};
static inline const char *
@@ -42,6 +47,7 @@ wheel_state_to_str(enum wheel_state state)
switch(state) {
CASE_RETURN_STRING(WHEEL_STATE_NONE);
CASE_RETURN_STRING(WHEEL_STATE_PRESSED);
+ CASE_RETURN_STRING(WHEEL_STATE_ACCUMULATING_SCROLL);
CASE_RETURN_STRING(WHEEL_STATE_SCROLLING);
}
return NULL;
@@ -53,7 +59,9 @@ wheel_event_to_str(enum wheel_event event)
switch(event) {
CASE_RETURN_STRING(WHEEL_EVENT_PRESS);
CASE_RETURN_STRING(WHEEL_EVENT_RELEASE);
+ CASE_RETURN_STRING(WHEEL_EVENT_SCROLL_ACCUMULATED);
CASE_RETURN_STRING(WHEEL_EVENT_SCROLL);
+ CASE_RETURN_STRING(WHEEL_EVENT_SCROLL_TIMEOUT);
}
return NULL;
}
@@ -67,6 +75,19 @@ log_wheel_bug(struct fallback_dispatch *dispatch, enum wheel_event event)
wheel_state_to_str(dispatch->wheel.state));
}
+static inline void
+wheel_set_scroll_timer(struct fallback_dispatch *dispatch, uint64_t time)
+{
+ libinput_timer_set(&dispatch->wheel.scroll_timer,
+ time + WHEEL_SCROLL_TIMEOUT);
+}
+
+static inline void
+wheel_cancel_scroll_timer(struct fallback_dispatch *dispatch)
+{
+ libinput_timer_cancel(&dispatch->wheel.scroll_timer);
+}
+
static void
wheel_handle_event_on_state_none(struct fallback_dispatch *dispatch,
enum wheel_event event,
@@ -77,9 +98,11 @@ wheel_handle_event_on_state_none(struct fallback_dispatch *dispatch,
dispatch->wheel.state = WHEEL_STATE_PRESSED;
break;
case WHEEL_EVENT_SCROLL:
- dispatch->wheel.state = WHEEL_STATE_SCROLLING;
+ dispatch->wheel.state = WHEEL_STATE_ACCUMULATING_SCROLL;
break;
case WHEEL_EVENT_RELEASE:
+ case WHEEL_EVENT_SCROLL_ACCUMULATED:
+ case WHEEL_EVENT_SCROLL_TIMEOUT:
log_wheel_bug(dispatch, event);
break;
}
@@ -98,6 +121,31 @@ wheel_handle_event_on_state_pressed(struct fallback_dispatch *dispatch,
/* Ignore scroll while the wheel is pressed */
break;
case WHEEL_EVENT_PRESS:
+ case WHEEL_EVENT_SCROLL_ACCUMULATED:
+ case WHEEL_EVENT_SCROLL_TIMEOUT:
+ log_wheel_bug(dispatch, event);
+ break;
+ }
+}
+
+static void
+wheel_handle_event_on_state_accumulating_scroll(struct fallback_dispatch *dispatch,
+ enum wheel_event event,
+ uint64_t time)
+{
+ switch (event) {
+ case WHEEL_EVENT_PRESS:
+ dispatch->wheel.state = WHEEL_STATE_PRESSED;
+ break;
+ case WHEEL_EVENT_SCROLL_ACCUMULATED:
+ dispatch->wheel.state = WHEEL_STATE_SCROLLING;
+ wheel_set_scroll_timer(dispatch, time);
+ break;
+ case WHEEL_EVENT_SCROLL:
+ /* Ignore scroll while accumulating deltas */
+ break;
+ case WHEEL_EVENT_RELEASE:
+ case WHEEL_EVENT_SCROLL_TIMEOUT:
log_wheel_bug(dispatch, event);
break;
}
@@ -111,10 +159,17 @@ wheel_handle_event_on_state_scrolling(struct fallback_dispatch *dispatch,
switch (event) {
case WHEEL_EVENT_PRESS:
dispatch->wheel.state = WHEEL_STATE_PRESSED;
+ wheel_cancel_scroll_timer(dispatch);
break;
case WHEEL_EVENT_SCROLL:
+ wheel_cancel_scroll_timer(dispatch);
+ wheel_set_scroll_timer(dispatch, time);
+ break;
+ case WHEEL_EVENT_SCROLL_TIMEOUT:
+ dispatch->wheel.state = WHEEL_STATE_NONE;
break;
case WHEEL_EVENT_RELEASE:
+ case WHEEL_EVENT_SCROLL_ACCUMULATED:
log_wheel_bug(dispatch, event);
break;
}
@@ -134,6 +189,11 @@ wheel_handle_event(struct fallback_dispatch *dispatch,
case WHEEL_STATE_PRESSED:
wheel_handle_event_on_state_pressed(dispatch, event, time);
break;
+ case WHEEL_STATE_ACCUMULATING_SCROLL:
+ wheel_handle_event_on_state_accumulating_scroll(dispatch,
+ event,
+ time);
+ break;
case WHEEL_STATE_SCROLLING:
wheel_handle_event_on_state_scrolling(dispatch, event, time);
break;
@@ -249,6 +309,20 @@ wheel_handle_state_pressed(struct fallback_dispatch *dispatch,
}
static void
+wheel_handle_state_accumulating_scroll(struct fallback_dispatch *dispatch,
+ struct evdev_device *device,
+ uint64_t time)
+{
+ if (abs(dispatch->wheel.hi_res.x) >= ACC_V120_THRESHOLD ||
+ abs(dispatch->wheel.hi_res.y) >= ACC_V120_THRESHOLD) {
+ wheel_handle_event(dispatch,
+ WHEEL_EVENT_SCROLL_ACCUMULATED,
+ time);
+ wheel_flush_scroll(dispatch, device, time);
+ }
+}
+
+static void
wheel_handle_state_scrolling(struct fallback_dispatch *dispatch,
struct evdev_device *device,
uint64_t time)
@@ -342,16 +416,31 @@ fallback_wheel_handle_state(struct fallback_dispatch *dispatch,
case WHEEL_STATE_PRESSED:
wheel_handle_state_pressed(dispatch, device, time);
break;
+ case WHEEL_STATE_ACCUMULATING_SCROLL:
+ wheel_handle_state_accumulating_scroll(dispatch, device, time);
+ break;
case WHEEL_STATE_SCROLLING:
wheel_handle_state_scrolling(dispatch, device, time);
break;
}
}
+static void
+wheel_init_scroll_timer(uint64_t now, void *data)
+{
+ struct evdev_device *device = data;
+ struct fallback_dispatch *dispatch =
+ fallback_dispatch(device->dispatch);
+
+ wheel_handle_event(dispatch, WHEEL_EVENT_SCROLL_TIMEOUT, now);
+}
+
void
fallback_init_wheel(struct fallback_dispatch *dispatch,
struct evdev_device *device)
{
+ char timer_name[64];
+
dispatch->wheel.state = WHEEL_STATE_NONE;
/* On kernel < 5.0 we need to emulate high-resolution
@@ -369,4 +458,14 @@ fallback_init_wheel(struct fallback_dispatch *dispatch,
EV_REL,
REL_HWHEEL_HI_RES)))
dispatch->wheel.emulate_hi_res_wheel = true;
+
+ snprintf(timer_name,
+ sizeof(timer_name),
+ "%s wheel scroll",
+ evdev_device_get_sysname(device));
+ libinput_timer_init(&dispatch->wheel.scroll_timer,
+ evdev_libinput_context(device),
+ timer_name,
+ wheel_init_scroll_timer,
+ device);
}
diff --git a/test/litest.c b/test/litest.c
index 187dd345..69d5154b 100644
--- a/test/litest.c
+++ b/test/litest.c
@@ -4300,6 +4300,12 @@ litest_timeout_finger_switch(void)
}
void
+litest_timeout_wheel_scroll(void)
+{
+ msleep(600);
+}
+
+void
litest_timeout_edgescroll(void)
{
msleep(300);
diff --git a/test/litest.h b/test/litest.h
index b6ffbf4e..990161b9 100644
--- a/test/litest.h
+++ b/test/litest.h
@@ -895,6 +895,9 @@ void
litest_timeout_buttonscroll(void);
void
+litest_timeout_wheel_scroll(void);
+
+void
litest_timeout_edgescroll(void);
void
diff --git a/test/test-pointer.c b/test/test-pointer.c
index 218fe074..77a63963 100644
--- a/test/test-pointer.c
+++ b/test/test-pointer.c
@@ -867,6 +867,53 @@ START_TEST(pointer_scroll_wheel_hires_send_only_lores_horizontal)
}
END_TEST
+START_TEST(pointer_scroll_wheel_inhibit_small_deltas)
+{
+ 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_drain_events(dev->libinput);
+
+ /* Scroll deltas bellow the threshold (60) must be ignored */
+ litest_event(dev, EV_REL, REL_WHEEL_HI_RES, 15);
+ litest_event(dev, EV_REL, REL_WHEEL_HI_RES, 15);
+ litest_event(dev, EV_SYN, SYN_REPORT, 0);
+ libinput_dispatch(li);
+ litest_assert_empty_queue(li);
+
+ /* The accumulated scroll is 30, add 30 to trigger scroll */
+ litest_event(dev, EV_REL, REL_WHEEL_HI_RES, 30);
+ litest_event(dev, EV_SYN, SYN_REPORT, 0);
+ libinput_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 */
+ litest_event(dev, EV_REL, REL_WHEEL_HI_RES, 5);
+ litest_event(dev, EV_SYN, SYN_REPORT, 0);
+ libinput_dispatch(li);
+ test_high_and_low_wheel_events_value(dev, REL_WHEEL_HI_RES, -5);
+
+ /* When the scroll timeout is triggered, ignore small deltas again */
+ litest_timeout_wheel_scroll();
+
+ litest_event(dev, EV_REL, REL_WHEEL_HI_RES, -15);
+ litest_event(dev, EV_REL, REL_WHEEL_HI_RES, -15);
+ litest_event(dev, EV_SYN, SYN_REPORT, 0);
+ libinput_dispatch(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, 15);
+ litest_event(dev, EV_SYN, SYN_REPORT, 0);
+ libinput_dispatch(li);
+ litest_assert_empty_queue(li);
+}
+END_TEST
+
START_TEST(pointer_scroll_natural_defaults)
{
struct litest_device *dev = litest_current_device();
@@ -3495,6 +3542,7 @@ TEST_COLLECTION(pointer)
litest_add(pointer_scroll_wheel_hires, LITEST_WHEEL, LITEST_TABLET);
litest_add(pointer_scroll_wheel_hires_send_only_lores_vertical, LITEST_WHEEL, LITEST_TABLET);
litest_add(pointer_scroll_wheel_hires_send_only_lores_horizontal, LITEST_WHEEL, LITEST_TABLET);
+ litest_add(pointer_scroll_wheel_inhibit_small_deltas, LITEST_WHEEL, LITEST_TABLET);
litest_add(pointer_scroll_button, LITEST_RELATIVE|LITEST_BUTTON, LITEST_ANY);
litest_add(pointer_scroll_button_noscroll, LITEST_ABSOLUTE|LITEST_BUTTON, LITEST_RELATIVE);
litest_add(pointer_scroll_button_noscroll, LITEST_ANY, LITEST_RELATIVE|LITEST_BUTTON);