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 newline at end of file
+
\ 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;