From 355a3e2f64abe148f0be61147968b8300cf90298 Mon Sep 17 00:00:00 2001 From: satrmb <10471-satrmb_true-email-is-private_contact-via-web@gitlab.freedesktop.org> Date: Sun, 2 Aug 2020 20:53:16 +0200 Subject: [PATCH] touchpad: implement hold-and-tap This feature allows a user, as the name suggests, to hold fingers on a touchpad while tapping with additional fingers to produce the same effect as without the holding fingers. The holding fingers may even have moved, pressed the clickpad button (unless it is still down at the time of the tap), or be participating in a tap-and-drag, though in the latter case a tap with the same number of fingers as was used to start the tap-and-drag is ignored, just like a click from another mouse-like device would be. Signed-off-by: satrmb <10471-satrmb@users.noreply.gitlab.freedesktop.org> --- doc/touchpad-tap-state-machine.svg | 2 +- src/evdev-mt-touchpad-tap.c | 261 +++++++++++++++++++++-------- 2 files changed, 194 insertions(+), 69 deletions(-) diff --git a/doc/touchpad-tap-state-machine.svg b/doc/touchpad-tap-state-machine.svg index 318ff7bb..45b90d4d 100644 --- a/doc/touchpad-tap-state-machine.svg +++ b/doc/touchpad-tap-state-machine.svg @@ -1,3 +1,3 @@ -no(this section exists for [n] = 1, [n] = 2, and [n] = 3)nonoyesyesyes
IDLE
IDLE
firstfinger downTOUCH_TOUCHTOUCHsecondfinger downTOUCH_TOUCHTOUCH_2thirdfinger downTOUCH_TOUCHTOUCH_3TOUCH_2_RELEASEeitherfinger upremainingfinger upanyfinger upTOUCH_3_RELEASEeitherfinger upTOUCH_3_RELEASE_2remainingfinger upTOUCH_IDLETOUCH_IDLETOUCH_IDLETOUCH_IDLETOUCH_IDLEpalmTOUCH_DEADeitherfinger palmTOUCH_DEADanyfinger palmTOUCH_DEADeitherfinger palmTOUCH_DEADremainingfinger palmTOUCH_DEADremainingfinger palmTOUCH_DEAD2-finger tap3-finger tapfinger 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 taptimeouttimeoutHOLD3-finger taptimeout3-finger tapTOUCH_2_HOLDTOUCH_3_HOLDtimeouttimeoutyestimeoutpalmfinger 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 >thresholdmove >thresholdmove >threshold3-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 downset tap stateto DEADfinger downTOUCH_DEADthumbyesdrag state== IDLE?TOUCH_DEADthumbTOUCH_DEADnodrag state== IDLE?drag state== IDLE?drag state== IDLE?3-finger tapfinger downTOUCH_TOUCH3-finger tapfinger downTOUCH_TOUCHfinger downTOUCH_TOUCH3-finger tapall fingersTOUCH_DEADfourthfinger downTOUCH_DEADto draggingstate machine:move >threshold
Viewer does not support full SVG 1.1
\ No newline at end of file +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 diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c index f6d9290b..c819127d 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -204,11 +204,12 @@ tp_tap_clear_timer(struct tp_dispatch *tp) } static void -tp_tap_kill_all_touches(struct tp_dispatch *tp) +tp_tap_kill_all_touches(struct tp_dispatch *tp, + struct tp_touch *except) { struct tp_touch *t; tp_for_each_touch(tp, t) { - if (t->tap.state == TAP_TOUCH_STATE_TOUCH) + if (t != except && t->tap.state == TAP_TOUCH_STATE_TOUCH) t->tap.state = TAP_TOUCH_STATE_DEAD; } } @@ -238,7 +239,9 @@ tp_drag_idle_handle_event(struct tp_dispatch *tp, tp->tap.saved_press_time, 1, LIBINPUT_BUTTON_STATE_PRESSED); - if (tp->tap.drag_enabled) { + /* taps may end with fingers remaining down, + * those should not start a drag */ + if (tp->tap.drag_enabled && tp->tap.nfingers_down == 0) { tp->tap.drag_state = DRAG_STATE_1FGTAP_TAPPED; tp_tap_set_drag_timer(tp, time, 1); } else { @@ -253,7 +256,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) { + if (tp->tap.drag_enabled && tp->tap.nfingers_down == 0) { tp->tap.drag_state = DRAG_STATE_2FGTAP_TAPPED; tp_tap_set_drag_timer(tp, time, 2); } else { @@ -268,8 +271,6 @@ tp_drag_idle_handle_event(struct tp_dispatch *tp, tp->tap.saved_press_time, 3, LIBINPUT_BUTTON_STATE_PRESSED); - /* three-finger taps may end with fingers remaining down, - * see gitlab#499; those should not start a drag */ if (tp->tap.drag_enabled && tp->tap.nfingers_down == 0) { tp->tap.drag_state = DRAG_STATE_3FGTAP_TAPPED; tp_tap_set_drag_timer(tp, time, 3); @@ -479,9 +480,46 @@ tp_drag_dragging_handle_event(struct tp_dispatch *tp, } break; case TAP_EVENT_1FGTAP: + if (!tp->tap.hold_tap_enabled) + log_drag_bug(tp, t, event); + else if (nfingers_tapped != 1) { + tp_tap_notify(tp, + tp->tap.saved_press_time, + 1, + LIBINPUT_BUTTON_STATE_PRESSED); + tp_tap_notify(tp, + tp->tap.saved_release_time, + 1, + LIBINPUT_BUTTON_STATE_RELEASED); + } + break; case TAP_EVENT_2FGTAP: + if (!tp->tap.hold_tap_enabled) + log_drag_bug(tp, t, event); + else if (nfingers_tapped != 2) { + tp_tap_notify(tp, + tp->tap.saved_press_time, + 2, + LIBINPUT_BUTTON_STATE_PRESSED); + tp_tap_notify(tp, + tp->tap.saved_release_time, + 2, + LIBINPUT_BUTTON_STATE_RELEASED); + } + break; case TAP_EVENT_3FGTAP: - log_drag_bug(tp, t, event); + if (!tp->tap.hold_tap_enabled) + log_drag_bug(tp, t, event); + else if (nfingers_tapped != 3) { + tp_tap_notify(tp, + tp->tap.saved_press_time, + 3, + LIBINPUT_BUTTON_STATE_PRESSED); + tp_tap_notify(tp, + tp->tap.saved_release_time, + 3, + LIBINPUT_BUTTON_STATE_RELEASED); + } break; } } @@ -733,6 +771,7 @@ tp_tap_idle_handle_event(struct tp_dispatch *tp, tp_tap_set_timer(tp, time); break; case TAP_EVENT_RELEASE: + log_tap_bug(tp, t, event); break; case TAP_EVENT_MOTION: log_tap_bug(tp, t, event); @@ -749,7 +788,7 @@ tp_tap_idle_handle_event(struct tp_dispatch *tp, log_tap_bug(tp, t, event); break; case TAP_EVENT_PALM: - tp->tap.state = TAP_STATE_IDLE; + log_tap_bug(tp, t, event); break; case TAP_EVENT_1FGTAP: case TAP_EVENT_2FGTAP: @@ -772,15 +811,21 @@ tp_tap_touch_handle_event(struct tp_dispatch *tp, tp_tap_set_timer(tp, time); break; case TAP_EVENT_RELEASE: - tp->tap.saved_release_time = time; - tp_drag_handle_event(tp, t, TAP_EVENT_1FGTAP, time); - tp->tap.state = TAP_STATE_IDLE; + 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) + tp->tap.state = TAP_STATE_IDLE; + else + tp->tap.state = TAP_STATE_DEAD; + } break; case TAP_EVENT_MOTION: tp->tap.state = TAP_STATE_DEAD; break; case TAP_EVENT_TIMEOUT: - if (tp->tap.drag_state == DRAG_STATE_IDLE) + if (!tp->tap.hold_tap_enabled && + tp->tap.drag_state == DRAG_STATE_IDLE) tp->tap.state = TAP_STATE_HOLD; else tp->tap.state = TAP_STATE_DEAD; @@ -793,14 +838,20 @@ tp_tap_touch_handle_event(struct tp_dispatch *tp, log_tap_bug(tp, t, event); break; case TAP_EVENT_THUMB: - if (tp->tap.drag_state == DRAG_STATE_IDLE) { + if (tp->tap.drag_state == DRAG_STATE_IDLE && + tp->tap.nfingers_down == 1) { tp->tap.state = TAP_STATE_IDLE; t->tap.is_thumb = true; tp->tap.nfingers_down--; } break; case TAP_EVENT_PALM: - tp->tap.state = TAP_STATE_IDLE; + if (t->tap.state == TAP_TOUCH_STATE_TOUCH) { + if (tp->tap.nfingers_down == 0) + tp->tap.state = TAP_STATE_IDLE; + else + tp->tap.state = TAP_STATE_DEAD; + } break; case TAP_EVENT_1FGTAP: case TAP_EVENT_2FGTAP: @@ -865,15 +916,18 @@ tp_tap_touch2_handle_event(struct tp_dispatch *tp, tp_tap_set_timer(tp, time); break; case TAP_EVENT_RELEASE: - tp->tap.state = TAP_STATE_TOUCH_2_RELEASE; - tp->tap.saved_release_time = time; - tp_tap_set_timer(tp, time); + if (t->tap.state == TAP_TOUCH_STATE_TOUCH) { + tp->tap.state = TAP_STATE_TOUCH_2_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.drag_state == DRAG_STATE_IDLE) + if (!tp->tap.hold_tap_enabled && + tp->tap.drag_state == DRAG_STATE_IDLE) tp->tap.state = TAP_STATE_TOUCH_2_HOLD; else tp->tap.state = TAP_STATE_DEAD; @@ -888,7 +942,8 @@ tp_tap_touch2_handle_event(struct tp_dispatch *tp, case TAP_EVENT_THUMB: break; case TAP_EVENT_PALM: - tp->tap.state = TAP_STATE_TOUCH; + if (t->tap.state == TAP_TOUCH_STATE_TOUCH) + tp->tap.state = TAP_STATE_TOUCH; break; case TAP_EVENT_1FGTAP: case TAP_EVENT_2FGTAP: @@ -945,19 +1000,29 @@ tp_tap_touch2_release_handle_event(struct tp_dispatch *tp, switch (event) { case TAP_EVENT_TOUCH: - tp->tap.state = TAP_STATE_TOUCH_2; + if (tp->tap.hold_tap_enabled) { + tp_tap_kill_all_touches(tp, t); + tp->tap.state = TAP_STATE_TOUCH; + } else + tp->tap.state = TAP_STATE_TOUCH_2; tp->tap.saved_press_time = time; tp_tap_set_timer(tp, time); break; case TAP_EVENT_RELEASE: - tp_drag_handle_event(tp, t, TAP_EVENT_2FGTAP, time); - tp->tap.state = TAP_STATE_IDLE; + if (t->tap.state == TAP_TOUCH_STATE_TOUCH) { + tp_drag_handle_event(tp, t, TAP_EVENT_2FGTAP, time); + if (tp->tap.nfingers_down == 0) + tp->tap.state = TAP_STATE_IDLE; + else + tp->tap.state = TAP_STATE_DEAD; + } break; case TAP_EVENT_MOTION: tp->tap.state = TAP_STATE_DEAD; break; case TAP_EVENT_TIMEOUT: - if (tp->tap.drag_state == DRAG_STATE_IDLE) + if (!tp->tap.hold_tap_enabled && + tp->tap.drag_state == DRAG_STATE_IDLE) tp->tap.state = TAP_STATE_HOLD; else tp->tap.state = TAP_STATE_DEAD; @@ -971,15 +1036,21 @@ tp_tap_touch2_release_handle_event(struct tp_dispatch *tp, 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. The tap timer also gets reset on palm detection. - * Let's wait and see if that's an issue. - */ - tp_drag_handle_event(tp, t, TAP_EVENT_1FGTAP, time); - tp->tap.state = TAP_STATE_IDLE; + if (t->tap.state == TAP_TOUCH_STATE_TOUCH) { + /* 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. The tap timer also gets + * reset on palm detection. Let's wait and see if that's + * an issue. + */ + tp_drag_handle_event(tp, t, TAP_EVENT_1FGTAP, time); + if (tp->tap.nfingers_down == 0) + tp->tap.state = TAP_STATE_IDLE; + else + tp->tap.state = TAP_STATE_DEAD; + } break; case TAP_EVENT_1FGTAP: case TAP_EVENT_2FGTAP: @@ -1002,21 +1073,24 @@ tp_tap_touch3_handle_event(struct tp_dispatch *tp, * out of DRAGGING_OR_DOUBLETAP or DRAGLOCK_CONTINUE */ tp_drag_handle_event(tp, t, TAP_EVENT_MOTION, time); break; + case TAP_EVENT_RELEASE: + if (t->tap.state == TAP_TOUCH_STATE_TOUCH) { + tp->tap.state = TAP_STATE_TOUCH_3_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.drag_state == DRAG_STATE_IDLE) + if (!tp->tap.hold_tap_enabled && + tp->tap.drag_state == DRAG_STATE_IDLE) tp->tap.state = TAP_STATE_TOUCH_3_HOLD; else tp->tap.state = TAP_STATE_DEAD; tp_gesture_tap_timeout(tp, time); break; - case TAP_EVENT_RELEASE: - tp->tap.state = TAP_STATE_TOUCH_3_RELEASE; - tp->tap.saved_release_time = time; - tp_tap_set_timer(tp, time); - break; case TAP_EVENT_BUTTON: tp->tap.state = TAP_STATE_BUTTON; break; @@ -1026,7 +1100,8 @@ tp_tap_touch3_handle_event(struct tp_dispatch *tp, case TAP_EVENT_THUMB: break; case TAP_EVENT_PALM: - tp->tap.state = TAP_STATE_TOUCH_2; + if (t->tap.state == TAP_TOUCH_STATE_TOUCH) + tp->tap.state = TAP_STATE_TOUCH_2; break; case TAP_EVENT_1FGTAP: case TAP_EVENT_2FGTAP: @@ -1081,28 +1156,41 @@ tp_tap_touch3_release_handle_event(struct tp_dispatch *tp, switch (event) { case TAP_EVENT_TOUCH: - tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, time); - tp->tap.state = TAP_STATE_TOUCH_3; + if (tp->tap.hold_tap_enabled) { + tp_tap_kill_all_touches(tp, t); + tp->tap.state = TAP_STATE_TOUCH; + } else { + tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, time); + tp->tap.state = TAP_STATE_TOUCH_3; + } tp->tap.saved_press_time = time; tp_tap_set_timer(tp, time); break; case TAP_EVENT_RELEASE: - tp->tap.state = TAP_STATE_TOUCH_3_RELEASE_2; - tp_tap_set_timer(tp, time); + if (t->tap.state == TAP_TOUCH_STATE_TOUCH) { + tp->tap.state = TAP_STATE_TOUCH_3_RELEASE_2; + tp_tap_set_timer(tp, time); + } break; case TAP_EVENT_MOTION: - tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, time); + 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: - 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; - else + if (tp->tap.hold_tap_enabled) tp->tap.state = TAP_STATE_DEAD; + 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; + else + tp->tap.state = TAP_STATE_DEAD; + } break; case TAP_EVENT_BUTTON: - tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, time); + 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: @@ -1111,7 +1199,8 @@ tp_tap_touch3_release_handle_event(struct tp_dispatch *tp, case TAP_EVENT_THUMB: break; case TAP_EVENT_PALM: - tp->tap.state = TAP_STATE_TOUCH_2_RELEASE; + if (t->tap.state == TAP_TOUCH_STATE_TOUCH) + tp->tap.state = TAP_STATE_TOUCH_2_RELEASE; break; case TAP_EVENT_1FGTAP: case TAP_EVENT_2FGTAP: @@ -1129,28 +1218,44 @@ tp_tap_touch3_release2_handle_event(struct tp_dispatch *tp, switch (event) { case TAP_EVENT_TOUCH: - tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, time); - tp->tap.state = TAP_STATE_TOUCH_2; + if (tp->tap.hold_tap_enabled) { + tp_tap_kill_all_touches(tp, t); + tp->tap.state = TAP_STATE_TOUCH; + } else { + tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, time); + tp->tap.state = TAP_STATE_TOUCH_2; + } tp->tap.saved_press_time = time; tp_tap_set_timer(tp, time); break; case TAP_EVENT_RELEASE: - tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, time); - tp->tap.state = TAP_STATE_IDLE; + if (t->tap.state == TAP_TOUCH_STATE_TOUCH) { + tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, time); + if (tp->tap.nfingers_down == 0) + tp->tap.state = TAP_STATE_IDLE; + else + tp->tap.state = TAP_STATE_DEAD; + } break; case TAP_EVENT_MOTION: - tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, time); + 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: - tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, time); - if (tp->tap.drag_state == DRAG_STATE_IDLE) - tp->tap.state = TAP_STATE_HOLD; - else + if (tp->tap.hold_tap_enabled) tp->tap.state = TAP_STATE_DEAD; + 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; + else + tp->tap.state = TAP_STATE_DEAD; + } break; case TAP_EVENT_BUTTON: - tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, time); + 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: @@ -1159,8 +1264,13 @@ tp_tap_touch3_release2_handle_event(struct tp_dispatch *tp, case TAP_EVENT_THUMB: break; case TAP_EVENT_PALM: - tp_drag_handle_event(tp, t, TAP_EVENT_2FGTAP, time); - tp->tap.state = TAP_STATE_IDLE; + if (t->tap.state == TAP_TOUCH_STATE_TOUCH) { + tp_drag_handle_event(tp, t, TAP_EVENT_2FGTAP, time); + if (tp->tap.nfingers_down == 0) + tp->tap.state = TAP_STATE_IDLE; + else + tp->tap.state = TAP_STATE_DEAD; + } break; case TAP_EVENT_1FGTAP: case TAP_EVENT_2FGTAP: @@ -1211,11 +1321,17 @@ tp_tap_dead_handle_event(struct tp_dispatch *tp, { switch (event) { + case TAP_EVENT_TOUCH: + if (tp->tap.hold_tap_enabled) { + tp->tap.state = TAP_STATE_TOUCH; + tp->tap.saved_press_time = time; + tp_tap_set_timer(tp, time); + } + break; case TAP_EVENT_RELEASE: if (tp->tap.nfingers_down == 0) tp->tap.state = TAP_STATE_IDLE; break; - case TAP_EVENT_TOUCH: case TAP_EVENT_MOTION: case TAP_EVENT_TIMEOUT: break; @@ -1318,7 +1434,7 @@ tp_tap_handle_event(struct tp_dispatch *tp, if ((tp->tap.state == TAP_STATE_IDLE || tp->tap.state == TAP_STATE_BUTTON || tp->tap.state == TAP_STATE_DEAD)) - tp_tap_kill_all_touches(tp); + tp_tap_kill_all_touches(tp, NULL); } static bool @@ -1394,12 +1510,12 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time) if (t->palm.state != PALM_NONE) { assert(!t->tap.is_palm); 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--; tp_tap_handle_event(tp, t, TAP_EVENT_PALM, time); } + t->tap.state = TAP_TOUCH_STATE_DEAD; } else if (t->state == TOUCH_BEGIN) { /* The simple version: if a touch is a thumb on * begin we ignore it. All other thumb touches @@ -1428,8 +1544,10 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time) /* Any touch exceeding the threshold turns all * touches into DEAD */ - tp_tap_kill_all_touches(tp); - tp_tap_handle_event(tp, t, TAP_EVENT_MOTION, time); + if (t->tap.state == TAP_TOUCH_STATE_TOUCH) { + tp_tap_kill_all_touches(tp, NULL); + tp_tap_handle_event(tp, t, TAP_EVENT_MOTION, time); + } } } @@ -1466,7 +1584,14 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time) } assert(tp->tap.nfingers_down <= tp->nfingers_down); - if (tp->nfingers_down == 0) + if (tp->nfingers_down == 0 || + tp->tap.state == TAP_STATE_IDLE || + tp->tap.drag_state == DRAG_STATE_1FGTAP_TAPPED || + tp->tap.drag_state == DRAG_STATE_2FGTAP_TAPPED || + tp->tap.drag_state == DRAG_STATE_3FGTAP_TAPPED || + tp->tap.drag_state == DRAG_STATE_1FGTAP_DRAGLOCK_WAIT || + tp->tap.drag_state == DRAG_STATE_2FGTAP_DRAGLOCK_WAIT || + tp->tap.drag_state == DRAG_STATE_3FGTAP_DRAGLOCK_WAIT) assert(tp->tap.nfingers_down == 0); return filter_motion;