Improve disambiguation between two-finger pinch and scroll
authornovenary <streetwalkermc@gmail.com>
Sun, 4 Apr 2021 15:30:41 +0000 (18:30 +0300)
committerPeter Hutterer <peter.hutterer@who-t.net>
Wed, 19 May 2021 05:12:58 +0000 (05:12 +0000)
A pinch is defined as two fingers moving in different directions, and a
scroll as two fingers moving in the same direction.

Often enough when the user is trying to pinch, we may initially see both
fingers moving in the same direction and decide that they want to
scroll.

Add a grace period during which we may transition to a pinch in those
situations.

Test fix: touchpad_trackpoint_buttons_2fg_scroll emits movements that
change the distance between fingers, which triggers this new transition
and makes the test fail; correct this.

Signed-off-by: novenary <streetwalkermc@gmail.com>
src/evdev-mt-touchpad-gestures.c
test/test-touchpad.c

index 2f9e21360634ebebe2e79af914b6224a5aa08ebb..16841308dd93c2164bd546a1b0e915c3b69bb9a5 100644 (file)
@@ -32,6 +32,8 @@
 #define DEFAULT_GESTURE_SWIPE_TIMEOUT ms2us(150)
 #define DEFAULT_GESTURE_PINCH_TIMEOUT ms2us(150)
 
+#define PINCH_DISAMBIGUATION_MOVE_THRESHOLD 1.5 /* mm */
+
 static inline const char*
 gesture_state_to_str(enum tp_gesture_state state)
 {
@@ -546,6 +548,33 @@ tp_gesture_detect_motion_gestures(struct tp_dispatch *tp, uint64_t time)
        return GESTURE_STATE_PINCH;
 }
 
+static bool
+tp_gesture_is_pinch(struct tp_dispatch *tp)
+{
+       struct tp_touch *first = tp->gesture.touches[0],
+                       *second = tp->gesture.touches[1];
+       uint32_t dir1, dir2;
+       struct phys_coords first_moved, second_moved;
+       double first_mm, second_mm;
+
+       dir1 = tp_gesture_get_direction(tp, first);
+       dir2 = tp_gesture_get_direction(tp, second);
+       if (tp_gesture_same_directions(dir1, dir2))
+               return false;
+
+       first_moved = tp_gesture_mm_moved(tp, first);
+       first_mm = hypot(first_moved.x, first_moved.y);
+       if (first_mm < PINCH_DISAMBIGUATION_MOVE_THRESHOLD)
+               return false;
+
+       second_moved = tp_gesture_mm_moved(tp, second);
+       second_mm = hypot(second_moved.x, second_moved.y);
+       if (second_mm < PINCH_DISAMBIGUATION_MOVE_THRESHOLD)
+               return false;
+
+       return true;
+}
+
 static enum tp_gesture_state
 tp_gesture_handle_state_none(struct tp_dispatch *tp, uint64_t time)
 {
@@ -624,6 +653,16 @@ tp_gesture_handle_state_scroll(struct tp_dispatch *tp, uint64_t time)
        if (tp->scroll.method != LIBINPUT_CONFIG_SCROLL_2FG)
                return GESTURE_STATE_SCROLL;
 
+       /* We may confuse a pinch for a scroll initially,
+        * allow ourselves to correct our guess.
+        */
+       if (time < (tp->gesture.initial_time + DEFAULT_GESTURE_PINCH_TIMEOUT) &&
+           tp_gesture_is_pinch(tp)) {
+               tp_gesture_cancel(tp, time);
+               tp_gesture_init_pinch(tp);
+               return GESTURE_STATE_PINCH;
+       }
+
        raw = tp_get_average_touches_delta(tp);
 
        /* scroll is not accelerated */
index faa4fd58ad2455015b67c3ca48f02a33198a5ec9..7afd8882e5142686c0e1d8c56cf5272da330bde9 100644 (file)
@@ -3401,9 +3401,9 @@ START_TEST(touchpad_trackpoint_buttons_2fg_scroll)
 
        litest_drain_events(li);
 
-       litest_touch_down(touchpad, 0, 49, 70);
-       litest_touch_down(touchpad, 1, 51, 70);
-       litest_touch_move_two_touches(touchpad, 49, 70, 51, 70, 0, -40, 10);
+       litest_touch_down(touchpad, 0, 40, 70);
+       litest_touch_down(touchpad, 1, 60, 70);
+       litest_touch_move_two_touches(touchpad, 40, 70, 60, 70, 0, -40, 10);
 
        libinput_dispatch(li);
        litest_wait_for_event(li);