mirror of
https://gitlab.freedesktop.org/libinput/libinput.git
synced 2026-01-26 00:10:23 +01:00
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 <peter.hutterer@who-t.net>
This commit is contained in:
parent
dda6f733af
commit
4536b5b38f
9 changed files with 294 additions and 285 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
t->thumb.state = THUMB_STATE_MAYBE;
|
||||
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)
|
||||
{
|
||||
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)
|
||||
if (!tp->thumb.detect_thumbs)
|
||||
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);
|
||||
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;
|
||||
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_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);
|
||||
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.
|
||||
*/
|
||||
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);
|
||||
/* Handle the thumb lifting off the touchpad */
|
||||
if (t->state == TOUCH_END && t->index == tp->thumb.index) {
|
||||
tp_thumb_lift(tp);
|
||||
return;
|
||||
}
|
||||
|
||||
/* 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 this touch is not the only one, thumb updates happen by context
|
||||
* instead of here
|
||||
*/
|
||||
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)
|
||||
first = t;
|
||||
else
|
||||
second = t;
|
||||
if (t->state == TOUCH_BEGIN)
|
||||
newest = t;
|
||||
|
||||
if (first && second)
|
||||
break;
|
||||
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;
|
||||
continue;
|
||||
}
|
||||
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
@ -237,12 +241,6 @@ struct tp_touch {
|
|||
struct device_coords initial;
|
||||
} 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;
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -3808,12 +3808,6 @@ litest_timeout_hysteresis(void)
|
|||
msleep(90);
|
||||
}
|
||||
|
||||
void
|
||||
litest_timeout_thumb(void)
|
||||
{
|
||||
msleep(320);
|
||||
}
|
||||
|
||||
void
|
||||
litest_push_event_frame(struct litest_device *dev)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -847,9 +847,6 @@ litest_timeout_touch_arbitration(void);
|
|||
void
|
||||
litest_timeout_hysteresis(void);
|
||||
|
||||
void
|
||||
litest_timeout_thumb(void);
|
||||
|
||||
void
|
||||
litest_push_event_frame(struct litest_device *dev);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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_move_to(dev, 0, 55, 99, 60, 99, 50);
|
||||
litest_assert_empty_queue(li);
|
||||
|
||||
/* 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_update_no_motion)
|
||||
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);
|
||||
|
||||
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);
|
||||
litest_touch_up(dev, 0);
|
||||
|
||||
litest_assert_empty_queue(li);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(touchpad_thumb_area_small_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);
|
||||
|
||||
/* movement less than the threshold */
|
||||
/* Thumb below lower line - fast movement - 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, 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_touch_move_to(dev, 0, 50, 99, 90, 99, 30);
|
||||
litest_drain_events(li);
|
||||
|
||||
/* moving within the area triggers events */
|
||||
litest_touch_down(dev, 0, 50, 99);
|
||||
litest_touch_move_to(dev, 0, 50, 99, 80, 99, 10);
|
||||
/* 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);
|
||||
litest_touch_down(dev, 0, 90, 99); /* thumb */
|
||||
libinput_dispatch(li);
|
||||
litest_timeout_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);
|
||||
/* 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);
|
||||
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();
|
||||
libinput_dispatch(li);
|
||||
|
||||
/* move touch to trigger the thumb detection */
|
||||
litest_touch_move(dev, 1, 70, 99.2);
|
||||
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);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue