diff options
author | Peter Hutterer <peter.hutterer@who-t.net> | 2019-01-07 08:27:22 +1000 |
---|---|---|
committer | Peter Hutterer <peter.hutterer@who-t.net> | 2019-01-31 05:17:28 +0000 |
commit | ab1dbcc996e00fdbb6177bdee349375c7901e8e4 (patch) | |
tree | 5cc22fd20d99fdb2e9f8a89038477abef9df2c12 | |
parent | caa8f3fe61b9eeb4a5c9d189da86f7c5e6b4500f (diff) | |
download | libinput-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.c | 50 | ||||
-rw-r--r-- | src/evdev-fallback.h | 13 | ||||
-rw-r--r-- | test/test-tablet.c | 20 |
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); |