From 4536b5b38faea0e43168f2349dd4c13b6489ff6e Mon Sep 17 00:00:00 2001 From: Matt Mayfield Date: Fri, 21 Jun 2019 13:19:22 +1000 Subject: [PATCH] touchpad: revamp thumb detection Instead of a simple yes/no/maybe for thumbs, have a more extensive state machine that keeps track of the thumb. Since we only support one thumb anyway, the tracking moves to the tp_dispatch struct. Test case changes: touchpad_clickfinger_3fg_tool_position: with better thumb detection we can now handle this properly and expect a right button (2fg) press for the test case touchpad_thumb_no_doublethumb_with_timeout: two thumbs are now always two fingers, so let's switch to axis events here Signed-off-by: Peter Hutterer --- src/evdev-mt-touchpad-gestures.c | 16 +- src/evdev-mt-touchpad-tap.c | 2 +- src/evdev-mt-touchpad-thumb.c | 330 ++++++++++++++++++++++++++------------- src/evdev-mt-touchpad.c | 20 +-- src/evdev-mt-touchpad.h | 34 ++-- test/litest.c | 6 - test/litest.h | 3 - test/test-touchpad-buttons.c | 4 +- test/test-touchpad.c | 164 ++++--------------- 9 files changed, 294 insertions(+), 285 deletions(-) diff --git a/src/evdev-mt-touchpad-gestures.c b/src/evdev-mt-touchpad-gestures.c index 7d4a13f..4a0287c 100644 --- a/src/evdev-mt-touchpad-gestures.c +++ b/src/evdev-mt-touchpad-gestures.c @@ -549,10 +549,16 @@ tp_gesture_handle_state_unknown(struct tp_dispatch *tp, uint64_t time) 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 @@ -711,8 +717,8 @@ tp_gesture_post_events(struct tp_dispatch *tp, uint64_t time) 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; diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c index 551c70f..a482849 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -1015,7 +1015,7 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time) /* 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; } diff --git a/src/evdev-mt-touchpad-thumb.c b/src/evdev-mt-touchpad-thumb.c index 41e6f41..5b0cf96 100644 --- a/src/evdev-mt-touchpad-thumb.c +++ b/src/evdev-mt-touchpad-thumb.c @@ -25,70 +25,79 @@ #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; } @@ -101,10 +110,94 @@ tp_thumb_detect_pressure_size(const struct tp_dispatch *tp, 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 @@ -112,77 +205,62 @@ tp_thumb_update_touch(struct tp_dispatch *tp, 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 @@ -190,45 +268,81 @@ tp_thumb_update_multifinger(struct tp_dispatch *tp) { 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 @@ -289,6 +403,8 @@ tp_init_thumb(struct tp_dispatch *tp) } } + tp_thumb_reset(tp); + quirks_unref(q); evdev_log_debug(device, diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index 8f43b4b..4ffc4a3 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -353,8 +353,6 @@ tp_begin_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) 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; @@ -784,7 +782,7 @@ tp_touch_active_for_gesture(const struct tp_dispatch *tp, const struct tp_touch 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); } @@ -1717,11 +1715,9 @@ tp_process_state(struct tp_dispatch *tp, uint64_t time) } } - /* 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) @@ -1770,6 +1766,9 @@ tp_post_process_state(struct tp_dispatch *tp, uint64_t time) tp->queued = TOUCHPAD_EVENT_NONE; + if (tp->nfingers_down == 0) + tp_thumb_reset(tp); + tp_tap_post_process_state(tp); } @@ -1961,6 +1960,8 @@ tp_clear_state(struct tp_dispatch *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); @@ -1970,6 +1971,8 @@ tp_clear_state(struct tp_dispatch *tp) } tp_release_fake_touches(tp); + tp_thumb_reset(tp); + tp_handle_state(tp, now); } @@ -3301,7 +3304,6 @@ tp_init_sendevents(struct tp_dispatch *tp, tp_keyboard_timeout, tp); } - static bool tp_pass_sanity_check(struct tp_dispatch *tp, struct evdev_device *device) diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index ef1265b..5df284f 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -139,9 +139,13 @@ enum tp_gesture_state { }; 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 { @@ -238,12 +242,6 @@ struct tp_touch { } 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; @@ -457,6 +455,10 @@ struct tp_dispatch { bool use_size; int size_threshold; + + enum tp_thumb_state state; + unsigned int index; + bool pinch_eligible; } thumb; struct { @@ -685,7 +687,14 @@ bool 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); @@ -704,9 +713,4 @@ tp_thumb_update_multifinger(struct tp_dispatch *tp); 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 diff --git a/test/litest.c b/test/litest.c index b31a3f4..5b09ec4 100644 --- a/test/litest.c +++ b/test/litest.c @@ -3809,12 +3809,6 @@ litest_timeout_hysteresis(void) } 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); diff --git a/test/litest.h b/test/litest.h index 98b536b..85a0a1f 100644 --- a/test/litest.h +++ b/test/litest.h @@ -848,9 +848,6 @@ void litest_timeout_hysteresis(void); void -litest_timeout_thumb(void); - -void litest_push_event_frame(struct litest_device *dev); void diff --git a/test/test-touchpad-buttons.c b/test/test-touchpad-buttons.c index 73c0a60..4759b89 100644 --- a/test/test-touchpad-buttons.c +++ b/test/test-touchpad-buttons.c @@ -817,9 +817,9 @@ START_TEST(touchpad_clickfinger_3fg_tool_position) 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 diff --git a/test/test-touchpad.c b/test/test-touchpad.c index 32d4fb3..3850676 100644 --- a/test/test-touchpad.c +++ b/test/test-touchpad.c @@ -5006,7 +5006,7 @@ has_thumb_detect(struct litest_device *dev) 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; @@ -5018,86 +5018,38 @@ START_TEST(touchpad_thumb_area_begin_no_motion) 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); @@ -5147,10 +5099,6 @@ START_TEST(touchpad_thumb_area_clickfinger) 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; @@ -5162,13 +5110,7 @@ START_TEST(touchpad_thumb_area_clickfinger) 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); @@ -5189,13 +5131,7 @@ START_TEST(touchpad_thumb_area_clickfinger) 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); @@ -5217,10 +5153,6 @@ START_TEST(touchpad_thumb_area_btnarea) 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; @@ -5232,11 +5164,8 @@ START_TEST(touchpad_thumb_area_btnarea) 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 */ @@ -5265,17 +5194,17 @@ START_TEST(touchpad_thumb_no_doublethumb) 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); @@ -5286,42 +5215,6 @@ START_TEST(touchpad_thumb_no_doublethumb) } 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(); @@ -7137,15 +7030,12 @@ TEST_COLLECTION(touchpad) 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); -- 2.7.4