From 2a9d250fc6030cf284a081e405956dc2bc1e78aa Mon Sep 17 00:00:00 2001 From: satrmb <10471-satrmb_true-email-is-private_contact-via-web@gitlab.freedesktop.org> Date: Sat, 1 Aug 2020 18:52:04 +0200 Subject: [PATCH] touchpad: split off dragging from the tapping state machine No user-visible changes. Signed-off-by: satrmb <10471-satrmb@users.noreply.gitlab.freedesktop.org> --- doc/touchpad-tap-state-machine.svg | 2 +- src/evdev-mt-touchpad-tap.c | 933 ++++++++++++++++------------- src/evdev-mt-touchpad.h | 42 +- 3 files changed, 528 insertions(+), 449 deletions(-) diff --git a/doc/touchpad-tap-state-machine.svg b/doc/touchpad-tap-state-machine.svg index f3086d28..e62dd83e 100644 --- a/doc/touchpad-tap-state-machine.svg +++ b/doc/touchpad-tap-state-machine.svg @@ -1,3 +1,3 @@ -
(this section exists for [n] = 1, [n] = 2, and [n] = 3)
(this section exists for [n] = 1, [n] = 2, and [n] = 3)
IDLETOUCHfirstfinger downfinger upbutton 1presstimeoutmove > thresholdsecondfinger downTOUCH_2secondfinger upbutton 2pressmove > thresholdtimeoutbutton 1releasebutton 2release[n]FGTAP_TAPPEDtimeoutfirstfinger down[n]FGTAP_DRAGGINGfirstfinger upbutton [n]releaseIDLEthirdfinger downTOUCH_3button 3pressbutton 3releasemove > thresholdIDLEtimeoutfirstfinger upIDLEfourthfinger down[n]FGTAP_DRAGGING_OR_DOUBLETAPtimeoutfirstfinger upbutton [n]releasesecondfinger downmove > thresholdHOLDfirstfinger upsecondfinger downTOUCH_2_HOLDsecondfinger upthirdfinger downTOUCH_3_HOLDfourthfinger downDEADany finger upfourthfinger upany finger upany finger upIDLEif fingercount == 0[n]FGTAP_DRAGGING_2secondfinger downthirdfinger downphysbuttonpressphysbuttonpressbutton [n]release[n]FGTAP_DRAGGING_WAITtimeoutfirstfinger downTOUCH_TOUCHTOUCH_IDLETOUCH_DEADTOUCH_DEADTOUCH_IDLETOUCH_TOUCHTOUCH_IDLETOUCH_IDLETOUCH_TOUCHthat fingerTOUCH_IDLETOUCH_DEADthat fingerTOUCH_IDLETOUCH_TOUCHTOUCH_IDLETOUCH_TOUCHTOUCH_DEADTOUCH_IDLETOUCH_TOUCHTOUCH_TOUCHTOUCH_IDLETOUCH_TOUCHthat fingerTOUCH_IDLETOUCH_DEADTOUCH_DEADTOUCH_DEADTOUCH_DEADTOUCH_DEADTOUCH_DEADTOUCH_DEADTOUCH_DEADTOUCH_DEAD[n]FGTAP_DRAGGING_OR_TAPfirstfinger uptimeoutmove > thresholdTOUCH_IDLEdrag lockenabled?
no
no
yes
yes
thumbTOUCH_DEADTOUCH_2_RELEASEsecondfinger uptimeoutmove > thresholdfirstfinger downTOUCH_IDLEfirstfinger upsecondfinger downTOUCH_DEADTOUCH_DEADdragdisabled?
yes
yes
palmeither fingerpalmremaining fingerpalmany fingerpalmthat fingerTOUCH_DEADthat fingerTOUCH_DEADpalmany fingerpalmthat fingerTOUCH_DEADTOUCH_DEADpalmTOUCH_DEADany fingerpalmthat fingerTOUCH_DEADeither fingerpalmthat fingerTOUCH_DEADpalmTOUCH_DEADany fingerpalmthat fingerTOUCH_DEADTOUCH_DEADmove > thresholdmove > thresholdbutton 1press
[n] = 1
[n] = 1
[n] = 1
[n] = 1
no
no
button [n]releaseIDLEdragdisabled?
[n] = 2
[n] = 2
no
no
yes
yes
dragdisabled?
yes
yes
no
no
[n] = 3
[n] = 3
TOUCH_3_RELEASEtimeoutTOUCH_DEADTOUCH_DEADeither fingerpalmthat fingerTOUCH_DEADmove > thresholdthirdfinger downTOUCH_TOUCHTOUCH_3_RELEASE_2remainingfinger upthat fingerTOUCH_IDLEeither finger upthat fingerTOUCH_IDLEsecondfinger downremaining fingerpalmTOUCH_DEADtimeoutfirstfinger upbutton 3pressbutton 3releasebutton 3pressbutton 3releaseeither finger upthat fingerTOUCH_IDLEsecondfinger downbutton [n]releasebutton 3pressbutton 3releaseTOUCH_DEADbutton 3pressbutton 3releasebutton 3pressbutton 3release
Viewer does not support full SVG 1.1
+yesno(this section exists for [n] = 1, [n] = 2, and [n] = 3)noyesyes
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_DEADDEADall 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]releaseyesBUTTONclickpadbuttonpressbutton [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?3-finger tapfinger downTOUCH_TOUCH3-finger tapfinger downTOUCH_TOUCHfinger downTOUCH_TOUCH3-finger tapall fingersTOUCH_DEADfourthfinger downTOUCH_DEADto draggingstate machine:move >thresholdnodrag state== IDLE?to draggingstate machine:move >thresholdfinger downnoyesnon-palmfinger count> 2?palmnonon-palmfinger count== 0?palm upthumbanyfinger palm
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 5c34c6df..35a334b9 100644 --- a/src/evdev-mt-touchpad-tap.c +++ b/src/evdev-mt-touchpad-tap.c @@ -44,6 +44,9 @@ enum tap_event { TAP_EVENT_THUMB, TAP_EVENT_PALM, TAP_EVENT_PALM_UP, + TAP_EVENT_1FGTAP, /**< valid for dragging state machine only */ + TAP_EVENT_2FGTAP, /**< valid for dragging state machine only */ + TAP_EVENT_3FGTAP, /**< valid for dragging state machine only */ }; /***************************************** @@ -58,13 +61,10 @@ enum tap_event { static inline const char* tap_state_to_str(enum tp_tap_state state) { - switch(state) { + switch (state) { CASE_RETURN_STRING(TAP_STATE_IDLE); CASE_RETURN_STRING(TAP_STATE_HOLD); CASE_RETURN_STRING(TAP_STATE_TOUCH); - CASE_RETURN_STRING(TAP_STATE_1FGTAP_TAPPED); - CASE_RETURN_STRING(TAP_STATE_2FGTAP_TAPPED); - CASE_RETURN_STRING(TAP_STATE_3FGTAP_TAPPED); CASE_RETURN_STRING(TAP_STATE_TOUCH_2); CASE_RETURN_STRING(TAP_STATE_TOUCH_2_HOLD); CASE_RETURN_STRING(TAP_STATE_TOUCH_2_RELEASE); @@ -72,30 +72,40 @@ 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_1FGTAP_DRAGGING); - CASE_RETURN_STRING(TAP_STATE_2FGTAP_DRAGGING); - CASE_RETURN_STRING(TAP_STATE_3FGTAP_DRAGGING); - CASE_RETURN_STRING(TAP_STATE_1FGTAP_DRAGLOCK_WAIT); - CASE_RETURN_STRING(TAP_STATE_2FGTAP_DRAGLOCK_WAIT); - CASE_RETURN_STRING(TAP_STATE_3FGTAP_DRAGLOCK_WAIT); - CASE_RETURN_STRING(TAP_STATE_1FGTAP_DRAGGING_OR_DOUBLETAP); - CASE_RETURN_STRING(TAP_STATE_2FGTAP_DRAGGING_OR_DOUBLETAP); - CASE_RETURN_STRING(TAP_STATE_3FGTAP_DRAGGING_OR_DOUBLETAP); - CASE_RETURN_STRING(TAP_STATE_1FGTAP_DRAGLOCK_CONTINUE); - CASE_RETURN_STRING(TAP_STATE_2FGTAP_DRAGLOCK_CONTINUE); - CASE_RETURN_STRING(TAP_STATE_3FGTAP_DRAGLOCK_CONTINUE); - CASE_RETURN_STRING(TAP_STATE_1FGTAP_DRAGGING_2); - CASE_RETURN_STRING(TAP_STATE_2FGTAP_DRAGGING_2); - CASE_RETURN_STRING(TAP_STATE_3FGTAP_DRAGGING_2); CASE_RETURN_STRING(TAP_STATE_DEAD); } return NULL; } +static inline const char* +drag_state_to_str(enum tp_drag_state state) +{ + switch (state) { + CASE_RETURN_STRING(DRAG_STATE_IDLE); + CASE_RETURN_STRING(DRAG_STATE_1FGTAP_TAPPED); + CASE_RETURN_STRING(DRAG_STATE_2FGTAP_TAPPED); + CASE_RETURN_STRING(DRAG_STATE_3FGTAP_TAPPED); + CASE_RETURN_STRING(DRAG_STATE_1FGTAP_DRAGGING_OR_DOUBLETAP); + CASE_RETURN_STRING(DRAG_STATE_2FGTAP_DRAGGING_OR_DOUBLETAP); + CASE_RETURN_STRING(DRAG_STATE_3FGTAP_DRAGGING_OR_DOUBLETAP); + CASE_RETURN_STRING(DRAG_STATE_1FGTAP_DRAGGING); + CASE_RETURN_STRING(DRAG_STATE_2FGTAP_DRAGGING); + CASE_RETURN_STRING(DRAG_STATE_3FGTAP_DRAGGING); + CASE_RETURN_STRING(DRAG_STATE_1FGTAP_DRAGLOCK_WAIT); + CASE_RETURN_STRING(DRAG_STATE_2FGTAP_DRAGLOCK_WAIT); + CASE_RETURN_STRING(DRAG_STATE_3FGTAP_DRAGLOCK_WAIT); + CASE_RETURN_STRING(DRAG_STATE_1FGTAP_DRAGLOCK_CONTINUE); + CASE_RETURN_STRING(DRAG_STATE_2FGTAP_DRAGLOCK_CONTINUE); + CASE_RETURN_STRING(DRAG_STATE_3FGTAP_DRAGLOCK_CONTINUE); + CASE_RETURN_STRING(DRAG_STATE_BUTTON); + } + return NULL; +} + static inline const char* tap_event_to_str(enum tap_event event) { - switch(event) { + switch (event) { CASE_RETURN_STRING(TAP_EVENT_TOUCH); CASE_RETURN_STRING(TAP_EVENT_MOTION); CASE_RETURN_STRING(TAP_EVENT_RELEASE); @@ -104,6 +114,9 @@ tap_event_to_str(enum tap_event event) CASE_RETURN_STRING(TAP_EVENT_THUMB); CASE_RETURN_STRING(TAP_EVENT_PALM); CASE_RETURN_STRING(TAP_EVENT_PALM_UP); + CASE_RETURN_STRING(TAP_EVENT_1FGTAP); + CASE_RETURN_STRING(TAP_EVENT_2FGTAP); + CASE_RETURN_STRING(TAP_EVENT_3FGTAP); } return NULL; } @@ -113,12 +126,23 @@ log_tap_bug(struct tp_dispatch *tp, struct tp_touch *t, enum tap_event event) { evdev_log_bug_libinput(tp->device, "%d: invalid tap event %s in state %s\n", - t->index, + t ? (int)t->index : -1, tap_event_to_str(event), tap_state_to_str(tp->tap.state)); } +static inline void +log_drag_bug(struct tp_dispatch *tp, struct tp_touch *t, enum tap_event event) +{ + evdev_log_bug_libinput(tp->device, + "%d: invalid drag event %s in state %s\n", + t ? (int)t->index : -1, + tap_event_to_str(event), + drag_state_to_str(tp->tap.drag_state)); + +} + static void tp_tap_notify(struct tp_dispatch *tp, uint64_t time, @@ -196,11 +220,61 @@ tp_drag_idle_handle_event(struct tp_dispatch *tp, case TAP_EVENT_RELEASE: case TAP_EVENT_MOTION: case TAP_EVENT_TIMEOUT: + break; case TAP_EVENT_BUTTON: + tp->tap.drag_state = DRAG_STATE_BUTTON; + break; case TAP_EVENT_THUMB: case TAP_EVENT_PALM: case TAP_EVENT_PALM_UP: break; + case TAP_EVENT_1FGTAP: + tp_tap_notify(tp, + tp->tap.saved_press_time, + 1, + LIBINPUT_BUTTON_STATE_PRESSED); + if (tp->tap.drag_enabled) { + tp->tap.drag_state = DRAG_STATE_1FGTAP_TAPPED; + tp_tap_set_drag_timer(tp, time, 1); + } else { + tp_tap_notify(tp, + tp->tap.saved_release_time, + 1, + LIBINPUT_BUTTON_STATE_RELEASED); + } + break; + case TAP_EVENT_2FGTAP: + tp_tap_notify(tp, + tp->tap.saved_press_time, + 2, + LIBINPUT_BUTTON_STATE_PRESSED); + if (tp->tap.drag_enabled) { + tp->tap.drag_state = DRAG_STATE_2FGTAP_TAPPED; + tp_tap_set_drag_timer(tp, time, 2); + } else { + tp_tap_notify(tp, + tp->tap.saved_release_time, + 2, + LIBINPUT_BUTTON_STATE_RELEASED); + } + break; + case TAP_EVENT_3FGTAP: + tp_tap_notify(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); + } else { + tp_tap_notify(tp, + tp->tap.saved_release_time, + 3, + LIBINPUT_BUTTON_STATE_RELEASED); + } + break; } } @@ -211,44 +285,56 @@ tp_drag_tapped_handle_event(struct tp_dispatch *tp, int nfingers_tapped) { switch (event) { - case TAP_EVENT_MOTION: - case TAP_EVENT_RELEASE: - log_tap_bug(tp, t, event); - break; case TAP_EVENT_TOUCH: { - enum tp_tap_state dest[3] = { - TAP_STATE_1FGTAP_DRAGGING_OR_DOUBLETAP, - TAP_STATE_2FGTAP_DRAGGING_OR_DOUBLETAP, - TAP_STATE_3FGTAP_DRAGGING_OR_DOUBLETAP, + enum tp_drag_state dest[3] = { + DRAG_STATE_1FGTAP_DRAGGING_OR_DOUBLETAP, + DRAG_STATE_2FGTAP_DRAGGING_OR_DOUBLETAP, + DRAG_STATE_3FGTAP_DRAGGING_OR_DOUBLETAP, }; assert(nfingers_tapped >= 1 && nfingers_tapped <= 3); - tp->tap.state = dest[nfingers_tapped - 1]; - tp->tap.saved_press_time = time; - tp_tap_set_timer(tp, time); + tp->tap.drag_state = dest[nfingers_tapped - 1]; + /* saved_release_time may get overwritten by + * a subsequent tap, but then we still need it to end + * the tap that brought us here */ + tp->tap.saved_multitap_release_time = tp->tap.saved_release_time; break; } + case TAP_EVENT_RELEASE: + /* not a bug, just the result of receiving the tap event + * that brought us here before the release event that triggered + * the tap event */ + break; + case TAP_EVENT_MOTION: + log_drag_bug(tp, t, event); + break; case TAP_EVENT_TIMEOUT: - tp->tap.state = TAP_STATE_IDLE; tp_tap_notify(tp, tp->tap.saved_release_time, nfingers_tapped, LIBINPUT_BUTTON_STATE_RELEASED); + tp->tap.drag_state = DRAG_STATE_IDLE; break; case TAP_EVENT_BUTTON: - tp->tap.state = TAP_STATE_DEAD; tp_tap_notify(tp, tp->tap.saved_release_time, nfingers_tapped, LIBINPUT_BUTTON_STATE_RELEASED); + tp->tap.drag_state = DRAG_STATE_BUTTON; break; case TAP_EVENT_THUMB: - log_tap_bug(tp, t, event); + log_drag_bug(tp, t, event); break; case TAP_EVENT_PALM: - log_tap_bug(tp, t, event); + /* same as a release event; the palm may have been the last + * touch out of multiple short ones triggering a tap */ break; case TAP_EVENT_PALM_UP: break; + case TAP_EVENT_1FGTAP: + case TAP_EVENT_2FGTAP: + case TAP_EVENT_3FGTAP: + log_drag_bug(tp, t, event); + break; } } @@ -259,60 +345,72 @@ tp_drag_dragging_or_doubletap_handle_event(struct tp_dispatch *tp, int nfingers_tapped) { switch (event) { - case TAP_EVENT_TOUCH: { + case TAP_EVENT_TOUCH: + /* only one finger allowed at start of drag */ tp_tap_notify(tp, - tp->tap.saved_release_time, + tp->tap.saved_multitap_release_time, nfingers_tapped, LIBINPUT_BUTTON_STATE_RELEASED); - tp->tap.state = TAP_STATE_TOUCH_2; - tp->tap.saved_press_time = time; - tp_tap_set_timer(tp, time); + tp->tap.drag_state = DRAG_STATE_IDLE; + break; + case TAP_EVENT_RELEASE: { + /* we should have received a tap before the release, + * since there was no timeout or movement */ + log_drag_bug(tp, t, event); + tp_tap_notify(tp, + tp->tap.saved_multitap_release_time, + nfingers_tapped, + LIBINPUT_BUTTON_STATE_RELEASED); + tp->tap.drag_state = DRAG_STATE_IDLE; break; } - case TAP_EVENT_RELEASE: - tp->tap.state = TAP_STATE_1FGTAP_TAPPED; + case TAP_EVENT_MOTION: + case TAP_EVENT_TIMEOUT: { + enum tp_drag_state dest[3] = { + DRAG_STATE_1FGTAP_DRAGGING, + DRAG_STATE_2FGTAP_DRAGGING, + DRAG_STATE_3FGTAP_DRAGGING, + }; + assert(nfingers_tapped >= 1 && nfingers_tapped <= 3); + tp->tap.drag_state = dest[nfingers_tapped - 1]; + break; + } + case TAP_EVENT_BUTTON: tp_tap_notify(tp, - tp->tap.saved_release_time, + tp->tap.saved_multitap_release_time, + nfingers_tapped, + LIBINPUT_BUTTON_STATE_RELEASED); + tp->tap.drag_state = DRAG_STATE_BUTTON; + break; + case TAP_EVENT_THUMB: + break; + case TAP_EVENT_PALM: { + enum tp_drag_state dest[3] = { + DRAG_STATE_1FGTAP_TAPPED, + DRAG_STATE_2FGTAP_TAPPED, + DRAG_STATE_3FGTAP_TAPPED, + }; + assert(nfingers_tapped >= 1 && nfingers_tapped <= 3); + tp->tap.drag_state = dest[nfingers_tapped - 1]; + break; + } + case TAP_EVENT_PALM_UP: + break; + case TAP_EVENT_1FGTAP: + tp_tap_notify(tp, + tp->tap.saved_multitap_release_time, nfingers_tapped, LIBINPUT_BUTTON_STATE_RELEASED); tp_tap_notify(tp, tp->tap.saved_press_time, 1, LIBINPUT_BUTTON_STATE_PRESSED); - tp->tap.saved_release_time = time; + tp->tap.drag_state = DRAG_STATE_1FGTAP_TAPPED; tp_tap_set_timer(tp, time); break; - case TAP_EVENT_MOTION: - case TAP_EVENT_TIMEOUT: { - enum tp_tap_state dest[3] = { - TAP_STATE_1FGTAP_DRAGGING, - TAP_STATE_2FGTAP_DRAGGING, - TAP_STATE_3FGTAP_DRAGGING, - }; - assert(nfingers_tapped >= 1 && nfingers_tapped <= 3); - tp->tap.state = dest[nfingers_tapped - 1]; - break; - } - case TAP_EVENT_BUTTON: - tp->tap.state = TAP_STATE_DEAD; - tp_tap_notify(tp, - tp->tap.saved_release_time, - nfingers_tapped, - LIBINPUT_BUTTON_STATE_RELEASED); - break; - case TAP_EVENT_THUMB: - break; - case TAP_EVENT_PALM: { - enum tp_tap_state dest[3] = { - TAP_STATE_1FGTAP_TAPPED, - TAP_STATE_2FGTAP_TAPPED, - TAP_STATE_3FGTAP_TAPPED, - }; - assert(nfingers_tapped >= 1 && nfingers_tapped <= 3); - tp->tap.state = dest[nfingers_tapped - 1]; - break; - } - case TAP_EVENT_PALM_UP: + case TAP_EVENT_2FGTAP: + case TAP_EVENT_3FGTAP: + log_drag_bug(tp, t, event); break; } } @@ -325,57 +423,65 @@ tp_drag_dragging_handle_event(struct tp_dispatch *tp, { switch (event) { - case TAP_EVENT_TOUCH: { - enum tp_tap_state dest[3] = { - TAP_STATE_1FGTAP_DRAGGING_2, - TAP_STATE_2FGTAP_DRAGGING_2, - TAP_STATE_3FGTAP_DRAGGING_2, - }; - assert(nfingers_tapped >= 1 && nfingers_tapped <= 3); - tp->tap.state = dest[nfingers_tapped - 1]; - break; - } - case TAP_EVENT_RELEASE: - if (tp->tap.drag_lock != LIBINPUT_CONFIG_DRAG_LOCK_DISABLED) { - enum tp_tap_state dest[3] = { - TAP_STATE_1FGTAP_DRAGLOCK_WAIT, - TAP_STATE_2FGTAP_DRAGLOCK_WAIT, - TAP_STATE_3FGTAP_DRAGLOCK_WAIT, - }; - assert(nfingers_tapped >= 1 && nfingers_tapped <= 3); - tp->tap.state = dest[nfingers_tapped - 1]; - if (tp->tap.drag_lock == LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_TIMEOUT) - tp_tap_set_draglock_timer(tp, time); - } else { + case TAP_EVENT_TOUCH: + if (tp->tap.nfingers_down > 2) { tp_tap_notify(tp, time, nfingers_tapped, LIBINPUT_BUTTON_STATE_RELEASED); - tp->tap.state = TAP_STATE_IDLE; + tp->tap.drag_state = DRAG_STATE_IDLE; + } + break; + case TAP_EVENT_RELEASE: + if (tp->tap.nfingers_down == 0) { + if (tp->tap.drag_lock != LIBINPUT_CONFIG_DRAG_LOCK_DISABLED) { + enum tp_drag_state dest[3] = { + DRAG_STATE_1FGTAP_DRAGLOCK_WAIT, + DRAG_STATE_2FGTAP_DRAGLOCK_WAIT, + DRAG_STATE_3FGTAP_DRAGLOCK_WAIT, + }; + assert(nfingers_tapped >= 1 && + nfingers_tapped <= 3); + tp->tap.drag_state = dest[nfingers_tapped - 1]; + if (tp->tap.drag_lock == LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_TIMEOUT) + tp_tap_set_draglock_timer(tp, time); + } else { + tp_tap_notify(tp, + time, + nfingers_tapped, + LIBINPUT_BUTTON_STATE_RELEASED); + tp->tap.drag_state = DRAG_STATE_IDLE; + } } break; case TAP_EVENT_MOTION: case TAP_EVENT_TIMEOUT: - /* noop */ break; case TAP_EVENT_BUTTON: - tp->tap.state = TAP_STATE_DEAD; tp_tap_notify(tp, time, nfingers_tapped, LIBINPUT_BUTTON_STATE_RELEASED); + tp->tap.drag_state = DRAG_STATE_BUTTON; break; case TAP_EVENT_THUMB: break; case TAP_EVENT_PALM: - tp_tap_notify(tp, - tp->tap.saved_release_time, - nfingers_tapped, - LIBINPUT_BUTTON_STATE_RELEASED); - tp->tap.state = TAP_STATE_IDLE; + if (tp->tap.nfingers_down == 0) { + tp_tap_notify(tp, + time, + nfingers_tapped, + LIBINPUT_BUTTON_STATE_RELEASED); + tp->tap.drag_state = DRAG_STATE_IDLE; + } break; case TAP_EVENT_PALM_UP: break; + case TAP_EVENT_1FGTAP: + case TAP_EVENT_2FGTAP: + case TAP_EVENT_3FGTAP: + log_drag_bug(tp, t, event); + break; } } @@ -388,40 +494,44 @@ tp_drag_draglock_wait_handle_event(struct tp_dispatch *tp, switch (event) { case TAP_EVENT_TOUCH: { - enum tp_tap_state dest[3] = { - TAP_STATE_1FGTAP_DRAGLOCK_CONTINUE, - TAP_STATE_2FGTAP_DRAGLOCK_CONTINUE, - TAP_STATE_3FGTAP_DRAGLOCK_CONTINUE, + enum tp_drag_state dest[3] = { + DRAG_STATE_1FGTAP_DRAGLOCK_CONTINUE, + DRAG_STATE_2FGTAP_DRAGLOCK_CONTINUE, + DRAG_STATE_3FGTAP_DRAGLOCK_CONTINUE, }; assert(nfingers_tapped >= 1 && nfingers_tapped <= 3); - tp->tap.state = dest[nfingers_tapped - 1]; - tp_tap_set_timer(tp, time); + tp->tap.drag_state = dest[nfingers_tapped - 1]; break; } case TAP_EVENT_RELEASE: case TAP_EVENT_MOTION: - log_tap_bug(tp, t, event); + log_drag_bug(tp, t, event); break; case TAP_EVENT_TIMEOUT: - tp->tap.state = TAP_STATE_IDLE; tp_tap_notify(tp, time, nfingers_tapped, LIBINPUT_BUTTON_STATE_RELEASED); + tp->tap.drag_state = DRAG_STATE_IDLE; break; case TAP_EVENT_BUTTON: - tp->tap.state = TAP_STATE_DEAD; tp_tap_notify(tp, time, nfingers_tapped, LIBINPUT_BUTTON_STATE_RELEASED); + tp->tap.drag_state = DRAG_STATE_BUTTON; break; case TAP_EVENT_THUMB: case TAP_EVENT_PALM: - log_tap_bug(tp, t, event); + log_drag_bug(tp, t, event); break; case TAP_EVENT_PALM_UP: break; + case TAP_EVENT_1FGTAP: + case TAP_EVENT_2FGTAP: + case TAP_EVENT_3FGTAP: + log_drag_bug(tp, t, event); + break; } } @@ -438,101 +548,66 @@ tp_drag_draglock_continue_handle_event(struct tp_dispatch *tp, time, nfingers_tapped, LIBINPUT_BUTTON_STATE_RELEASED); - tp_tap_clear_timer(tp); + tp->tap.drag_state = DRAG_STATE_IDLE; + /* ignore taps with more than one finger, they shall + * only end drag-lock instead of emitting button events + * or starting a new drag */ tp_tap_move_to_dead(tp, t); break; } - case TAP_EVENT_RELEASE: - tp->tap.state = TAP_STATE_IDLE; + case TAP_EVENT_RELEASE: { + /* we should have received a tap before the release, + * since there was no timeout or movement */ + log_drag_bug(tp, t, event); tp_tap_notify(tp, time, nfingers_tapped, LIBINPUT_BUTTON_STATE_RELEASED); + tp->tap.drag_state = DRAG_STATE_IDLE; break; + } case TAP_EVENT_MOTION: case TAP_EVENT_TIMEOUT: { - enum tp_tap_state dest[3] = { - TAP_STATE_1FGTAP_DRAGGING, - TAP_STATE_2FGTAP_DRAGGING, - TAP_STATE_3FGTAP_DRAGGING, + enum tp_drag_state dest[3] = { + DRAG_STATE_1FGTAP_DRAGGING, + DRAG_STATE_2FGTAP_DRAGGING, + DRAG_STATE_3FGTAP_DRAGGING, }; assert(nfingers_tapped >= 1 && nfingers_tapped <= 3); - tp->tap.state = dest[nfingers_tapped - 1]; + tp->tap.drag_state = dest[nfingers_tapped - 1]; break; } case TAP_EVENT_BUTTON: - tp->tap.state = TAP_STATE_DEAD; tp_tap_notify(tp, time, nfingers_tapped, LIBINPUT_BUTTON_STATE_RELEASED); + tp->tap.drag_state = DRAG_STATE_BUTTON; break; case TAP_EVENT_THUMB: break; case TAP_EVENT_PALM: { - enum tp_tap_state dest[3] = { - TAP_STATE_1FGTAP_DRAGLOCK_WAIT, - TAP_STATE_2FGTAP_DRAGLOCK_WAIT, - TAP_STATE_3FGTAP_DRAGLOCK_WAIT, + enum tp_drag_state dest[3] = { + DRAG_STATE_1FGTAP_DRAGLOCK_WAIT, + DRAG_STATE_2FGTAP_DRAGLOCK_WAIT, + DRAG_STATE_3FGTAP_DRAGLOCK_WAIT, }; assert(nfingers_tapped >= 1 && nfingers_tapped <= 3); - tp->tap.state = dest[nfingers_tapped - 1]; + tp->tap.drag_state = dest[nfingers_tapped - 1]; break; } case TAP_EVENT_PALM_UP: break; - } -} - -static void -tp_drag_dragging2_handle_event(struct tp_dispatch *tp, - struct tp_touch *t, - enum tap_event event, uint64_t time, - int nfingers_tapped) -{ - - switch (event) { - case TAP_EVENT_RELEASE: { - enum tp_tap_state dest[3] = { - TAP_STATE_1FGTAP_DRAGGING, - TAP_STATE_2FGTAP_DRAGGING, - TAP_STATE_3FGTAP_DRAGGING, - }; - assert(nfingers_tapped >= 1 && nfingers_tapped <= 3); - tp->tap.state = dest[nfingers_tapped - 1]; - break; - } - case TAP_EVENT_TOUCH: - tp->tap.state = TAP_STATE_DEAD; + case TAP_EVENT_1FGTAP: tp_tap_notify(tp, time, nfingers_tapped, LIBINPUT_BUTTON_STATE_RELEASED); + tp->tap.drag_state = DRAG_STATE_IDLE; break; - case TAP_EVENT_MOTION: - case TAP_EVENT_TIMEOUT: - /* noop */ - break; - case TAP_EVENT_BUTTON: - tp->tap.state = TAP_STATE_DEAD; - tp_tap_notify(tp, - time, - nfingers_tapped, - LIBINPUT_BUTTON_STATE_RELEASED); - break; - case TAP_EVENT_THUMB: - break; - case TAP_EVENT_PALM: { - enum tp_tap_state dest[3] = { - TAP_STATE_1FGTAP_DRAGGING, - TAP_STATE_2FGTAP_DRAGGING, - TAP_STATE_3FGTAP_DRAGGING, - }; - assert(nfingers_tapped >= 1 && nfingers_tapped <= 3); - tp->tap.state = dest[nfingers_tapped - 1]; - break; - } - case TAP_EVENT_PALM_UP: + case TAP_EVENT_2FGTAP: + case TAP_EVENT_3FGTAP: + log_drag_bug(tp, t, event); break; } } @@ -542,15 +617,30 @@ tp_drag_button_handle_event(struct tp_dispatch *tp, struct tp_touch *t, enum tap_event event, uint64_t time) { + switch (event) { - case TAP_EVENT_TOUCH: case TAP_EVENT_RELEASE: + if (tp->tap.nfingers_down == 0) + tp->tap.drag_state = DRAG_STATE_IDLE; + break; + case TAP_EVENT_TOUCH: case TAP_EVENT_MOTION: case TAP_EVENT_TIMEOUT: + break; case TAP_EVENT_BUTTON: + log_drag_bug(tp, t, event); + break; case TAP_EVENT_THUMB: + break; case TAP_EVENT_PALM: case TAP_EVENT_PALM_UP: + if (tp->tap.nfingers_down == 0) + tp->tap.drag_state = DRAG_STATE_IDLE; + break; + case TAP_EVENT_1FGTAP: + case TAP_EVENT_2FGTAP: + case TAP_EVENT_3FGTAP: + log_drag_bug(tp, t, event); break; } } @@ -561,6 +651,78 @@ tp_drag_handle_event(struct tp_dispatch *tp, enum tap_event event, uint64_t time) { + enum tp_drag_state current; + + current = tp->tap.drag_state; + + switch(tp->tap.drag_state) { + case DRAG_STATE_IDLE: + tp_drag_idle_handle_event(tp, t, event, time); + break; + case DRAG_STATE_1FGTAP_TAPPED: + tp_drag_tapped_handle_event(tp, t, event, time, 1); + break; + case DRAG_STATE_2FGTAP_TAPPED: + tp_drag_tapped_handle_event(tp, t, event, time, 2); + break; + case DRAG_STATE_3FGTAP_TAPPED: + tp_drag_tapped_handle_event(tp, t, event, time, 3); + break; + case DRAG_STATE_1FGTAP_DRAGGING_OR_DOUBLETAP: + tp_drag_dragging_or_doubletap_handle_event(tp, t, event, time, + 1); + break; + case DRAG_STATE_2FGTAP_DRAGGING_OR_DOUBLETAP: + tp_drag_dragging_or_doubletap_handle_event(tp, t, event, time, + 2); + break; + case DRAG_STATE_3FGTAP_DRAGGING_OR_DOUBLETAP: + tp_drag_dragging_or_doubletap_handle_event(tp, t, event, time, + 3); + break; + case DRAG_STATE_1FGTAP_DRAGGING: + tp_drag_dragging_handle_event(tp, t, event, time, 1); + break; + case DRAG_STATE_2FGTAP_DRAGGING: + tp_drag_dragging_handle_event(tp, t, event, time, 2); + break; + case DRAG_STATE_3FGTAP_DRAGGING: + tp_drag_dragging_handle_event(tp, t, event, time, 3); + break; + case DRAG_STATE_1FGTAP_DRAGLOCK_WAIT: + tp_drag_draglock_wait_handle_event(tp, t, event, time, 1); + break; + case DRAG_STATE_2FGTAP_DRAGLOCK_WAIT: + tp_drag_draglock_wait_handle_event(tp, t, event, time, 2); + break; + case DRAG_STATE_3FGTAP_DRAGLOCK_WAIT: + tp_drag_draglock_wait_handle_event(tp, t, event, time, 3); + break; + case DRAG_STATE_1FGTAP_DRAGLOCK_CONTINUE: + tp_drag_draglock_continue_handle_event(tp, t, event, time, 1); + break; + case DRAG_STATE_2FGTAP_DRAGLOCK_CONTINUE: + tp_drag_draglock_continue_handle_event(tp, t, event, time, 2); + break; + case DRAG_STATE_3FGTAP_DRAGLOCK_CONTINUE: + tp_drag_draglock_continue_handle_event(tp, t, event, time, 3); + break; + case DRAG_STATE_BUTTON: + tp_drag_button_handle_event(tp, t, event, time); + break; + } + + if (tp->tap.drag_state == DRAG_STATE_BUTTON) + tp_tap_clear_timer(tp); + + if (current != tp->tap.drag_state) + evdev_log_debug(tp->device, + "drag: touch %d (%s), drag state %s → %s → %s\n", + t ? (int)t->index : -1, + t ? touch_state_to_str(t->state) : "", + drag_state_to_str(current), + tap_event_to_str(event), + drag_state_to_str(tp->tap.drag_state)); } static void @@ -592,6 +754,11 @@ tp_tap_idle_handle_event(struct tp_dispatch *tp, break; case TAP_EVENT_PALM_UP: break; + case TAP_EVENT_1FGTAP: + case TAP_EVENT_2FGTAP: + case TAP_EVENT_3FGTAP: + log_tap_bug(tp, t, event); + break; } } @@ -608,27 +775,18 @@ tp_tap_touch_handle_event(struct tp_dispatch *tp, tp_tap_set_timer(tp, time); break; case TAP_EVENT_RELEASE: - tp_tap_notify(tp, - tp->tap.saved_press_time, - 1, - LIBINPUT_BUTTON_STATE_PRESSED); - if (tp->tap.drag_enabled) { - tp->tap.state = TAP_STATE_1FGTAP_TAPPED; - tp->tap.saved_release_time = time; - tp_tap_set_drag_timer(tp, time, 1); - } else { - tp_tap_notify(tp, - time, - 1, - LIBINPUT_BUTTON_STATE_RELEASED); - tp->tap.state = TAP_STATE_IDLE; - } + tp->tap.saved_release_time = time; + tp_drag_handle_event(tp, t, TAP_EVENT_1FGTAP, time); + tp->tap.state = TAP_STATE_IDLE; break; case TAP_EVENT_MOTION: tp_tap_move_to_dead(tp, t); break; case TAP_EVENT_TIMEOUT: - tp->tap.state = TAP_STATE_HOLD; + if (tp->tap.drag_state == DRAG_STATE_IDLE) + tp->tap.state = TAP_STATE_HOLD; + else + tp->tap.state = TAP_STATE_DEAD; tp_tap_clear_timer(tp); tp_gesture_tap_timeout(tp, time); break; @@ -636,18 +794,24 @@ tp_tap_touch_handle_event(struct tp_dispatch *tp, tp->tap.state = TAP_STATE_DEAD; break; 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); + if (tp->tap.drag_state == DRAG_STATE_IDLE) { + 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; - tp_tap_clear_timer(tp); break; case TAP_EVENT_PALM_UP: break; + case TAP_EVENT_1FGTAP: + case TAP_EVENT_2FGTAP: + case TAP_EVENT_3FGTAP: + log_tap_bug(tp, t, event); + break; } } @@ -685,6 +849,11 @@ tp_tap_hold_handle_event(struct tp_dispatch *tp, break; case TAP_EVENT_PALM_UP: break; + case TAP_EVENT_1FGTAP: + case TAP_EVENT_2FGTAP: + case TAP_EVENT_3FGTAP: + log_tap_bug(tp, t, event); + break; } } @@ -709,7 +878,10 @@ tp_tap_touch2_handle_event(struct tp_dispatch *tp, tp_tap_move_to_dead(tp, t); break; case TAP_EVENT_TIMEOUT: - tp->tap.state = TAP_STATE_TOUCH_2_HOLD; + if (tp->tap.drag_state == DRAG_STATE_IDLE) + tp->tap.state = TAP_STATE_TOUCH_2_HOLD; + else + tp->tap.state = TAP_STATE_DEAD; tp_gesture_tap_timeout(tp, time); break; case TAP_EVENT_BUTTON: @@ -722,6 +894,11 @@ tp_tap_touch2_handle_event(struct tp_dispatch *tp, break; case TAP_EVENT_PALM_UP: break; + case TAP_EVENT_1FGTAP: + case TAP_EVENT_2FGTAP: + case TAP_EVENT_3FGTAP: + log_tap_bug(tp, t, event); + break; } } @@ -744,7 +921,6 @@ tp_tap_touch2_hold_handle_event(struct tp_dispatch *tp, tp_tap_move_to_dead(tp, t); break; case TAP_EVENT_TIMEOUT: - tp->tap.state = TAP_STATE_TOUCH_2_HOLD; break; case TAP_EVENT_BUTTON: tp->tap.state = TAP_STATE_DEAD; @@ -756,6 +932,11 @@ tp_tap_touch2_hold_handle_event(struct tp_dispatch *tp, break; case TAP_EVENT_PALM_UP: break; + case TAP_EVENT_1FGTAP: + case TAP_EVENT_2FGTAP: + case TAP_EVENT_3FGTAP: + log_tap_bug(tp, t, event); + break; } } @@ -767,31 +948,30 @@ tp_tap_touch2_release_handle_event(struct tp_dispatch *tp, switch (event) { case TAP_EVENT_TOUCH: - tp->tap.state = TAP_STATE_TOUCH_2_HOLD; + if (tp->tap.drag_state == DRAG_STATE_IDLE) + tp->tap.state = TAP_STATE_TOUCH_2_HOLD; + else { + /* 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_DEAD; + } t->tap.state = TAP_TOUCH_STATE_DEAD; tp_tap_clear_timer(tp); break; case TAP_EVENT_RELEASE: - tp_tap_notify(tp, - tp->tap.saved_press_time, - 2, - LIBINPUT_BUTTON_STATE_PRESSED); - if (tp->tap.drag_enabled) { - tp->tap.state = TAP_STATE_2FGTAP_TAPPED; - tp_tap_set_drag_timer(tp, time, 2); - } else { - tp_tap_notify(tp, - tp->tap.saved_release_time, - 2, - LIBINPUT_BUTTON_STATE_RELEASED); - tp->tap.state = TAP_STATE_IDLE; - } + tp_drag_handle_event(tp, t, TAP_EVENT_2FGTAP, time); + tp->tap.state = TAP_STATE_IDLE; break; case TAP_EVENT_MOTION: tp_tap_move_to_dead(tp, t); break; case TAP_EVENT_TIMEOUT: - tp->tap.state = TAP_STATE_HOLD; + 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->tap.state = TAP_STATE_DEAD; @@ -803,27 +983,19 @@ tp_tap_touch2_release_handle_event(struct tp_dispatch *tp, * 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. + * time. The tap timer also gets reset on palm detection. + * 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) { - /* For a single-finger tap the timer delay is the same - * as for the release of the finger that became a palm, - * no reset necessary */ - tp->tap.state = TAP_STATE_1FGTAP_TAPPED; - } else { - tp_tap_notify(tp, - tp->tap.saved_release_time, - 1, - LIBINPUT_BUTTON_STATE_RELEASED); - tp->tap.state = TAP_STATE_IDLE; - } + tp_drag_handle_event(tp, t, TAP_EVENT_1FGTAP, time); + tp->tap.state = TAP_STATE_IDLE; break; case TAP_EVENT_PALM_UP: break; + case TAP_EVENT_1FGTAP: + case TAP_EVENT_2FGTAP: + case TAP_EVENT_3FGTAP: + log_tap_bug(tp, t, event); + break; } } @@ -837,6 +1009,9 @@ tp_tap_touch3_handle_event(struct tp_dispatch *tp, case TAP_EVENT_TOUCH: tp->tap.state = TAP_STATE_DEAD; tp_tap_clear_timer(tp); + /* 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); break; case TAP_EVENT_MOTION: tp_tap_move_to_dead(tp, t); @@ -861,6 +1036,11 @@ tp_tap_touch3_handle_event(struct tp_dispatch *tp, break; case TAP_EVENT_PALM_UP: break; + case TAP_EVENT_1FGTAP: + case TAP_EVENT_2FGTAP: + case TAP_EVENT_3FGTAP: + log_tap_bug(tp, t, event); + break; } } @@ -873,7 +1053,6 @@ tp_tap_touch3_hold_handle_event(struct tp_dispatch *tp, switch (event) { case TAP_EVENT_TOUCH: tp->tap.state = TAP_STATE_DEAD; - tp_tap_set_timer(tp, time); break; case TAP_EVENT_RELEASE: tp->tap.state = TAP_STATE_TOUCH_2_HOLD; @@ -893,6 +1072,11 @@ tp_tap_touch3_hold_handle_event(struct tp_dispatch *tp, break; case TAP_EVENT_PALM_UP: break; + case TAP_EVENT_1FGTAP: + case TAP_EVENT_2FGTAP: + case TAP_EVENT_3FGTAP: + log_tap_bug(tp, t, event); + break; } } @@ -904,14 +1088,7 @@ tp_tap_touch3_release_handle_event(struct tp_dispatch *tp, switch (event) { case TAP_EVENT_TOUCH: - 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); + 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); @@ -921,36 +1098,15 @@ tp_tap_touch3_release_handle_event(struct tp_dispatch *tp, tp_tap_set_timer(tp, time); break; case TAP_EVENT_MOTION: - 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); + tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, time); tp_tap_move_to_dead(tp, t); break; case TAP_EVENT_TIMEOUT: - 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); + tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, time); tp->tap.state = TAP_STATE_TOUCH_2_HOLD; break; case TAP_EVENT_BUTTON: - 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); + tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, time); tp->tap.state = TAP_STATE_DEAD; break; case TAP_EVENT_THUMB: @@ -960,6 +1116,11 @@ tp_tap_touch3_release_handle_event(struct tp_dispatch *tp, break; case TAP_EVENT_PALM_UP: break; + case TAP_EVENT_1FGTAP: + case TAP_EVENT_2FGTAP: + case TAP_EVENT_3FGTAP: + log_tap_bug(tp, t, event); + break; } } @@ -971,92 +1132,40 @@ tp_tap_touch3_release2_handle_event(struct tp_dispatch *tp, switch (event) { case TAP_EVENT_TOUCH: - 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); + 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_tap_notify(tp, - tp->tap.saved_press_time, - 3, - LIBINPUT_BUTTON_STATE_PRESSED); - if (tp->tap.drag_enabled) { - tp->tap.state = TAP_STATE_3FGTAP_TAPPED; - tp_tap_set_drag_timer(tp, time, 3); - } else { - tp_tap_notify(tp, - tp->tap.saved_release_time, - 3, - LIBINPUT_BUTTON_STATE_RELEASED); - tp->tap.state = TAP_STATE_IDLE; - } + tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, time); + tp->tap.state = TAP_STATE_IDLE; break; case TAP_EVENT_MOTION: - 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); + tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, time); tp_tap_move_to_dead(tp, t); break; case TAP_EVENT_TIMEOUT: - 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); + tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, time); tp->tap.state = TAP_STATE_HOLD; break; case TAP_EVENT_BUTTON: - 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); + tp_drag_handle_event(tp, t, TAP_EVENT_3FGTAP, time); tp->tap.state = TAP_STATE_DEAD; break; case TAP_EVENT_THUMB: break; case TAP_EVENT_PALM: - tp_tap_notify(tp, - tp->tap.saved_press_time, - 2, - LIBINPUT_BUTTON_STATE_PRESSED); - if (tp->tap.drag_enabled) { - /* Resetting the timer to the appropriate delay - * for a two-finger tap would be ideal, but the - * timestamp of the last real finger release is lost, - * so the in-progress similar delay for release - * of the finger which became a palm instead - * will have to do */ - tp->tap.state = TAP_STATE_2FGTAP_TAPPED; - } else { - tp_tap_notify(tp, - tp->tap.saved_release_time, - 2, - LIBINPUT_BUTTON_STATE_RELEASED); - tp->tap.state = TAP_STATE_IDLE; - } + tp_drag_handle_event(tp, t, TAP_EVENT_2FGTAP, time); + tp->tap.state = TAP_STATE_IDLE; break; case TAP_EVENT_PALM_UP: break; + case TAP_EVENT_1FGTAP: + case TAP_EVENT_2FGTAP: + case TAP_EVENT_3FGTAP: + log_tap_bug(tp, t, event); + break; } } @@ -1084,6 +1193,11 @@ tp_tap_dead_handle_event(struct tp_dispatch *tp, if (tp->tap.nfingers_down == 0) tp->tap.state = TAP_STATE_IDLE; break; + case TAP_EVENT_1FGTAP: + case TAP_EVENT_2FGTAP: + case TAP_EVENT_3FGTAP: + log_tap_bug(tp, t, event); + break; } } @@ -1107,15 +1221,6 @@ tp_tap_handle_event(struct tp_dispatch *tp, case TAP_STATE_HOLD: tp_tap_hold_handle_event(tp, t, event, time); break; - case TAP_STATE_1FGTAP_TAPPED: - tp_drag_tapped_handle_event(tp, t, event, time, 1); - break; - case TAP_STATE_2FGTAP_TAPPED: - tp_drag_tapped_handle_event(tp, t, event, time, 2); - break; - case TAP_STATE_3FGTAP_TAPPED: - tp_drag_tapped_handle_event(tp, t, event, time, 3); - break; case TAP_STATE_TOUCH_2: tp_tap_touch2_handle_event(tp, t, event, time); break; @@ -1137,62 +1242,11 @@ 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_1FGTAP_DRAGGING_OR_DOUBLETAP: - tp_drag_dragging_or_doubletap_handle_event(tp, t, event, time, - 1); - break; - case TAP_STATE_2FGTAP_DRAGGING_OR_DOUBLETAP: - tp_drag_dragging_or_doubletap_handle_event(tp, t, event, time, - 2); - break; - case TAP_STATE_3FGTAP_DRAGGING_OR_DOUBLETAP: - tp_drag_dragging_or_doubletap_handle_event(tp, t, event, time, - 3); - break; - case TAP_STATE_1FGTAP_DRAGGING: - tp_drag_dragging_handle_event(tp, t, event, time, 1); - break; - case TAP_STATE_2FGTAP_DRAGGING: - tp_drag_dragging_handle_event(tp, t, event, time, 2); - break; - case TAP_STATE_3FGTAP_DRAGGING: - tp_drag_dragging_handle_event(tp, t, event, time, 3); - break; - case TAP_STATE_1FGTAP_DRAGLOCK_WAIT: - tp_drag_draglock_wait_handle_event(tp, t, event, time, 1); - break; - case TAP_STATE_2FGTAP_DRAGLOCK_WAIT: - tp_drag_draglock_wait_handle_event(tp, t, event, time, 2); - break; - case TAP_STATE_3FGTAP_DRAGLOCK_WAIT: - tp_drag_draglock_wait_handle_event(tp, t, event, time, 3); - break; - case TAP_STATE_1FGTAP_DRAGLOCK_CONTINUE: - tp_drag_draglock_continue_handle_event(tp, t, event, time, 1); - break; - case TAP_STATE_2FGTAP_DRAGLOCK_CONTINUE: - tp_drag_draglock_continue_handle_event(tp, t, event, time, 2); - break; - case TAP_STATE_3FGTAP_DRAGLOCK_CONTINUE: - tp_drag_draglock_continue_handle_event(tp, t, event, time, 3); - break; - case TAP_STATE_1FGTAP_DRAGGING_2: - tp_drag_dragging2_handle_event(tp, t, event, time, 1); - break; - case TAP_STATE_2FGTAP_DRAGGING_2: - tp_drag_dragging2_handle_event(tp, t, event, time, 2); - break; - case TAP_STATE_3FGTAP_DRAGGING_2: - tp_drag_dragging2_handle_event(tp, t, event, time, 3); - break; case TAP_STATE_DEAD: tp_tap_dead_handle_event(tp, t, event, time); break; } - if (tp->tap.state == TAP_STATE_IDLE || tp->tap.state == TAP_STATE_DEAD) - tp_tap_clear_timer(tp); - if (current != tp->tap.state) evdev_log_debug(tp->device, "tap: touch %d (%s), tap state %s → %s → %s\n", @@ -1201,6 +1255,23 @@ tp_tap_handle_event(struct tp_dispatch *tp, tap_state_to_str(current), tap_event_to_str(event), tap_state_to_str(tp->tap.state)); + + /* any taps resulting from the current event have been delivered by now, + * so it's fine to forward the original event to the dragging + * state machine here */ + tp_drag_handle_event(tp, t, event, time); + + if ((tp->tap.state == TAP_STATE_IDLE || + tp->tap.state == TAP_STATE_HOLD || + tp->tap.state == TAP_STATE_TOUCH_2_HOLD || + tp->tap.state == TAP_STATE_TOUCH_3_HOLD || + tp->tap.state == TAP_STATE_DEAD) && + (tp->tap.drag_state == DRAG_STATE_IDLE || + tp->tap.drag_state == DRAG_STATE_1FGTAP_DRAGGING || + tp->tap.drag_state == DRAG_STATE_2FGTAP_DRAGGING || + tp->tap.drag_state == DRAG_STATE_3FGTAP_DRAGGING || + tp->tap.drag_state == DRAG_STATE_BUTTON)) + tp_tap_clear_timer(tp); } static bool @@ -1289,9 +1360,9 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time) t->tap.is_palm = true; t->tap.state = TAP_TOUCH_STATE_DEAD; if (t->state != TOUCH_BEGIN) { - tp_tap_handle_event(tp, t, TAP_EVENT_PALM, time); assert(tp->tap.nfingers_down > 0); tp->tap.nfingers_down--; + tp_tap_handle_event(tp, t, TAP_EVENT_PALM, time); } } else if (t->state == TOUCH_BEGIN) { /* The simple version: if a touch is a thumb on @@ -1337,25 +1408,30 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time) * exceed it. This prevents small motion events while we're waiting * on a decision if a tap is a tap. */ - switch (tp->tap.state) { - case TAP_STATE_TOUCH: - case TAP_STATE_1FGTAP_TAPPED: - case TAP_STATE_2FGTAP_TAPPED: - case TAP_STATE_3FGTAP_TAPPED: - case TAP_STATE_1FGTAP_DRAGGING_OR_DOUBLETAP: - case TAP_STATE_2FGTAP_DRAGGING_OR_DOUBLETAP: - case TAP_STATE_3FGTAP_DRAGGING_OR_DOUBLETAP: - case TAP_STATE_1FGTAP_DRAGLOCK_CONTINUE: - case TAP_STATE_2FGTAP_DRAGLOCK_CONTINUE: - case TAP_STATE_3FGTAP_DRAGLOCK_CONTINUE: - case TAP_STATE_TOUCH_2: - case TAP_STATE_TOUCH_3: + switch (tp->tap.drag_state) { + case DRAG_STATE_1FGTAP_DRAGGING_OR_DOUBLETAP: + case DRAG_STATE_2FGTAP_DRAGGING_OR_DOUBLETAP: + case DRAG_STATE_3FGTAP_DRAGGING_OR_DOUBLETAP: + case DRAG_STATE_1FGTAP_DRAGLOCK_CONTINUE: + case DRAG_STATE_2FGTAP_DRAGLOCK_CONTINUE: + case DRAG_STATE_3FGTAP_DRAGLOCK_CONTINUE: filter_motion = 1; break; - + case DRAG_STATE_IDLE: + switch (tp->tap.state) { + case TAP_STATE_TOUCH: + case TAP_STATE_TOUCH_2: + case TAP_STATE_TOUCH_2_RELEASE: + case TAP_STATE_TOUCH_3: + case TAP_STATE_TOUCH_3_RELEASE: + case TAP_STATE_TOUCH_3_RELEASE_2: + filter_motion = 1; + break; + default: + break; + } default: break; - } assert(tp->tap.nfingers_down <= tp->nfingers_down); @@ -1368,7 +1444,8 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time) static inline void tp_tap_update_map(struct tp_dispatch *tp) { - if (tp->tap.state != TAP_STATE_IDLE) + if (tp->tap.state != TAP_STATE_IDLE || + tp->tap.drag_state != DRAG_STATE_IDLE) return; if (tp->tap.map != tp->tap.want_map) @@ -1422,6 +1499,7 @@ tp_tap_enabled_update(struct tp_dispatch *tp, bool suspended, bool enabled, uint } tp->tap.state = TAP_STATE_IDLE; + tp->tap.drag_state = DRAG_STATE_IDLE; tp->tap.nfingers_down = 0; } else { tp_release_all_taps(tp, time); @@ -1611,6 +1689,7 @@ tp_init_tap(struct tp_dispatch *tp) tp->device->base.config.tap = &tp->tap.config; tp->tap.state = TAP_STATE_IDLE; + tp->tap.drag_state = DRAG_STATE_IDLE; tp->tap.enabled = tp_tap_default(tp->device); tp->tap.map = LIBINPUT_CONFIG_TAP_MAP_LRM; tp->tap.want_map = tp->tap.map; @@ -1657,6 +1736,7 @@ tp_release_all_taps(struct tp_dispatch *tp, uint64_t now) } tp->tap.state = TAP_STATE_IDLE; + tp->tap.drag_state = DRAG_STATE_IDLE; tp->tap.nfingers_down = 0; } @@ -1675,32 +1755,27 @@ tp_tap_resume(struct tp_dispatch *tp, uint64_t time) bool tp_tap_dragging(const struct tp_dispatch *tp) { - switch (tp->tap.state) { - case TAP_STATE_1FGTAP_DRAGGING: - case TAP_STATE_2FGTAP_DRAGGING: - case TAP_STATE_3FGTAP_DRAGGING: - case TAP_STATE_1FGTAP_DRAGGING_2: - case TAP_STATE_2FGTAP_DRAGGING_2: - case TAP_STATE_3FGTAP_DRAGGING_2: - case TAP_STATE_1FGTAP_DRAGLOCK_WAIT: - case TAP_STATE_2FGTAP_DRAGLOCK_WAIT: - case TAP_STATE_3FGTAP_DRAGLOCK_WAIT: - case TAP_STATE_1FGTAP_DRAGLOCK_CONTINUE: - case TAP_STATE_2FGTAP_DRAGLOCK_CONTINUE: - case TAP_STATE_3FGTAP_DRAGLOCK_CONTINUE: - return true; - default: + switch (tp->tap.drag_state) { + case DRAG_STATE_IDLE: + case DRAG_STATE_1FGTAP_TAPPED: + case DRAG_STATE_2FGTAP_TAPPED: + case DRAG_STATE_3FGTAP_TAPPED: + case DRAG_STATE_1FGTAP_DRAGGING_OR_DOUBLETAP: + case DRAG_STATE_2FGTAP_DRAGGING_OR_DOUBLETAP: + case DRAG_STATE_3FGTAP_DRAGGING_OR_DOUBLETAP: return false; + default: + return true; } } bool tp_tap_dragging_or_double_tapping(const struct tp_dispatch *tp) { - switch (tp->tap.state) { - case TAP_STATE_1FGTAP_DRAGGING_OR_DOUBLETAP: - case TAP_STATE_2FGTAP_DRAGGING_OR_DOUBLETAP: - case TAP_STATE_3FGTAP_DRAGGING_OR_DOUBLETAP: + switch (tp->tap.drag_state) { + case DRAG_STATE_1FGTAP_DRAGGING_OR_DOUBLETAP: + case DRAG_STATE_2FGTAP_DRAGGING_OR_DOUBLETAP: + case DRAG_STATE_3FGTAP_DRAGGING_OR_DOUBLETAP: return true; default: return false; diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h index 5286c036..991e3a87 100644 --- a/src/evdev-mt-touchpad.h +++ b/src/evdev-mt-touchpad.h @@ -106,9 +106,6 @@ enum tp_tap_state { TAP_STATE_IDLE = 4, TAP_STATE_TOUCH, TAP_STATE_HOLD, - TAP_STATE_1FGTAP_TAPPED, - TAP_STATE_2FGTAP_TAPPED, - TAP_STATE_3FGTAP_TAPPED, TAP_STATE_TOUCH_2, TAP_STATE_TOUCH_2_HOLD, TAP_STATE_TOUCH_2_RELEASE, @@ -116,24 +113,29 @@ enum tp_tap_state { TAP_STATE_TOUCH_3_HOLD, TAP_STATE_TOUCH_3_RELEASE, TAP_STATE_TOUCH_3_RELEASE_2, - TAP_STATE_1FGTAP_DRAGGING_OR_DOUBLETAP, - TAP_STATE_2FGTAP_DRAGGING_OR_DOUBLETAP, - TAP_STATE_3FGTAP_DRAGGING_OR_DOUBLETAP, - TAP_STATE_1FGTAP_DRAGLOCK_CONTINUE, - TAP_STATE_2FGTAP_DRAGLOCK_CONTINUE, - TAP_STATE_3FGTAP_DRAGLOCK_CONTINUE, - TAP_STATE_1FGTAP_DRAGGING, - TAP_STATE_2FGTAP_DRAGGING, - TAP_STATE_3FGTAP_DRAGGING, - TAP_STATE_1FGTAP_DRAGLOCK_WAIT, - TAP_STATE_2FGTAP_DRAGLOCK_WAIT, - TAP_STATE_3FGTAP_DRAGLOCK_WAIT, - TAP_STATE_1FGTAP_DRAGGING_2, - TAP_STATE_2FGTAP_DRAGGING_2, - TAP_STATE_3FGTAP_DRAGGING_2, TAP_STATE_DEAD, /**< finger count exceeded */ }; +enum tp_drag_state { + DRAG_STATE_IDLE, + DRAG_STATE_1FGTAP_TAPPED, + DRAG_STATE_2FGTAP_TAPPED, + DRAG_STATE_3FGTAP_TAPPED, + DRAG_STATE_1FGTAP_DRAGGING_OR_DOUBLETAP, + DRAG_STATE_2FGTAP_DRAGGING_OR_DOUBLETAP, + DRAG_STATE_3FGTAP_DRAGGING_OR_DOUBLETAP, + DRAG_STATE_1FGTAP_DRAGGING, + DRAG_STATE_2FGTAP_DRAGGING, + DRAG_STATE_3FGTAP_DRAGGING, + DRAG_STATE_1FGTAP_DRAGLOCK_WAIT, + DRAG_STATE_2FGTAP_DRAGLOCK_WAIT, + DRAG_STATE_3FGTAP_DRAGLOCK_WAIT, + DRAG_STATE_1FGTAP_DRAGLOCK_CONTINUE, + DRAG_STATE_2FGTAP_DRAGLOCK_CONTINUE, + DRAG_STATE_3FGTAP_DRAGLOCK_CONTINUE, + DRAG_STATE_BUTTON, /**< clickpad button pressed */ +}; + enum tp_tap_touch_state { TAP_TOUCH_STATE_IDLE = 16, /**< not in touch */ TAP_TOUCH_STATE_TOUCH, /**< touching, may tap */ @@ -429,9 +431,11 @@ struct tp_dispatch { bool suspended; struct libinput_timer timer; enum tp_tap_state state; + enum tp_drag_state drag_state; uint32_t buttons_pressed; uint64_t saved_press_time, - saved_release_time; + saved_release_time, + saved_multitap_release_time; enum libinput_config_tap_button_map map; enum libinput_config_tap_button_map want_map;