From eca2f8c9c68c586e0466e67ea805032c2a641404 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 13 Aug 2018 15:25:48 +1000 Subject: [PATCH] touchpad: improve pointer jump detection Previously, we had a hard threshold of 20mm per event frame. That is just about achievable by really fast movements (in which case you don't care too much about the jumps anyway because you've already hit the edge of the screen). Sometimes pointer jumps have lower deltas that are achievable even on slower, more likely motions. Analysis of finger motion has shown that while a delta >7mm per event is possible, jumping _by_ 7mm between two events is unlikely and indicates a pointer jump. So let's diff the most recent delta and the current delta, if it increases by 7mm between two event frames let's say it's a pointer jump and discard it. Helps with but does not fully resolve: https://gitlab.freedesktop.org/libinput/libinput/issues/80 https://gitlab.freedesktop.org/libinput/libinput/issues/36 Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad.c | 19 +++++++++++++++--- src/evdev-mt-touchpad.h | 4 ++++ test/test-touchpad.c | 43 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 3 deletions(-) diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index a2273b65..1393c308 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -1440,8 +1440,9 @@ tp_detect_jumps(const struct tp_dispatch *tp, struct tp_touch *t) { struct device_coords delta; struct phys_coords mm; - const int JUMP_THRESHOLD_MM = 20; struct tp_history_point *last; + double distance; + bool is_jump = false; /* We haven't seen pointer jumps on Wacom tablets yet, so exclude * those. @@ -1449,8 +1450,10 @@ tp_detect_jumps(const struct tp_dispatch *tp, struct tp_touch *t) if (tp->device->model_flags & EVDEV_MODEL_WACOM_TOUCHPAD) return false; - if (t->history.count == 0) + if (t->history.count == 0) { + t->jumps.last_delta_mm = 0.0; return false; + } /* called before tp_motion_history_push, so offset 0 is the most * recent coordinate */ @@ -1459,7 +1462,17 @@ tp_detect_jumps(const struct tp_dispatch *tp, struct tp_touch *t) delta.y = abs(t->point.y - last->point.y); mm = evdev_device_unit_delta_to_mm(tp->device, &delta); - return hypot(mm.x, mm.y) > JUMP_THRESHOLD_MM; + distance = hypot(mm.x, mm.y); + + /* Cursor jump if: + * - current single-event delta is >20mm, or + * - we increased the delta by over 7mm within a frame. + */ + is_jump = distance > 20.0 || + (distance - t->jumps.last_delta_mm) > 7; + t->jumps.last_delta_mm = distance; + + return is_jump; } static void diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 2c46d28c..00332ad3 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -176,6 +176,10 @@ struct tp_touch { unsigned int count; } history; + struct { + double last_delta_mm; + } jumps; + struct { struct device_coords center; uint8_t x_motion_history; diff --git a/test/test-touchpad.c b/test/test-touchpad.c index 525bf475..68dc7229 100644 --- a/test/test-touchpad.c +++ b/test/test-touchpad.c @@ -5540,6 +5540,8 @@ START_TEST(touchpad_jump_finger_motion) litest_touch_move_to(dev, 0, 20, 30, 90, 30, 10, 0); litest_drain_events(li); + /* this test uses a specific test device to trigger a >20mm jump to + * test jumps. These numbers may not work on any other device */ litest_disable_log_handler(li); litest_touch_move_to(dev, 0, 90, 30, 20, 80, 1, 0); litest_assert_empty_queue(li); @@ -5566,6 +5568,46 @@ START_TEST(touchpad_jump_finger_motion) } END_TEST +START_TEST(touchpad_jump_delta) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct libinput_event *event; + struct libinput_event_pointer *ptrev; + + litest_touch_down(dev, 0, 20, 30); + litest_touch_move_to(dev, 0, 20, 30, 90, 30, 10, 0); + litest_drain_events(li); + + /* this test uses a specific test device to trigger a >7mm but <20mm + * jump to test the delta jumps. These numbers may not work on any + * other device */ + litest_disable_log_handler(li); + litest_touch_move_to(dev, 0, 90, 30, 90, 80, 1, 0); + litest_assert_empty_queue(li); + litest_restore_log_handler(li); + + litest_touch_move_to(dev, 0, 90, 80, 91, 81, 10, 0); + litest_touch_up(dev, 0); + + /* expect lots of little events, no big jump */ + libinput_dispatch(li); + event = libinput_get_event(li); + do { + double dx, dy; + + ptrev = litest_is_motion_event(event); + dx = libinput_event_pointer_get_dx(ptrev); + dy = libinput_event_pointer_get_dy(ptrev); + ck_assert_int_lt(abs((int)dx), 20); + ck_assert_int_lt(abs((int)dy), 20); + + libinput_event_destroy(event); + event = libinput_get_event(li); + } while (event != NULL); +} +END_TEST + START_TEST(touchpad_disabled_on_mouse) { struct litest_device *dev = litest_current_device(); @@ -6749,6 +6791,7 @@ TEST_COLLECTION(touchpad) litest_add("touchpad:time", touchpad_time_usec, LITEST_TOUCHPAD, LITEST_ANY); litest_add_for_device("touchpad:jumps", touchpad_jump_finger_motion, LITEST_SYNAPTICS_CLICKPAD_X220); + litest_add_for_device("touchpad:jumps", touchpad_jump_delta, LITEST_SYNAPTICS_CLICKPAD_X220); litest_add_for_device("touchpad:sendevents", touchpad_disabled_on_mouse, LITEST_SYNAPTICS_CLICKPAD_X220); litest_add_for_device("touchpad:sendevents", touchpad_disabled_on_mouse_suspend_mouse, LITEST_SYNAPTICS_CLICKPAD_X220);