diff --git a/doc/touchpad-tap-state-machine.svg b/doc/touchpad-tap-state-machine.svg index 1bf640ec..cae45429 100644 --- a/doc/touchpad-tap-state-machine.svg +++ b/doc/touchpad-tap-state-machine.svg @@ -1,1253 +1,1248 @@ - + - + - - - - - - - + + + + + + + - - IDLE + IDLE - + - - TOUCH + TOUCH - + - - first - - finger down + first + finger down - - - + + + - - finger up + finger up - - - + + + - - button 1 - - press + button 1 + press - + - - timeout + timeout - - - + + + - - move > - - threshold + move > + threshold - - - + + + - - second - - finger down + second + finger down - - - + + + - - TOUCH_2 + TOUCH_2 - + - - second - - finger up + second + finger up - - - + + + - - button 2 - - press + button 2 + press - + - - move > - - threshold + move > + threshold - + - - timeout + timeout - - - - - + + + + + - - button 1 - - release + button 1 + release - + - - button 2 - - release + button 2 + release - - - - - + + + + + - - TAPPED + TAPPED - + - - timeout + timeout - - - + + + - - first - - finger down + first + finger down - - - + + + - - DRAGGING + DRAGGING - + - - first - - finger up + first + finger up - + - - btn1 - - release + btn1 + release - - - - - - - + + + + + + + - - IDLE + IDLE - + - - third - - finger down + third + finger down - - - + + + - - TOUCH_3 + TOUCH_3 - - - + + + - - button 3 - - press + button 3 + press - + - - button 3 - - release + button 3 + release - - - + + + - - move > - - threshold + move > + threshold - - - + + + - - IDLE + IDLE - + - - timeout + timeout - - - + + + - - first - - finger up + first + finger up - - - + + + - - IDLE + IDLE - + - - fourth - - finger down + fourth + finger down - - - - - + + + + + - - DRAGGING_OR_DOUBLETAP + DRAGGING_OR_DOUBLETAP - - - + + + - - timeout + timeout - - - + + + - - first - - finger up + first + finger up - - - + + + - - button 1 - - release + button 1 + release - + - - button 1 - - press + button 1 + press - + - - btn1 - - release + btn1 + release - - - + + + - - second - - finger down + second + finger down - - - + + + - - move > - - threshold + move > + threshold - - - - - + + + + + - - HOLD + HOLD - + - - first - - finger up + first + finger up - - - - - + + + + + - - second - - finger down + second + finger down - - - - - + + + + + - - TOUCH_2_HOLD + TOUCH_2_HOLD - + - - second - - finger up + second + finger up - - - + + + - - first - - finger up + first + finger up - - - - - - - + + + + + - - third - - finger down + third + finger down - - - - - - - + + + + + + + - - TOUCH_3_HOLD + TOUCH_3_HOLD - - - + + + - - fourth - - finger down + fourth + finger down - + - - DEAD + DEAD - - - - - - - + + + + + + + - - any finger up + any finger up - + - - fourth - - finger up + fourth + finger up - + - - any finger up + any finger up - - - - + + + + - - - yes + + yes - + - - any finger up + any finger up - - - - - - - - - + + + + + + + + + - - IDLE + IDLE - + - - if finger - - count == 0 + if finger + count == 0 - - - - - - - + + + + + + + - - second - - finger up + second + finger up - + - - DRAGGING_2 + DRAGGING_2 - - - - - + + + + + - - first - - finger up + first + finger up - - - - - - - + + + + + + + - - second - - finger down + second + finger down - - - - - - - + + + + + + + - - third - - finger down + third + finger down - - - + + + - - btn1 - - release + btn1 + release - - - + + + - - phys - - button - - press + phys + button + press - - - - - - - - - - - - - + + + + + + + + + + + + + - - phys - - button - - press + phys + button + press - - - + + + - - button 1 - - release + button 1 + release - - - - - - - - - - - + + + + + + + + + + + - - DRAGGING_WAIT + DRAGGING_WAIT - + - - timeout + timeout - - - - - - - + + + + + + + - - first - - finger down + first + finger down - - - + + + - - TOUCH_TOUCH + TOUCH_TOUCH - + - - TOUCH_IDLE + TOUCH_IDLE - - - - - - - - - + + + + + + + + + - - TOUCH_DEAD + TOUCH_DEAD - - - - - - - - - + + + + + + + + + - - TOUCH_DEAD + TOUCH_DEAD - - - - - - - + + + + + + + - - TOUCH_IDLE + TOUCH_IDLE - - - + + + - - TOUCH_TOUCH + TOUCH_TOUCH - - - - - + + + + + - - TOUCH_IDLE + TOUCH_IDLE - - - + + + - - TOUCH_IDLE + TOUCH_IDLE - - - + + + - - TOUCH_TOUCH + TOUCH_TOUCH - - - + + + - - that finger - - TOUCH_IDLE + that finger + TOUCH_IDLE - - - + + + - - TOUCH_DEAD + TOUCH_DEAD - - - - - - - + + + + + + + - - that finger - - TOUCH_IDLE + that finger + TOUCH_IDLE - - - - + + + + - - - no + + no - + - - TOUCH_TOUCH + TOUCH_TOUCH - - - + + + - - TOUCH_IDLE + TOUCH_IDLE - + - - TOUCH_TOUCH + TOUCH_TOUCH - - - + + + - - TOUCH_DEAD + TOUCH_DEAD - - - - - + + + + + - - TOUCH_IDLE + TOUCH_IDLE - - - + + + - - TOUCH_TOUCH + TOUCH_TOUCH - + - - TOUCH_TOUCH + TOUCH_TOUCH - - - + - - TOUCH_IDLE + TOUCH_IDLE - - - + + + - - TOUCH_IDLE + TOUCH_IDLE - - - + + + - - TOUCH_TOUCH + TOUCH_TOUCH - - - + + + - - TOUCH_IDLE + TOUCH_IDLE - - - + + + - - TOUCH_TOUCH + TOUCH_TOUCH - - - + + + - - that finger - - TOUCH_IDLE + that finger + TOUCH_IDLE - - - + + + - - TOUCH_DEAD + TOUCH_DEAD - - - + + + - - TOUCH_DEAD + TOUCH_DEAD - + - - TOUCH_DEAD + TOUCH_DEAD - + - - TOUCH_DEAD + TOUCH_DEAD - - - + + + - - TOUCH_DEAD + TOUCH_DEAD - - - + + + - - TOUCH_DEAD + TOUCH_DEAD - - - + + + - - that finger state == - - TOUCH_TOUCH + that finger state == + TOUCH_TOUCH - + - - TOUCH_DEAD + TOUCH_DEAD - - - + + + - - TOUCH_DEAD + TOUCH_DEAD - + - - TOUCH_DEAD + TOUCH_DEAD - + - - first - - finger down + first + finger down - + - - MULTITAP + MULTITAP - - - - - + + + + + - - timeout + timeout - - - - - + + + + + - - IDLE + IDLE - - - - - - - + + + + + + + - - MULTITAP_DOWN + MULTITAP_DOWN - + - - button 1 - - press + first + finger up - - - - - + + + + + - - first - - finger up + timeout - - - + + + - - button 1 - - release + second + finger down - - - + - - timeout + move > + threshold - + + + + + + + + + - - second - - finger down + TOUCH_TOUCH - + + + - - move > - - threshold + TOUCH_IDLE - - - - - - - - - - - - - - - - - - TOUCH_TOUCH - - - - - - - TOUCH_IDLE - - - - + - - phys - - button - - press + phys + button + press - - - - - - - + + + + + + + - - DRAGGING_OR_TAP + DRAGGING_OR_TAP - + - - first - - finger up + first + finger up - - - + + + - - timeout + timeout - - - - - + + + + + - - move > - - threshold + move > + threshold - - - - - - - - - - - + + + + + + + + + + + - - TOUCH_IDLE + TOUCH_IDLE - - - - - - - - + + + + + + + + - -
-
- drag lock
- enabled?
-
-
-
- - [Not supported by viewer] + +
+
drag lock
enabled?
+
+
+ [Not supported by viewer]
- - - + + + - -
-
- no
-
-
- - [Not supported by viewer] + +
+
no
+
+
+ no
- - - + + + - -
-
- yes
-
-
-
- - [Not supported by viewer] + +
+
yes
+
+
+ yes<br>
- + - - thumb + thumb - - - + + + - - TOUCH_DEAD + TOUCH_DEAD - - - - - - - + + + + + + + - - TOUCH_2_RELEASE + TOUCH_2_RELEASE - + - - second - - finger up + second + finger up - - - + + + - - timeout + timeout - - - + + + - - move > - - threshold + move > + threshold - - - - - - - - - + + + + + + + + + - - first - - finger down + first + finger down - - - - - + + + + + - - TOUCH_IDLE + TOUCH_IDLE - - - + + + - - first - - finger up + first + finger up - - - - - + + + + + - - second - - finger down + second + finger down - - - + + + - - TOUCH_DEAD + TOUCH_DEAD - + - - TOUCH_DEAD + TOUCH_DEAD - - - - - - - - - - - + + + + + + + + + + - -
-
- no
-
-
- - [Not supported by viewer] + +
+
drag
disabled?
+
+
+ drag<br>disabled?<br>
- - - + + + - -
-
- yes
-
-
- - [Not supported by viewer] + +
+
no
+
+
+ no
- - + + + - -
-
- drag
- disabled?
-
-
-
- - [Not supported by viewer] + +
+
yes
+
+
+ yes
+ + + palm + + + + + + either finger + palm + + + + + + remaining +  finger + palm + + + + + + any finger + palm + + + + + + + + that finger + TOUCH_DEAD + + + + + + that finger + TOUCH_DEAD + + + + + + + + + + palm + + + + + + + + + + any finger + palm + + + + + + that finger + TOUCH_DEAD + + + + + + + + TOUCH_DEAD + + + + + + + + palm + + + + TOUCH_DEAD + + + + + + + + + + any finger + palm + + + + + + + + that finger + TOUCH_DEAD + + + + + + either finger + palm + + + + + + that finger + TOUCH_DEAD + + + + + + + + palm + + + + + + + + TOUCH_DEAD + + + + + + + + any finger + palm + + + + + + that finger + TOUCH_DEAD + + + + + + + + + + + + palm + + + + + + + + + + + + + + + + button 1 + press + + + + + + TOUCH_DEAD + + + + + + btn1 + release + + + + + + + + + + MULTITAP_PALM + + + + + + first + finger down + + + + + + TOUCH_TOUCH + + + + + + timeout + + + + + + phys + button + press +
- diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c index bfcef973..14025d2f 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -44,6 +44,8 @@ enum tap_event { TAP_EVENT_BUTTON, TAP_EVENT_TIMEOUT, TAP_EVENT_THUMB, + TAP_EVENT_PALM, + TAP_EVENT_PALM_UP, }; /***************************************** @@ -77,6 +79,7 @@ tap_state_to_str(enum tp_tap_state state) CASE_RETURN_STRING(TAP_STATE_DRAGGING_2); CASE_RETURN_STRING(TAP_STATE_MULTITAP); CASE_RETURN_STRING(TAP_STATE_MULTITAP_DOWN); + CASE_RETURN_STRING(TAP_STATE_MULTITAP_PALM); CASE_RETURN_STRING(TAP_STATE_DEAD); } return NULL; @@ -92,6 +95,8 @@ tap_event_to_str(enum tap_event event) CASE_RETURN_STRING(TAP_EVENT_TIMEOUT); CASE_RETURN_STRING(TAP_EVENT_BUTTON); CASE_RETURN_STRING(TAP_EVENT_THUMB); + CASE_RETURN_STRING(TAP_EVENT_PALM); + CASE_RETURN_STRING(TAP_EVENT_PALM_UP); } return NULL; } @@ -178,6 +183,12 @@ tp_tap_idle_handle_event(struct tp_dispatch *tp, case TAP_EVENT_THUMB: log_tap_bug(tp, event); break; + case TAP_EVENT_PALM: + tp->tap.state = TAP_STATE_IDLE; + t->tap.state = TAP_TOUCH_STATE_DEAD; + break; + case TAP_EVENT_PALM_UP: + break; } } @@ -221,9 +232,17 @@ tp_tap_touch_handle_event(struct tp_dispatch *tp, case TAP_EVENT_THUMB: tp->tap.state = TAP_STATE_IDLE; t->tap.is_thumb = true; + tp->tap.nfingers_down--; t->tap.state = TAP_TOUCH_STATE_DEAD; tp_tap_clear_timer(tp); break; + case TAP_EVENT_PALM: + tp->tap.state = TAP_STATE_IDLE; + t->tap.state = TAP_TOUCH_STATE_DEAD; + tp_tap_clear_timer(tp); + break; + case TAP_EVENT_PALM_UP: + break; } } @@ -251,8 +270,14 @@ tp_tap_hold_handle_event(struct tp_dispatch *tp, case TAP_EVENT_THUMB: tp->tap.state = TAP_STATE_IDLE; t->tap.is_thumb = true; + tp->tap.nfingers_down--; t->tap.state = TAP_TOUCH_STATE_DEAD; break; + case TAP_EVENT_PALM: + tp->tap.state = TAP_STATE_IDLE; + break; + case TAP_EVENT_PALM_UP: + break; } } @@ -286,6 +311,10 @@ tp_tap_tapped_handle_event(struct tp_dispatch *tp, LIBINPUT_BUTTON_STATE_RELEASED); break; case TAP_EVENT_THUMB: + case TAP_EVENT_PALM: + log_tap_bug(tp, event); + break; + case TAP_EVENT_PALM_UP: break; } } @@ -318,6 +347,12 @@ tp_tap_touch2_handle_event(struct tp_dispatch *tp, break; case TAP_EVENT_THUMB: break; + case TAP_EVENT_PALM: + tp->tap.state = TAP_STATE_TOUCH; + tp_tap_set_timer(tp, time); /* overwrite timer */ + break; + case TAP_EVENT_PALM_UP: + break; } } @@ -345,6 +380,11 @@ tp_tap_touch2_hold_handle_event(struct tp_dispatch *tp, break; case TAP_EVENT_THUMB: break; + case TAP_EVENT_PALM: + tp->tap.state = TAP_STATE_HOLD; + break; + case TAP_EVENT_PALM_UP: + break; } } @@ -380,6 +420,31 @@ tp_tap_touch2_release_handle_event(struct tp_dispatch *tp, break; case TAP_EVENT_THUMB: break; + case TAP_EVENT_PALM: + /* There's only one saved press time and it's overwritten by + * the last touch down. So in the case of finger down, palm + * down, finger up, palm detected, we use the + * palm touch's press time here instead of the finger's press + * time. Let's wait and see if that's an issue. + */ + tp_tap_notify(tp, + tp->tap.saved_press_time, + 1, + LIBINPUT_BUTTON_STATE_PRESSED); + if (tp->tap.drag_enabled) { + tp->tap.state = TAP_STATE_TAPPED; + tp->tap.saved_release_time = time; + tp_tap_set_timer(tp, time); + } else { + tp_tap_notify(tp, + time, + 1, + LIBINPUT_BUTTON_STATE_RELEASED); + tp->tap.state = TAP_STATE_IDLE; + } + break; + case TAP_EVENT_PALM_UP: + break; } } @@ -414,6 +479,11 @@ tp_tap_touch3_handle_event(struct tp_dispatch *tp, break; case TAP_EVENT_THUMB: break; + case TAP_EVENT_PALM: + tp->tap.state = TAP_STATE_TOUCH_2; + break; + case TAP_EVENT_PALM_UP: + break; } } @@ -439,6 +509,11 @@ tp_tap_touch3_hold_handle_event(struct tp_dispatch *tp, break; case TAP_EVENT_THUMB: break; + case TAP_EVENT_PALM: + tp->tap.state = TAP_STATE_TOUCH_2_HOLD; + break; + case TAP_EVENT_PALM_UP: + break; } } @@ -472,6 +547,11 @@ tp_tap_dragging_or_doubletap_handle_event(struct tp_dispatch *tp, break; case TAP_EVENT_THUMB: break; + case TAP_EVENT_PALM: + tp->tap.state = TAP_STATE_TAPPED; + break; + case TAP_EVENT_PALM_UP: + break; } } @@ -507,6 +587,15 @@ tp_tap_dragging_handle_event(struct tp_dispatch *tp, break; case TAP_EVENT_THUMB: break; + case TAP_EVENT_PALM: + tp_tap_notify(tp, + tp->tap.saved_release_time, + 1, + LIBINPUT_BUTTON_STATE_RELEASED); + tp->tap.state = TAP_STATE_IDLE; + break; + case TAP_EVENT_PALM_UP: + break; } } @@ -533,6 +622,9 @@ tp_tap_dragging_wait_handle_event(struct tp_dispatch *tp, tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED); break; case TAP_EVENT_THUMB: + case TAP_EVENT_PALM: + break; + case TAP_EVENT_PALM_UP: break; } } @@ -562,6 +654,15 @@ tp_tap_dragging_tap_handle_event(struct tp_dispatch *tp, break; case TAP_EVENT_THUMB: break; + case TAP_EVENT_PALM: + tp_tap_notify(tp, + tp->tap.saved_release_time, + 1, + LIBINPUT_BUTTON_STATE_RELEASED); + tp->tap.state = TAP_STATE_IDLE; + break; + case TAP_EVENT_PALM_UP: + break; } } @@ -589,6 +690,11 @@ tp_tap_dragging2_handle_event(struct tp_dispatch *tp, break; case TAP_EVENT_THUMB: break; + case TAP_EVENT_PALM: + tp->tap.state = TAP_STATE_DRAGGING_OR_DOUBLETAP; + break; + case TAP_EVENT_PALM_UP: + break; } } @@ -629,6 +735,9 @@ tp_tap_multitap_handle_event(struct tp_dispatch *tp, tp_tap_clear_timer(tp); break; case TAP_EVENT_THUMB: + case TAP_EVENT_PALM: + break; + case TAP_EVENT_PALM_UP: break; } } @@ -667,6 +776,42 @@ tp_tap_multitap_down_handle_event(struct tp_dispatch *tp, break; case TAP_EVENT_THUMB: break; + case TAP_EVENT_PALM: + tp->tap.state = TAP_STATE_MULTITAP_PALM; + break; + case TAP_EVENT_PALM_UP: + break; + } +} + +static void +tp_tap_multitap_palm_handle_event(struct tp_dispatch *tp, + struct tp_touch *t, + enum tap_event event, + uint64_t time) +{ + switch (event) { + case TAP_EVENT_RELEASE: + /* This is the palm finger */ + break; + case TAP_EVENT_TOUCH: + tp->tap.state = TAP_STATE_MULTITAP_DOWN; + break; + case TAP_EVENT_MOTION: + break; + case TAP_EVENT_TIMEOUT: + case TAP_EVENT_BUTTON: + tp->tap.state = TAP_STATE_IDLE; + tp_tap_clear_timer(tp); + tp_tap_notify(tp, + tp->tap.saved_release_time, + 1, + LIBINPUT_BUTTON_STATE_RELEASED); + break; + case TAP_EVENT_THUMB: + case TAP_EVENT_PALM: + case TAP_EVENT_PALM_UP: + break; } } @@ -679,7 +824,7 @@ tp_tap_dead_handle_event(struct tp_dispatch *tp, switch (event) { case TAP_EVENT_RELEASE: - if (tp->nfingers_down == 0) + if (tp->tap.nfingers_down == 0) tp->tap.state = TAP_STATE_IDLE; break; case TAP_EVENT_TOUCH: @@ -689,6 +834,11 @@ tp_tap_dead_handle_event(struct tp_dispatch *tp, break; case TAP_EVENT_THUMB: break; + case TAP_EVENT_PALM: + case TAP_EVENT_PALM_UP: + if (tp->tap.nfingers_down == 0) + tp->tap.state = TAP_STATE_IDLE; + break; } } @@ -751,6 +901,9 @@ tp_tap_handle_event(struct tp_dispatch *tp, case TAP_STATE_MULTITAP_DOWN: tp_tap_multitap_down_handle_event(tp, t, event, time); break; + case TAP_STATE_MULTITAP_PALM: + tp_tap_multitap_palm_handle_event(tp, t, event, time); + break; case TAP_STATE_DEAD: tp_tap_dead_handle_event(tp, t, event, time); break; @@ -777,6 +930,8 @@ tp_tap_exceeds_motion_threshold(struct tp_dispatch *tp, * touchpads are likely to give us pointer jumps. * This triggers the movement threshold, making three-finger taps * less reliable (#101435) + * + * This uses the real nfingers_down, not the one for taps. */ if (tp->device->model_flags & EVDEV_MODEL_SYNAPTICS_SERIAL_TOUCHPAD && (tp->nfingers_down > 2 || tp->old_nfingers_down > 2) && @@ -822,10 +977,32 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time) if (t->tap.is_thumb) continue; + /* A palm tap needs to be properly relased because we might + * be who-knows-where in the state machine. Otherwise, we + * ignore any event from it. + */ + if (t->tap.is_palm) { + if (t->state == TOUCH_END) + tp_tap_handle_event(tp, + t, + TAP_EVENT_PALM_UP, + time); + continue; + } + if (t->state == TOUCH_HOVERING) continue; - if (t->state == TOUCH_BEGIN) { + if (t->palm.state != PALM_NONE) { + assert(!t->tap.is_palm); + tp_tap_handle_event(tp, t, TAP_EVENT_PALM, time); + t->tap.is_palm = true; + t->tap.state = TAP_TOUCH_STATE_DEAD; + if (t->state != TOUCH_BEGIN) { + assert(tp->tap.nfingers_down > 0); + tp->tap.nfingers_down--; + } + } else 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 */ @@ -836,6 +1013,7 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time) t->tap.state = TAP_TOUCH_STATE_TOUCH; t->tap.initial = t->point; + tp->tap.nfingers_down++; tp_tap_handle_event(tp, t, TAP_EVENT_TOUCH, time); /* If we think this is a palm, pretend there's a @@ -846,8 +1024,10 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time) tp_tap_handle_event(tp, t, TAP_EVENT_MOTION, time); } else if (t->state == TOUCH_END) { - if (t->was_down) + if (t->was_down) { + tp->tap.nfingers_down--; tp_tap_handle_event(tp, t, TAP_EVENT_RELEASE, time); + } t->tap.state = TAP_TOUCH_STATE_IDLE; } else if (tp->tap.state != TAP_STATE_IDLE && tp_tap_exceeds_motion_threshold(tp, t)) { @@ -890,6 +1070,10 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time) } + assert(tp->tap.nfingers_down <= tp->nfingers_down); + if (tp->nfingers_down == 0) + assert(tp->tap.nfingers_down == 0); + return filter_motion; } @@ -938,9 +1122,19 @@ tp_tap_enabled_update(struct tp_dispatch *tp, bool suspended, bool enabled, uint return; if (tp_tap_enabled(tp)) { - /* Must restart in DEAD if fingers are down atm */ - tp->tap.state = - tp->nfingers_down ? TAP_STATE_DEAD : TAP_STATE_IDLE; + struct tp_touch *t; + + /* On resume, all touches are considered palms */ + tp_for_each_touch(tp, t) { + if (t->state == TOUCH_NONE) + continue; + + t->tap.is_palm = true; + t->tap.state = TAP_TOUCH_STATE_DEAD; + } + + tp->tap.state = TAP_STATE_IDLE; + tp->tap.nfingers_down = 0; } else { tp_release_all_taps(tp, time); } @@ -1154,6 +1348,7 @@ tp_remove_tap(struct tp_dispatch *tp) void tp_release_all_taps(struct tp_dispatch *tp, uint64_t now) { + struct tp_touch *t; int i; for (i = 1; i <= 3; i++) { @@ -1161,7 +1356,20 @@ tp_release_all_taps(struct tp_dispatch *tp, uint64_t now) tp_tap_notify(tp, now, i, LIBINPUT_BUTTON_STATE_RELEASED); } - tp->tap.state = tp->nfingers_down ? TAP_STATE_DEAD : TAP_STATE_IDLE; + /* To neutralize all current touches, we make them all palms */ + tp_for_each_touch(tp, t) { + if (t->state == TOUCH_NONE) + continue; + + if (t->tap.is_palm) + continue; + + t->tap.is_palm = true; + t->tap.state = TAP_TOUCH_STATE_DEAD; + } + + tp->tap.state = TAP_STATE_IDLE; + tp->tap.nfingers_down = 0; } void diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c index e90ec2d2..40220d6e 100644 --- a/src/evdev-mt-touchpad.c +++ b/src/evdev-mt-touchpad.c @@ -290,6 +290,7 @@ tp_begin_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time) t->thumb.state = THUMB_STATE_MAYBE; t->thumb.first_touch_time = time; t->tap.is_thumb = false; + t->tap.is_palm = false; assert(tp->nfingers_down >= 1); tp->hysteresis.last_motion_time = time; } diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 3da6c58e..0dee8aaa 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -103,6 +103,7 @@ enum tp_tap_state { TAP_STATE_DRAGGING_2, TAP_STATE_MULTITAP, TAP_STATE_MULTITAP_DOWN, + TAP_STATE_MULTITAP_PALM, TAP_STATE_DEAD, /**< finger count exceeded */ }; @@ -195,6 +196,7 @@ struct tp_touch { enum tp_tap_touch_state state; struct device_coords initial; bool is_thumb; + bool is_palm; } tap; struct { @@ -356,6 +358,8 @@ struct tp_dispatch { bool drag_enabled; bool drag_lock_enabled; + + unsigned int nfingers_down; /* number of fingers down for tapping (excl. thumb/palm) */ } tap; struct { diff --git a/test/test-touchpad-tap.c b/test/test-touchpad-tap.c index 6fc3c4f6..06caeb20 100644 --- a/test/test-touchpad-tap.c +++ b/test/test-touchpad-tap.c @@ -2375,12 +2375,870 @@ START_TEST(touchpad_drag_lock_default_unavailable) } END_TEST +static inline bool +touchpad_has_palm_pressure(struct litest_device *dev) +{ + struct libevdev *evdev = dev->evdev; + + if (libevdev_has_event_code(evdev, EV_ABS, ABS_MT_PRESSURE)) + return true; + + return false; +} + +START_TEST(touchpad_tap_palm_on_idle) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct axis_replacement axes[] = { + { ABS_MT_PRESSURE, 75 }, + { -1, 0 } + }; + + if (!touchpad_has_palm_pressure(dev)) + return; + + litest_enable_tap(dev->libinput_device); + litest_drain_events(li); + + /* Finger down is immediately palm */ + + litest_touch_down_extended(dev, 0, 50, 50, axes); + litest_touch_up(dev, 0); + + libinput_dispatch(li); + + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(touchpad_tap_palm_on_touch) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct axis_replacement axes[] = { + { ABS_MT_PRESSURE, 75 }, + { -1, 0 } + }; + + if (!touchpad_has_palm_pressure(dev)) + return; + + litest_enable_tap(dev->libinput_device); + litest_drain_events(li); + + /* Finger down is palm after touch begin */ + + litest_touch_down(dev, 0, 50, 50); + litest_touch_move_to_extended(dev, 0, 50, 50, 50, 50, axes, 1, 1); + litest_touch_up(dev, 0); + + libinput_dispatch(li); + + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(touchpad_tap_palm_on_touch_hold_timeout) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct axis_replacement axes[] = { + { ABS_MT_PRESSURE, 75 }, + { -1, 0 } + }; + + if (!touchpad_has_palm_pressure(dev)) + return; + + litest_enable_tap(dev->libinput_device); + litest_drain_events(li); + + /* Finger down is palm after tap timeout */ + + litest_touch_down(dev, 0, 50, 50); + libinput_dispatch(li); + litest_timeout_tap(); + libinput_dispatch(li); + litest_touch_move_to_extended(dev, 0, 50, 50, 50, 50, axes, 1, 1); + litest_touch_up(dev, 0); + + libinput_dispatch(li); + + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(touchpad_tap_palm_on_touch_hold_move) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct axis_replacement axes[] = { + { ABS_MT_PRESSURE, 75 }, + { -1, 0 } + }; + + if (!touchpad_has_palm_pressure(dev)) + return; + + litest_enable_tap(dev->libinput_device); + litest_drain_events(li); + + /* Finger down is palm after tap move threshold */ + + litest_touch_down(dev, 0, 50, 50); + litest_touch_move_to(dev, 0, 50, 50, 60, 60, 10, 1); + litest_drain_events(li); + + litest_touch_move_to_extended(dev, 0, 50, 50, 50, 50, axes, 1, 1); + litest_touch_up(dev, 0); + + libinput_dispatch(li); + + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(touchpad_tap_palm_on_tapped) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct axis_replacement axes[] = { + { ABS_MT_PRESSURE, 75 }, + { -1, 0 } + }; + + if (!touchpad_has_palm_pressure(dev)) + return; + + litest_enable_tap(dev->libinput_device); + litest_drain_events(li); + + /* tap + palm down */ + + litest_touch_down(dev, 0, 50, 50); + litest_touch_up(dev, 0); + libinput_dispatch(li); + + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + + litest_touch_down(dev, 0, 50, 50); + litest_touch_move_to_extended(dev, 0, 50, 50, 50, 50, axes, 1, 1); + litest_touch_up(dev, 0); + + libinput_dispatch(li); + litest_timeout_tap(); + libinput_dispatch(li); + + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + + + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(touchpad_tap_palm_on_tapped_2fg) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct axis_replacement axes[] = { + { ABS_MT_PRESSURE, 75 }, + { -1, 0 } + }; + + if (!touchpad_has_palm_pressure(dev)) + return; + + litest_enable_tap(dev->libinput_device); + litest_drain_events(li); + + /* tap + palm down + tap with second finger */ + + litest_touch_down(dev, 0, 50, 50); + litest_touch_up(dev, 0); + libinput_dispatch(li); + + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + + litest_touch_down(dev, 0, 50, 50); + litest_touch_move_to_extended(dev, 0, 50, 50, 50, 50, axes, 1, 1); + + libinput_dispatch(li); + + litest_touch_down(dev, 1, 50, 50); + litest_touch_up(dev, 1); + libinput_dispatch(li); + + litest_timeout_tap(); + libinput_dispatch(li); + + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + + + litest_touch_up(dev, 0); + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(touchpad_tap_palm_on_drag) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct axis_replacement axes[] = { + { ABS_MT_PRESSURE, 75 }, + { -1, 0 } + }; + + if (!touchpad_has_palm_pressure(dev)) + return; + + litest_enable_tap(dev->libinput_device); + litest_drain_events(li); + + /* tap + finger down (->drag), finger turns into palm */ + + litest_touch_down(dev, 0, 50, 50); + litest_touch_up(dev, 0); + libinput_dispatch(li); + + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + + litest_touch_down(dev, 0, 50, 50); + libinput_dispatch(li); + litest_timeout_tap(); + libinput_dispatch(li); + + litest_touch_move_to_extended(dev, 0, 50, 50, 50, 50, axes, 1, 1); + libinput_dispatch(li); + + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + + litest_touch_up(dev, 0); + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(touchpad_tap_palm_on_drag_2fg) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct axis_replacement axes[] = { + { ABS_MT_PRESSURE, 75 }, + { -1, 0 } + }; + int which = _i; /* ranged test */ + int this = which % 2, + other = (which + 1) % 2; + + if (!touchpad_has_palm_pressure(dev)) + return; + + litest_enable_tap(dev->libinput_device); + litest_drain_events(li); + + /* tap + finger down, 2nd finger down, finger turns to palm */ + + litest_touch_down(dev, 0, 50, 50); + litest_touch_up(dev, 0); + libinput_dispatch(li); + + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + + litest_touch_down(dev, this, 50, 50); + litest_touch_down(dev, other, 60, 50); + libinput_dispatch(li); + + litest_touch_move_to_extended(dev, this, 50, 50, 50, 50, axes, 1, 1); + libinput_dispatch(li); + + litest_touch_move_to(dev, other, 60, 50, 65, 50, 10, 1); + litest_assert_only_typed_events(li, + LIBINPUT_EVENT_POINTER_MOTION); + litest_touch_up(dev, other); + + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + + litest_touch_up(dev, this); + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(touchpad_tap_palm_on_touch_2) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct axis_replacement axes[] = { + { ABS_MT_PRESSURE, 75 }, + { -1, 0 } + }; + int which = _i; /* ranged test */ + int this = which % 2, + other = (which + 1) % 2; + + if (!touchpad_has_palm_pressure(dev)) + return; + + litest_enable_tap(dev->libinput_device); + litest_drain_events(li); + + /* 2fg tap with one finger detected as palm */ + litest_touch_down(dev, 0, 50, 50); + litest_touch_down(dev, 1, 60, 60); + litest_drain_events(li); + litest_touch_move_to_extended(dev, this, 50, 50, 50, 50, axes, 1, 1); + + + litest_touch_up(dev, this); + litest_touch_up(dev, other); + + libinput_dispatch(li); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_timeout_tap(); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + + + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(touchpad_tap_palm_on_touch_2_retouch) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct axis_replacement axes[] = { + { ABS_MT_PRESSURE, 75 }, + { -1, 0 } + }; + int which = _i; /* ranged test */ + int this = which % 2, + other = (which + 1) % 2; + + + if (!touchpad_has_palm_pressure(dev)) + return; + + litest_enable_tap(dev->libinput_device); + litest_drain_events(li); + + /* 2fg tap with one finger detected as palm, that finger is lifted + * and taps again as not-palm */ + litest_touch_down(dev, this, 50, 50); + litest_touch_down(dev, other, 60, 60); + litest_drain_events(li); + litest_touch_move_to_extended(dev, this, 50, 50, 50, 50, axes, 1, 1); + litest_touch_up(dev, this); + libinput_dispatch(li); + + litest_touch_down(dev, this, 70, 70); + litest_touch_up(dev, this); + litest_touch_up(dev, other); + + libinput_dispatch(li); + litest_assert_button_event(li, + BTN_RIGHT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_timeout_tap(); + litest_assert_button_event(li, + BTN_RIGHT, + LIBINPUT_BUTTON_STATE_RELEASED); + + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(touchpad_tap_palm_on_touch_3) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct axis_replacement axes[] = { + { ABS_MT_PRESSURE, 75 }, + { -1, 0 } + }; + int which = _i; /* ranged test */ + int this = which % 3; + + if (libevdev_get_abs_maximum(dev->evdev, ABS_MT_SLOT) <= 3) + return; + + if (!touchpad_has_palm_pressure(dev)) + return; + + litest_enable_tap(dev->libinput_device); + litest_drain_events(li); + + /* 3fg tap with one finger detected as palm, that finger is lifted, + other two fingers lifted cause 2fg tap */ + litest_touch_down(dev, this, 50, 50); + litest_touch_down(dev, (this + 1) % 3, 60, 50); + litest_touch_down(dev, (this + 2) % 3, 70, 50); + litest_drain_events(li); + litest_touch_move_to_extended(dev, this, 50, 50, 50, 50, axes, 1, 1); + litest_touch_up(dev, this); + libinput_dispatch(li); + + litest_touch_up(dev, (this + 1) % 3); + litest_touch_up(dev, (this + 2) % 3); + + libinput_dispatch(li); + litest_assert_button_event(li, + BTN_RIGHT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_timeout_tap(); + litest_assert_button_event(li, + BTN_RIGHT, + LIBINPUT_BUTTON_STATE_RELEASED); + + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(touchpad_tap_palm_on_touch_3_retouch) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct axis_replacement axes[] = { + { ABS_MT_PRESSURE, 75 }, + { -1, 0 } + }; + int which = _i; /* ranged test */ + int this = which % 3; + + if (libevdev_get_abs_maximum(dev->evdev, ABS_MT_SLOT) <= 3) + return; + + if (!touchpad_has_palm_pressure(dev)) + return; + + litest_enable_tap(dev->libinput_device); + litest_drain_events(li); + + /* 3fg tap with one finger detected as palm, that finger is lifted, + then put down again as normal finger -> 3fg tap */ + litest_touch_down(dev, this, 50, 50); + litest_touch_down(dev, (this + 1) % 3, 60, 50); + litest_touch_down(dev, (this + 2) % 3, 70, 50); + litest_drain_events(li); + litest_timeout_tap(); + libinput_dispatch(li); + + litest_touch_move_to_extended(dev, this, 50, 50, 50, 50, axes, 1, 1); + litest_touch_up(dev, this); + libinput_dispatch(li); + + litest_touch_down(dev, this, 50, 50); + litest_touch_up(dev, this); + litest_touch_up(dev, (this + 1) % 3); + litest_touch_up(dev, (this + 2) % 3); + + libinput_dispatch(li); + litest_assert_button_event(li, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_timeout_tap(); + litest_assert_button_event(li, + BTN_MIDDLE, + LIBINPUT_BUTTON_STATE_RELEASED); + + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(touchpad_tap_palm_on_touch_4) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct axis_replacement axes[] = { + { ABS_MT_PRESSURE, 75 }, + { -1, 0 } + }; + int which = _i; /* ranged test */ + int this = which % 4; + + if (libevdev_get_abs_maximum(dev->evdev, ABS_MT_SLOT) <= 4) + return; + + if (!touchpad_has_palm_pressure(dev)) + return; + + litest_enable_tap(dev->libinput_device); + litest_drain_events(li); + + /* 3fg tap with one finger detected as palm, that finger is lifted, + other two fingers lifted cause 2fg tap */ + litest_touch_down(dev, this, 50, 50); + litest_touch_down(dev, (this + 1) % 4, 60, 50); + litest_touch_down(dev, (this + 2) % 4, 70, 50); + litest_touch_down(dev, (this + 3) % 4, 80, 50); + litest_drain_events(li); + litest_touch_move_to_extended(dev, this, 50, 50, 50, 50, axes, 1, 1); + litest_touch_up(dev, this); + libinput_dispatch(li); + + litest_touch_up(dev, (this + 1) % 4); + litest_touch_up(dev, (this + 2) % 4); + litest_touch_up(dev, (this + 3) % 4); + + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(touchpad_tap_palm_after_tap) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct axis_replacement axes[] = { + { ABS_MT_PRESSURE, 75 }, + { -1, 0 } + }; + + if (!touchpad_has_palm_pressure(dev)) + return; + + litest_enable_tap(dev->libinput_device); + litest_drain_events(li); + + + litest_touch_down(dev, 0, 50, 50); + litest_touch_up(dev, 0); + libinput_dispatch(li); + + libinput_dispatch(li); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + + litest_touch_down(dev, 0, 50, 50); + litest_touch_move_to_extended(dev, 0, 50, 50, 50, 50, axes, 1, 1); + litest_touch_up(dev, 0); + libinput_dispatch(li); + + litest_timeout_tap(); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + + + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(touchpad_tap_palm_multitap) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct axis_replacement axes[] = { + { ABS_MT_PRESSURE, 75 }, + { -1, 0 } + }; + int range = _i, /* ranged test */ + ntaps; + + if (!touchpad_has_palm_pressure(dev)) + return; + + litest_enable_tap(dev->libinput_device); + litest_drain_events(li); + + for (ntaps = 0; ntaps <= range; ntaps++) { + litest_touch_down(dev, 0, 50, 50); + litest_touch_up(dev, 0); + libinput_dispatch(li); + msleep(10); + } + + litest_touch_down(dev, 0, 50, 50); + litest_touch_move_to_extended(dev, 0, 50, 50, 50, 50, axes, 1, 1); + litest_touch_up(dev, 0); + libinput_dispatch(li); + litest_timeout_tap(); + libinput_dispatch(li); + + for (ntaps = 0; ntaps <= range; ntaps++) { + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + } + + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(touchpad_tap_palm_multitap_timeout) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct axis_replacement axes[] = { + { ABS_MT_PRESSURE, 75 }, + { -1, 0 } + }; + int range = _i, /* ranged test */ + ntaps; + + if (!touchpad_has_palm_pressure(dev)) + return; + + litest_enable_tap(dev->libinput_device); + litest_drain_events(li); + + for (ntaps = 0; ntaps <= range; ntaps++) { + litest_touch_down(dev, 0, 50, 50); + litest_touch_up(dev, 0); + libinput_dispatch(li); + msleep(10); + } + + litest_touch_down(dev, 0, 50, 50); + litest_touch_move_to_extended(dev, 0, 50, 50, 50, 50, axes, 1, 1); + libinput_dispatch(li); + litest_timeout_tap(); + libinput_dispatch(li); + + for (ntaps = 0; ntaps <= range; ntaps++) { + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + } + + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(touchpad_tap_palm_multitap_down_again) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct axis_replacement axes[] = { + { ABS_MT_PRESSURE, 75 }, + { -1, 0 } + }; + int range = _i, /* ranged test */ + ntaps; + + if (!touchpad_has_palm_pressure(dev)) + return; + + litest_enable_tap(dev->libinput_device); + litest_drain_events(li); + + for (ntaps = 0; ntaps <= range; ntaps++) { + litest_touch_down(dev, 0, 50, 50); + litest_touch_up(dev, 0); + libinput_dispatch(li); + msleep(10); + } + + litest_touch_down(dev, 0, 50, 50); + litest_touch_move_to_extended(dev, 0, 50, 50, 50, 50, axes, 1, 1); + libinput_dispatch(li); + + /* keep palm finger down */ + for (ntaps = 0; ntaps <= range; ntaps++) { + litest_touch_down(dev, 1, 50, 50); + litest_touch_up(dev, 1); + libinput_dispatch(li); + msleep(10); + } + + for (ntaps = 0; ntaps <= 2 * range; ntaps++) { + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + } + + litest_touch_up(dev, 0); + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(touchpad_tap_palm_multitap_click) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct axis_replacement axes[] = { + { ABS_MT_PRESSURE, 75 }, + { -1, 0 } + }; + int range = _i, /* ranged test */ + ntaps; + + if (!touchpad_has_palm_pressure(dev)) + return; + + litest_enable_tap(dev->libinput_device); + litest_drain_events(li); + + for (ntaps = 0; ntaps <= range; ntaps++) { + litest_touch_down(dev, 0, 50, 50); + litest_touch_up(dev, 0); + libinput_dispatch(li); + msleep(10); + } + + litest_touch_down(dev, 0, 50, 50); + litest_touch_move_to_extended(dev, 0, 50, 50, 50, 50, axes, 1, 1); + libinput_dispatch(li); + /* keep palm finger down */ + + litest_button_click(dev, BTN_LEFT, true); + litest_button_click(dev, BTN_LEFT, false); + libinput_dispatch(li); + + for (ntaps = 0; ntaps <= range; ntaps++) { + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + } + + /* the click */ + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + litest_touch_up(dev, 0); + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(touchpad_tap_palm_click_then_tap) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + struct axis_replacement axes[] = { + { ABS_MT_PRESSURE, 75 }, + { -1, 0 } + }; + + if (!touchpad_has_palm_pressure(dev)) + return; + + litest_enable_tap(dev->libinput_device); + litest_drain_events(li); + + litest_touch_down_extended(dev, 0, 50, 50, axes); + libinput_dispatch(li); + + litest_button_click(dev, BTN_LEFT, true); + litest_button_click(dev, BTN_LEFT, false); + libinput_dispatch(li); + + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + + litest_touch_up(dev, 0); + litest_assert_empty_queue(li); + + litest_touch_down(dev, 0, 50, 50); + litest_touch_up(dev, 0); + libinput_dispatch(li); + litest_timeout_tap(); + libinput_dispatch(li); + + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_PRESSED); + litest_assert_button_event(li, + BTN_LEFT, + LIBINPUT_BUTTON_STATE_RELEASED); + + litest_assert_empty_queue(li); +} +END_TEST + +START_TEST(touchpad_tap_palm_dwt_tap) +{ + struct litest_device *dev = litest_current_device(); + struct litest_device *keyboard; + struct libinput *li = dev->libinput; + struct axis_replacement axes[] = { + { ABS_MT_PRESSURE, 75 }, + { -1, 0 } + }; + + if (!touchpad_has_palm_pressure(dev)) + return; + + keyboard = litest_add_device(li, LITEST_KEYBOARD); + + litest_enable_tap(dev->libinput_device); + litest_drain_events(li); + + litest_keyboard_key(keyboard, KEY_A, true); + litest_keyboard_key(keyboard, KEY_B, true); + litest_keyboard_key(keyboard, KEY_A, false); + litest_drain_events(li); + + litest_touch_down(dev, 0, 50, 50); + libinput_dispatch(li); + + litest_keyboard_key(keyboard, KEY_B, false); + litest_drain_events(li); + litest_timeout_dwt_long(); + libinput_dispatch(li); + + /* Changes to palm after dwt timeout */ + litest_touch_move_to_extended(dev, 0, 50, 50, 50, 50, axes, 1, 1); + libinput_dispatch(li); + + litest_touch_up(dev, 0); + libinput_dispatch(li); + + litest_assert_empty_queue(li); +} +END_TEST + void litest_setup_tests_touchpad_tap(void) { struct range multitap_range = {3, 5}; struct range tap_map_range = { LIBINPUT_CONFIG_TAP_MAP_LRM, LIBINPUT_CONFIG_TAP_MAP_LMR + 1 }; + struct range range_2fg = {0, 2}; + struct range range_3fg = {0, 3}; + struct range range_4fg = {0, 4}; litest_add("tap-1fg:1fg", touchpad_1fg_tap, LITEST_TOUCHPAD, LITEST_ANY); litest_add("tap-1fg:1fg", touchpad_1fg_doubletap, LITEST_TOUCHPAD, LITEST_ANY); @@ -2455,4 +3313,25 @@ litest_setup_tests_touchpad_tap(void) litest_add("tap:drag", touchpad_drag_disabled, LITEST_TOUCHPAD, LITEST_ANY); litest_add("tap:drag", touchpad_drag_disabled_immediate, LITEST_TOUCHPAD, LITEST_ANY); litest_add_ranged("tap-multitap:drag", touchpad_drag_disabled_multitap_no_drag, LITEST_TOUCHPAD, LITEST_ANY, &multitap_range); + + litest_add("tap:palm", touchpad_tap_palm_on_idle, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("tap:palm", touchpad_tap_palm_on_touch, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("tap:palm", touchpad_tap_palm_on_touch_hold_timeout, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("tap:palm", touchpad_tap_palm_on_touch_hold_move, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("tap:palm", touchpad_tap_palm_on_tapped, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("tap:palm", touchpad_tap_palm_on_tapped_2fg, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH); + litest_add("tap:palm", touchpad_tap_palm_on_drag, LITEST_TOUCHPAD, LITEST_ANY); + litest_add_ranged("tap:palm", touchpad_tap_palm_on_drag_2fg, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, &range_2fg); + litest_add_ranged("tap:palm", touchpad_tap_palm_on_touch_2, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, &range_2fg); + litest_add_ranged("tap:palm", touchpad_tap_palm_on_touch_2_retouch, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, &range_2fg); + litest_add_ranged("tap:palm", touchpad_tap_palm_on_touch_3, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, &range_3fg); + litest_add_ranged("tap:palm", touchpad_tap_palm_on_touch_3_retouch, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, &range_3fg); + litest_add_ranged("tap:palm", touchpad_tap_palm_on_touch_4, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, &range_4fg); + litest_add("tap:palm", touchpad_tap_palm_after_tap, LITEST_TOUCHPAD, LITEST_ANY); + litest_add_ranged("tap:palm", touchpad_tap_palm_multitap, LITEST_TOUCHPAD, LITEST_ANY, &multitap_range); + litest_add_ranged("tap:palm", touchpad_tap_palm_multitap_timeout, LITEST_TOUCHPAD, LITEST_ANY, &multitap_range); + litest_add_ranged("tap:palm", touchpad_tap_palm_multitap_down_again, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, &multitap_range); + litest_add_ranged("tap:palm", touchpad_tap_palm_multitap_click, LITEST_TOUCHPAD, LITEST_ANY, &multitap_range); + litest_add("tap:palm", touchpad_tap_palm_click_then_tap, LITEST_TOUCHPAD, LITEST_ANY); + litest_add("tap:palm", touchpad_tap_palm_dwt_tap, LITEST_TOUCHPAD, LITEST_ANY); }