Commit 73870d93 authored by Matt Mayfield's avatar Matt Mayfield Committed by Peter Hutterer

touchpad: restore thumb detection while keeping fixes from !292

!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's avatarMatt Mayfield <mdmayfield@yahoo.com>
parent 4ff6d6e3
Pipeline #150046 passed with stages
in 16 minutes and 2 seconds
......@@ -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];
}
......
......@@ -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.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 && mm.x > SCROLL_MM_X) {
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);
}
}
......
......@@ -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;
......
......@@ -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;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment