touchpad: revamp thumb detection
authorMatt Mayfield <mdmayfield@users.noreply.github.com>
Fri, 21 Jun 2019 03:19:22 +0000 (13:19 +1000)
committerPeter Hutterer <peter.hutterer@who-t.net>
Tue, 16 Jul 2019 23:33:14 +0000 (09:33 +1000)
Instead of a simple yes/no/maybe for thumbs, have a more extensive state
machine that keeps track of the thumb. Since we only support one thumb anyway,
the tracking moves to the tp_dispatch struct.

Test case changes:
touchpad_clickfinger_3fg_tool_position:
  with better thumb detection we can now handle this properly and expect a
  right button (2fg) press for the test case
touchpad_thumb_no_doublethumb_with_timeout:
  two thumbs are now always two fingers, so let's switch to axis events here

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
src/evdev-mt-touchpad-gestures.c
src/evdev-mt-touchpad-tap.c
src/evdev-mt-touchpad-thumb.c
src/evdev-mt-touchpad.c
src/evdev-mt-touchpad.h
test/litest.c
test/litest.h
test/test-touchpad-buttons.c
test/test-touchpad.c

index 7d4a13f..4a0287c 100644 (file)
@@ -549,10 +549,16 @@ tp_gesture_handle_state_unknown(struct tp_dispatch *tp, uint64_t time)
                        return GESTURE_STATE_SCROLL;
                }
 
-               /* If we get here, either both fingers have passed the inner
-                * threshold (handled below), or >2 fingers are involved
-                * (handled in a future event when both have moved enough).
+               /* If more than 2 fingers are involved, and the thumb moves
+                * while the fingers stay still, assume a pinch if eligible.
                 */
+               if (finger_mm < inner &&
+                   tp->gesture.finger_count > 2 &&
+                   tp->gesture.enabled &&
+                   tp->thumb.pinch_eligible) {
+                       tp_gesture_init_pinch(tp);
+                       return GESTURE_STATE_PINCH;
+               }
        }
 
        /* If either touch is still inside the inner threshold, we can't
@@ -711,8 +717,8 @@ tp_gesture_post_events(struct tp_dispatch *tp, uint64_t time)
        if (tp->gesture.finger_count == 0)
                return;
 
-       /* When tap-and-dragging, or a clickpad is clicked force 1fg mode */
-       if (tp_tap_dragging(tp) || (tp->buttons.is_clickpad && tp->buttons.state)) {
+       /* When tap-and-dragging, force 1fg mode. */
+       if (tp_tap_dragging(tp)) {
                tp_gesture_cancel(tp, time);
                tp->gesture.finger_count = 1;
                tp->gesture.finger_count_pending = 0;
index 551c70f..a482849 100644 (file)
@@ -1015,7 +1015,7 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time)
                        /* The simple version: if a touch is a thumb on
                         * begin we ignore it. All other thumb touches
                         * follow the normal tap state for now */
-                       if (tp_thumb_ignored(tp, t)) {
+                       if (tp_thumb_ignored_for_tap(tp, t)) {
                                t->tap.is_thumb = true;
                                continue;
                        }
index 41e6f41..5b0cf96 100644 (file)
 #include "config.h"
 #include "evdev-mt-touchpad.h"
 
-#define THUMB_MOVE_TIMEOUT ms2us(300)
-
-bool
-tp_thumb_ignored(const struct tp_dispatch *tp, const struct tp_touch *t)
-{
-       return t->thumb.state == THUMB_STATE_YES;
-}
+/* distance between fingers to assume it is not a scroll */
+#define SCROLL_MM_X 35
+#define SCROLL_MM_Y 25
 
 static inline const char*
 thumb_state_to_str(enum tp_thumb_state state)
 {
        switch(state){
-       CASE_RETURN_STRING(THUMB_STATE_NO);
-       CASE_RETURN_STRING(THUMB_STATE_YES);
-       CASE_RETURN_STRING(THUMB_STATE_MAYBE);
+       CASE_RETURN_STRING(THUMB_STATE_FINGER);
+       CASE_RETURN_STRING(THUMB_STATE_JAILED);
+       CASE_RETURN_STRING(THUMB_STATE_PINCH);
+       CASE_RETURN_STRING(THUMB_STATE_SUPPRESSED);
+       CASE_RETURN_STRING(THUMB_STATE_REVIVED);
+       CASE_RETURN_STRING(THUMB_STATE_REVIVED_JAILED);
+       CASE_RETURN_STRING(THUMB_STATE_DEAD);
        }
 
        return NULL;
 }
 
-void
+static void
 tp_thumb_set_state(struct tp_dispatch *tp,
                   struct tp_touch *t,
                   enum tp_thumb_state state)
 {
-       if (t->thumb.state == state)
+       unsigned int index = t ? t->index : UINT_MAX;
+
+       if (tp->thumb.state == state && tp->thumb.index == index)
                return;
 
        evdev_log_debug(tp->device,
                        "thumb: touch %d, %s → %s\n",
-                       t->index,
-                       thumb_state_to_str(t->thumb.state),
+                       (int)index,
+                       thumb_state_to_str(tp->thumb.state),
                        thumb_state_to_str(state));
 
-       t->thumb.state = state;
+       tp->thumb.state = state;
+       tp->thumb.index = index;
 }
 
 void
-tp_thumb_reset(struct tp_dispatch *tp, struct tp_touch *t)
+tp_thumb_reset(struct tp_dispatch *tp)
+{
+       tp->thumb.state = THUMB_STATE_FINGER;
+       tp->thumb.index = UINT_MAX;
+       tp->thumb.pinch_eligible = true;
+}
+
+static void
+tp_thumb_lift(struct tp_dispatch *tp)
 {
-       t->thumb.state = THUMB_STATE_MAYBE;
+       tp->thumb.state = THUMB_STATE_FINGER;
+       tp->thumb.index = UINT_MAX;
 }
 
 static bool
 tp_thumb_in_exclusion_area(const struct tp_dispatch *tp,
-                          const struct tp_touch *t,
-                          uint64_t time)
+                          const struct tp_touch *t)
 {
        return (t->point.y > tp->thumb.lower_thumb_line &&
-               tp->scroll.method != LIBINPUT_CONFIG_SCROLL_EDGE &&
-               t->thumb.first_touch_time + THUMB_MOVE_TIMEOUT < time);
+               tp->scroll.method != LIBINPUT_CONFIG_SCROLL_EDGE);
 
 }
 
 static bool
 tp_thumb_detect_pressure_size(const struct tp_dispatch *tp,
-                             const struct tp_touch *t,
-                             uint64_t time)
+                             const struct tp_touch *t)
 {
        bool is_thumb = false;
 
        if (tp->thumb.use_pressure &&
            t->pressure > tp->thumb.pressure_threshold &&
-           tp_thumb_in_exclusion_area(tp, t, time)) {
+           tp_thumb_in_exclusion_area(tp, t)) {
                is_thumb = true;
        }
 
@@ -101,10 +110,94 @@ tp_thumb_detect_pressure_size(const struct tp_dispatch *tp,
        return is_thumb;
 }
 
+static bool
+tp_thumb_needs_jail(const struct tp_dispatch *tp, const struct tp_touch *t)
+{
+       if (t->point.y < tp->thumb.upper_thumb_line)
+               return false;
+
+       if (!tp_thumb_in_exclusion_area(tp, t) &&
+           !tp_thumb_detect_pressure_size(tp, t))
+               return false;
+
+       if (t->speed.exceeded_count >= 10)
+               return false;
+
+       return true;
+}
+
+bool
+tp_thumb_ignored(const struct tp_dispatch *tp, const struct tp_touch *t)
+{
+       return (tp->thumb.detect_thumbs &&
+               tp->thumb.index == t->index &&
+               (tp->thumb.state == THUMB_STATE_JAILED ||
+                tp->thumb.state == THUMB_STATE_PINCH ||
+                tp->thumb.state == THUMB_STATE_SUPPRESSED ||
+                tp->thumb.state == THUMB_STATE_REVIVED_JAILED ||
+                tp->thumb.state == THUMB_STATE_DEAD));
+}
+
+bool
+tp_thumb_ignored_for_tap(const struct tp_dispatch *tp,
+                        const struct tp_touch *t)
+{
+       return (tp->thumb.detect_thumbs &&
+               tp->thumb.index == t->index &&
+               (tp->thumb.state == THUMB_STATE_PINCH ||
+                tp->thumb.state == THUMB_STATE_SUPPRESSED ||
+                tp->thumb.state == THUMB_STATE_DEAD));
+}
+
+bool
+tp_thumb_ignored_for_gesture(const struct tp_dispatch *tp,
+                            const struct tp_touch *t)
+{
+       return (tp->thumb.detect_thumbs &&
+               tp->thumb.index == t->index &&
+               (tp->thumb.state == THUMB_STATE_JAILED ||
+                tp->thumb.state == THUMB_STATE_SUPPRESSED ||
+                tp->thumb.state == THUMB_STATE_REVIVED_JAILED ||
+                tp->thumb.state == THUMB_STATE_DEAD));
+}
+
 void
 tp_thumb_suppress(struct tp_dispatch *tp, struct tp_touch *t)
 {
-       tp_thumb_set_state(tp, t, THUMB_STATE_YES);
+       if(tp->thumb.state == THUMB_STATE_FINGER ||
+          tp->thumb.state == THUMB_STATE_JAILED ||
+          tp->thumb.state == THUMB_STATE_PINCH ||
+          tp->thumb.index != t->index) {
+               tp_thumb_set_state(tp, t, THUMB_STATE_SUPPRESSED);
+               return;
+       }
+
+       tp_thumb_set_state(tp, t, THUMB_STATE_DEAD);
+}
+
+static void
+tp_thumb_pinch(struct tp_dispatch *tp, struct tp_touch *t)
+{
+       if(tp->thumb.state == THUMB_STATE_FINGER ||
+          tp->thumb.state == THUMB_STATE_JAILED ||
+          tp->thumb.index != t->index)
+               tp_thumb_set_state(tp, t, THUMB_STATE_PINCH);
+       else if (tp->thumb.state != THUMB_STATE_PINCH)
+               tp_thumb_suppress(tp, t);
+}
+
+static void
+tp_thumb_revive(struct tp_dispatch *tp, struct tp_touch *t)
+{
+       if((tp->thumb.state != THUMB_STATE_SUPPRESSED &&
+           tp->thumb.state != THUMB_STATE_PINCH) ||
+          (tp->thumb.index != t->index))
+               return;
+
+       if(tp_thumb_needs_jail(tp, t))
+               tp_thumb_set_state(tp, t, THUMB_STATE_REVIVED_JAILED);
+       else
+               tp_thumb_set_state(tp, t, THUMB_STATE_REVIVED);
 }
 
 void
@@ -112,77 +205,62 @@ tp_thumb_update_touch(struct tp_dispatch *tp,
                      struct tp_touch *t,
                      uint64_t time)
 {
-       /* once a thumb, always a thumb, once ruled out always ruled out */
-       if (!tp->thumb.detect_thumbs ||
-           t->thumb.state != THUMB_STATE_MAYBE)
-               return;
-
-       if (t->point.y < tp->thumb.upper_thumb_line) {
-               /* if a potential thumb is above the line, it won't ever
-                * label as thumb */
-               tp_thumb_set_state(tp, t, THUMB_STATE_NO);
+       if (!tp->thumb.detect_thumbs)
                return;
-       }
-
-       /* If the thumb moves by more than 7mm, it's not a resting thumb */
-       if (t->state == TOUCH_BEGIN) {
-               t->thumb.initial = t->point;
-       } else if (t->state == TOUCH_UPDATE) {
-               struct device_float_coords delta;
-               struct phys_coords mm;
-
-               delta = device_delta(t->point, t->thumb.initial);
-               mm = tp_phys_delta(tp, delta);
-               if (length_in_mm(mm) > 7) {
-                       tp_thumb_set_state(tp, t, THUMB_STATE_NO);
-                       return;
-               }
-       }
 
-       /* If the finger is below the upper thumb line and we have another
-        * finger in the same area, neither finger is a thumb (unless we've
-        * already labeled it as such).
+       /* Once any active touch exceeds the speed threshold, don't
+        * try to detect pinches until all touches lift.
         */
-       if (t->point.y > tp->thumb.upper_thumb_line &&
-           tp->nfingers_down > 1) {
-               struct tp_touch *other;
-
-               tp_for_each_touch(tp, other) {
-                       if (other->state != TOUCH_BEGIN &&
-                           other->state != TOUCH_UPDATE)
-                               continue;
-
-                       if (other->point.y > tp->thumb.upper_thumb_line) {
-                               tp_thumb_set_state(tp, t, THUMB_STATE_NO);
-                               if (other->thumb.state == THUMB_STATE_MAYBE)
-                                       tp_thumb_set_state(tp,
-                                                          other,
-                                                          THUMB_STATE_NO);
+       if (t->speed.exceeded_count >= 10 &&
+           tp->thumb.pinch_eligible &&
+           tp->gesture.state == GESTURE_STATE_NONE) {
+               tp->thumb.pinch_eligible = false;
+               if(tp->thumb.state == THUMB_STATE_PINCH) {
+                       struct tp_touch *thumb;
+                       tp_for_each_touch(tp, thumb) {
+                               if (thumb->index != tp->thumb.index)
+                                       continue;
+
+                               tp_thumb_set_state(tp, thumb, THUMB_STATE_SUPPRESSED);
                                break;
                        }
                }
        }
 
-       /* Note: a thumb at the edge of the touchpad won't trigger the
-        * threshold, the surface area is usually too small. So we have a
-        * two-stage detection: pressure and time within the area.
-        * A finger that remains at the very bottom of the touchpad becomes
-        * a thumb.
+       /* Handle the thumb lifting off the touchpad */
+       if (t->state == TOUCH_END && t->index == tp->thumb.index) {
+               tp_thumb_lift(tp);
+               return;
+       }
+
+       /* If this touch is not the only one, thumb updates happen by context
+        * instead of here
         */
-       if (tp_thumb_detect_pressure_size(tp, t, time) ||
-           tp_thumb_in_exclusion_area(tp, t, time))
-               tp_thumb_set_state(tp, t, THUMB_STATE_YES);
-
-       /* now what? we marked it as thumb, so:
-        *
-        * - pointer motion must ignore this touch
-        * - clickfinger must ignore this touch for finger count
-        * - software buttons are unaffected
-        * - edge scrolling unaffected
-        * - gestures: unaffected
-        * - tapping: honour thumb on begin, ignore it otherwise for now,
-        *   this gets a tad complicated otherwise
+       if (tp->nfingers_down > 1)
+               return;
+
+       /* If we arrived here by other fingers lifting off, revive current touch
+        * if appropriate
+        */
+       tp_thumb_revive(tp, t);
+
+       /* First new touch below the lower_thumb_line, or below the upper_thumb_
+        * line if hardware can't verify it's a finger, starts as JAILED.
+        */
+       if (t->state == TOUCH_BEGIN && tp_thumb_needs_jail(tp, t)) {
+               tp_thumb_set_state(tp, t, THUMB_STATE_JAILED);
+               return;
+       }
+
+       /* If a touch breaks the speed threshold, or leaves the thumb area
+        * (upper or lower, depending on HW detection), it "escapes" jail.
         */
+       if (tp->thumb.state == THUMB_STATE_JAILED &&
+           !(tp_thumb_needs_jail(tp, t)))
+               tp_thumb_set_state(tp, t, THUMB_STATE_FINGER);
+       if (tp->thumb.state == THUMB_STATE_REVIVED_JAILED &&
+           !(tp_thumb_needs_jail(tp, t)))
+               tp_thumb_set_state(tp, t, THUMB_STATE_REVIVED);
 }
 
 void
@@ -190,45 +268,81 @@ tp_thumb_update_multifinger(struct tp_dispatch *tp)
 {
        struct tp_touch *t;
        struct tp_touch *first = NULL,
-                       *second = NULL;
+                       *second = NULL,
+                       *newest = NULL;
        struct device_coords distance;
        struct phys_coords mm;
+       unsigned int speed_exceeded_count = 0;
 
+       /* Get the first and second bottom-most touches, the max speed exceeded
+        * count overall, and the newest touch (or one of them, if more).
+        */
        tp_for_each_touch(tp, t) {
                if (t->state == TOUCH_NONE ||
                    t->state == TOUCH_HOVERING)
                        continue;
 
-               if (t->state != TOUCH_BEGIN)
+               if (t->state == TOUCH_BEGIN)
+                       newest = t;
+
+               speed_exceeded_count = max(speed_exceeded_count,
+                                          t->speed.exceeded_count);
+
+               if (!first) {
+                       first = t;
+                       continue;
+               }
+
+               if (t->point.y > first->point.y) {
+                       second = first;
                        first = t;
-               else
-                       second = t;
+                       continue;
+               }
 
-               if (first && second)
-                       break;
+               if (!second || t->point.y > second->point.y ) {
+                       second = t;
+               }
        }
 
+       if (!first || !second)
+               return;
+
        assert(first);
        assert(second);
 
-       if (tp->scroll.method == LIBINPUT_CONFIG_SCROLL_2FG) {
-               /* If the second finger comes down next to the other one, we
-                * assume this is a scroll motion.
-                */
-               distance.x = abs(first->point.x - second->point.x);
-               distance.y = abs(first->point.y - second->point.y);
-               mm = evdev_device_unit_delta_to_mm(tp->device, &distance);
+       distance.x = abs(first->point.x - second->point.x);
+       distance.y = abs(first->point.y - second->point.y);
+       mm = evdev_device_unit_delta_to_mm(tp->device, &distance);
 
-               if (mm.x <= 25 && mm.y <= 15)
-                       return;
+       /* Speed-based thumb detection: if an existing touch is moving, and
+        * a new touch arrives, mark it as a thumb if it doesn't qualify as a
+        * 2-finger scroll.
+        */
+       if (newest &&
+           tp->nfingers_down == 2 &&
+           speed_exceeded_count > 5 &&
+           (tp->scroll.method != LIBINPUT_CONFIG_SCROLL_2FG ||
+            (mm.x > SCROLL_MM_X || mm.y > SCROLL_MM_Y))) {
+               evdev_log_debug(tp->device,
+                               "touch %d is speed-based thumb\n",
+                               newest->index);
+               tp_thumb_suppress(tp, newest);
+               return;
        }
 
-       /* Finger are too far apart or 2fg scrolling is disabled, mark
-        * second finger as thumb */
-       evdev_log_debug(tp->device,
-                       "touch %d is speed-based thumb\n",
-                       second->index);
-       tp_thumb_suppress(tp, second);
+       /* Position-based thumb detection: When a new touch arrives, check the
+        * two lowest touches. If they qualify for 2-finger scrolling, clear
+        * thumb status. If not, mark the lower touch (based on pinch_eligible)
+        * as either PINCH or SUPPRESSED.
+        */
+       if (mm.y > SCROLL_MM_Y) {
+               if (tp->thumb.pinch_eligible)
+                       tp_thumb_pinch(tp, first);
+               else
+                       tp_thumb_suppress(tp, first);
+       } else {
+               tp_thumb_lift(tp);
+       }
 }
 
 void
@@ -289,6 +403,8 @@ tp_init_thumb(struct tp_dispatch *tp)
                }
        }
 
+       tp_thumb_reset(tp);
+
        quirks_unref(q);
 
        evdev_log_debug(device,
index 8f43b4b..4ffc4a3 100644 (file)
@@ -353,8 +353,6 @@ tp_begin_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
        t->was_down = true;
        tp->nfingers_down++;
        t->palm.time = time;
-       tp_thumb_reset(tp, t);
-       t->thumb.first_touch_time = time;
        t->tap.is_thumb = false;
        t->tap.is_palm = false;
        t->speed.exceeded_count = 0;
@@ -784,7 +782,7 @@ tp_touch_active_for_gesture(const struct tp_dispatch *tp, const struct tp_touch
        return (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE) &&
                t->palm.state == PALM_NONE &&
                !t->pinned.is_pinned &&
-               !tp_thumb_ignored(tp, t) &&
+               !tp_thumb_ignored_for_gesture(tp, t) &&
                tp_button_touch_active(tp, t) &&
                tp_edge_scroll_touch_active(tp, t);
 }
@@ -1717,11 +1715,9 @@ tp_process_state(struct tp_dispatch *tp, uint64_t time)
                }
        }
 
-       /* If we have one touch that exceeds the speed and we get a new
-        * touch down while doing that, the second touch is a thumb */
-       if (have_new_touch &&
-           tp->nfingers_down == 2 &&
-           speed_exceeded_count > 5)
+       if (tp->thumb.detect_thumbs &&
+           have_new_touch &&
+           tp->nfingers_down >= 2)
                tp_thumb_update_multifinger(tp);
 
        if (restart_filter)
@@ -1770,6 +1766,9 @@ tp_post_process_state(struct tp_dispatch *tp, uint64_t time)
 
        tp->queued = TOUCHPAD_EVENT_NONE;
 
+       if (tp->nfingers_down == 0)
+               tp_thumb_reset(tp);
+
        tp_tap_post_process_state(tp);
 }
 
@@ -1961,6 +1960,8 @@ tp_clear_state(struct tp_dispatch *tp)
         *
         * Then lift all touches so the touchpad is in a neutral state.
         *
+        * Then reset thumb state.
+        *
         */
        tp_release_all_buttons(tp, now);
        tp_release_all_taps(tp, now);
@@ -1970,6 +1971,8 @@ tp_clear_state(struct tp_dispatch *tp)
        }
        tp_release_fake_touches(tp);
 
+       tp_thumb_reset(tp);
+
        tp_handle_state(tp, now);
 }
 
@@ -3301,7 +3304,6 @@ tp_init_sendevents(struct tp_dispatch *tp,
                            tp_keyboard_timeout, tp);
 }
 
-
 static bool
 tp_pass_sanity_check(struct tp_dispatch *tp,
                     struct evdev_device *device)
index ef1265b..5df284f 100644 (file)
@@ -139,9 +139,13 @@ enum tp_gesture_state {
 };
 
 enum tp_thumb_state {
-       THUMB_STATE_NO,
-       THUMB_STATE_YES,
-       THUMB_STATE_MAYBE,
+       THUMB_STATE_FINGER,
+       THUMB_STATE_JAILED,
+       THUMB_STATE_PINCH,
+       THUMB_STATE_SUPPRESSED,
+       THUMB_STATE_REVIVED,
+       THUMB_STATE_REVIVED_JAILED,
+       THUMB_STATE_DEAD,
 };
 
 enum tp_jump_state {
@@ -238,12 +242,6 @@ struct tp_touch {
        } gesture;
 
        struct {
-               enum tp_thumb_state state;
-               uint64_t first_touch_time;
-               struct device_coords initial;
-       } thumb;
-
-       struct {
                double last_speed; /* speed in mm/s at last sample */
                unsigned int exceeded_count;
        } speed;
@@ -457,6 +455,10 @@ struct tp_dispatch {
 
                bool use_size;
                int size_threshold;
+
+               enum tp_thumb_state state;
+               unsigned int index;
+               bool pinch_eligible;
        } thumb;
 
        struct {
@@ -685,7 +687,14 @@ bool
 tp_thumb_ignored(const struct tp_dispatch *tp, const struct tp_touch *t);
 
 void
-tp_thumb_reset(struct tp_dispatch *tp, struct tp_touch *t);
+tp_thumb_reset(struct tp_dispatch *tp);
+
+bool
+tp_thumb_ignored_for_gesture(const struct tp_dispatch *tp, const struct tp_touch *t);
+
+bool
+tp_thumb_ignored_for_tap(const struct tp_dispatch *tp,
+                        const struct tp_touch *t);
 
 void
 tp_thumb_suppress(struct tp_dispatch *tp, struct tp_touch *t);
@@ -704,9 +713,4 @@ tp_thumb_update_multifinger(struct tp_dispatch *tp);
 void
 tp_init_thumb(struct tp_dispatch *tp);
 
-void
-tp_thumb_set_state(struct tp_dispatch *tp,
-                  struct tp_touch *t,
-                  enum tp_thumb_state state);
-
 #endif
index b31a3f4..5b09ec4 100644 (file)
@@ -3809,12 +3809,6 @@ litest_timeout_hysteresis(void)
 }
 
 void
-litest_timeout_thumb(void)
-{
-       msleep(320);
-}
-
-void
 litest_push_event_frame(struct litest_device *dev)
 {
        litest_assert_int_ge(dev->skip_ev_syn, 0);
index 98b536b..85a0a1f 100644 (file)
@@ -848,9 +848,6 @@ void
 litest_timeout_hysteresis(void);
 
 void
-litest_timeout_thumb(void);
-
-void
 litest_push_event_frame(struct litest_device *dev);
 
 void
index 73c0a60..4759b89 100644 (file)
@@ -817,9 +817,9 @@ START_TEST(touchpad_clickfinger_3fg_tool_position)
        litest_event(dev, EV_SYN, SYN_REPORT, 0);
        libinput_dispatch(li);
 
-       litest_assert_button_event(li, BTN_MIDDLE,
+       litest_assert_button_event(li, BTN_RIGHT,
                                   LIBINPUT_BUTTON_STATE_PRESSED);
-       litest_assert_button_event(li, BTN_MIDDLE,
+       litest_assert_button_event(li, BTN_RIGHT,
                                   LIBINPUT_BUTTON_STATE_RELEASED);
 }
 END_TEST
index 32d4fb3..3850676 100644 (file)
@@ -5006,7 +5006,7 @@ has_thumb_detect(struct litest_device *dev)
        return h >= 50.0;
 }
 
-START_TEST(touchpad_thumb_area_begin_no_motion)
+START_TEST(touchpad_thumb_lower_area_movement)
 {
        struct litest_device *dev = litest_current_device();
        struct libinput *li = dev->libinput;
@@ -5018,86 +5018,38 @@ START_TEST(touchpad_thumb_area_begin_no_motion)
 
        litest_drain_events(li);
 
+       /* Thumb below lower line - slow movement - no events */
        litest_touch_down(dev, 0, 50, 99);
-       libinput_dispatch(li);
-       litest_timeout_thumb();
-       libinput_dispatch(li);
-       litest_touch_move_to(dev, 0, 50, 99, 80, 99, 10);
-       litest_touch_up(dev, 0);
-
+       litest_touch_move_to(dev, 0, 55, 99, 60, 99, 50);
        litest_assert_empty_queue(li);
-}
-END_TEST
-
-START_TEST(touchpad_thumb_area_update_no_motion)
-{
-       struct litest_device *dev = litest_current_device();
-       struct libinput *li = dev->libinput;
-
-       litest_disable_tap(dev->libinput_device);
-       litest_enable_clickfinger(dev);
-
-       if (!has_thumb_detect(dev))
-               return;
-
-       litest_drain_events(li);
 
-       litest_touch_down(dev, 0, 59, 99);
-       litest_touch_move_to(dev, 0, 59, 99, 61, 99, 10);
-       libinput_dispatch(li);
-       /* the first move may trigger events, but not after the timeout */
-       litest_drain_events(li);
-       litest_timeout_thumb();
-       libinput_dispatch(li);
-       litest_touch_move_to(dev, 0, 61, 99, 80, 99, 10);
+       /* Thumb below lower line - fast movement - events */
+       litest_touch_move_to(dev, 0, 60, 99, 90, 99, 30);
        litest_touch_up(dev, 0);
 
-       litest_assert_empty_queue(li);
+       litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
 }
 END_TEST
 
-START_TEST(touchpad_thumb_area_small_move)
+START_TEST(touchpad_thumb_lower_area_movement_rethumb)
 {
        struct litest_device *dev = litest_current_device();
        struct libinput *li = dev->libinput;
 
-       litest_disable_tap(dev->libinput_device);
-       litest_enable_clickfinger(dev);
-
        if (!has_thumb_detect(dev))
                return;
 
-       litest_drain_events(li);
-
-       /* movement less than the threshold */
-       litest_touch_down(dev, 0, 50, 99);
-       libinput_dispatch(li);
-       litest_timeout_thumb();
-       libinput_dispatch(li);
-
-       litest_touch_move_to(dev, 0, 50, 99, 52, 99, 10);
-       litest_touch_up(dev, 0);
-
-       litest_assert_empty_queue(li);
-}
-END_TEST
-
-START_TEST(touchpad_thumb_area_large_move)
-{
-       struct litest_device *dev = litest_current_device();
-       struct libinput *li = dev->libinput;
-
        litest_disable_tap(dev->libinput_device);
-       litest_enable_clickfinger(dev);
-
-       if (!has_thumb_detect(dev))
-               return;
 
        litest_drain_events(li);
 
-       /* moving within the area triggers events */
+       /* Thumb below lower line - fast movement - events */
        litest_touch_down(dev, 0, 50, 99);
-       litest_touch_move_to(dev, 0, 50, 99, 80, 99, 10);
+       litest_touch_move_to(dev, 0, 50, 99, 90, 99, 30);
+       litest_drain_events(li);
+
+       /* slow movement after being a non-touch - still events */
+       litest_touch_move_to(dev, 0, 90, 99, 60, 99, 50);
        litest_touch_up(dev, 0);
 
        litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
@@ -5147,10 +5099,6 @@ START_TEST(touchpad_thumb_area_clickfinger)
        struct litest_device *dev = litest_current_device();
        struct libinput *li = dev->libinput;
        struct libinput_event *event;
-       struct axis_replacement axes[] = {
-               { ABS_MT_PRESSURE, 31 },
-               { -1, 0 }
-       };
 
        if (!has_thumb_detect(dev))
                return;
@@ -5162,13 +5110,7 @@ START_TEST(touchpad_thumb_area_clickfinger)
 
        litest_drain_events(li);
 
-       litest_touch_down(dev, 0, 50, 99);
-       libinput_dispatch(li);
-       litest_timeout_thumb();
-       libinput_dispatch(li);
-       /* Need an extra event because the thumb doesn't have proper timers.
-          Shouldn't matter in real life */
-       litest_touch_move_extended(dev, 0, 55, 99, axes);
+       litest_touch_down(dev, 0, 50, 99); /* thumb */
        libinput_dispatch(li);
        litest_touch_down(dev, 1, 60, 50);
        libinput_dispatch(li);
@@ -5189,13 +5131,7 @@ START_TEST(touchpad_thumb_area_clickfinger)
 
        litest_drain_events(li);
 
-       litest_touch_down(dev, 1, 60, 99);
-       libinput_dispatch(li);
-       litest_timeout_thumb();
-       libinput_dispatch(li);
-       /* Need an extra event because the thumb doesn't have proper timers.
-          Shouldn't matter in real life */
-       litest_touch_move_extended(dev, 1, 60, 99, axes);
+       litest_touch_down(dev, 1, 60, 99); /* thumb */
        libinput_dispatch(li);
        litest_touch_down(dev, 0, 50, 50);
        libinput_dispatch(li);
@@ -5217,10 +5153,6 @@ START_TEST(touchpad_thumb_area_btnarea)
        struct litest_device *dev = litest_current_device();
        struct libinput *li = dev->libinput;
        struct libinput_event *event;
-       struct axis_replacement axes[] = {
-               { ABS_MT_PRESSURE, 31 },
-               { -1, 0 }
-       };
 
        if (!has_thumb_detect(dev))
                return;
@@ -5232,11 +5164,8 @@ START_TEST(touchpad_thumb_area_btnarea)
 
        litest_drain_events(li);
 
-       litest_touch_down(dev, 0, 90, 99);
-       libinput_dispatch(li);
-       litest_timeout_thumb();
+       litest_touch_down(dev, 0, 90, 99); /* thumb */
        libinput_dispatch(li);
-       litest_touch_move_extended(dev, 0, 95, 99, axes);
        litest_button_click(dev, BTN_LEFT, true);
 
        /* button areas work as usual with a thumb */
@@ -5265,17 +5194,17 @@ START_TEST(touchpad_thumb_no_doublethumb)
 
        litest_drain_events(li);
 
+       /* two touches in thumb area but we can't have two thumbs */
        litest_touch_down(dev, 0, 50, 99);
-       litest_touch_down(dev, 1, 70, 99);
-       /* move touch to trigger the thumb detection */
-       litest_touch_move(dev, 0, 50, 99.2);
-
-       libinput_dispatch(li);
-       litest_timeout_thumb();
+       /* random sleep interval. we don't have a thumb timer, but let's not
+        * put both touches down and move them immediately because that
+        * should always be a scroll event anyway. Go with a delay in
+        * between to make it more likely that this is really testing thumb
+        * detection.
+        */
+       msleep(200);
        libinput_dispatch(li);
-
-       /* move touch to trigger the thumb detection */
-       litest_touch_move(dev, 1, 70, 99.2);
+       litest_touch_down(dev, 1, 70, 99);
        libinput_dispatch(li);
 
        litest_touch_move_two_touches(dev, 50, 99, 70, 99, 0, -20, 10);
@@ -5286,42 +5215,6 @@ START_TEST(touchpad_thumb_no_doublethumb)
 }
 END_TEST
 
-START_TEST(touchpad_thumb_no_doublethumb_with_timeout)
-{
-       struct litest_device *dev = litest_current_device();
-       struct libinput *li = dev->libinput;
-
-       litest_disable_tap(dev->libinput_device);
-       litest_enable_clickfinger(dev);
-
-       if (!has_thumb_detect(dev))
-               return;
-
-       litest_drain_events(li);
-
-       litest_touch_down(dev, 0, 50, 99.9);
-       libinput_dispatch(li);
-       litest_timeout_thumb();
-       libinput_dispatch(li);
-       /* Thumbs don't have a timeout handler, so we have to move the thumb
-        * a bit to trigger. */
-       litest_touch_move(dev, 0, 50, 99.8);
-
-       /* first touch should now be a thumb */
-
-       litest_touch_down(dev, 1, 70, 99.9);
-       libinput_dispatch(li);
-       litest_timeout_thumb();
-       libinput_dispatch(li);
-       litest_touch_move(dev, 1, 70, 99.8);
-       litest_touch_move_two_touches(dev, 50, 99, 70, 99, 0, -20, 10);
-       litest_touch_up(dev, 0);
-       litest_touch_up(dev, 1);
-
-       litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
-}
-END_TEST
-
 START_TEST(touchpad_tool_tripletap_touch_count)
 {
        struct litest_device *dev = litest_current_device();
@@ -7137,15 +7030,12 @@ TEST_COLLECTION(touchpad)
        litest_add_for_device("touchpad:dwt", touchpad_dwt_multiple_keyboards_bothkeys_modifier, LITEST_SYNAPTICS_I2C);
        litest_add_ranged_for_device("touchpad:dwt", touchpad_dwt_multiple_keyboards_remove, LITEST_SYNAPTICS_I2C, &twice);
 
-       litest_add("touchpad:thumb", touchpad_thumb_area_begin_no_motion, LITEST_CLICKPAD, LITEST_ANY);
-       litest_add("touchpad:thumb", touchpad_thumb_area_update_no_motion, LITEST_CLICKPAD, LITEST_ANY);
-       litest_add("touchpad:thumb", touchpad_thumb_area_small_move, LITEST_CLICKPAD, LITEST_ANY);
-       litest_add("touchpad:thumb", touchpad_thumb_area_large_move, LITEST_CLICKPAD, LITEST_ANY);
+       litest_add("touchpad:thumb", touchpad_thumb_lower_area_movement, LITEST_CLICKPAD, LITEST_ANY);
+       litest_add("touchpad:thumb", touchpad_thumb_lower_area_movement_rethumb, LITEST_CLICKPAD, LITEST_ANY);
        litest_add("touchpad:thumb", touchpad_thumb_speed_empty_slots, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
        litest_add("touchpad:thumb", touchpad_thumb_area_clickfinger, LITEST_CLICKPAD, LITEST_ANY);
        litest_add("touchpad:thumb", touchpad_thumb_area_btnarea, LITEST_CLICKPAD, LITEST_ANY);
        litest_add("touchpad:thumb", touchpad_thumb_no_doublethumb, LITEST_CLICKPAD, LITEST_ANY);
-       litest_add("touchpad:thumb", touchpad_thumb_no_doublethumb_with_timeout, LITEST_CLICKPAD, LITEST_ANY);
 
        litest_add_for_device("touchpad:bugs", touchpad_tool_tripletap_touch_count, LITEST_SYNAPTICS_TOPBUTTONPAD);
        litest_add_for_device("touchpad:bugs", touchpad_tool_tripletap_touch_count_late, LITEST_SYNAPTICS_TOPBUTTONPAD);