{
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) {
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);
t->millis = time;
tp->nfingers_down++;
t->palm.time = time;
+ t->is_thumb = false;
+ t->tap.is_thumb = false;
assert(tp->nfingers_down >= 1);
}
else
tp_end_sequence(tp, t, time);
break;
+ case ABS_MT_PRESSURE:
+ t->pressure = e->value;
+ break;
}
}
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);
}
}
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)
{
struct tp_touch *t;
if (!t->dirty)
continue;
+ tp_thumb_detect(tp, t);
tp_palm_detect(tp, t, time);
tp_motion_hysteresis(tp, t);
}
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)
{
if (tp_init_gesture(tp) != 0)
return -1;
+ if (tp_init_thumb(tp) != 0)
+ return -1;
+
device->seat_caps |= EVDEV_DEVICE_POINTER;
return 0;
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];
struct {
enum tp_tap_touch_state state;
struct device_coords initial;
+ bool is_thumb;
} tap;
struct {
double prev_scale;
double angle;
struct device_float_coords center;
+ uint32_t thumb_mask;
} gesture;
struct {
uint64_t keyboard_last_press_time;
} dwt;
+
+ struct {
+ bool detect_thumbs;
+ int threshold;
+ } thumb;
};
#define tp_for_each_touch(_tp, _t) \