return GESTURE_STATE_SCROLL;
}
- /* If we get here, either both fingers have passed the inner
- * threshold (handled below), or >2 fingers are involved
- * (handled in a future event when both have moved enough).
+ /* If more than 2 fingers are involved, and the thumb moves
+ * while the fingers stay still, assume a pinch if eligible.
*/
+ if (finger_mm < inner &&
+ tp->gesture.finger_count > 2 &&
+ tp->gesture.enabled &&
+ tp->thumb.pinch_eligible) {
+ tp_gesture_init_pinch(tp);
+ return GESTURE_STATE_PINCH;
+ }
}
/* If either touch is still inside the inner threshold, we can't
if (tp->gesture.finger_count == 0)
return;
- /* When tap-and-dragging, or a clickpad is clicked force 1fg mode */
- if (tp_tap_dragging(tp) || (tp->buttons.is_clickpad && tp->buttons.state)) {
+ /* When tap-and-dragging, force 1fg mode. */
+ if (tp_tap_dragging(tp)) {
tp_gesture_cancel(tp, time);
tp->gesture.finger_count = 1;
tp->gesture.finger_count_pending = 0;
/* 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 (tp_thumb_ignored(tp, t)) {
+ if (tp_thumb_ignored_for_tap(tp, t)) {
t->tap.is_thumb = true;
continue;
}
#include "config.h"
#include "evdev-mt-touchpad.h"
-#define THUMB_MOVE_TIMEOUT ms2us(300)
-
-bool
-tp_thumb_ignored(const struct tp_dispatch *tp, const struct tp_touch *t)
-{
- return t->thumb.state == THUMB_STATE_YES;
-}
+/* distance between fingers to assume it is not a scroll */
+#define SCROLL_MM_X 35
+#define SCROLL_MM_Y 25
static inline const char*
thumb_state_to_str(enum tp_thumb_state state)
{
switch(state){
- CASE_RETURN_STRING(THUMB_STATE_NO);
- CASE_RETURN_STRING(THUMB_STATE_YES);
- CASE_RETURN_STRING(THUMB_STATE_MAYBE);
+ CASE_RETURN_STRING(THUMB_STATE_FINGER);
+ CASE_RETURN_STRING(THUMB_STATE_JAILED);
+ CASE_RETURN_STRING(THUMB_STATE_PINCH);
+ CASE_RETURN_STRING(THUMB_STATE_SUPPRESSED);
+ CASE_RETURN_STRING(THUMB_STATE_REVIVED);
+ CASE_RETURN_STRING(THUMB_STATE_REVIVED_JAILED);
+ CASE_RETURN_STRING(THUMB_STATE_DEAD);
}
return NULL;
}
-void
+static void
tp_thumb_set_state(struct tp_dispatch *tp,
struct tp_touch *t,
enum tp_thumb_state state)
{
- if (t->thumb.state == state)
+ unsigned int index = t ? t->index : UINT_MAX;
+
+ if (tp->thumb.state == state && tp->thumb.index == index)
return;
evdev_log_debug(tp->device,
"thumb: touch %d, %s → %s\n",
- t->index,
- thumb_state_to_str(t->thumb.state),
+ (int)index,
+ thumb_state_to_str(tp->thumb.state),
thumb_state_to_str(state));
- t->thumb.state = state;
+ tp->thumb.state = state;
+ tp->thumb.index = index;
}
void
-tp_thumb_reset(struct tp_dispatch *tp, struct tp_touch *t)
+tp_thumb_reset(struct tp_dispatch *tp)
+{
+ tp->thumb.state = THUMB_STATE_FINGER;
+ tp->thumb.index = UINT_MAX;
+ tp->thumb.pinch_eligible = true;
+}
+
+static void
+tp_thumb_lift(struct tp_dispatch *tp)
{
- t->thumb.state = THUMB_STATE_MAYBE;
+ tp->thumb.state = THUMB_STATE_FINGER;
+ tp->thumb.index = UINT_MAX;
}
static bool
tp_thumb_in_exclusion_area(const struct tp_dispatch *tp,
- const struct tp_touch *t,
- uint64_t time)
+ const struct tp_touch *t)
{
return (t->point.y > tp->thumb.lower_thumb_line &&
- tp->scroll.method != LIBINPUT_CONFIG_SCROLL_EDGE &&
- t->thumb.first_touch_time + THUMB_MOVE_TIMEOUT < time);
+ tp->scroll.method != LIBINPUT_CONFIG_SCROLL_EDGE);
}
static bool
tp_thumb_detect_pressure_size(const struct tp_dispatch *tp,
- const struct tp_touch *t,
- uint64_t time)
+ const struct tp_touch *t)
{
bool is_thumb = false;
if (tp->thumb.use_pressure &&
t->pressure > tp->thumb.pressure_threshold &&
- tp_thumb_in_exclusion_area(tp, t, time)) {
+ tp_thumb_in_exclusion_area(tp, t)) {
is_thumb = true;
}
return is_thumb;
}
+static bool
+tp_thumb_needs_jail(const struct tp_dispatch *tp, const struct tp_touch *t)
+{
+ if (t->point.y < tp->thumb.upper_thumb_line)
+ return false;
+
+ if (!tp_thumb_in_exclusion_area(tp, t) &&
+ !tp_thumb_detect_pressure_size(tp, t))
+ return false;
+
+ if (t->speed.exceeded_count >= 10)
+ return false;
+
+ return true;
+}
+
+bool
+tp_thumb_ignored(const struct tp_dispatch *tp, const struct tp_touch *t)
+{
+ return (tp->thumb.detect_thumbs &&
+ tp->thumb.index == t->index &&
+ (tp->thumb.state == THUMB_STATE_JAILED ||
+ tp->thumb.state == THUMB_STATE_PINCH ||
+ tp->thumb.state == THUMB_STATE_SUPPRESSED ||
+ tp->thumb.state == THUMB_STATE_REVIVED_JAILED ||
+ tp->thumb.state == THUMB_STATE_DEAD));
+}
+
+bool
+tp_thumb_ignored_for_tap(const struct tp_dispatch *tp,
+ const struct tp_touch *t)
+{
+ return (tp->thumb.detect_thumbs &&
+ tp->thumb.index == t->index &&
+ (tp->thumb.state == THUMB_STATE_PINCH ||
+ tp->thumb.state == THUMB_STATE_SUPPRESSED ||
+ tp->thumb.state == THUMB_STATE_DEAD));
+}
+
+bool
+tp_thumb_ignored_for_gesture(const struct tp_dispatch *tp,
+ const struct tp_touch *t)
+{
+ return (tp->thumb.detect_thumbs &&
+ tp->thumb.index == t->index &&
+ (tp->thumb.state == THUMB_STATE_JAILED ||
+ tp->thumb.state == THUMB_STATE_SUPPRESSED ||
+ tp->thumb.state == THUMB_STATE_REVIVED_JAILED ||
+ tp->thumb.state == THUMB_STATE_DEAD));
+}
+
void
tp_thumb_suppress(struct tp_dispatch *tp, struct tp_touch *t)
{
- tp_thumb_set_state(tp, t, THUMB_STATE_YES);
+ if(tp->thumb.state == THUMB_STATE_FINGER ||
+ tp->thumb.state == THUMB_STATE_JAILED ||
+ tp->thumb.state == THUMB_STATE_PINCH ||
+ tp->thumb.index != t->index) {
+ tp_thumb_set_state(tp, t, THUMB_STATE_SUPPRESSED);
+ return;
+ }
+
+ tp_thumb_set_state(tp, t, THUMB_STATE_DEAD);
+}
+
+static void
+tp_thumb_pinch(struct tp_dispatch *tp, struct tp_touch *t)
+{
+ if(tp->thumb.state == THUMB_STATE_FINGER ||
+ tp->thumb.state == THUMB_STATE_JAILED ||
+ tp->thumb.index != t->index)
+ tp_thumb_set_state(tp, t, THUMB_STATE_PINCH);
+ else if (tp->thumb.state != THUMB_STATE_PINCH)
+ tp_thumb_suppress(tp, t);
+}
+
+static void
+tp_thumb_revive(struct tp_dispatch *tp, struct tp_touch *t)
+{
+ if((tp->thumb.state != THUMB_STATE_SUPPRESSED &&
+ tp->thumb.state != THUMB_STATE_PINCH) ||
+ (tp->thumb.index != t->index))
+ return;
+
+ if(tp_thumb_needs_jail(tp, t))
+ tp_thumb_set_state(tp, t, THUMB_STATE_REVIVED_JAILED);
+ else
+ tp_thumb_set_state(tp, t, THUMB_STATE_REVIVED);
}
void
struct tp_touch *t,
uint64_t time)
{
- /* once a thumb, always a thumb, once ruled out always ruled out */
- if (!tp->thumb.detect_thumbs ||
- t->thumb.state != THUMB_STATE_MAYBE)
- return;
-
- if (t->point.y < tp->thumb.upper_thumb_line) {
- /* if a potential thumb is above the line, it won't ever
- * label as thumb */
- tp_thumb_set_state(tp, t, THUMB_STATE_NO);
+ if (!tp->thumb.detect_thumbs)
return;
- }
-
- /* If the thumb moves by more than 7mm, it's not a resting thumb */
- if (t->state == TOUCH_BEGIN) {
- t->thumb.initial = t->point;
- } else if (t->state == TOUCH_UPDATE) {
- struct device_float_coords delta;
- struct phys_coords mm;
-
- delta = device_delta(t->point, t->thumb.initial);
- mm = tp_phys_delta(tp, delta);
- if (length_in_mm(mm) > 7) {
- tp_thumb_set_state(tp, t, THUMB_STATE_NO);
- return;
- }
- }
- /* If the finger is below the upper thumb line and we have another
- * finger in the same area, neither finger is a thumb (unless we've
- * already labeled it as such).
+ /* Once any active touch exceeds the speed threshold, don't
+ * try to detect pinches until all touches lift.
*/
- if (t->point.y > tp->thumb.upper_thumb_line &&
- tp->nfingers_down > 1) {
- struct tp_touch *other;
-
- tp_for_each_touch(tp, other) {
- if (other->state != TOUCH_BEGIN &&
- other->state != TOUCH_UPDATE)
- continue;
-
- if (other->point.y > tp->thumb.upper_thumb_line) {
- tp_thumb_set_state(tp, t, THUMB_STATE_NO);
- if (other->thumb.state == THUMB_STATE_MAYBE)
- tp_thumb_set_state(tp,
- other,
- THUMB_STATE_NO);
+ if (t->speed.exceeded_count >= 10 &&
+ tp->thumb.pinch_eligible &&
+ tp->gesture.state == GESTURE_STATE_NONE) {
+ tp->thumb.pinch_eligible = false;
+ if(tp->thumb.state == THUMB_STATE_PINCH) {
+ struct tp_touch *thumb;
+ tp_for_each_touch(tp, thumb) {
+ if (thumb->index != tp->thumb.index)
+ continue;
+
+ tp_thumb_set_state(tp, thumb, THUMB_STATE_SUPPRESSED);
break;
}
}
}
- /* Note: a thumb at the edge of the touchpad won't trigger the
- * threshold, the surface area is usually too small. So we have a
- * two-stage detection: pressure and time within the area.
- * A finger that remains at the very bottom of the touchpad becomes
- * a thumb.
+ /* Handle the thumb lifting off the touchpad */
+ if (t->state == TOUCH_END && t->index == tp->thumb.index) {
+ tp_thumb_lift(tp);
+ return;
+ }
+
+ /* If this touch is not the only one, thumb updates happen by context
+ * instead of here
*/
- if (tp_thumb_detect_pressure_size(tp, t, time) ||
- tp_thumb_in_exclusion_area(tp, t, time))
- tp_thumb_set_state(tp, t, THUMB_STATE_YES);
-
- /* 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: unaffected
- * - tapping: honour thumb on begin, ignore it otherwise for now,
- * this gets a tad complicated otherwise
+ if (tp->nfingers_down > 1)
+ return;
+
+ /* If we arrived here by other fingers lifting off, revive current touch
+ * if appropriate
+ */
+ tp_thumb_revive(tp, t);
+
+ /* First new touch below the lower_thumb_line, or below the upper_thumb_
+ * line if hardware can't verify it's a finger, starts as JAILED.
+ */
+ if (t->state == TOUCH_BEGIN && tp_thumb_needs_jail(tp, t)) {
+ tp_thumb_set_state(tp, t, THUMB_STATE_JAILED);
+ return;
+ }
+
+ /* If a touch breaks the speed threshold, or leaves the thumb area
+ * (upper or lower, depending on HW detection), it "escapes" jail.
*/
+ if (tp->thumb.state == THUMB_STATE_JAILED &&
+ !(tp_thumb_needs_jail(tp, t)))
+ tp_thumb_set_state(tp, t, THUMB_STATE_FINGER);
+ if (tp->thumb.state == THUMB_STATE_REVIVED_JAILED &&
+ !(tp_thumb_needs_jail(tp, t)))
+ tp_thumb_set_state(tp, t, THUMB_STATE_REVIVED);
}
void
{
struct tp_touch *t;
struct tp_touch *first = NULL,
- *second = NULL;
+ *second = NULL,
+ *newest = 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).
+ */
tp_for_each_touch(tp, t) {
if (t->state == TOUCH_NONE ||
t->state == TOUCH_HOVERING)
continue;
- if (t->state != TOUCH_BEGIN)
+ if (t->state == TOUCH_BEGIN)
+ newest = t;
+
+ speed_exceeded_count = max(speed_exceeded_count,
+ t->speed.exceeded_count);
+
+ if (!first) {
+ first = t;
+ continue;
+ }
+
+ if (t->point.y > first->point.y) {
+ second = first;
first = t;
- else
- second = t;
+ continue;
+ }
- if (first && second)
- break;
+ if (!second || t->point.y > second->point.y ) {
+ second = t;
+ }
}
+ if (!first || !second)
+ return;
+
assert(first);
assert(second);
- if (tp->scroll.method == LIBINPUT_CONFIG_SCROLL_2FG) {
- /* If the second finger comes down next to the other one, we
- * assume this is a scroll motion.
- */
- distance.x = abs(first->point.x - second->point.x);
- distance.y = abs(first->point.y - second->point.y);
- mm = evdev_device_unit_delta_to_mm(tp->device, &distance);
+ distance.x = abs(first->point.x - second->point.x);
+ distance.y = abs(first->point.y - second->point.y);
+ mm = evdev_device_unit_delta_to_mm(tp->device, &distance);
- if (mm.x <= 25 && mm.y <= 15)
- return;
+ /* Speed-based thumb detection: if an existing touch is moving, and
+ * a new touch arrives, mark it as a thumb if it doesn't qualify as a
+ * 2-finger scroll.
+ */
+ if (newest &&
+ 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))) {
+ evdev_log_debug(tp->device,
+ "touch %d is speed-based thumb\n",
+ newest->index);
+ tp_thumb_suppress(tp, newest);
+ return;
}
- /* Finger are too far apart or 2fg scrolling is disabled, mark
- * second finger as thumb */
- evdev_log_debug(tp->device,
- "touch %d is speed-based thumb\n",
- second->index);
- tp_thumb_suppress(tp, second);
+ /* Position-based thumb detection: When a new touch arrives, check the
+ * two lowest touches. If they qualify for 2-finger scrolling, clear
+ * thumb status. If not, mark the lower touch (based on pinch_eligible)
+ * as either PINCH or SUPPRESSED.
+ */
+ if (mm.y > SCROLL_MM_Y) {
+ if (tp->thumb.pinch_eligible)
+ tp_thumb_pinch(tp, first);
+ else
+ tp_thumb_suppress(tp, first);
+ } else {
+ tp_thumb_lift(tp);
+ }
}
void
}
}
+ tp_thumb_reset(tp);
+
quirks_unref(q);
evdev_log_debug(device,
t->was_down = true;
tp->nfingers_down++;
t->palm.time = time;
- tp_thumb_reset(tp, t);
- t->thumb.first_touch_time = time;
t->tap.is_thumb = false;
t->tap.is_palm = false;
t->speed.exceeded_count = 0;
return (t->state == TOUCH_BEGIN || t->state == TOUCH_UPDATE) &&
t->palm.state == PALM_NONE &&
!t->pinned.is_pinned &&
- !tp_thumb_ignored(tp, t) &&
+ !tp_thumb_ignored_for_gesture(tp, t) &&
tp_button_touch_active(tp, t) &&
tp_edge_scroll_touch_active(tp, t);
}
}
}
- /* If we have one touch that exceeds the speed and we get a new
- * touch down while doing that, the second touch is a thumb */
- if (have_new_touch &&
- tp->nfingers_down == 2 &&
- speed_exceeded_count > 5)
+ if (tp->thumb.detect_thumbs &&
+ have_new_touch &&
+ tp->nfingers_down >= 2)
tp_thumb_update_multifinger(tp);
if (restart_filter)
tp->queued = TOUCHPAD_EVENT_NONE;
+ if (tp->nfingers_down == 0)
+ tp_thumb_reset(tp);
+
tp_tap_post_process_state(tp);
}
*
* Then lift all touches so the touchpad is in a neutral state.
*
+ * Then reset thumb state.
+ *
*/
tp_release_all_buttons(tp, now);
tp_release_all_taps(tp, now);
}
tp_release_fake_touches(tp);
+ tp_thumb_reset(tp);
+
tp_handle_state(tp, now);
}
tp_keyboard_timeout, tp);
}
-
static bool
tp_pass_sanity_check(struct tp_dispatch *tp,
struct evdev_device *device)
};
enum tp_thumb_state {
- THUMB_STATE_NO,
- THUMB_STATE_YES,
- THUMB_STATE_MAYBE,
+ THUMB_STATE_FINGER,
+ THUMB_STATE_JAILED,
+ THUMB_STATE_PINCH,
+ THUMB_STATE_SUPPRESSED,
+ THUMB_STATE_REVIVED,
+ THUMB_STATE_REVIVED_JAILED,
+ THUMB_STATE_DEAD,
};
enum tp_jump_state {
} gesture;
struct {
- enum tp_thumb_state state;
- uint64_t first_touch_time;
- struct device_coords initial;
- } thumb;
-
- struct {
double last_speed; /* speed in mm/s at last sample */
unsigned int exceeded_count;
} speed;
bool use_size;
int size_threshold;
+
+ enum tp_thumb_state state;
+ unsigned int index;
+ bool pinch_eligible;
} thumb;
struct {
tp_thumb_ignored(const struct tp_dispatch *tp, const struct tp_touch *t);
void
-tp_thumb_reset(struct tp_dispatch *tp, struct tp_touch *t);
+tp_thumb_reset(struct tp_dispatch *tp);
+
+bool
+tp_thumb_ignored_for_gesture(const struct tp_dispatch *tp, const struct tp_touch *t);
+
+bool
+tp_thumb_ignored_for_tap(const struct tp_dispatch *tp,
+ const struct tp_touch *t);
void
tp_thumb_suppress(struct tp_dispatch *tp, struct tp_touch *t);
void
tp_init_thumb(struct tp_dispatch *tp);
-void
-tp_thumb_set_state(struct tp_dispatch *tp,
- struct tp_touch *t,
- enum tp_thumb_state state);
-
#endif
}
void
-litest_timeout_thumb(void)
-{
- msleep(320);
-}
-
-void
litest_push_event_frame(struct litest_device *dev)
{
litest_assert_int_ge(dev->skip_ev_syn, 0);
litest_timeout_hysteresis(void);
void
-litest_timeout_thumb(void);
-
-void
litest_push_event_frame(struct litest_device *dev);
void
litest_event(dev, EV_SYN, SYN_REPORT, 0);
libinput_dispatch(li);
- litest_assert_button_event(li, BTN_MIDDLE,
+ litest_assert_button_event(li, BTN_RIGHT,
LIBINPUT_BUTTON_STATE_PRESSED);
- litest_assert_button_event(li, BTN_MIDDLE,
+ litest_assert_button_event(li, BTN_RIGHT,
LIBINPUT_BUTTON_STATE_RELEASED);
}
END_TEST
return h >= 50.0;
}
-START_TEST(touchpad_thumb_area_begin_no_motion)
+START_TEST(touchpad_thumb_lower_area_movement)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
litest_drain_events(li);
+ /* Thumb below lower line - slow movement - no events */
litest_touch_down(dev, 0, 50, 99);
- libinput_dispatch(li);
- litest_timeout_thumb();
- libinput_dispatch(li);
- litest_touch_move_to(dev, 0, 50, 99, 80, 99, 10);
- litest_touch_up(dev, 0);
-
+ litest_touch_move_to(dev, 0, 55, 99, 60, 99, 50);
litest_assert_empty_queue(li);
-}
-END_TEST
-
-START_TEST(touchpad_thumb_area_update_no_motion)
-{
- struct litest_device *dev = litest_current_device();
- struct libinput *li = dev->libinput;
-
- litest_disable_tap(dev->libinput_device);
- litest_enable_clickfinger(dev);
-
- if (!has_thumb_detect(dev))
- return;
-
- litest_drain_events(li);
- litest_touch_down(dev, 0, 59, 99);
- litest_touch_move_to(dev, 0, 59, 99, 61, 99, 10);
- libinput_dispatch(li);
- /* the first move may trigger events, but not after the timeout */
- litest_drain_events(li);
- litest_timeout_thumb();
- libinput_dispatch(li);
- litest_touch_move_to(dev, 0, 61, 99, 80, 99, 10);
+ /* Thumb below lower line - fast movement - events */
+ litest_touch_move_to(dev, 0, 60, 99, 90, 99, 30);
litest_touch_up(dev, 0);
- litest_assert_empty_queue(li);
+ litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
}
END_TEST
-START_TEST(touchpad_thumb_area_small_move)
+START_TEST(touchpad_thumb_lower_area_movement_rethumb)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
- litest_disable_tap(dev->libinput_device);
- litest_enable_clickfinger(dev);
-
if (!has_thumb_detect(dev))
return;
- litest_drain_events(li);
-
- /* movement less than the threshold */
- litest_touch_down(dev, 0, 50, 99);
- libinput_dispatch(li);
- litest_timeout_thumb();
- libinput_dispatch(li);
-
- litest_touch_move_to(dev, 0, 50, 99, 52, 99, 10);
- litest_touch_up(dev, 0);
-
- litest_assert_empty_queue(li);
-}
-END_TEST
-
-START_TEST(touchpad_thumb_area_large_move)
-{
- struct litest_device *dev = litest_current_device();
- struct libinput *li = dev->libinput;
-
litest_disable_tap(dev->libinput_device);
- litest_enable_clickfinger(dev);
-
- if (!has_thumb_detect(dev))
- return;
litest_drain_events(li);
- /* moving within the area triggers events */
+ /* Thumb below lower line - fast movement - events */
litest_touch_down(dev, 0, 50, 99);
- litest_touch_move_to(dev, 0, 50, 99, 80, 99, 10);
+ litest_touch_move_to(dev, 0, 50, 99, 90, 99, 30);
+ litest_drain_events(li);
+
+ /* slow movement after being a non-touch - still events */
+ litest_touch_move_to(dev, 0, 90, 99, 60, 99, 50);
litest_touch_up(dev, 0);
litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
struct libinput_event *event;
- struct axis_replacement axes[] = {
- { ABS_MT_PRESSURE, 31 },
- { -1, 0 }
- };
if (!has_thumb_detect(dev))
return;
litest_drain_events(li);
- litest_touch_down(dev, 0, 50, 99);
- libinput_dispatch(li);
- litest_timeout_thumb();
- libinput_dispatch(li);
- /* Need an extra event because the thumb doesn't have proper timers.
- Shouldn't matter in real life */
- litest_touch_move_extended(dev, 0, 55, 99, axes);
+ litest_touch_down(dev, 0, 50, 99); /* thumb */
libinput_dispatch(li);
litest_touch_down(dev, 1, 60, 50);
libinput_dispatch(li);
litest_drain_events(li);
- litest_touch_down(dev, 1, 60, 99);
- libinput_dispatch(li);
- litest_timeout_thumb();
- libinput_dispatch(li);
- /* Need an extra event because the thumb doesn't have proper timers.
- Shouldn't matter in real life */
- litest_touch_move_extended(dev, 1, 60, 99, axes);
+ litest_touch_down(dev, 1, 60, 99); /* thumb */
libinput_dispatch(li);
litest_touch_down(dev, 0, 50, 50);
libinput_dispatch(li);
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
struct libinput_event *event;
- struct axis_replacement axes[] = {
- { ABS_MT_PRESSURE, 31 },
- { -1, 0 }
- };
if (!has_thumb_detect(dev))
return;
litest_drain_events(li);
- litest_touch_down(dev, 0, 90, 99);
- libinput_dispatch(li);
- litest_timeout_thumb();
+ litest_touch_down(dev, 0, 90, 99); /* thumb */
libinput_dispatch(li);
- litest_touch_move_extended(dev, 0, 95, 99, axes);
litest_button_click(dev, BTN_LEFT, true);
/* button areas work as usual with a thumb */
litest_drain_events(li);
+ /* two touches in thumb area but we can't have two thumbs */
litest_touch_down(dev, 0, 50, 99);
- litest_touch_down(dev, 1, 70, 99);
- /* move touch to trigger the thumb detection */
- litest_touch_move(dev, 0, 50, 99.2);
-
- libinput_dispatch(li);
- litest_timeout_thumb();
+ /* random sleep interval. we don't have a thumb timer, but let's not
+ * put both touches down and move them immediately because that
+ * should always be a scroll event anyway. Go with a delay in
+ * between to make it more likely that this is really testing thumb
+ * detection.
+ */
+ msleep(200);
libinput_dispatch(li);
-
- /* move touch to trigger the thumb detection */
- litest_touch_move(dev, 1, 70, 99.2);
+ litest_touch_down(dev, 1, 70, 99);
libinput_dispatch(li);
litest_touch_move_two_touches(dev, 50, 99, 70, 99, 0, -20, 10);
}
END_TEST
-START_TEST(touchpad_thumb_no_doublethumb_with_timeout)
-{
- struct litest_device *dev = litest_current_device();
- struct libinput *li = dev->libinput;
-
- litest_disable_tap(dev->libinput_device);
- litest_enable_clickfinger(dev);
-
- if (!has_thumb_detect(dev))
- return;
-
- litest_drain_events(li);
-
- litest_touch_down(dev, 0, 50, 99.9);
- libinput_dispatch(li);
- litest_timeout_thumb();
- libinput_dispatch(li);
- /* Thumbs don't have a timeout handler, so we have to move the thumb
- * a bit to trigger. */
- litest_touch_move(dev, 0, 50, 99.8);
-
- /* first touch should now be a thumb */
-
- litest_touch_down(dev, 1, 70, 99.9);
- libinput_dispatch(li);
- litest_timeout_thumb();
- libinput_dispatch(li);
- litest_touch_move(dev, 1, 70, 99.8);
- litest_touch_move_two_touches(dev, 50, 99, 70, 99, 0, -20, 10);
- litest_touch_up(dev, 0);
- litest_touch_up(dev, 1);
-
- litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_MOTION);
-}
-END_TEST
-
START_TEST(touchpad_tool_tripletap_touch_count)
{
struct litest_device *dev = litest_current_device();
litest_add_for_device("touchpad:dwt", touchpad_dwt_multiple_keyboards_bothkeys_modifier, LITEST_SYNAPTICS_I2C);
litest_add_ranged_for_device("touchpad:dwt", touchpad_dwt_multiple_keyboards_remove, LITEST_SYNAPTICS_I2C, &twice);
- litest_add("touchpad:thumb", touchpad_thumb_area_begin_no_motion, LITEST_CLICKPAD, LITEST_ANY);
- litest_add("touchpad:thumb", touchpad_thumb_area_update_no_motion, LITEST_CLICKPAD, LITEST_ANY);
- litest_add("touchpad:thumb", touchpad_thumb_area_small_move, LITEST_CLICKPAD, LITEST_ANY);
- litest_add("touchpad:thumb", touchpad_thumb_area_large_move, LITEST_CLICKPAD, LITEST_ANY);
+ litest_add("touchpad:thumb", touchpad_thumb_lower_area_movement, LITEST_CLICKPAD, LITEST_ANY);
+ litest_add("touchpad:thumb", touchpad_thumb_lower_area_movement_rethumb, LITEST_CLICKPAD, LITEST_ANY);
litest_add("touchpad:thumb", touchpad_thumb_speed_empty_slots, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
litest_add("touchpad:thumb", touchpad_thumb_area_clickfinger, LITEST_CLICKPAD, LITEST_ANY);
litest_add("touchpad:thumb", touchpad_thumb_area_btnarea, LITEST_CLICKPAD, LITEST_ANY);
litest_add("touchpad:thumb", touchpad_thumb_no_doublethumb, LITEST_CLICKPAD, LITEST_ANY);
- litest_add("touchpad:thumb", touchpad_thumb_no_doublethumb_with_timeout, LITEST_CLICKPAD, LITEST_ANY);
litest_add_for_device("touchpad:bugs", touchpad_tool_tripletap_touch_count, LITEST_SYNAPTICS_TOPBUTTONPAD);
litest_add_for_device("touchpad:bugs", touchpad_tool_tripletap_touch_count_late, LITEST_SYNAPTICS_TOPBUTTONPAD);