mirror of
https://gitlab.freedesktop.org/libinput/libinput.git
synced 2026-05-03 20:08:05 +02:00
touchpad: add pressure-based thumb-detection
All touchpad recordings seen so far show that a value above 100 is definitely a thumb or a palm. Values below are harder to discern, and the same isn't true for touchpads supporting ABS_PRESSURE instead of ABS_MT_PRESSURE. The handling of a touch is as outlined in tp_thumb_detect: * thumbs are ignored for pointer motion * thumbs cancel gestures * thumbs are ignored for clickfinger count * edge scrolling doesn't care either way * software buttons don't care either way * tap: only if thumb on begin The handling of thumbs while tapping is the simplest approach only, more to come in follow-up patches. Note that "thumb" is the synonym for "this touch is too big to be a fingertip". Which means that a light thumb touch will still be counted as a finger. The side-effect here is that thumbs resting a the bottom edge of the touchpad will almost certainly not trigger the pressure threshold because most of the thumb is off the touchpad. Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net> Reviewed-by: Hans de Goede <hdegoede@redhat.com>
This commit is contained in:
parent
4a3288dc59
commit
3dcf28b919
5 changed files with 110 additions and 1 deletions
|
|
@ -832,6 +832,9 @@ tp_check_clickfinger_distance(struct tp_dispatch *tp,
|
|||
if (!t1 || !t2)
|
||||
return 0;
|
||||
|
||||
if (t1->is_thumb || t2->is_thumb)
|
||||
return 0;
|
||||
|
||||
x = abs(t1->point.x - t2->point.x);
|
||||
y = abs(t1->point.y - t2->point.y);
|
||||
|
||||
|
|
|
|||
|
|
@ -525,11 +525,34 @@ tp_gesture_handle_state(struct tp_dispatch *tp, uint64_t time)
|
|||
{
|
||||
unsigned int active_touches = 0;
|
||||
struct tp_touch *t;
|
||||
uint32_t old_thumb_mask, thumb_mask = 0;
|
||||
int i = 0;
|
||||
|
||||
tp_for_each_touch(tp, t)
|
||||
tp_for_each_touch(tp, t) {
|
||||
if (tp_touch_active(tp, t))
|
||||
active_touches++;
|
||||
|
||||
if (t->is_thumb)
|
||||
thumb_mask |= 1 << i;
|
||||
i++;
|
||||
}
|
||||
|
||||
old_thumb_mask = tp->gesture.thumb_mask;
|
||||
tp->gesture.thumb_mask = thumb_mask;
|
||||
|
||||
/* active touches does not include thumb touches, need to count those
|
||||
* separately, in a bitmask.
|
||||
* then, if the finger count changes and/or the thumb count changes
|
||||
* -> cancel gesture.
|
||||
*/
|
||||
if (thumb_mask != old_thumb_mask) {
|
||||
/* if a thumb is detected during a gesture, that gesture is
|
||||
* cancelled and the user effectively needs to restart. we
|
||||
* could be smarter, but the complexity isn't worth it */
|
||||
tp_gesture_cancel(tp, time);
|
||||
return;
|
||||
}
|
||||
|
||||
if (active_touches != tp->gesture.finger_count) {
|
||||
/* If all fingers are lifted immediately end the gesture */
|
||||
if (active_touches == 0) {
|
||||
|
|
|
|||
|
|
@ -691,7 +691,20 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time)
|
|||
tp->queued & TOUCHPAD_EVENT_BUTTON_PRESS)
|
||||
t->tap.state = TAP_TOUCH_STATE_DEAD;
|
||||
|
||||
/* If a touch was considered thumb for tapping once, we
|
||||
* ignore it for the rest of lifetime */
|
||||
if (t->tap.is_thumb)
|
||||
continue;
|
||||
|
||||
if (t->state == TOUCH_BEGIN) {
|
||||
/* 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 (t->is_thumb) {
|
||||
t->tap.is_thumb = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
t->tap.state = TAP_TOUCH_STATE_TOUCH;
|
||||
t->tap.initial = t->point;
|
||||
tp_tap_handle_event(tp, t, TAP_EVENT_TOUCH, time);
|
||||
|
|
|
|||
|
|
@ -212,6 +212,8 @@ tp_begin_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
|
|||
t->millis = time;
|
||||
tp->nfingers_down++;
|
||||
t->palm.time = time;
|
||||
t->is_thumb = false;
|
||||
t->tap.is_thumb = false;
|
||||
assert(tp->nfingers_down >= 1);
|
||||
}
|
||||
|
||||
|
|
@ -314,6 +316,9 @@ tp_process_absolute(struct tp_dispatch *tp,
|
|||
else
|
||||
tp_end_sequence(tp, t, time);
|
||||
break;
|
||||
case ABS_MT_PRESSURE:
|
||||
t->pressure = e->value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -461,6 +466,7 @@ tp_touch_active(struct tp_dispatch *tp, struct tp_touch *t)
|
|||
return (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE) &&
|
||||
t->palm.state == PALM_NONE &&
|
||||
!t->pinned.is_pinned &&
|
||||
!t->is_thumb &&
|
||||
tp_button_touch_active(tp, t) &&
|
||||
tp_edge_scroll_touch_active(tp, t);
|
||||
}
|
||||
|
|
@ -602,6 +608,33 @@ out:
|
|||
t->palm.state == PALM_TYPING ? "typing" : "trackpoint");
|
||||
}
|
||||
|
||||
static void
|
||||
tp_thumb_detect(struct tp_dispatch *tp, struct tp_touch *t)
|
||||
{
|
||||
/* once a thumb, always a thumb */
|
||||
if (!tp->thumb.detect_thumbs || t->is_thumb)
|
||||
return;
|
||||
|
||||
/* Note: a thumb at the edge of the touchpad won't trigger the
|
||||
* threshold, the surface areas is usually too small.
|
||||
*/
|
||||
if (t->pressure < tp->thumb.threshold)
|
||||
return;
|
||||
|
||||
t->is_thumb = true;
|
||||
|
||||
/* 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: cancel
|
||||
* - tapping: honour thumb on begin, ignore it otherwise for now,
|
||||
* this gets a tad complicated otherwise
|
||||
*/
|
||||
}
|
||||
|
||||
static void
|
||||
tp_unhover_abs_distance(struct tp_dispatch *tp, uint64_t time)
|
||||
{
|
||||
|
|
@ -720,6 +753,7 @@ tp_process_state(struct tp_dispatch *tp, uint64_t time)
|
|||
if (!t->dirty)
|
||||
continue;
|
||||
|
||||
tp_thumb_detect(tp, t);
|
||||
tp_palm_detect(tp, t, time);
|
||||
|
||||
tp_motion_hysteresis(tp, t);
|
||||
|
|
@ -1476,6 +1510,30 @@ tp_init_sendevents(struct tp_dispatch *tp,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
tp_init_thumb(struct tp_dispatch *tp)
|
||||
{
|
||||
struct evdev_device *device = tp->device;
|
||||
const struct input_absinfo *abs;
|
||||
|
||||
abs = libevdev_get_abs_info(device->evdev, ABS_MT_PRESSURE);
|
||||
if (!abs)
|
||||
return 0;
|
||||
|
||||
if (abs->maximum - abs->minimum < 255)
|
||||
return 0;
|
||||
|
||||
/* The touchpads we looked at so far have a clear thumb threshold of
|
||||
* ~100, you don't reach that with a normal finger interaction.
|
||||
* Note: "thumb" means massive touch that should not interact, not
|
||||
* "using the tip of my thumb for a pinch gestures".
|
||||
*/
|
||||
tp->thumb.threshold = 100;
|
||||
tp->thumb.detect_thumbs = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
tp_sanity_check(struct tp_dispatch *tp,
|
||||
struct evdev_device *device)
|
||||
|
|
@ -1558,6 +1616,9 @@ tp_init(struct tp_dispatch *tp,
|
|||
if (tp_init_gesture(tp) != 0)
|
||||
return -1;
|
||||
|
||||
if (tp_init_thumb(tp) != 0)
|
||||
return -1;
|
||||
|
||||
device->seat_caps |= EVDEV_DEVICE_POINTER;
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -141,9 +141,11 @@ struct tp_touch {
|
|||
enum touch_state state;
|
||||
bool has_ended; /* TRACKING_ID == -1 */
|
||||
bool dirty;
|
||||
bool is_thumb;
|
||||
struct device_coords point;
|
||||
uint64_t millis;
|
||||
int distance; /* distance == 0 means touch */
|
||||
int pressure;
|
||||
|
||||
struct {
|
||||
struct device_coords samples[TOUCHPAD_HISTORY_LENGTH];
|
||||
|
|
@ -173,6 +175,7 @@ struct tp_touch {
|
|||
struct {
|
||||
enum tp_tap_touch_state state;
|
||||
struct device_coords initial;
|
||||
bool is_thumb;
|
||||
} tap;
|
||||
|
||||
struct {
|
||||
|
|
@ -234,6 +237,7 @@ struct tp_dispatch {
|
|||
double prev_scale;
|
||||
double angle;
|
||||
struct device_float_coords center;
|
||||
uint32_t thumb_mask;
|
||||
} gesture;
|
||||
|
||||
struct {
|
||||
|
|
@ -317,6 +321,11 @@ struct tp_dispatch {
|
|||
|
||||
uint64_t keyboard_last_press_time;
|
||||
} dwt;
|
||||
|
||||
struct {
|
||||
bool detect_thumbs;
|
||||
int threshold;
|
||||
} thumb;
|
||||
};
|
||||
|
||||
#define tp_for_each_touch(_tp, _t) \
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue