[PATCH v2 libinput] touchpad: work palm detection into the tap state machine

Peter Hutterer peter.hutterer at who-t.net
Fri Nov 10 01:21:06 UTC 2017


Unlike the already-existing thumb detection, a touch may be labelled palm at
any time, not just during the initial touch down. This requires full
integration into the tap state machine to unwind properly. For most states, a
palm detection simply ignores the finger and reverts to the most recent state.

One exception is the case of two fingers down, one finger up followed by the
remaining finger detected as a palm finger. This triggers a single-finger tap
but with timestamps that may be from the wrong finger. Since we're within a
short tap timeout anyway this should not matter too much.

The special state PALM_UP is only handled in one condition (DEAD). Once a
touch is a palm we basically skip over it from then on. If we end up in the
DEAD state after a button press we still need to handle the palm up events
accordingly to be able to return to IDLE. That transition also requires us to
have an accurate count of the real fingers down (palms don't count) so we need
a separate nfingers_down counter for tapping.

https://bugs.freedesktop.org/show_bug.cgi?id=103210

Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
Changes to v1:
- amended after some testing feedback, see but 103210
- suspending/resuming tapping (e.g. for dwt) now works correctly
- add the PALM_UP event to handle the DEAD->IDLE transition

 src/evdev-mt-touchpad-tap.c | 220 ++++++++++-
 src/evdev-mt-touchpad.c     |   1 +
 src/evdev-mt-touchpad.h     |   4 +
 test/test-touchpad-tap.c    | 879 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 1097 insertions(+), 7 deletions(-)

diff --git a/src/evdev-mt-touchpad-tap.c b/src/evdev-mt-touchpad-tap.c
index bfcef973..24920327 100644
--- a/src/evdev-mt-touchpad-tap.c
+++ b/src/evdev-mt-touchpad-tap.c
@@ -44,6 +44,8 @@ enum tap_event {
 	TAP_EVENT_BUTTON,
 	TAP_EVENT_TIMEOUT,
 	TAP_EVENT_THUMB,
+	TAP_EVENT_PALM,
+	TAP_EVENT_PALM_UP,
 };
 
 /*****************************************
@@ -77,6 +79,7 @@ tap_state_to_str(enum tp_tap_state state)
 	CASE_RETURN_STRING(TAP_STATE_DRAGGING_2);
 	CASE_RETURN_STRING(TAP_STATE_MULTITAP);
 	CASE_RETURN_STRING(TAP_STATE_MULTITAP_DOWN);
+	CASE_RETURN_STRING(TAP_STATE_MULTITAP_PALM);
 	CASE_RETURN_STRING(TAP_STATE_DEAD);
 	}
 	return NULL;
@@ -92,6 +95,8 @@ tap_event_to_str(enum tap_event event)
 	CASE_RETURN_STRING(TAP_EVENT_TIMEOUT);
 	CASE_RETURN_STRING(TAP_EVENT_BUTTON);
 	CASE_RETURN_STRING(TAP_EVENT_THUMB);
+	CASE_RETURN_STRING(TAP_EVENT_PALM);
+	CASE_RETURN_STRING(TAP_EVENT_PALM_UP);
 	}
 	return NULL;
 }
@@ -178,6 +183,12 @@ tp_tap_idle_handle_event(struct tp_dispatch *tp,
 	case TAP_EVENT_THUMB:
 		log_tap_bug(tp, event);
 		break;
+	case TAP_EVENT_PALM:
+		tp->tap.state = TAP_STATE_IDLE;
+		t->tap.state = TAP_TOUCH_STATE_DEAD;
+		break;
+	case TAP_EVENT_PALM_UP:
+		break;
 	}
 }
 
@@ -224,6 +235,13 @@ tp_tap_touch_handle_event(struct tp_dispatch *tp,
 		t->tap.state = TAP_TOUCH_STATE_DEAD;
 		tp_tap_clear_timer(tp);
 		break;
+	case TAP_EVENT_PALM:
+		tp->tap.state = TAP_STATE_IDLE;
+		t->tap.state = TAP_TOUCH_STATE_DEAD;
+		tp_tap_clear_timer(tp);
+		break;
+	case TAP_EVENT_PALM_UP:
+		break;
 	}
 }
 
@@ -253,6 +271,11 @@ tp_tap_hold_handle_event(struct tp_dispatch *tp,
 		t->tap.is_thumb = true;
 		t->tap.state = TAP_TOUCH_STATE_DEAD;
 		break;
+	case TAP_EVENT_PALM:
+		tp->tap.state = TAP_STATE_IDLE;
+		break;
+	case TAP_EVENT_PALM_UP:
+		break;
 	}
 }
 
@@ -286,6 +309,10 @@ tp_tap_tapped_handle_event(struct tp_dispatch *tp,
 			      LIBINPUT_BUTTON_STATE_RELEASED);
 		break;
 	case TAP_EVENT_THUMB:
+	case TAP_EVENT_PALM:
+		log_tap_bug(tp, event);
+		break;
+	case TAP_EVENT_PALM_UP:
 		break;
 	}
 }
@@ -318,6 +345,12 @@ tp_tap_touch2_handle_event(struct tp_dispatch *tp,
 		break;
 	case TAP_EVENT_THUMB:
 		break;
+	case TAP_EVENT_PALM:
+		tp->tap.state = TAP_STATE_TOUCH;
+		tp_tap_set_timer(tp, time); /* overwrite timer */
+		break;
+	case TAP_EVENT_PALM_UP:
+		break;
 	}
 }
 
@@ -345,6 +378,11 @@ tp_tap_touch2_hold_handle_event(struct tp_dispatch *tp,
 		break;
 	case TAP_EVENT_THUMB:
 		break;
+	case TAP_EVENT_PALM:
+		tp->tap.state = TAP_STATE_HOLD;
+		break;
+	case TAP_EVENT_PALM_UP:
+		break;
 	}
 }
 
@@ -380,6 +418,31 @@ tp_tap_touch2_release_handle_event(struct tp_dispatch *tp,
 		break;
 	case TAP_EVENT_THUMB:
 		break;
+	case TAP_EVENT_PALM:
+		/* There's only one saved press time and it's overwritten by
+		 * the last touch down. So in the case of finger down, palm
+		 * down, finger up, palm detected, we use the
+		 * palm touch's press time here instead of the finger's press
+		 * time. Let's wait and see if that's an issue.
+		 */
+		tp_tap_notify(tp,
+			      tp->tap.saved_press_time,
+			      1,
+			      LIBINPUT_BUTTON_STATE_PRESSED);
+		if (tp->tap.drag_enabled) {
+			tp->tap.state = TAP_STATE_TAPPED;
+			tp->tap.saved_release_time = time;
+			tp_tap_set_timer(tp, time);
+		} else {
+			tp_tap_notify(tp,
+				      time,
+				      1,
+				      LIBINPUT_BUTTON_STATE_RELEASED);
+			tp->tap.state = TAP_STATE_IDLE;
+		}
+		break;
+	case TAP_EVENT_PALM_UP:
+		break;
 	}
 }
 
@@ -414,6 +477,11 @@ tp_tap_touch3_handle_event(struct tp_dispatch *tp,
 		break;
 	case TAP_EVENT_THUMB:
 		break;
+	case TAP_EVENT_PALM:
+		tp->tap.state = TAP_STATE_TOUCH_2;
+		break;
+	case TAP_EVENT_PALM_UP:
+		break;
 	}
 }
 
@@ -439,6 +507,11 @@ tp_tap_touch3_hold_handle_event(struct tp_dispatch *tp,
 		break;
 	case TAP_EVENT_THUMB:
 		break;
+	case TAP_EVENT_PALM:
+		tp->tap.state = TAP_STATE_TOUCH_2_HOLD;
+		break;
+	case TAP_EVENT_PALM_UP:
+		break;
 	}
 }
 
@@ -472,6 +545,11 @@ tp_tap_dragging_or_doubletap_handle_event(struct tp_dispatch *tp,
 		break;
 	case TAP_EVENT_THUMB:
 		break;
+	case TAP_EVENT_PALM:
+		tp->tap.state = TAP_STATE_TAPPED;
+		break;
+	case TAP_EVENT_PALM_UP:
+		break;
 	}
 }
 
@@ -507,6 +585,15 @@ tp_tap_dragging_handle_event(struct tp_dispatch *tp,
 		break;
 	case TAP_EVENT_THUMB:
 		break;
+	case TAP_EVENT_PALM:
+		tp_tap_notify(tp,
+			      tp->tap.saved_release_time,
+			      1,
+			      LIBINPUT_BUTTON_STATE_RELEASED);
+		tp->tap.state = TAP_STATE_IDLE;
+		break;
+	case TAP_EVENT_PALM_UP:
+		break;
 	}
 }
 
@@ -533,6 +620,9 @@ tp_tap_dragging_wait_handle_event(struct tp_dispatch *tp,
 		tp_tap_notify(tp, time, 1, LIBINPUT_BUTTON_STATE_RELEASED);
 		break;
 	case TAP_EVENT_THUMB:
+	case TAP_EVENT_PALM:
+		break;
+	case TAP_EVENT_PALM_UP:
 		break;
 	}
 }
@@ -562,6 +652,15 @@ tp_tap_dragging_tap_handle_event(struct tp_dispatch *tp,
 		break;
 	case TAP_EVENT_THUMB:
 		break;
+	case TAP_EVENT_PALM:
+		tp_tap_notify(tp,
+			      tp->tap.saved_release_time,
+			      1,
+			      LIBINPUT_BUTTON_STATE_RELEASED);
+		tp->tap.state = TAP_STATE_IDLE;
+		break;
+	case TAP_EVENT_PALM_UP:
+		break;
 	}
 }
 
@@ -589,6 +688,11 @@ tp_tap_dragging2_handle_event(struct tp_dispatch *tp,
 		break;
 	case TAP_EVENT_THUMB:
 		break;
+	case TAP_EVENT_PALM:
+		tp->tap.state = TAP_STATE_DRAGGING_OR_DOUBLETAP;
+		break;
+	case TAP_EVENT_PALM_UP:
+		break;
 	}
 }
 
@@ -629,6 +733,9 @@ tp_tap_multitap_handle_event(struct tp_dispatch *tp,
 		tp_tap_clear_timer(tp);
 		break;
 	case TAP_EVENT_THUMB:
+	case TAP_EVENT_PALM:
+		break;
+	case TAP_EVENT_PALM_UP:
 		break;
 	}
 }
@@ -667,6 +774,42 @@ tp_tap_multitap_down_handle_event(struct tp_dispatch *tp,
 		break;
 	case TAP_EVENT_THUMB:
 		break;
+	case TAP_EVENT_PALM:
+		tp->tap.state = TAP_STATE_MULTITAP_PALM;
+		break;
+	case TAP_EVENT_PALM_UP:
+		break;
+	}
+}
+
+static void
+tp_tap_multitap_palm_handle_event(struct tp_dispatch *tp,
+				  struct tp_touch *t,
+				  enum tap_event event,
+				  uint64_t time)
+{
+	switch (event) {
+	case TAP_EVENT_RELEASE:
+		/* This is the palm finger */
+		break;
+	case TAP_EVENT_TOUCH:
+		tp->tap.state = TAP_STATE_MULTITAP_DOWN;
+		break;
+	case TAP_EVENT_MOTION:
+		break;
+	case TAP_EVENT_TIMEOUT:
+	case TAP_EVENT_BUTTON:
+		tp->tap.state = TAP_STATE_IDLE;
+		tp_tap_clear_timer(tp);
+		tp_tap_notify(tp,
+			      tp->tap.saved_release_time,
+			      1,
+			      LIBINPUT_BUTTON_STATE_RELEASED);
+		break;
+	case TAP_EVENT_THUMB:
+	case TAP_EVENT_PALM:
+	case TAP_EVENT_PALM_UP:
+		break;
 	}
 }
 
@@ -679,7 +822,7 @@ tp_tap_dead_handle_event(struct tp_dispatch *tp,
 
 	switch (event) {
 	case TAP_EVENT_RELEASE:
-		if (tp->nfingers_down == 0)
+		if (tp->tap.nfingers_down == 0)
 			tp->tap.state = TAP_STATE_IDLE;
 		break;
 	case TAP_EVENT_TOUCH:
@@ -689,6 +832,11 @@ tp_tap_dead_handle_event(struct tp_dispatch *tp,
 		break;
 	case TAP_EVENT_THUMB:
 		break;
+	case TAP_EVENT_PALM:
+	case TAP_EVENT_PALM_UP:
+		if (tp->tap.nfingers_down == 0)
+			tp->tap.state = TAP_STATE_IDLE;
+		break;
 	}
 }
 
@@ -751,6 +899,9 @@ tp_tap_handle_event(struct tp_dispatch *tp,
 	case TAP_STATE_MULTITAP_DOWN:
 		tp_tap_multitap_down_handle_event(tp, t, event, time);
 		break;
+	case TAP_STATE_MULTITAP_PALM:
+		tp_tap_multitap_palm_handle_event(tp, t, event, time);
+		break;
 	case TAP_STATE_DEAD:
 		tp_tap_dead_handle_event(tp, t, event, time);
 		break;
@@ -777,6 +928,8 @@ tp_tap_exceeds_motion_threshold(struct tp_dispatch *tp,
 	 * touchpads are likely to give us pointer jumps.
 	 * This triggers the movement threshold, making three-finger taps
 	 * less reliable (#101435)
+	 *
+	 * This uses the real nfingers_down, not the one for taps.
 	 */
 	if (tp->device->model_flags & EVDEV_MODEL_SYNAPTICS_SERIAL_TOUCHPAD &&
 	    (tp->nfingers_down > 2 || tp->old_nfingers_down > 2) &&
@@ -822,10 +975,32 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time)
 		if (t->tap.is_thumb)
 			continue;
 
+		/* A palm tap needs to be properly relased because we might
+		 * be who-knows-where in the state machine. Otherwise, we
+		 * ignore any event from it.
+		 */
+		if (t->tap.is_palm) {
+			if (t->state == TOUCH_END)
+				tp_tap_handle_event(tp,
+						    t,
+						    TAP_EVENT_PALM_UP,
+						    time);
+			continue;
+		}
+
 		if (t->state == TOUCH_HOVERING)
 			continue;
 
-		if (t->state == TOUCH_BEGIN) {
+		if (t->palm.state != PALM_NONE) {
+			assert(!t->tap.is_palm);
+			tp_tap_handle_event(tp, t, TAP_EVENT_PALM, time);
+			t->tap.is_palm = true;
+			t->tap.state = TAP_TOUCH_STATE_DEAD;
+			if (t->state != TOUCH_BEGIN) {
+				assert(tp->tap.nfingers_down > 0);
+				tp->tap.nfingers_down--;
+			}
+		} else if (t->state == TOUCH_BEGIN) {
 			/* The simple version: if a touch is a thumb on
 			 * begin we ignore it. All other thumb touches
 			 * follow the normal tap state for now */
@@ -836,6 +1011,7 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time)
 
 			t->tap.state = TAP_TOUCH_STATE_TOUCH;
 			t->tap.initial = t->point;
+			tp->tap.nfingers_down++;
 			tp_tap_handle_event(tp, t, TAP_EVENT_TOUCH, time);
 
 			/* If we think this is a palm, pretend there's a
@@ -846,8 +1022,10 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time)
 				tp_tap_handle_event(tp, t, TAP_EVENT_MOTION, time);
 
 		} else if (t->state == TOUCH_END) {
-			if (t->was_down)
+			if (t->was_down) {
+				tp->tap.nfingers_down--;
 				tp_tap_handle_event(tp, t, TAP_EVENT_RELEASE, time);
+			}
 			t->tap.state = TAP_TOUCH_STATE_IDLE;
 		} else if (tp->tap.state != TAP_STATE_IDLE &&
 			   tp_tap_exceeds_motion_threshold(tp, t)) {
@@ -890,6 +1068,10 @@ tp_tap_handle_state(struct tp_dispatch *tp, uint64_t time)
 
 	}
 
+	assert(tp->tap.nfingers_down <= tp->nfingers_down);
+	if (tp->nfingers_down == 0)
+		assert(tp->tap.nfingers_down == 0);
+
 	return filter_motion;
 }
 
@@ -938,9 +1120,19 @@ tp_tap_enabled_update(struct tp_dispatch *tp, bool suspended, bool enabled, uint
 		return;
 
 	if (tp_tap_enabled(tp)) {
-		/* Must restart in DEAD if fingers are down atm */
-		tp->tap.state =
-			tp->nfingers_down ? TAP_STATE_DEAD : TAP_STATE_IDLE;
+		struct tp_touch *t;
+
+		/* On resume, all touches are considered palms */
+		tp_for_each_touch(tp, t) {
+			if (t->state == TOUCH_NONE)
+				continue;
+
+			t->tap.is_palm = true;
+			t->tap.state = TAP_TOUCH_STATE_DEAD;
+		}
+
+		tp->tap.state = TAP_STATE_IDLE;
+		tp->tap.nfingers_down = 0;
 	} else {
 		tp_release_all_taps(tp, time);
 	}
@@ -1154,6 +1346,7 @@ tp_remove_tap(struct tp_dispatch *tp)
 void
 tp_release_all_taps(struct tp_dispatch *tp, uint64_t now)
 {
+	struct tp_touch *t;
 	int i;
 
 	for (i = 1; i <= 3; i++) {
@@ -1161,7 +1354,20 @@ tp_release_all_taps(struct tp_dispatch *tp, uint64_t now)
 			tp_tap_notify(tp, now, i, LIBINPUT_BUTTON_STATE_RELEASED);
 	}
 
-	tp->tap.state = tp->nfingers_down ? TAP_STATE_DEAD : TAP_STATE_IDLE;
+	/* To neutralize all current touches, we make them all palms */
+	tp_for_each_touch(tp, t) {
+		if (t->state == TOUCH_NONE)
+			continue;
+
+		if (t->tap.is_palm)
+			continue;
+
+		t->tap.is_palm = true;
+		t->tap.state = TAP_TOUCH_STATE_DEAD;
+	}
+
+	tp->tap.state = TAP_STATE_IDLE;
+	tp->tap.nfingers_down = 0;
 }
 
 void
diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c
index a7dad940..7d77c03a 100644
--- a/src/evdev-mt-touchpad.c
+++ b/src/evdev-mt-touchpad.c
@@ -290,6 +290,7 @@ tp_begin_touch(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
 	t->thumb.state = THUMB_STATE_MAYBE;
 	t->thumb.first_touch_time = time;
 	t->tap.is_thumb = false;
+	t->tap.is_palm = false;
 	assert(tp->nfingers_down >= 1);
 	tp->hysteresis.last_motion_time = time;
 }
diff --git a/src/evdev-mt-touchpad.h b/src/evdev-mt-touchpad.h
index 3da6c58e..0dee8aaa 100644
--- a/src/evdev-mt-touchpad.h
+++ b/src/evdev-mt-touchpad.h
@@ -103,6 +103,7 @@ enum tp_tap_state {
 	TAP_STATE_DRAGGING_2,
 	TAP_STATE_MULTITAP,
 	TAP_STATE_MULTITAP_DOWN,
+	TAP_STATE_MULTITAP_PALM,
 	TAP_STATE_DEAD, /**< finger count exceeded */
 };
 
@@ -195,6 +196,7 @@ struct tp_touch {
 		enum tp_tap_touch_state state;
 		struct device_coords initial;
 		bool is_thumb;
+		bool is_palm;
 	} tap;
 
 	struct {
@@ -356,6 +358,8 @@ struct tp_dispatch {
 
 		bool drag_enabled;
 		bool drag_lock_enabled;
+
+		unsigned int nfingers_down;	/* number of fingers down for tapping (excl. thumb/palm) */
 	} tap;
 
 	struct {
diff --git a/test/test-touchpad-tap.c b/test/test-touchpad-tap.c
index 6fc3c4f6..06caeb20 100644
--- a/test/test-touchpad-tap.c
+++ b/test/test-touchpad-tap.c
@@ -2375,12 +2375,870 @@ START_TEST(touchpad_drag_lock_default_unavailable)
 }
 END_TEST
 
+static inline bool
+touchpad_has_palm_pressure(struct litest_device *dev)
+{
+	struct libevdev *evdev = dev->evdev;
+
+	if (libevdev_has_event_code(evdev, EV_ABS, ABS_MT_PRESSURE))
+		return true;
+
+	return false;
+}
+
+START_TEST(touchpad_tap_palm_on_idle)
+{
+	struct litest_device *dev = litest_current_device();
+	struct libinput *li = dev->libinput;
+	struct axis_replacement axes[] = {
+		{ ABS_MT_PRESSURE, 75 },
+		{ -1, 0 }
+	};
+
+	if (!touchpad_has_palm_pressure(dev))
+		return;
+
+	litest_enable_tap(dev->libinput_device);
+	litest_drain_events(li);
+
+	/* Finger down is immediately palm */
+
+	litest_touch_down_extended(dev, 0, 50, 50, axes);
+	litest_touch_up(dev, 0);
+
+	libinput_dispatch(li);
+
+	litest_assert_empty_queue(li);
+}
+END_TEST
+
+START_TEST(touchpad_tap_palm_on_touch)
+{
+	struct litest_device *dev = litest_current_device();
+	struct libinput *li = dev->libinput;
+	struct axis_replacement axes[] = {
+		{ ABS_MT_PRESSURE, 75 },
+		{ -1, 0 }
+	};
+
+	if (!touchpad_has_palm_pressure(dev))
+		return;
+
+	litest_enable_tap(dev->libinput_device);
+	litest_drain_events(li);
+
+	/* Finger down is palm after touch begin */
+
+	litest_touch_down(dev, 0, 50, 50);
+	litest_touch_move_to_extended(dev, 0, 50, 50, 50, 50, axes, 1, 1);
+	litest_touch_up(dev, 0);
+
+	libinput_dispatch(li);
+
+	litest_assert_empty_queue(li);
+}
+END_TEST
+
+START_TEST(touchpad_tap_palm_on_touch_hold_timeout)
+{
+	struct litest_device *dev = litest_current_device();
+	struct libinput *li = dev->libinput;
+	struct axis_replacement axes[] = {
+		{ ABS_MT_PRESSURE, 75 },
+		{ -1, 0 }
+	};
+
+	if (!touchpad_has_palm_pressure(dev))
+		return;
+
+	litest_enable_tap(dev->libinput_device);
+	litest_drain_events(li);
+
+	/* Finger down is palm after tap timeout */
+
+	litest_touch_down(dev, 0, 50, 50);
+	libinput_dispatch(li);
+	litest_timeout_tap();
+	libinput_dispatch(li);
+	litest_touch_move_to_extended(dev, 0, 50, 50, 50, 50, axes, 1, 1);
+	litest_touch_up(dev, 0);
+
+	libinput_dispatch(li);
+
+	litest_assert_empty_queue(li);
+}
+END_TEST
+
+START_TEST(touchpad_tap_palm_on_touch_hold_move)
+{
+	struct litest_device *dev = litest_current_device();
+	struct libinput *li = dev->libinput;
+	struct axis_replacement axes[] = {
+		{ ABS_MT_PRESSURE, 75 },
+		{ -1, 0 }
+	};
+
+	if (!touchpad_has_palm_pressure(dev))
+		return;
+
+	litest_enable_tap(dev->libinput_device);
+	litest_drain_events(li);
+
+	/* Finger down is palm after tap move threshold */
+
+	litest_touch_down(dev, 0, 50, 50);
+	litest_touch_move_to(dev, 0, 50, 50, 60, 60, 10, 1);
+	litest_drain_events(li);
+
+	litest_touch_move_to_extended(dev, 0, 50, 50, 50, 50, axes, 1, 1);
+	litest_touch_up(dev, 0);
+
+	libinput_dispatch(li);
+
+	litest_assert_empty_queue(li);
+}
+END_TEST
+
+START_TEST(touchpad_tap_palm_on_tapped)
+{
+	struct litest_device *dev = litest_current_device();
+	struct libinput *li = dev->libinput;
+	struct axis_replacement axes[] = {
+		{ ABS_MT_PRESSURE, 75 },
+		{ -1, 0 }
+	};
+
+	if (!touchpad_has_palm_pressure(dev))
+		return;
+
+	litest_enable_tap(dev->libinput_device);
+	litest_drain_events(li);
+
+	/* tap + palm down */
+
+	litest_touch_down(dev, 0, 50, 50);
+	litest_touch_up(dev, 0);
+	libinput_dispatch(li);
+
+	litest_assert_button_event(li,
+				   BTN_LEFT,
+				   LIBINPUT_BUTTON_STATE_PRESSED);
+
+	litest_touch_down(dev, 0, 50, 50);
+	litest_touch_move_to_extended(dev, 0, 50, 50, 50, 50, axes, 1, 1);
+	litest_touch_up(dev, 0);
+
+	libinput_dispatch(li);
+	litest_timeout_tap();
+	libinput_dispatch(li);
+
+	litest_assert_button_event(li,
+				   BTN_LEFT,
+				   LIBINPUT_BUTTON_STATE_RELEASED);
+
+
+	litest_assert_empty_queue(li);
+}
+END_TEST
+
+START_TEST(touchpad_tap_palm_on_tapped_2fg)
+{
+	struct litest_device *dev = litest_current_device();
+	struct libinput *li = dev->libinput;
+	struct axis_replacement axes[] = {
+		{ ABS_MT_PRESSURE, 75 },
+		{ -1, 0 }
+	};
+
+	if (!touchpad_has_palm_pressure(dev))
+		return;
+
+	litest_enable_tap(dev->libinput_device);
+	litest_drain_events(li);
+
+	/* tap + palm down + tap with second finger */
+
+	litest_touch_down(dev, 0, 50, 50);
+	litest_touch_up(dev, 0);
+	libinput_dispatch(li);
+
+	litest_assert_button_event(li,
+				   BTN_LEFT,
+				   LIBINPUT_BUTTON_STATE_PRESSED);
+
+	litest_touch_down(dev, 0, 50, 50);
+	litest_touch_move_to_extended(dev, 0, 50, 50, 50, 50, axes, 1, 1);
+
+	libinput_dispatch(li);
+
+	litest_touch_down(dev, 1, 50, 50);
+	litest_touch_up(dev, 1);
+	libinput_dispatch(li);
+
+	litest_timeout_tap();
+	libinput_dispatch(li);
+
+	litest_assert_button_event(li,
+				   BTN_LEFT,
+				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_assert_button_event(li,
+				   BTN_LEFT,
+				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li,
+				   BTN_LEFT,
+				   LIBINPUT_BUTTON_STATE_RELEASED);
+
+
+	litest_touch_up(dev, 0);
+	litest_assert_empty_queue(li);
+}
+END_TEST
+
+START_TEST(touchpad_tap_palm_on_drag)
+{
+	struct litest_device *dev = litest_current_device();
+	struct libinput *li = dev->libinput;
+	struct axis_replacement axes[] = {
+		{ ABS_MT_PRESSURE, 75 },
+		{ -1, 0 }
+	};
+
+	if (!touchpad_has_palm_pressure(dev))
+		return;
+
+	litest_enable_tap(dev->libinput_device);
+	litest_drain_events(li);
+
+	/* tap + finger down (->drag), finger turns into palm */
+
+	litest_touch_down(dev, 0, 50, 50);
+	litest_touch_up(dev, 0);
+	libinput_dispatch(li);
+
+	litest_assert_button_event(li,
+				   BTN_LEFT,
+				   LIBINPUT_BUTTON_STATE_PRESSED);
+
+	litest_touch_down(dev, 0, 50, 50);
+	libinput_dispatch(li);
+	litest_timeout_tap();
+	libinput_dispatch(li);
+
+	litest_touch_move_to_extended(dev, 0, 50, 50, 50, 50, axes, 1, 1);
+	libinput_dispatch(li);
+
+	litest_assert_button_event(li,
+				   BTN_LEFT,
+				   LIBINPUT_BUTTON_STATE_RELEASED);
+
+	litest_touch_up(dev, 0);
+	litest_assert_empty_queue(li);
+}
+END_TEST
+
+START_TEST(touchpad_tap_palm_on_drag_2fg)
+{
+	struct litest_device *dev = litest_current_device();
+	struct libinput *li = dev->libinput;
+	struct axis_replacement axes[] = {
+		{ ABS_MT_PRESSURE, 75 },
+		{ -1, 0 }
+	};
+	int which = _i; /* ranged test */
+	int this = which % 2,
+	    other = (which + 1) % 2;
+
+	if (!touchpad_has_palm_pressure(dev))
+		return;
+
+	litest_enable_tap(dev->libinput_device);
+	litest_drain_events(li);
+
+	/* tap + finger down, 2nd finger down, finger turns to palm */
+
+	litest_touch_down(dev, 0, 50, 50);
+	litest_touch_up(dev, 0);
+	libinput_dispatch(li);
+
+	litest_assert_button_event(li,
+				   BTN_LEFT,
+				   LIBINPUT_BUTTON_STATE_PRESSED);
+
+	litest_touch_down(dev, this, 50, 50);
+	litest_touch_down(dev, other, 60, 50);
+	libinput_dispatch(li);
+
+	litest_touch_move_to_extended(dev, this, 50, 50, 50, 50, axes, 1, 1);
+	libinput_dispatch(li);
+
+	litest_touch_move_to(dev, other, 60, 50, 65, 50, 10, 1);
+	litest_assert_only_typed_events(li,
+					LIBINPUT_EVENT_POINTER_MOTION);
+	litest_touch_up(dev, other);
+
+	litest_assert_button_event(li,
+				   BTN_LEFT,
+				   LIBINPUT_BUTTON_STATE_RELEASED);
+
+	litest_touch_up(dev, this);
+	litest_assert_empty_queue(li);
+}
+END_TEST
+
+START_TEST(touchpad_tap_palm_on_touch_2)
+{
+	struct litest_device *dev = litest_current_device();
+	struct libinput *li = dev->libinput;
+	struct axis_replacement axes[] = {
+		{ ABS_MT_PRESSURE, 75 },
+		{ -1, 0 }
+	};
+	int which = _i; /* ranged test */
+	int this = which % 2,
+	    other = (which + 1) % 2;
+
+	if (!touchpad_has_palm_pressure(dev))
+		return;
+
+	litest_enable_tap(dev->libinput_device);
+	litest_drain_events(li);
+
+	/* 2fg tap with one finger detected as palm */
+	litest_touch_down(dev, 0, 50, 50);
+	litest_touch_down(dev, 1, 60, 60);
+	litest_drain_events(li);
+	litest_touch_move_to_extended(dev, this, 50, 50, 50, 50, axes, 1, 1);
+
+
+	litest_touch_up(dev, this);
+	litest_touch_up(dev, other);
+
+	libinput_dispatch(li);
+	litest_assert_button_event(li,
+				   BTN_LEFT,
+				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_timeout_tap();
+	litest_assert_button_event(li,
+				   BTN_LEFT,
+				   LIBINPUT_BUTTON_STATE_RELEASED);
+
+
+	litest_assert_empty_queue(li);
+}
+END_TEST
+
+START_TEST(touchpad_tap_palm_on_touch_2_retouch)
+{
+	struct litest_device *dev = litest_current_device();
+	struct libinput *li = dev->libinput;
+	struct axis_replacement axes[] = {
+		{ ABS_MT_PRESSURE, 75 },
+		{ -1, 0 }
+	};
+	int which = _i; /* ranged test */
+	int this = which % 2,
+	    other = (which + 1) % 2;
+
+
+	if (!touchpad_has_palm_pressure(dev))
+		return;
+
+	litest_enable_tap(dev->libinput_device);
+	litest_drain_events(li);
+
+	/* 2fg tap with one finger detected as palm, that finger is lifted
+	 * and taps again as not-palm  */
+	litest_touch_down(dev, this, 50, 50);
+	litest_touch_down(dev, other, 60, 60);
+	litest_drain_events(li);
+	litest_touch_move_to_extended(dev, this, 50, 50, 50, 50, axes, 1, 1);
+	litest_touch_up(dev, this);
+	libinput_dispatch(li);
+
+	litest_touch_down(dev, this, 70, 70);
+	litest_touch_up(dev, this);
+	litest_touch_up(dev, other);
+
+	libinput_dispatch(li);
+	litest_assert_button_event(li,
+				   BTN_RIGHT,
+				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_timeout_tap();
+	litest_assert_button_event(li,
+				   BTN_RIGHT,
+				   LIBINPUT_BUTTON_STATE_RELEASED);
+
+	litest_assert_empty_queue(li);
+}
+END_TEST
+
+START_TEST(touchpad_tap_palm_on_touch_3)
+{
+	struct litest_device *dev = litest_current_device();
+	struct libinput *li = dev->libinput;
+	struct axis_replacement axes[] = {
+		{ ABS_MT_PRESSURE, 75 },
+		{ -1, 0 }
+	};
+	int which = _i; /* ranged test */
+	int this = which % 3;
+
+	if (libevdev_get_abs_maximum(dev->evdev, ABS_MT_SLOT) <= 3)
+		return;
+
+	if (!touchpad_has_palm_pressure(dev))
+		return;
+
+	litest_enable_tap(dev->libinput_device);
+	litest_drain_events(li);
+
+	/* 3fg tap with one finger detected as palm, that finger is lifted,
+	   other two fingers lifted cause 2fg tap */
+	litest_touch_down(dev, this, 50, 50);
+	litest_touch_down(dev, (this + 1) % 3, 60, 50);
+	litest_touch_down(dev, (this + 2) % 3, 70, 50);
+	litest_drain_events(li);
+	litest_touch_move_to_extended(dev, this, 50, 50, 50, 50, axes, 1, 1);
+	litest_touch_up(dev, this);
+	libinput_dispatch(li);
+
+	litest_touch_up(dev, (this + 1) % 3);
+	litest_touch_up(dev, (this + 2) % 3);
+
+	libinput_dispatch(li);
+	litest_assert_button_event(li,
+				   BTN_RIGHT,
+				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_timeout_tap();
+	litest_assert_button_event(li,
+				   BTN_RIGHT,
+				   LIBINPUT_BUTTON_STATE_RELEASED);
+
+	litest_assert_empty_queue(li);
+}
+END_TEST
+
+START_TEST(touchpad_tap_palm_on_touch_3_retouch)
+{
+	struct litest_device *dev = litest_current_device();
+	struct libinput *li = dev->libinput;
+	struct axis_replacement axes[] = {
+		{ ABS_MT_PRESSURE, 75 },
+		{ -1, 0 }
+	};
+	int which = _i; /* ranged test */
+	int this = which % 3;
+
+	if (libevdev_get_abs_maximum(dev->evdev, ABS_MT_SLOT) <= 3)
+		return;
+
+	if (!touchpad_has_palm_pressure(dev))
+		return;
+
+	litest_enable_tap(dev->libinput_device);
+	litest_drain_events(li);
+
+	/* 3fg tap with one finger detected as palm, that finger is lifted,
+	   then put down again as normal finger -> 3fg tap */
+	litest_touch_down(dev, this, 50, 50);
+	litest_touch_down(dev, (this + 1) % 3, 60, 50);
+	litest_touch_down(dev, (this + 2) % 3, 70, 50);
+	litest_drain_events(li);
+	litest_timeout_tap();
+	libinput_dispatch(li);
+
+	litest_touch_move_to_extended(dev, this, 50, 50, 50, 50, axes, 1, 1);
+	litest_touch_up(dev, this);
+	libinput_dispatch(li);
+
+	litest_touch_down(dev, this, 50, 50);
+	litest_touch_up(dev, this);
+	litest_touch_up(dev, (this + 1) % 3);
+	litest_touch_up(dev, (this + 2) % 3);
+
+	libinput_dispatch(li);
+	litest_assert_button_event(li,
+				   BTN_MIDDLE,
+				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_timeout_tap();
+	litest_assert_button_event(li,
+				   BTN_MIDDLE,
+				   LIBINPUT_BUTTON_STATE_RELEASED);
+
+	litest_assert_empty_queue(li);
+}
+END_TEST
+
+START_TEST(touchpad_tap_palm_on_touch_4)
+{
+	struct litest_device *dev = litest_current_device();
+	struct libinput *li = dev->libinput;
+	struct axis_replacement axes[] = {
+		{ ABS_MT_PRESSURE, 75 },
+		{ -1, 0 }
+	};
+	int which = _i; /* ranged test */
+	int this = which % 4;
+
+	if (libevdev_get_abs_maximum(dev->evdev, ABS_MT_SLOT) <= 4)
+		return;
+
+	if (!touchpad_has_palm_pressure(dev))
+		return;
+
+	litest_enable_tap(dev->libinput_device);
+	litest_drain_events(li);
+
+	/* 3fg tap with one finger detected as palm, that finger is lifted,
+	   other two fingers lifted cause 2fg tap */
+	litest_touch_down(dev, this, 50, 50);
+	litest_touch_down(dev, (this + 1) % 4, 60, 50);
+	litest_touch_down(dev, (this + 2) % 4, 70, 50);
+	litest_touch_down(dev, (this + 3) % 4, 80, 50);
+	litest_drain_events(li);
+	litest_touch_move_to_extended(dev, this, 50, 50, 50, 50, axes, 1, 1);
+	litest_touch_up(dev, this);
+	libinput_dispatch(li);
+
+	litest_touch_up(dev, (this + 1) % 4);
+	litest_touch_up(dev, (this + 2) % 4);
+	litest_touch_up(dev, (this + 3) % 4);
+
+	litest_assert_empty_queue(li);
+}
+END_TEST
+
+START_TEST(touchpad_tap_palm_after_tap)
+{
+	struct litest_device *dev = litest_current_device();
+	struct libinput *li = dev->libinput;
+	struct axis_replacement axes[] = {
+		{ ABS_MT_PRESSURE, 75 },
+		{ -1, 0 }
+	};
+
+	if (!touchpad_has_palm_pressure(dev))
+		return;
+
+	litest_enable_tap(dev->libinput_device);
+	litest_drain_events(li);
+
+
+	litest_touch_down(dev, 0, 50, 50);
+	litest_touch_up(dev, 0);
+	libinput_dispatch(li);
+
+	libinput_dispatch(li);
+	litest_assert_button_event(li,
+				   BTN_LEFT,
+				   LIBINPUT_BUTTON_STATE_PRESSED);
+
+	litest_touch_down(dev, 0, 50, 50);
+	litest_touch_move_to_extended(dev, 0, 50, 50, 50, 50, axes, 1, 1);
+	litest_touch_up(dev, 0);
+	libinput_dispatch(li);
+
+	litest_timeout_tap();
+	litest_assert_button_event(li,
+				   BTN_LEFT,
+				   LIBINPUT_BUTTON_STATE_RELEASED);
+
+
+	litest_assert_empty_queue(li);
+}
+END_TEST
+
+START_TEST(touchpad_tap_palm_multitap)
+{
+	struct litest_device *dev = litest_current_device();
+	struct libinput *li = dev->libinput;
+	struct axis_replacement axes[] = {
+		{ ABS_MT_PRESSURE, 75 },
+		{ -1, 0 }
+	};
+	int range = _i, /* ranged test */
+	    ntaps;
+
+	if (!touchpad_has_palm_pressure(dev))
+		return;
+
+	litest_enable_tap(dev->libinput_device);
+	litest_drain_events(li);
+
+	for (ntaps = 0; ntaps <= range; ntaps++) {
+		litest_touch_down(dev, 0, 50, 50);
+		litest_touch_up(dev, 0);
+		libinput_dispatch(li);
+		msleep(10);
+	}
+
+	litest_touch_down(dev, 0, 50, 50);
+	litest_touch_move_to_extended(dev, 0, 50, 50, 50, 50, axes, 1, 1);
+	litest_touch_up(dev, 0);
+	libinput_dispatch(li);
+	litest_timeout_tap();
+	libinput_dispatch(li);
+
+	for (ntaps = 0; ntaps <= range; ntaps++) {
+		litest_assert_button_event(li,
+					   BTN_LEFT,
+					   LIBINPUT_BUTTON_STATE_PRESSED);
+		litest_assert_button_event(li,
+					   BTN_LEFT,
+					   LIBINPUT_BUTTON_STATE_RELEASED);
+	}
+
+	litest_assert_empty_queue(li);
+}
+END_TEST
+
+START_TEST(touchpad_tap_palm_multitap_timeout)
+{
+	struct litest_device *dev = litest_current_device();
+	struct libinput *li = dev->libinput;
+	struct axis_replacement axes[] = {
+		{ ABS_MT_PRESSURE, 75 },
+		{ -1, 0 }
+	};
+	int range = _i, /* ranged test */
+	    ntaps;
+
+	if (!touchpad_has_palm_pressure(dev))
+		return;
+
+	litest_enable_tap(dev->libinput_device);
+	litest_drain_events(li);
+
+	for (ntaps = 0; ntaps <= range; ntaps++) {
+		litest_touch_down(dev, 0, 50, 50);
+		litest_touch_up(dev, 0);
+		libinput_dispatch(li);
+		msleep(10);
+	}
+
+	litest_touch_down(dev, 0, 50, 50);
+	litest_touch_move_to_extended(dev, 0, 50, 50, 50, 50, axes, 1, 1);
+	libinput_dispatch(li);
+	litest_timeout_tap();
+	libinput_dispatch(li);
+
+	for (ntaps = 0; ntaps <= range; ntaps++) {
+		litest_assert_button_event(li,
+					   BTN_LEFT,
+					   LIBINPUT_BUTTON_STATE_PRESSED);
+		litest_assert_button_event(li,
+					   BTN_LEFT,
+					   LIBINPUT_BUTTON_STATE_RELEASED);
+	}
+
+	litest_assert_empty_queue(li);
+}
+END_TEST
+
+START_TEST(touchpad_tap_palm_multitap_down_again)
+{
+	struct litest_device *dev = litest_current_device();
+	struct libinput *li = dev->libinput;
+	struct axis_replacement axes[] = {
+		{ ABS_MT_PRESSURE, 75 },
+		{ -1, 0 }
+	};
+	int range = _i, /* ranged test */
+	    ntaps;
+
+	if (!touchpad_has_palm_pressure(dev))
+		return;
+
+	litest_enable_tap(dev->libinput_device);
+	litest_drain_events(li);
+
+	for (ntaps = 0; ntaps <= range; ntaps++) {
+		litest_touch_down(dev, 0, 50, 50);
+		litest_touch_up(dev, 0);
+		libinput_dispatch(li);
+		msleep(10);
+	}
+
+	litest_touch_down(dev, 0, 50, 50);
+	litest_touch_move_to_extended(dev, 0, 50, 50, 50, 50, axes, 1, 1);
+	libinput_dispatch(li);
+
+	/* keep palm finger down */
+	for (ntaps = 0; ntaps <= range; ntaps++) {
+		litest_touch_down(dev, 1, 50, 50);
+		litest_touch_up(dev, 1);
+		libinput_dispatch(li);
+		msleep(10);
+	}
+
+	for (ntaps = 0; ntaps <= 2 * range; ntaps++) {
+		litest_assert_button_event(li,
+					   BTN_LEFT,
+					   LIBINPUT_BUTTON_STATE_PRESSED);
+		litest_assert_button_event(li,
+					   BTN_LEFT,
+					   LIBINPUT_BUTTON_STATE_RELEASED);
+	}
+
+	litest_touch_up(dev, 0);
+	litest_assert_empty_queue(li);
+}
+END_TEST
+
+START_TEST(touchpad_tap_palm_multitap_click)
+{
+	struct litest_device *dev = litest_current_device();
+	struct libinput *li = dev->libinput;
+	struct axis_replacement axes[] = {
+		{ ABS_MT_PRESSURE, 75 },
+		{ -1, 0 }
+	};
+	int range = _i, /* ranged test */
+	    ntaps;
+
+	if (!touchpad_has_palm_pressure(dev))
+		return;
+
+	litest_enable_tap(dev->libinput_device);
+	litest_drain_events(li);
+
+	for (ntaps = 0; ntaps <= range; ntaps++) {
+		litest_touch_down(dev, 0, 50, 50);
+		litest_touch_up(dev, 0);
+		libinput_dispatch(li);
+		msleep(10);
+	}
+
+	litest_touch_down(dev, 0, 50, 50);
+	litest_touch_move_to_extended(dev, 0, 50, 50, 50, 50, axes, 1, 1);
+	libinput_dispatch(li);
+	/* keep palm finger down */
+
+	litest_button_click(dev, BTN_LEFT, true);
+	litest_button_click(dev, BTN_LEFT, false);
+	libinput_dispatch(li);
+
+	for (ntaps = 0; ntaps <= range; ntaps++) {
+		litest_assert_button_event(li,
+					   BTN_LEFT,
+					   LIBINPUT_BUTTON_STATE_PRESSED);
+		litest_assert_button_event(li,
+					   BTN_LEFT,
+					   LIBINPUT_BUTTON_STATE_RELEASED);
+	}
+
+	/* the click */
+	litest_assert_button_event(li,
+				   BTN_LEFT,
+				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li,
+				   BTN_LEFT,
+				   LIBINPUT_BUTTON_STATE_RELEASED);
+	litest_touch_up(dev, 0);
+	litest_assert_empty_queue(li);
+}
+END_TEST
+
+START_TEST(touchpad_tap_palm_click_then_tap)
+{
+	struct litest_device *dev = litest_current_device();
+	struct libinput *li = dev->libinput;
+	struct axis_replacement axes[] = {
+		{ ABS_MT_PRESSURE, 75 },
+		{ -1, 0 }
+	};
+
+	if (!touchpad_has_palm_pressure(dev))
+		return;
+
+	litest_enable_tap(dev->libinput_device);
+	litest_drain_events(li);
+
+	litest_touch_down_extended(dev, 0, 50, 50, axes);
+	libinput_dispatch(li);
+
+	litest_button_click(dev, BTN_LEFT, true);
+	litest_button_click(dev, BTN_LEFT, false);
+	libinput_dispatch(li);
+
+	litest_assert_button_event(li,
+				   BTN_LEFT,
+				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li,
+				   BTN_LEFT,
+				   LIBINPUT_BUTTON_STATE_RELEASED);
+
+	litest_touch_up(dev, 0);
+	litest_assert_empty_queue(li);
+
+	litest_touch_down(dev, 0, 50, 50);
+	litest_touch_up(dev, 0);
+	libinput_dispatch(li);
+	litest_timeout_tap();
+	libinput_dispatch(li);
+
+	litest_assert_button_event(li,
+				   BTN_LEFT,
+				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li,
+				   BTN_LEFT,
+				   LIBINPUT_BUTTON_STATE_RELEASED);
+
+	litest_assert_empty_queue(li);
+}
+END_TEST
+
+START_TEST(touchpad_tap_palm_dwt_tap)
+{
+	struct litest_device *dev = litest_current_device();
+	struct litest_device *keyboard;
+	struct libinput *li = dev->libinput;
+	struct axis_replacement axes[] = {
+		{ ABS_MT_PRESSURE, 75 },
+		{ -1, 0 }
+	};
+
+	if (!touchpad_has_palm_pressure(dev))
+		return;
+
+	keyboard = litest_add_device(li, LITEST_KEYBOARD);
+
+	litest_enable_tap(dev->libinput_device);
+	litest_drain_events(li);
+
+	litest_keyboard_key(keyboard, KEY_A, true);
+	litest_keyboard_key(keyboard, KEY_B, true);
+	litest_keyboard_key(keyboard, KEY_A, false);
+	litest_drain_events(li);
+
+	litest_touch_down(dev, 0, 50, 50);
+	libinput_dispatch(li);
+
+	litest_keyboard_key(keyboard, KEY_B, false);
+	litest_drain_events(li);
+	litest_timeout_dwt_long();
+	libinput_dispatch(li);
+
+	/* Changes to palm after dwt timeout */
+	litest_touch_move_to_extended(dev, 0, 50, 50, 50, 50, axes, 1, 1);
+	libinput_dispatch(li);
+
+	litest_touch_up(dev, 0);
+	libinput_dispatch(li);
+
+	litest_assert_empty_queue(li);
+}
+END_TEST
+
 void
 litest_setup_tests_touchpad_tap(void)
 {
 	struct range multitap_range = {3, 5};
 	struct range tap_map_range = { LIBINPUT_CONFIG_TAP_MAP_LRM,
 				       LIBINPUT_CONFIG_TAP_MAP_LMR + 1 };
+	struct range range_2fg = {0, 2};
+	struct range range_3fg = {0, 3};
+	struct range range_4fg = {0, 4};
 
 	litest_add("tap-1fg:1fg", touchpad_1fg_tap, LITEST_TOUCHPAD, LITEST_ANY);
 	litest_add("tap-1fg:1fg", touchpad_1fg_doubletap, LITEST_TOUCHPAD, LITEST_ANY);
@@ -2455,4 +3313,25 @@ litest_setup_tests_touchpad_tap(void)
 	litest_add("tap:drag", touchpad_drag_disabled, LITEST_TOUCHPAD, LITEST_ANY);
 	litest_add("tap:drag", touchpad_drag_disabled_immediate, LITEST_TOUCHPAD, LITEST_ANY);
 	litest_add_ranged("tap-multitap:drag", touchpad_drag_disabled_multitap_no_drag, LITEST_TOUCHPAD, LITEST_ANY, &multitap_range);
+
+	litest_add("tap:palm", touchpad_tap_palm_on_idle, LITEST_TOUCHPAD, LITEST_ANY);
+	litest_add("tap:palm", touchpad_tap_palm_on_touch, LITEST_TOUCHPAD, LITEST_ANY);
+	litest_add("tap:palm", touchpad_tap_palm_on_touch_hold_timeout, LITEST_TOUCHPAD, LITEST_ANY);
+	litest_add("tap:palm", touchpad_tap_palm_on_touch_hold_move, LITEST_TOUCHPAD, LITEST_ANY);
+	litest_add("tap:palm", touchpad_tap_palm_on_tapped, LITEST_TOUCHPAD, LITEST_ANY);
+	litest_add("tap:palm", touchpad_tap_palm_on_tapped_2fg, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
+	litest_add("tap:palm", touchpad_tap_palm_on_drag, LITEST_TOUCHPAD, LITEST_ANY);
+	litest_add_ranged("tap:palm", touchpad_tap_palm_on_drag_2fg, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, &range_2fg);
+	litest_add_ranged("tap:palm", touchpad_tap_palm_on_touch_2, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, &range_2fg);
+	litest_add_ranged("tap:palm", touchpad_tap_palm_on_touch_2_retouch, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, &range_2fg);
+	litest_add_ranged("tap:palm", touchpad_tap_palm_on_touch_3, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, &range_3fg);
+	litest_add_ranged("tap:palm", touchpad_tap_palm_on_touch_3_retouch, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, &range_3fg);
+	litest_add_ranged("tap:palm", touchpad_tap_palm_on_touch_4, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, &range_4fg);
+	litest_add("tap:palm", touchpad_tap_palm_after_tap, LITEST_TOUCHPAD, LITEST_ANY);
+	litest_add_ranged("tap:palm", touchpad_tap_palm_multitap, LITEST_TOUCHPAD, LITEST_ANY, &multitap_range);
+	litest_add_ranged("tap:palm", touchpad_tap_palm_multitap_timeout, LITEST_TOUCHPAD, LITEST_ANY, &multitap_range);
+	litest_add_ranged("tap:palm", touchpad_tap_palm_multitap_down_again, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH, &multitap_range);
+	litest_add_ranged("tap:palm", touchpad_tap_palm_multitap_click, LITEST_TOUCHPAD, LITEST_ANY, &multitap_range);
+	litest_add("tap:palm", touchpad_tap_palm_click_then_tap, LITEST_TOUCHPAD, LITEST_ANY);
+	litest_add("tap:palm", touchpad_tap_palm_dwt_tap, LITEST_TOUCHPAD, LITEST_ANY);
 }
-- 
2.13.6



More information about the wayland-devel mailing list