diff --git a/doc/touchpad-tap-state-machine.svg b/doc/touchpad-tap-state-machine.svg index 45b90d4d..6887559f 100644 --- a/doc/touchpad-tap-state-machine.svg +++ b/doc/touchpad-tap-state-machine.svg @@ -1,3 +1,3 @@ -nonoyes(this section exists for [n] = 1, [n] = 2, and [n] = 3)(this section exists for [m] = 1, [m] = 2, and [m] = 3),for a total of 9 times due to combination with [n]yesyesyesnononoyesnoyesyesyesnononononoyesyesnonoyesnonoyesyesyesyesyesyesyes
IDLE
IDLE
firstfinger downTOUCH_TOUCHTOUCHsecondfinger downTOUCH_TOUCHTOUCH_2thirdfinger downTOUCH_TOUCHTOUCH_3TOUCH_2_RELEASEyeseitherfinger upyesremainingfinger upanyfinger upTOUCH_3_RELEASEeitherfinger upTOUCH_3_RELEASE_2yesremainingfinger upTOUCH_IDLEyesTOUCH_IDLETOUCH_IDLEyesTOUCH_IDLETOUCH_IDLEpalmTOUCH_DEADeitherfinger palmTOUCH_DEADyesanyfinger palmTOUCH_DEADyeseitherfinger palmTOUCH_DEADyesremainingfinger palmTOUCH_DEADyesremainingfinger palmTOUCH_DEAD2-finger tap3-finger tapyesfinger upTOUCH_IDLE1-finger tapfourthfinger down
IDLE
IDLE
TAPPEDyes2-finger tappalmfinger upbutton [n]pressbutton [n]releaseDRAGGING_OR_DOUBLETAPfinger downtap-and-dragtimeouttimeoutmove >thresholdDRAGGINGpalmfinger upnoyesdrag-lockenabled?DRAGLOCK_WAITDRAGLOCK_CONTINUEfinger uppalmtimeoutmove >thresholddrag-locktimeout1-finger tapyesnonon-palmfinger count== 0?finger down1-finger taptimeouttimeouthold-and-tap enabled?HOLD3-finger tapnohold-and-tap enabled?timeouthold-and-tap enabled?3-finger tapTOUCH_2_HOLDTOUCH_3_HOLDtimeouthold-and-tap enabled?timeoutyesnohold-and-tap enabled?timeouthold-and-tap enabled?palmfinger upTOUCH_DEADTOUCH_IDLEeitherfinger palmeitherfinger upTOUCH_DEADTOUCH_IDLEanyfinger palmanyfinger upTOUCH_DEADTOUCH_IDLETOUCH_DEADDEADBUTTONclickpadbuttonreleaseall fingersTOUCH_DEADnoyesnon-palmfinger count== 0?clickpadbuttonpressanyfinger upTOUCH_IDLEanyfinger palmmove >thresholdmove >thresholdmove >thresholdmove >thresholdmove >thresholdmove >thresholdmove >thresholdhold-and-tap enabled?move >thresholdhold-and-tap enabled?move >thresholdhold-and-tap enabled?3-finger tapclickpadbuttonpressclickpadbuttonpressclickpadbuttonpressclickpadbuttonpressclickpadbuttonpressclickpadbuttonpressclickpadbuttonpressclickpadbuttonpressclickpadbuttonpress1-finger tap3-finger tapbutton [n]releaseBUTTONclickpadbuttonreleaseclickpadbuttonpressbutton [n]releaseclickpadbuttonpressclickpadbuttonpressclickpadbuttonpressclickpadbuttonpressclickpadbuttonpressbutton [n]press
[n] = 1
[n] = 1
[n] = 1
[n] = 1
[n] = 2
[n] = 2
[n] = 3
[n] = 3
notap-and-dragenabled?move >thresholdyesnonon-palmfinger count== 0?nonon-palmfinger count== 0?secondfinger downsecondfinger down1-finger tap2-finger tap
[m] = 1
[m] = 1
yes
[m] = 2
[m] = 2
3-finger tap
[m] = 3
[m] = 3
no[n] == [m]?button [m]pressbutton [m]releaseset tap stateto DEADfinger downTOUCH_TOUCHyeshold-and-tap enabled?TOUCH_DEADnoyesnothat fingerTOUCH_TOUCH?nothat fingerTOUCH_TOUCH?that fingerTOUCH_TOUCH?nothat fingerTOUCH_TOUCH?that fingerTOUCH_TOUCH?nothat fingerTOUCH_TOUCH?TOUCH_IDLETOUCH_IDLETOUCH_IDLEyesthat fingerTOUCH_TOUCH?nothat fingerTOUCH_TOUCH?TOUCH_IDLEthat fingerTOUCH_TOUCH?nothat fingerTOUCH_TOUCH?TOUCH_IDLEthat fingerTOUCH_TOUCH?nothat fingerTOUCH_TOUCH?TOUCH_IDLEthumbyesnodrag state== IDLE?yesnon-palmfinger count== 1?TOUCH_DEADthumbTOUCH_DEADnonodrag state== IDLE?drag state== IDLE?drag state== IDLE?nohold-and-tap enabled?3-finger tapnohold-and-tap enabled?nohold-and-tap enabled?finger downTOUCH_TOUCHnohold-and-tap enabled?3-finger tapfinger downTOUCH_TOUCHhold-and-tap enabled?finger downTOUCH_TOUCHnohold-and-tap enabled?3-finger tapall fingersexcept the new oneTOUCH_DEADall fingersTOUCH_DEADfourthfinger downTOUCH_DEADto draggingstate machine:move >threshold
Viewer does not support full SVG 1.1
\ No newline at end of file +(this section exists for [n] = 1, [n] = 2, and [n] = 3)(this section exists for [m] = 1, [m] = 2, and [m] = 3),for a total of 9 times due to combination with [n]yesnoyesnoyesnonononononononononoyesnononoyesnoyesnoyesyesyesnononononononoyesyesyesnonoyesnonoyesyesyesyesyesyesyesyesyesyesyesyesyesyes
IDLE
IDLE
firstfinger downTOUCH_TOUCHTOUCHsecondfinger downTOUCH_TOUCHTOUCH_2thirdfinger downTOUCH_TOUCHTOUCH_3TOUCH_2_RELEASEyeseitherfinger upyesremainingfinger upanyfinger upTOUCH_3_RELEASEeitherfinger upTOUCH_3_RELEASE_2yesremainingfinger upTOUCH_IDLEyesTOUCH_IDLETOUCH_IDLEyesTOUCH_IDLETOUCH_IDLEpalmTOUCH_DEADeitherfinger palmTOUCH_DEADyesanyfinger palmTOUCH_DEADyeseitherfinger palmTOUCH_DEADyesremainingfinger palmTOUCH_DEADyesremainingfinger palmTOUCH_DEAD2-finger tap3-finger tapyesfinger upTOUCH_IDLE1-finger tapfourthfinger down
IDLE
IDLE
TAPPEDyes2-finger tappalmfinger upbutton [n]pressbutton [n]releaseDRAGGING_OR_DOUBLETAPfinger downtap-and-dragtimeouttimeoutmove >thresholdDRAGGINGpalmfinger upnoyesdrag-lockenabled?DRAGLOCK_WAITDRAGLOCK_CONTINUEfinger uppalmtimeoutmove >thresholddrag-locktimeout1-finger tapyesnonon-palmfinger count== 0?finger down1-finger taptimeouttimeouthold-and-tap enabled?HOLD3-finger tapnohold-and-tap enabled?timeouthold-and-tap enabled?3-finger tapTOUCH_2_HOLDTOUCH_3_HOLDtimeouthold-and-tap enabled?timeoutyesnohold-and-tap enabled?timeouthold-and-tap enabled?palmfinger upTOUCH_DEADTOUCH_IDLEeitherfinger palmeitherfinger upTOUCH_DEADTOUCH_IDLEanyfinger palmanyfinger upTOUCH_DEADTOUCH_IDLETOUCH_TOUCHTOUCH_4_PLUSyesanyfinger palmyesanyfinger upTOUCH_IDLEyesTOUCH_DEADTOUCH_4_PLUS_HOLDtimeouthold-and-tap enabled?anyfinger upTOUCH_IDLEanyfinger palmTOUCH_DEADfourthfinger downTOUCH_TOUCHTOUCH_4_PLUS_RELEASEyesanyfinger palmyesanyfinger upyesTOUCH_DEADTOUCH_IDLEtimeouthold-and-tap enabled?TOUCH_4_PLUS_RELEASE_2timeouthold-and-tap enabled?yesanyfinger palmyesanyfinger upTOUCH_DEADTOUCH_IDLETOUCH_4_PLUS_RELEASE_3timeouthold-and-tap enabled?yesanyfinger palmyesanyfinger upyesTOUCH_DEADTOUCH_IDLE1-finger tap2-finger tap3-finger tapDEADBUTTONclickpadbuttonreleaseall fingersTOUCH_DEADnoyesnon-palmfinger count== 0?clickpadbuttonpressanyfinger upTOUCH_IDLEanyfinger palmmove >thresholdmove >thresholdmove >thresholdmove >thresholdmove >thresholdmove >thresholdmove >thresholdmove >thresholdmove >thresholdhold-and-tap enabled?move >thresholdmove >thresholdhold-and-tap enabled?yesnohold-and-tap enabled?move >thresholdhold-and-tap enabled?3-finger tapmove >thresholdyesnohold-and-tap enabled?move >thresholdyesnohold-and-tap enabled?clickpadbuttonpressclickpadbuttonpressclickpadbuttonpressyesnohold-and-tap enabled?1-finger tapclickpadbuttonpressyesnohold-and-tap enabled?2-finger tapclickpadbuttonpressclickpadbuttonpress3-finger tapyeshold-and-tap enabled?clickpadbuttonpressclickpadbuttonpressclickpadbuttonpressclickpadbuttonpressclickpadbuttonpressclickpadbuttonpressclickpadbuttonpressclickpadbuttonpress1-finger tap3-finger tapbutton [n]releaseBUTTONclickpadbuttonreleaseclickpadbuttonpressbutton [n]releaseclickpadbuttonpressclickpadbuttonpressclickpadbuttonpressclickpadbuttonpressclickpadbuttonpressbutton [n]press
[n] = 1
[n] = 1
[n] = 1
[n] = 1
[n] = 2
[n] = 2
[n] = 3
[n] = 3
notap-and-dragenabled?move >thresholdyesnonon-palmfinger count== 0?nonon-palmfinger count== 0?secondfinger downsecondfinger down1-finger tap2-finger tap
[m] = 1
[m] = 1
yes
[m] = 2
[m] = 2
3-finger tap
[m] = 3
[m] = 3
no[n] == [m]?button [m]pressbutton [m]releaseset tap stateto DEADfinger downTOUCH_TOUCHyeshold-and-tap enabled?TOUCH_DEADnoyesnothat fingerTOUCH_TOUCH?nothat fingerTOUCH_TOUCH?that fingerTOUCH_TOUCH?nothat fingerTOUCH_TOUCH?that fingerTOUCH_TOUCH?nothat fingerTOUCH_TOUCH?that fingerTOUCH_TOUCH?nothat fingerTOUCH_TOUCH?TOUCH_IDLETOUCH_IDLETOUCH_IDLETOUCH_IDLETOUCH_TOUCHfinger count == 0?yesthat fingerTOUCH_TOUCH?nothat fingerTOUCH_TOUCH?TOUCH_IDLEthat fingerTOUCH_TOUCH?nothat fingerTOUCH_TOUCH?nothat fingerTOUCH_TOUCH?that fingerTOUCH_TOUCH?TOUCH_IDLETOUCH_IDLEthat fingerTOUCH_TOUCH?that fingerTOUCH_TOUCH?nothat fingerTOUCH_TOUCH?nothat fingerTOUCH_TOUCH?TOUCH_IDLETOUCH_IDLEnothat fingerTOUCH_TOUCH?nothat fingerTOUCH_TOUCH?TOUCH_IDLETOUCH_TOUCHfinger count == 1?TOUCH_TOUCHfinger count == 2?noTOUCH_TOUCHfinger count == 3?thumbyesnodrag state== IDLE?yesnon-palmfinger count== 1?TOUCH_DEADthumbTOUCH_DEADnoyesnodrag state== IDLE?drag state== IDLE?drag state== IDLE?drag state== IDLE?yesnodrag state== DRAGLOCK_CONTINUE?1-finger tapnoTOUCH_TOUCHfinger count == 3?nohold-and-tap enabled?3-finger tapnohold-and-tap enabled?nohold-and-tap enabled?nonon-palmfinger count== 1?nononon-palmfinger count== 2?nonon-palmfinger count== 3?finger downTOUCH_TOUCHnohold-and-tap enabled?3-finger tapfinger downTOUCH_TOUCHhold-and-tap enabled?finger downTOUCH_TOUCHnohold-and-tap enabled?3-finger tapfinger downTOUCH_TOUCHyeshold-and-tap enabled?finger downTOUCH_TOUCHyeshold-and-tap enabled?finger downTOUCH_TOUCHyeshold-and-tap enabled?1-finger tap2-finger tap3-finger tapall fingersexcept the new oneTOUCH_DEADfinger downTOUCH_TOUCHfinger downTOUCH_TOUCHall fingersTOUCH_DEADno
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c index c819127d..8fa527b0 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -72,6 +72,11 @@ tap_state_to_str(enum tp_tap_state state) CASE_RETURN_STRING(TAP_STATE_TOUCH_3_HOLD); CASE_RETURN_STRING(TAP_STATE_TOUCH_3_RELEASE); CASE_RETURN_STRING(TAP_STATE_TOUCH_3_RELEASE_2); + CASE_RETURN_STRING(TAP_STATE_TOUCH_4_PLUS); + CASE_RETURN_STRING(TAP_STATE_TOUCH_4_PLUS_HOLD); + CASE_RETURN_STRING(TAP_STATE_TOUCH_4_PLUS_RELEASE); + CASE_RETURN_STRING(TAP_STATE_TOUCH_4_PLUS_RELEASE_2); + CASE_RETURN_STRING(TAP_STATE_TOUCH_4_PLUS_RELEASE_3); CASE_RETURN_STRING(TAP_STATE_BUTTON); CASE_RETURN_STRING(TAP_STATE_DEAD); } @@ -240,8 +245,10 @@ tp_drag_idle_handle_event(struct tp_dispatch *tp, 1, LIBINPUT_BUTTON_STATE_PRESSED); /* taps may end with fingers remaining down, - * those should not start a drag */ - if (tp->tap.drag_enabled && tp->tap.nfingers_down == 0) { + * those should not start a drag; + * also, the touch in the process of being released + * is still counted for nfingers_down */ + if (tp->tap.drag_enabled && tp->tap.nfingers_down == 1) { tp->tap.drag_state = DRAG_STATE_1FGTAP_TAPPED; tp_tap_set_drag_timer(tp, time, 1); } else { @@ -256,7 +263,7 @@ tp_drag_idle_handle_event(struct tp_dispatch *tp, tp->tap.saved_press_time, 2, LIBINPUT_BUTTON_STATE_PRESSED); - if (tp->tap.drag_enabled && tp->tap.nfingers_down == 0) { + if (tp->tap.drag_enabled && tp->tap.nfingers_down == 1) { tp->tap.drag_state = DRAG_STATE_2FGTAP_TAPPED; tp_tap_set_drag_timer(tp, time, 2); } else { @@ -271,7 +278,7 @@ tp_drag_idle_handle_event(struct tp_dispatch *tp, tp->tap.saved_press_time, 3, LIBINPUT_BUTTON_STATE_PRESSED); - if (tp->tap.drag_enabled && tp->tap.nfingers_down == 0) { + if (tp->tap.drag_enabled && tp->tap.nfingers_down == 1) { tp->tap.drag_state = DRAG_STATE_3FGTAP_TAPPED; tp_tap_set_drag_timer(tp, time, 3); } else { @@ -434,7 +441,7 @@ tp_drag_dragging_handle_event(struct tp_dispatch *tp, case TAP_EVENT_TOUCH: break; case TAP_EVENT_RELEASE: - if (tp->tap.nfingers_down == 0) { + if (tp->tap.nfingers_down == 1) { if (tp->tap.drag_lock != LIBINPUT_CONFIG_DRAG_LOCK_DISABLED) { enum tp_drag_state dest[3] = { DRAG_STATE_1FGTAP_DRAGLOCK_WAIT, @@ -471,7 +478,7 @@ tp_drag_dragging_handle_event(struct tp_dispatch *tp, case TAP_EVENT_THUMB: break; case TAP_EVENT_PALM: - if (tp->tap.nfingers_down == 0) { + if (tp->tap.nfingers_down == 1) { tp_tap_notify(tp, time, nfingers_tapped, @@ -814,7 +821,7 @@ tp_tap_touch_handle_event(struct tp_dispatch *tp, if (t->tap.state == TAP_TOUCH_STATE_TOUCH) { tp->tap.saved_release_time = time; tp_drag_handle_event(tp, t, TAP_EVENT_1FGTAP, time); - if (tp->tap.nfingers_down == 0) + if (tp->tap.nfingers_down == 1) tp->tap.state = TAP_STATE_IDLE; else tp->tap.state = TAP_STATE_DEAD; @@ -847,7 +854,7 @@ tp_tap_touch_handle_event(struct tp_dispatch *tp, break; case TAP_EVENT_PALM: if (t->tap.state == TAP_TOUCH_STATE_TOUCH) { - if (tp->tap.nfingers_down == 0) + if (tp->tap.nfingers_down == 1) tp->tap.state = TAP_STATE_IDLE; else tp->tap.state = TAP_STATE_DEAD; @@ -1001,6 +1008,7 @@ tp_tap_touch2_release_handle_event(struct tp_dispatch *tp, switch (event) { case TAP_EVENT_TOUCH: if (tp->tap.hold_tap_enabled) { + tp_drag_handle_event(tp, t, TAP_EVENT_1FGTAP, time); tp_tap_kill_all_touches(tp, t); tp->tap.state = TAP_STATE_TOUCH; } else @@ -1011,23 +1019,29 @@ tp_tap_touch2_release_handle_event(struct tp_dispatch *tp, case TAP_EVENT_RELEASE: if (t->tap.state == TAP_TOUCH_STATE_TOUCH) { tp_drag_handle_event(tp, t, TAP_EVENT_2FGTAP, time); - if (tp->tap.nfingers_down == 0) + if (tp->tap.nfingers_down == 1) tp->tap.state = TAP_STATE_IDLE; else tp->tap.state = TAP_STATE_DEAD; } break; case TAP_EVENT_MOTION: + if (tp->tap.hold_tap_enabled) + tp_drag_handle_event(tp, t, TAP_EVENT_1FGTAP, time); tp->tap.state = TAP_STATE_DEAD; break; case TAP_EVENT_TIMEOUT: - if (!tp->tap.hold_tap_enabled && - tp->tap.drag_state == DRAG_STATE_IDLE) + if (tp->tap.hold_tap_enabled) { + tp_drag_handle_event(tp, t, TAP_EVENT_1FGTAP, time); + tp->tap.state = TAP_STATE_DEAD; + } else if (tp->tap.drag_state == DRAG_STATE_IDLE) tp->tap.state = TAP_STATE_HOLD; else tp->tap.state = TAP_STATE_DEAD; break; case TAP_EVENT_BUTTON: + if (tp->tap.hold_tap_enabled) + tp_drag_handle_event(tp, t, TAP_EVENT_1FGTAP, time); tp->tap.state = TAP_STATE_BUTTON; break; case TAP_EVENT_BUTTON_UP: @@ -1046,7 +1060,7 @@ tp_tap_touch2_release_handle_event(struct tp_dispatch *tp, * an issue. */ tp_drag_handle_event(tp, t, TAP_EVENT_1FGTAP, time); - if (tp->tap.nfingers_down == 0) + if (tp->tap.nfingers_down == 1) tp->tap.state = TAP_STATE_IDLE; else tp->tap.state = TAP_STATE_DEAD; @@ -1068,10 +1082,9 @@ tp_tap_touch3_handle_event(struct tp_dispatch *tp, switch (event) { case TAP_EVENT_TOUCH: - tp->tap.state = TAP_STATE_DEAD; - /* this cannot be a tap anymore, get the dragging state machine - * out of DRAGGING_OR_DOUBLETAP or DRAGLOCK_CONTINUE */ - tp_drag_handle_event(tp, t, TAP_EVENT_MOTION, time); + tp->tap.state = TAP_STATE_TOUCH_4_PLUS; + tp->tap.saved_press_time = time; + tp_tap_set_timer(tp, time); break; case TAP_EVENT_RELEASE: if (t->tap.state == TAP_TOUCH_STATE_TOUCH) { @@ -1119,7 +1132,11 @@ tp_tap_touch3_hold_handle_event(struct tp_dispatch *tp, switch (event) { case TAP_EVENT_TOUCH: - tp->tap.state = TAP_STATE_DEAD; + /* Don't even try to revive the previous three touches + * for a tap like in the other hold states, because any tap + * with more than three fingers can only end drag-lock. + * If we got here the timeout for that has already elapsed. */ + tp->tap.state = TAP_STATE_TOUCH_4_PLUS_HOLD; break; case TAP_EVENT_RELEASE: tp->tap.state = TAP_STATE_TOUCH_2_HOLD; @@ -1157,6 +1174,7 @@ tp_tap_touch3_release_handle_event(struct tp_dispatch *tp, switch (event) { case TAP_EVENT_TOUCH: if (tp->tap.hold_tap_enabled) { + tp_drag_handle_event(tp, t, TAP_EVENT_1FGTAP, time); tp_tap_kill_all_touches(tp, t); tp->tap.state = TAP_STATE_TOUCH; } else { @@ -1173,14 +1191,17 @@ tp_tap_touch3_release_handle_event(struct tp_dispatch *tp, } break; case TAP_EVENT_MOTION: - if (!tp->tap.hold_tap_enabled) + if (tp->tap.hold_tap_enabled) + tp_drag_handle_event(tp, t, TAP_EVENT_1FGTAP, time); + else tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, time); tp->tap.state = TAP_STATE_DEAD; break; case TAP_EVENT_TIMEOUT: - if (tp->tap.hold_tap_enabled) + if (tp->tap.hold_tap_enabled) { + tp_drag_handle_event(tp, t, TAP_EVENT_1FGTAP, time); tp->tap.state = TAP_STATE_DEAD; - else { + } else { tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, time); if (tp->tap.drag_state == DRAG_STATE_IDLE) tp->tap.state = TAP_STATE_TOUCH_2_HOLD; @@ -1189,7 +1210,9 @@ tp_tap_touch3_release_handle_event(struct tp_dispatch *tp, } break; case TAP_EVENT_BUTTON: - if (!tp->tap.hold_tap_enabled) + if (tp->tap.hold_tap_enabled) + tp_drag_handle_event(tp, t, TAP_EVENT_1FGTAP, time); + else tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, time); tp->tap.state = TAP_STATE_BUTTON; break; @@ -1219,6 +1242,7 @@ tp_tap_touch3_release2_handle_event(struct tp_dispatch *tp, switch (event) { case TAP_EVENT_TOUCH: if (tp->tap.hold_tap_enabled) { + tp_drag_handle_event(tp, t, TAP_EVENT_2FGTAP, time); tp_tap_kill_all_touches(tp, t); tp->tap.state = TAP_STATE_TOUCH; } else { @@ -1231,21 +1255,24 @@ tp_tap_touch3_release2_handle_event(struct tp_dispatch *tp, case TAP_EVENT_RELEASE: if (t->tap.state == TAP_TOUCH_STATE_TOUCH) { tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, time); - if (tp->tap.nfingers_down == 0) + if (tp->tap.nfingers_down == 1) tp->tap.state = TAP_STATE_IDLE; else tp->tap.state = TAP_STATE_DEAD; } break; case TAP_EVENT_MOTION: - if (!tp->tap.hold_tap_enabled) + if (tp->tap.hold_tap_enabled) + tp_drag_handle_event(tp, t, TAP_EVENT_2FGTAP, time); + else tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, time); tp->tap.state = TAP_STATE_DEAD; break; case TAP_EVENT_TIMEOUT: - if (tp->tap.hold_tap_enabled) + if (tp->tap.hold_tap_enabled) { + tp_drag_handle_event(tp, t, TAP_EVENT_2FGTAP, time); tp->tap.state = TAP_STATE_DEAD; - else { + } else { tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, time); if (tp->tap.drag_state == DRAG_STATE_IDLE) tp->tap.state = TAP_STATE_HOLD; @@ -1254,7 +1281,9 @@ tp_tap_touch3_release2_handle_event(struct tp_dispatch *tp, } break; case TAP_EVENT_BUTTON: - if (!tp->tap.hold_tap_enabled) + if (tp->tap.hold_tap_enabled) + tp_drag_handle_event(tp, t, TAP_EVENT_2FGTAP, time); + else tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, time); tp->tap.state = TAP_STATE_BUTTON; break; @@ -1266,7 +1295,7 @@ tp_tap_touch3_release2_handle_event(struct tp_dispatch *tp, case TAP_EVENT_PALM: if (t->tap.state == TAP_TOUCH_STATE_TOUCH) { tp_drag_handle_event(tp, t, TAP_EVENT_2FGTAP, time); - if (tp->tap.nfingers_down == 0) + if (tp->tap.nfingers_down == 1) tp->tap.state = TAP_STATE_IDLE; else tp->tap.state = TAP_STATE_DEAD; @@ -1280,6 +1309,384 @@ tp_tap_touch3_release2_handle_event(struct tp_dispatch *tp, } } +static int +tp_count_alive_touches(struct tp_dispatch *tp) +{ + /* In the TOUCH_4_PLUS group of states we lose track of the exact + * number of fingers down which are still alive for tapping. + * There are several relatively unlikely events that may bring us back + * into the states corresponding to a smaller number of fingers, + * most prominently touches turning into palms. + * In these cases we need to figure out how many remaining + * alive touches there are. */ + struct tp_touch *t; + int nfingers = 0; + + /* When hold-and-tap is disabled, either all or no fingers are tapping, + * and the latter case occurs in the DEAD and BUTTON states only + * which don't have the need to ask for alive touches, + * so we have the count stored as the number of fingers. */ + if (!tp->tap.hold_tap_enabled) + return tp->tap.nfingers_down; + + /* If the setting is enabled however, tapping can happen with only some + * of the fingers down, i.e. there may be to-be-ignored holding touches, + * so we need to actually count the tapping ones. */ + tp_for_each_touch(tp, t) { + if (t->tap.state == TAP_TOUCH_STATE_TOUCH) + nfingers++; + } + return nfingers; +} + +static void +tp_tap_touch4plus_handle_event(struct tp_dispatch *tp, + struct tp_touch *t, + enum tap_event event, uint64_t time) +{ + + switch (event) { + case TAP_EVENT_TOUCH: + tp->tap.saved_press_time = time; + tp_tap_set_timer(tp, time); + break; + case TAP_EVENT_RELEASE: + if (t->tap.state == TAP_TOUCH_STATE_TOUCH) { + tp->tap.state = TAP_STATE_TOUCH_4_PLUS_RELEASE; + tp->tap.saved_release_time = time; + tp_tap_set_timer(tp, time); + } + break; + case TAP_EVENT_MOTION: + tp->tap.state = TAP_STATE_DEAD; + break; + case TAP_EVENT_TIMEOUT: + if (!tp->tap.hold_tap_enabled && + tp->tap.drag_state == DRAG_STATE_IDLE) + tp->tap.state = TAP_STATE_TOUCH_4_PLUS_HOLD; + else + tp->tap.state = TAP_STATE_DEAD; + break; + case TAP_EVENT_BUTTON: + tp->tap.state = TAP_STATE_BUTTON; + break; + case TAP_EVENT_BUTTON_UP: + log_tap_bug(tp, t, event); + break; + case TAP_EVENT_THUMB: + break; + case TAP_EVENT_PALM: + if (t->tap.state == TAP_TOUCH_STATE_TOUCH) { + int remaining = tp_count_alive_touches(tp) - 1; + assert(remaining >= 3); + if (remaining == 3) + tp->tap.state = TAP_STATE_TOUCH_3; + } + break; + case TAP_EVENT_1FGTAP: + case TAP_EVENT_2FGTAP: + case TAP_EVENT_3FGTAP: + log_tap_bug(tp, t, event); + break; + } +} + +static void +tp_tap_touch4plus_hold_handle_event(struct tp_dispatch *tp, + struct tp_touch *t, + enum tap_event event, uint64_t time) +{ + + switch (event) { + case TAP_EVENT_TOUCH: + break; + case TAP_EVENT_RELEASE: { + int remaining = tp_count_alive_touches(tp) - 1; + assert(remaining >= 3); + if (remaining == 3) + tp->tap.state = TAP_STATE_TOUCH_3_HOLD; + break; + } + case TAP_EVENT_MOTION: + tp->tap.state = TAP_STATE_DEAD; + break; + case TAP_EVENT_TIMEOUT: + break; + case TAP_EVENT_BUTTON: + tp->tap.state = TAP_STATE_BUTTON; + break; + case TAP_EVENT_BUTTON_UP: + log_tap_bug(tp, t, event); + break; + case TAP_EVENT_THUMB: + break; + case TAP_EVENT_PALM: { + int remaining = tp_count_alive_touches(tp) - 1; + assert(remaining >= 3); + if (remaining == 3) + tp->tap.state = TAP_STATE_TOUCH_3_HOLD; + break; + } + case TAP_EVENT_1FGTAP: + case TAP_EVENT_2FGTAP: + case TAP_EVENT_3FGTAP: + log_tap_bug(tp, t, event); + break; + } +} + +static void +tp_tap_touch4plus_release_handle_event(struct tp_dispatch *tp, + struct tp_touch *t, + enum tap_event event, uint64_t time) +{ + + switch (event) { + case TAP_EVENT_TOUCH: + if (tp->tap.hold_tap_enabled) { + tp_drag_handle_event(tp, t, TAP_EVENT_1FGTAP, time); + tp_tap_kill_all_touches(tp, t); + tp->tap.state = TAP_STATE_TOUCH; + } else + tp->tap.state = TAP_STATE_TOUCH_4_PLUS; + tp->tap.saved_press_time = time; + tp_tap_set_timer(tp, time); + break; + case TAP_EVENT_RELEASE: + if (t->tap.state == TAP_TOUCH_STATE_TOUCH) { + tp->tap.state = TAP_STATE_TOUCH_4_PLUS_RELEASE_2; + tp_tap_set_timer(tp, time); + } + break; + case TAP_EVENT_MOTION: + if (tp->tap.hold_tap_enabled) + tp_drag_handle_event(tp, t, TAP_EVENT_1FGTAP, time); + tp->tap.state = TAP_STATE_DEAD; + break; + case TAP_EVENT_TIMEOUT: + if (tp->tap.hold_tap_enabled) { + tp_drag_handle_event(tp, t, TAP_EVENT_1FGTAP, time); + tp->tap.state = TAP_STATE_DEAD; + } else { + if (tp->tap.drag_state == DRAG_STATE_IDLE) { + int remaining = tp_count_alive_touches(tp); + assert(remaining >= 3); + if (remaining == 3) + tp->tap.state = TAP_STATE_TOUCH_3_HOLD; + else + tp->tap.state = TAP_STATE_TOUCH_4_PLUS_HOLD; + } else + tp->tap.state = TAP_STATE_DEAD; + } + break; + case TAP_EVENT_BUTTON: + if (tp->tap.hold_tap_enabled) + tp_drag_handle_event(tp, t, TAP_EVENT_1FGTAP, time); + tp->tap.state = TAP_STATE_BUTTON; + break; + case TAP_EVENT_BUTTON_UP: + log_tap_bug(tp, t, event); + break; + case TAP_EVENT_THUMB: + break; + case TAP_EVENT_PALM: + if (t->tap.state == TAP_TOUCH_STATE_TOUCH) { + int remaining = tp_count_alive_touches(tp) - 1; + assert(remaining >= 2); + if (remaining == 2) + tp->tap.state = TAP_STATE_TOUCH_3_RELEASE; + } + break; + case TAP_EVENT_1FGTAP: + case TAP_EVENT_2FGTAP: + case TAP_EVENT_3FGTAP: + log_tap_bug(tp, t, event); + break; + } +} + +static void +tp_tap_touch4plus_release2_handle_event(struct tp_dispatch *tp, + struct tp_touch *t, + enum tap_event event, uint64_t time) +{ + + switch (event) { + case TAP_EVENT_TOUCH: + if (tp->tap.hold_tap_enabled) { + tp_drag_handle_event(tp, t, TAP_EVENT_2FGTAP, time); + tp_tap_kill_all_touches(tp, t); + tp->tap.state = TAP_STATE_TOUCH; + } else { + int remaining = tp_count_alive_touches(tp); + assert(remaining >= 3); + if (remaining == 3) + tp->tap.state = TAP_STATE_TOUCH_3; + else + tp->tap.state = TAP_STATE_TOUCH_4_PLUS; + } + tp->tap.saved_press_time = time; + tp_tap_set_timer(tp, time); + break; + case TAP_EVENT_RELEASE: + if (t->tap.state == TAP_TOUCH_STATE_TOUCH) { + tp->tap.state = TAP_STATE_TOUCH_4_PLUS_RELEASE_3; + tp_tap_set_timer(tp, time); + } + break; + case TAP_EVENT_MOTION: + if (tp->tap.hold_tap_enabled) + tp_drag_handle_event(tp, t, TAP_EVENT_2FGTAP, time); + tp->tap.state = TAP_STATE_DEAD; + break; + case TAP_EVENT_TIMEOUT: + if (tp->tap.hold_tap_enabled) { + tp_drag_handle_event(tp, t, TAP_EVENT_2FGTAP, time); + tp->tap.state = TAP_STATE_DEAD; + } else { + if (tp->tap.drag_state == DRAG_STATE_IDLE) { + int remaining = min(tp_count_alive_touches(tp), + 4); + enum tp_tap_state dest[3] = { + TAP_STATE_TOUCH_2_HOLD, + TAP_STATE_TOUCH_3_HOLD, + TAP_STATE_TOUCH_4_PLUS_HOLD, + }; + assert(remaining >= 2); + tp->tap.state = dest[remaining - 2]; + } else + tp->tap.state = TAP_STATE_DEAD; + } + break; + case TAP_EVENT_BUTTON: + if (tp->tap.hold_tap_enabled) + tp_drag_handle_event(tp, t, TAP_EVENT_2FGTAP, time); + tp->tap.state = TAP_STATE_BUTTON; + break; + case TAP_EVENT_BUTTON_UP: + log_tap_bug(tp, t, event); + break; + case TAP_EVENT_THUMB: + break; + case TAP_EVENT_PALM: + if (t->tap.state == TAP_TOUCH_STATE_TOUCH) { + int remaining = tp_count_alive_touches(tp) - 1; + assert(remaining >= 1); + if (remaining == 1) + tp->tap.state = TAP_STATE_TOUCH_3_RELEASE_2; + } + break; + case TAP_EVENT_1FGTAP: + case TAP_EVENT_2FGTAP: + case TAP_EVENT_3FGTAP: + log_tap_bug(tp, t, event); + break; + } +} + +static void +tp_tap_touch4plus_release3_handle_event(struct tp_dispatch *tp, + struct tp_touch *t, + enum tap_event event, uint64_t time) +{ + + switch (event) { + case TAP_EVENT_TOUCH: + if (tp->tap.hold_tap_enabled) { + tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, time); + tp_tap_kill_all_touches(tp, t); + tp->tap.state = TAP_STATE_TOUCH; + } else { + int remaining = min(tp_count_alive_touches(tp), + 4); + enum tp_tap_state dest[3] = { + TAP_STATE_TOUCH_2, + TAP_STATE_TOUCH_3, + TAP_STATE_TOUCH_4_PLUS, + }; + assert(remaining >= 2); + tp->tap.state = dest[remaining - 2]; + } + tp->tap.saved_press_time = time; + tp_tap_set_timer(tp, time); + break; + case TAP_EVENT_RELEASE: + if (t->tap.state == TAP_TOUCH_STATE_TOUCH) { + /* four-plus-finger taps are to be ignored, + * except for the purpose of stopping drag-lock. + * We also don't care about remaining fingers + * of this tap anymore */ + if (tp->tap.nfingers_down == 1) + tp->tap.state = TAP_STATE_IDLE; + else + tp->tap.state = TAP_STATE_DEAD; + if (tp->tap.drag_state == DRAG_STATE_1FGTAP_DRAGLOCK_CONTINUE || + tp->tap.drag_state == DRAG_STATE_2FGTAP_DRAGLOCK_CONTINUE || + tp->tap.drag_state == DRAG_STATE_3FGTAP_DRAGLOCK_CONTINUE) { + /* all taps have the same result here, + * a one-finger tap does the trick without + * introducing another event just for this */ + tp_drag_handle_event(tp, t, TAP_EVENT_1FGTAP, + time); + } + } + break; + case TAP_EVENT_MOTION: + if (tp->tap.hold_tap_enabled) + tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, time); + tp->tap.state = TAP_STATE_DEAD; + break; + case TAP_EVENT_TIMEOUT: + if (tp->tap.hold_tap_enabled) { + tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, time); + tp->tap.state = TAP_STATE_DEAD; + } else { + if (tp->tap.drag_state == DRAG_STATE_IDLE) { + int remaining = min(tp_count_alive_touches(tp), + 4); + enum tp_tap_state dest[4] = { + TAP_STATE_HOLD, + TAP_STATE_TOUCH_2_HOLD, + TAP_STATE_TOUCH_3_HOLD, + TAP_STATE_TOUCH_4_PLUS_HOLD, + }; + assert(remaining >= 1); + tp->tap.state = dest[remaining - 1]; + } else + tp->tap.state = TAP_STATE_DEAD; + } + break; + case TAP_EVENT_BUTTON: + if (tp->tap.hold_tap_enabled) + tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, time); + tp->tap.state = TAP_STATE_BUTTON; + break; + case TAP_EVENT_BUTTON_UP: + log_tap_bug(tp, t, event); + break; + case TAP_EVENT_THUMB: + break; + case TAP_EVENT_PALM: + if (t->tap.state == TAP_TOUCH_STATE_TOUCH) { + int remaining = tp_count_alive_touches(tp) - 1; + if (remaining == 0) { + tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, + time); + if (tp->tap.nfingers_down == 1) + tp->tap.state = TAP_STATE_IDLE; + else + tp->tap.state = TAP_STATE_DEAD; + } + } + break; + case TAP_EVENT_1FGTAP: + case TAP_EVENT_2FGTAP: + case TAP_EVENT_3FGTAP: + log_tap_bug(tp, t, event); + break; + } +} + static void tp_tap_button_handle_event(struct tp_dispatch *tp, struct tp_touch *t, @@ -1329,7 +1736,7 @@ tp_tap_dead_handle_event(struct tp_dispatch *tp, } break; case TAP_EVENT_RELEASE: - if (tp->tap.nfingers_down == 0) + if (tp->tap.nfingers_down == 1) tp->tap.state = TAP_STATE_IDLE; break; case TAP_EVENT_MOTION: @@ -1344,7 +1751,7 @@ tp_tap_dead_handle_event(struct tp_dispatch *tp, case TAP_EVENT_THUMB: break; case TAP_EVENT_PALM: - if (tp->tap.nfingers_down == 0) + if (tp->tap.nfingers_down == 1) tp->tap.state = TAP_STATE_IDLE; break; case TAP_EVENT_1FGTAP: @@ -1396,6 +1803,21 @@ tp_tap_handle_event(struct tp_dispatch *tp, case TAP_STATE_TOUCH_3_RELEASE_2: tp_tap_touch3_release2_handle_event(tp, t, event, time); break; + case TAP_STATE_TOUCH_4_PLUS: + tp_tap_touch4plus_handle_event(tp, t, event, time); + break; + case TAP_STATE_TOUCH_4_PLUS_HOLD: + tp_tap_touch4plus_hold_handle_event(tp, t, event, time); + break; + case TAP_STATE_TOUCH_4_PLUS_RELEASE: + tp_tap_touch4plus_release_handle_event(tp, t, event, time); + break; + case TAP_STATE_TOUCH_4_PLUS_RELEASE_2: + tp_tap_touch4plus_release2_handle_event(tp, t, event, time); + break; + case TAP_STATE_TOUCH_4_PLUS_RELEASE_3: + tp_tap_touch4plus_release3_handle_event(tp, t, event, time); + break; case TAP_STATE_BUTTON: tp_tap_button_handle_event(tp, t, event, time); break; @@ -1511,9 +1933,9 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time) assert(!t->tap.is_palm); t->tap.is_palm = true; if (t->state != TOUCH_BEGIN) { - assert(tp->tap.nfingers_down > 0); - tp->tap.nfingers_down--; + assert(tp->tap.nfingers_down >= 1); tp_tap_handle_event(tp, t, TAP_EVENT_PALM, time); + tp->tap.nfingers_down--; } t->tap.state = TAP_TOUCH_STATE_DEAD; } else if (t->state == TOUCH_BEGIN) { @@ -1532,8 +1954,8 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time) } else if (t->state == TOUCH_END) { if (t->was_down) { assert(tp->tap.nfingers_down >= 1); - tp->tap.nfingers_down--; tp_tap_handle_event(tp, t, TAP_EVENT_RELEASE, time); + tp->tap.nfingers_down--; } t->tap.state = TAP_TOUCH_STATE_IDLE; } else if (tp->tap.state != TAP_STATE_IDLE && diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 3e7f7d2c..1c601886 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -113,6 +113,11 @@ enum tp_tap_state { TAP_STATE_TOUCH_3_HOLD, TAP_STATE_TOUCH_3_RELEASE, TAP_STATE_TOUCH_3_RELEASE_2, + TAP_STATE_TOUCH_4_PLUS, + TAP_STATE_TOUCH_4_PLUS_HOLD, + TAP_STATE_TOUCH_4_PLUS_RELEASE, + TAP_STATE_TOUCH_4_PLUS_RELEASE_2, + TAP_STATE_TOUCH_4_PLUS_RELEASE_3, TAP_STATE_BUTTON, /**< clickpad button pressed */ TAP_STATE_DEAD, /**< clickpad button released, or finger moved */ }; diff --git a/test/test-touchpad-tap.c b/test/test-touchpad-tap.c index 90f27c5c..591ea273 100644 --- a/test/test-touchpad-tap.c +++ b/test/test-touchpad-tap.c @@ -5146,6 +5146,14 @@ START_TEST(touchpad_tap_palm_on_touch_4) litest_touch_up(dev, (this + 2) % 4); litest_touch_up(dev, (this + 3) % 4); + 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