summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Hutterer <peter.hutterer@who-t.net>2019-01-07 08:27:22 +1000
committerPeter Hutterer <peter.hutterer@who-t.net>2019-01-31 05:17:28 +0000
commitab1dbcc996e00fdbb6177bdee349375c7901e8e4 (patch)
tree5cc22fd20d99fdb2e9f8a89038477abef9df2c12
parentcaa8f3fe61b9eeb4a5c9d189da86f7c5e6b4500f (diff)
downloadlibinput-ab1dbcc996e00fdbb6177bdee349375c7901e8e4.tar.gz
fallback: add timer-based touch arbitration
When a hand is resting on a pen+touch device, lifting the hand may remove the stylus from proximity before the hand leaves the surface. If the kernel performs touch arbitration, this triggers a touch down on proximity out, followed by a touch up immediately after when the hand stops touching. This can cause ghost touch events. Prevent this by using a timer-based arbitration toggle. Same as 2a378beab032d742770e24a6822378faa90cf1f6 but for the fallback interface. Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
-rw-r--r--src/evdev-fallback.c50
-rw-r--r--src/evdev-fallback.h13
-rw-r--r--test/test-tablet.c20
3 files changed, 70 insertions, 13 deletions
diff --git a/src/evdev-fallback.c b/src/evdev-fallback.c
index c064557a..c584e095 100644
--- a/src/evdev-fallback.c
+++ b/src/evdev-fallback.c
@@ -1017,7 +1017,7 @@ fallback_interface_process(struct evdev_dispatch *evdev_dispatch,
{
struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch);
- if (dispatch->ignore_events)
+ if (dispatch->arbitration.in_arbitration)
return;
switch (event->type) {
@@ -1202,13 +1202,26 @@ fallback_interface_toggle_touch(struct evdev_dispatch *evdev_dispatch,
struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch);
bool ignore_events = !enable;
- if (ignore_events == dispatch->ignore_events)
+ if (ignore_events == dispatch->arbitration.ignore_events)
return;
- if (ignore_events)
+ if (ignore_events) {
+ libinput_timer_cancel(&dispatch->arbitration.arbitration_timer);
fallback_return_to_neutral_state(dispatch, device);
+ dispatch->arbitration.in_arbitration = true;
+ } else {
+ /* if in-kernel arbitration is in use and there is a touch
+ * and a pen in proximity, lifting the pen out of proximity
+ * causes a touch begin for the touch. On a hand-lift the
+ * proximity out precedes the touch up by a few ms, so we
+ * get what looks like a tap. Fix this by delaying
+ * arbitration by just a little bit so that any touch in
+ * event is caught as palm touch. */
+ libinput_timer_set(&dispatch->arbitration.arbitration_timer,
+ time + ms2us(90));
+ }
- dispatch->ignore_events = ignore_events;
+ dispatch->arbitration.ignore_events = ignore_events;
}
static void
@@ -1216,6 +1229,7 @@ fallback_interface_destroy(struct evdev_dispatch *evdev_dispatch)
{
struct fallback_dispatch *dispatch = fallback_dispatch(evdev_dispatch);
+ libinput_timer_destroy(&dispatch->arbitration.arbitration_timer);
libinput_timer_destroy(&dispatch->debounce.timer);
libinput_timer_destroy(&dispatch->debounce.timer_short);
@@ -1596,6 +1610,33 @@ fallback_dispatch_init_switch(struct fallback_dispatch *dispatch,
libinput_device_init_event_listener(&dispatch->tablet_mode.other.listener);
}
+static void
+fallback_arbitration_timeout(uint64_t now, void *data)
+{
+ struct fallback_dispatch *dispatch = data;
+
+ if (dispatch->arbitration.in_arbitration)
+ dispatch->arbitration.in_arbitration = false;
+}
+
+static void
+fallback_init_arbitration(struct fallback_dispatch *dispatch,
+ struct evdev_device *device)
+{
+ char timer_name[64];
+
+ snprintf(timer_name,
+ sizeof(timer_name),
+ "%s arbitration",
+ evdev_device_get_sysname(device));
+ libinput_timer_init(&dispatch->arbitration.arbitration_timer,
+ evdev_libinput_context(device),
+ timer_name,
+ fallback_arbitration_timeout,
+ dispatch);
+ dispatch->arbitration.in_arbitration = false;
+}
+
struct evdev_dispatch *
fallback_dispatch_create(struct libinput_device *libinput_device)
{
@@ -1652,6 +1693,7 @@ fallback_dispatch_create(struct libinput_device *libinput_device)
}
fallback_init_debounce(dispatch);
+ fallback_init_arbitration(dispatch, device);
return &dispatch->base;
}
diff --git a/src/evdev-fallback.h b/src/evdev-fallback.h
index 6f008160..47f9f5e1 100644
--- a/src/evdev-fallback.h
+++ b/src/evdev-fallback.h
@@ -118,10 +118,6 @@ struct fallback_dispatch {
enum evdev_event_type pending_event;
- /* true if we're reading events (i.e. not suspended) but we're
- ignoring them */
- bool ignore_events;
-
struct {
unsigned int button_code;
uint64_t button_time;
@@ -144,6 +140,15 @@ struct fallback_dispatch {
*/
struct list paired_keyboard_list;
} lid;
+
+ /* pen/touch arbitration has a delayed state, if ignore_events is
+ * true we want to ignore events, in_arbitration actually filters.
+ */
+ struct {
+ bool ignore_events;
+ bool in_arbitration;
+ struct libinput_timer arbitration_timer;
+ } arbitration;
};
static inline struct fallback_dispatch*
diff --git a/test/test-tablet.c b/test/test-tablet.c
index 1130aead..2d5b2d8b 100644
--- a/test/test-tablet.c
+++ b/test/test-tablet.c
@@ -4287,6 +4287,9 @@ START_TEST(touch_arbitration)
litest_touch_up(finger, 0);
litest_assert_empty_queue(li);
+ litest_timeout_touch_arbitration();
+ libinput_dispatch(li);
+
/* lift finger, expect expect events */
litest_touch_down(finger, 0, 30, 30);
litest_touch_move_to(finger, 0, 30, 30, 80, 80, 10);
@@ -4585,9 +4588,10 @@ START_TEST(touch_arbitration_keep_ignoring)
}
END_TEST
-START_TEST(intuos_touch_arbitration_late_touch_lift)
+START_TEST(touch_arbitration_late_touch_lift)
{
struct litest_device *tablet = litest_current_device();
+ enum litest_device_type other;
struct litest_device *finger;
struct libinput *li = tablet->libinput;
struct axis_replacement axes[] = {
@@ -4595,9 +4599,16 @@ START_TEST(intuos_touch_arbitration_late_touch_lift)
{ ABS_PRESSURE, 0 },
{ -1, -1 }
};
+ bool is_touchpad;
- finger = litest_add_device(li, LITEST_WACOM_FINGER);
- litest_enable_tap(finger->libinput_device);
+ other = paired_device(tablet);
+ if (other == LITEST_NO_DEVICE)
+ return;
+
+ finger = litest_add_device(li, other);
+ is_touchpad = !libevdev_has_property(finger->evdev, INPUT_PROP_DIRECT);
+ if (is_touchpad)
+ litest_enable_tap(finger->libinput_device);
litest_tablet_proximity_in(tablet, 10, 10, axes);
litest_tablet_motion(tablet, 10, 10, axes);
litest_tablet_motion(tablet, 20, 40, axes);
@@ -4901,8 +4912,7 @@ TEST_COLLECTION(tablet)
litest_add("tablet:touch-arbitration", touch_arbitration_remove_touch, LITEST_TABLET, LITEST_ANY);
litest_add("tablet:touch-arbitration", touch_arbitration_remove_tablet, LITEST_TOUCH, LITEST_ANY);
litest_add("tablet:touch-arbitration", touch_arbitration_keep_ignoring, LITEST_TABLET, LITEST_ANY);
-
- litest_add_for_device("tablet:touch-arbitration", intuos_touch_arbitration_late_touch_lift, LITEST_WACOM_INTUOS);
+ litest_add("tablet:touch-arbitration", touch_arbitration_late_touch_lift, LITEST_TABLET, LITEST_ANY);
litest_add_for_device("tablet:quirks", huion_static_btn_tool_pen, LITEST_HUION_TABLET);
litest_add_for_device("tablet:quirks", huion_static_btn_tool_pen_no_timeout_during_usage, LITEST_HUION_TABLET);