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];
}
/* 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)
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 ||
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;
/* 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))) {
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);
}
}