touchpad: restore thumb detection while keeping fixes from !292
authorMatt Mayfield <mdmayfield@users.noreply.github.com>
Sat, 4 Apr 2020 17:56:21 +0000 (12:56 -0500)
committerPeter Hutterer <peter.hutterer@who-t.net>
Fri, 22 May 2020 02:16:05 +0000 (02:16 +0000)
!292 improved libinput's ability to detect multiple-finger clicks when
the fingers were not aligned close to horizontally. However that caused
thumb detection to fail in several use cases.

This patch restores thumb detection for
- 2+ finger physical clickpad presses
- resting thumb while two-finger scrolling
- touches in the thumb exclusion area during multi-finger taps
and improves pinch detection when thumb is centered below fingers.

It also further enhances the flexibility of finger position for 2-, 3-,
or 4-finger taps: if all tapping fingers land on the touchpad within a
short time (currently 100ms), they will all count regardless of
position (unless below the lower_thumb_line).

Signed-off-by: Matt Mayfield <mdmayfield@yahoo.com>
src/evdev-mt-touchpad-gestures.c
src/evdev-mt-touchpad-thumb.c
src/evdev-mt-touchpad.c
src/evdev-mt-touchpad.h

index 375a07103355ef7762acfd52dfd25a69f0b6dfcd..6c63183bc9abb52c6aa97d8974cdf73b37b8d99c 100644 (file)
@@ -390,28 +390,30 @@ tp_gesture_handle_state_none(struct tp_dispatch *tp, uint64_t time)
        first = touches[0];
        second = touches[1];
 
-       /* For 3+ finger gestures we cheat. A human hand's finger
-        * arrangement means that for a 3 or 4 finger swipe gesture, the
-        * fingers are roughly arranged in a horizontal line.
-        * They will all move in the same direction, so we can simply look
-        * at the left and right-most ones only. If we have fake touches, we
-        * just take the left/right-most real touch position, since the fake
-        * touch has the same location as one of those.
+       /* For 3+ finger gestures, we only really need to track two touches.
+        * The human hand's finger arrangement means that for a pinch, the
+        * bottom-most touch will always be the thumb, and the top-most touch
+        * will always be one of the fingers.
         *
-        * For a 3 or 4 finger pinch gesture, 2 or 3 fingers are roughly in
-        * a horizontal line, with the thumb below and left (right-handed
-        * users) or right (left-handed users). Again, the row of non-thumb
-        * fingers moves identically so we can look at the left and
-        * right-most only and then treat it like a two-finger
-        * gesture.
+        * For 3+ finger swipes, the fingers will likely (but not necessarily)
+        * be in a horizontal line. They all move together, regardless, so it
+        * doesn't really matter which two of those touches we track.
+        *
+        * Tracking top and bottom is a change from previous versions, where
+        * we tracked leftmost and rightmost. This change enables:
+        *
+        * - More accurate pinch detection if thumb is near the center
+        * - Better resting-thumb detection while two-finger scrolling
+        * - On capable hardware, allow 3- or 4-finger swipes with resting
+        *   thumb or held-down clickpad
         */
        if (ntouches > 2) {
                second = touches[0];
 
                for (i = 1; i < ntouches && i < tp->num_slots; i++) {
-                       if (touches[i]->point.x < first->point.x)
+                       if (touches[i]->point.y < first->point.y)
                                first = touches[i];
-                       else if (touches[i]->point.x > second->point.x)
+                       else if (touches[i]->point.y >= second->point.y)
                                second = touches[i];
                }
 
index 3d9f9991ca23eca15cf0222c27215c37fe866fd8..2beaa54c93886ecfeecc3793bbc0222078bc4c4b 100644 (file)
@@ -28,6 +28,7 @@
 /* distance between fingers to assume it is not a scroll */
 #define SCROLL_MM_X 35
 #define SCROLL_MM_Y 25
+#define THUMB_TIMEOUT ms2us(100)
 
 static inline const char*
 thumb_state_to_str(enum tp_thumb_state state)
@@ -271,13 +272,15 @@ tp_thumb_update_multifinger(struct tp_dispatch *tp)
        struct tp_touch *t;
        struct tp_touch *first = NULL,
                        *second = NULL,
-                       *newest = NULL;
+                       *newest = NULL,
+                       *oldest = 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).
+        * count overall, and the newest and oldest touches.
         */
        tp_for_each_touch(tp, t) {
                if (t->state == TOUCH_NONE ||
@@ -290,6 +293,10 @@ tp_thumb_update_multifinger(struct tp_dispatch *tp)
                speed_exceeded_count = max(speed_exceeded_count,
                                           t->speed.exceeded_count);
 
+               if (!oldest || t->initial_time < oldest->initial_time) {
+                       oldest = t;
+               }
+
                if (!first) {
                        first = t;
                        continue;
@@ -315,11 +322,12 @@ tp_thumb_update_multifinger(struct tp_dispatch *tp)
 
        /* Speed-based thumb detection: if an existing finger is moving, and
         * a new touch arrives, mark it as a thumb if it doesn't qualify as a
-        * 2-finger scroll.
+        * 2-finger scroll. Also account for a thumb dropping onto the touchpad
+        * while scrolling or swiping.
         */
        if (newest &&
            tp->thumb.state == THUMB_STATE_FINGER &&
-           tp->nfingers_down == 2 &&
+           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))) {
@@ -330,20 +338,43 @@ tp_thumb_update_multifinger(struct tp_dispatch *tp)
                return;
        }
 
-       /* Position-based thumb detection: When a new touch arrives, check the
-        * two lowest touches. If they qualify for 2-finger scrolling, clear
-        * thumb status.
+       /* Contextual thumb detection: When a new touch arrives, check the
+        * timing and position of the two lowest touches.
         *
-        * If they were in distinct diagonal position, then mark the lower
-        * touch (based on pinch_eligible) as either PINCH or SUPPRESSED. If
-        * we're too close together for a thumb, lift that.
+        * If both touches are very close, regardless of timing, and no matter
+        * their absolute position on the touchpad, count them both as live
+        * to support responsive two-finger scrolling.
         */
-       if (mm.y > SCROLL_MM_Y && mm.x > SCROLL_MM_X) {
+
+       if (mm.x < SCROLL_MM_X && mm.y < SCROLL_MM_Y) {
+               tp_thumb_lift(tp);
+               return;
+       }
+
+       /* If all the touches arrived within a very short time, and all of them
+        * are above the lower_thumb_line, assume the touches are all live to
+        * enable double, triple, and quadruple taps, clicks, and gestures. (If
+        * there is an actual resting thumb, it will be detected later based on
+        * the behavior of the other touches.)
+        */
+
+       if ((newest->initial_time - oldest->initial_time) < THUMB_TIMEOUT &&
+         first->point.y < tp->thumb.lower_thumb_line) {
+               tp_thumb_lift(tp);
+               return;
+       }
+
+       /* If we're past the THUMB_TIMEOUT, and the touches are relatively far
+        * apart, then the new touch is unlikely to be a tap or clickfinger.
+        * Proceed with pre-1.14.901 thumb detection.
+       */
+
+       if (mm.y > SCROLL_MM_Y) {
                if (tp->thumb.pinch_eligible)
                        tp_thumb_pinch(tp, first);
                else
                        tp_thumb_suppress(tp, first);
-       } else if (mm.x < SCROLL_MM_X && mm.y < SCROLL_MM_Y) {
+       } else {
                tp_thumb_lift(tp);
        }
 }
index 42e8dec64a65d9414fa5e714d4babd7425112719..f84102fe3c3569c10c2e9043acec6889b53d2ff6 100644 (file)
@@ -350,6 +350,7 @@ tp_begin_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
        t->dirty = true;
        t->state = TOUCH_BEGIN;
        t->time = time;
+       t->initial_time = time;
        t->was_down = true;
        tp->nfingers_down++;
        t->palm.time = time;
index b981a61092ea03716054dd9880486d746ba792b4..75a87d4afcf95045210070e45d3aacab52ba74c7 100644 (file)
@@ -158,6 +158,7 @@ struct tp_touch {
        bool dirty;
        struct device_coords point;
        uint64_t time;
+       uint64_t initial_time;
        int pressure;
        bool is_tool_palm; /* MT_TOOL_PALM */
        int major, minor;