mirror of
https://gitlab.freedesktop.org/libinput/libinput.git
synced 2025-12-28 03:50:15 +01:00
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 2a378beab0 but for the fallback
interface.
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This commit is contained in:
parent
caa8f3fe61
commit
ab1dbcc996
3 changed files with 70 additions and 13 deletions
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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*
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue