diff --git a/src/evdev-mt-touchpad-gestures.c b/src/evdev-mt-touchpad-gestures.c index 375a0710..6c63183b 100644 --- a/src/evdev-mt-touchpad-gestures.c +++ b/src/evdev-mt-touchpad-gestures.c @@ -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]; } diff --git a/src/evdev-mt-touchpad-thumb.c b/src/evdev-mt-touchpad-thumb.c index 3d9f9991..2beaa54c 100644 --- a/src/evdev-mt-touchpad-thumb.c +++ b/src/evdev-mt-touchpad-thumb.c @@ -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); } } diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 42e8dec6..f84102fe 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -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; diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index b981a610..75a87d4a 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -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;