[PATCH libinput 2/2] touchpad: stop palm detection when a second finger is detected

Peter Hutterer peter.hutterer at who-t.net
Wed Jun 15 06:52:49 UTC 2016


This avoids accidental palm detection during two-finger scrolling if one
finger is inside the edge exclusion zone.

Palm detection is designed to avoid accidental touches while typing. If a
non-palm finger is on the touchpad already the user is unlikely to be typing.
So stop palm detection in this case and process the fingers as normal.

This implementation has a minor bug: if both palm touches start within the
palm exclusion zone within the same frame, neither will be labelled as palm
due to how we check the other touches. Since this is an extremeley niche case
we can live with that.

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

Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
 src/evdev-mt-touchpad.c | 46 ++++++++++++++++++++++++++++++++++-----
 test/touchpad.c         | 58 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 99 insertions(+), 5 deletions(-)

diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c
index 8c6b396..fdc5319 100644
--- a/src/evdev-mt-touchpad.c
+++ b/src/evdev-mt-touchpad.c
@@ -652,6 +652,35 @@ tp_palm_detect_move_out_of_edge(struct tp_dispatch *tp,
 	return false;
 }
 
+static inline bool
+tp_palm_detect_multifinger(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
+{
+	struct tp_touch *other;
+
+	if (tp->nfingers_down < 2)
+		return false;
+
+	/* If we have at least one other active non-palm touch make this
+	 * touch non-palm too. This avoids palm detection during two-finger
+	 * scrolling.
+	 *
+	 * Note: if both touches start in the palm zone within the same
+	 * frame the second touch will still be PALM_NONE and thus detected
+	 * here as non-palm touch. This is too niche to worry about for now.
+	 */
+	tp_for_each_touch(tp, other) {
+		if (other == t)
+			continue;
+
+		if (tp_touch_active(tp, other) &&
+		    other->palm.state == PALM_NONE) {
+			return true;
+		}
+	}
+
+	return false;
+}
+
 static void
 tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
 {
@@ -662,17 +691,24 @@ tp_palm_detect(struct tp_dispatch *tp, struct tp_touch *t, uint64_t time)
 	if (tp_palm_detect_trackpoint(tp, t, time))
 		goto out;
 
-	/* If labelled a touch as palm, we unlabel as palm when
-	   we move out of the palm edge zone within the timeout, provided
-	   the direction is within 45 degrees of the horizontal.
-	 */
 	if (t->palm.state == PALM_EDGE) {
-		if (tp_palm_detect_move_out_of_edge(tp, t, time)) {
+		if (tp_palm_detect_multifinger(tp, t, time)) {
+			t->palm.state = PALM_NONE;
+			log_debug(tp_libinput_context(tp),
+				  "palm: touch released, multiple fingers\n");
+
+		/* If labelled a touch as palm, we unlabel as palm when
+		   we move out of the palm edge zone within the timeout, provided
+		   the direction is within 45 degrees of the horizontal.
+		 */
+		} else if (tp_palm_detect_move_out_of_edge(tp, t, time)) {
 			t->palm.state = PALM_NONE;
 			log_debug(tp_libinput_context(tp),
 				  "palm: touch released, out of edge zone\n");
 		}
 		return;
+	} else if (tp_palm_detect_multifinger(tp, t, time)) {
+		return;
 	}
 
 	/* palm must start in exclusion zone, it's ok to move into
diff --git a/test/touchpad.c b/test/touchpad.c
index ea719a3..0b1cd0e 100644
--- a/test/touchpad.c
+++ b/test/touchpad.c
@@ -1135,6 +1135,62 @@ START_TEST(touchpad_palm_detect_tap_clickfinger)
 }
 END_TEST
 
+START_TEST(touchpad_no_palm_detect_2fg_scroll)
+{
+	struct litest_device *dev = litest_current_device();
+	struct libinput *li = dev->libinput;
+
+	if (!touchpad_has_palm_detect_size(dev) ||
+	    !litest_has_2fg_scroll(dev))
+		return;
+
+	litest_enable_2fg_scroll(dev);
+
+	litest_drain_events(li);
+
+	/* first finger is palm, second finger isn't so we trigger 2fg
+	 * scrolling */
+	litest_touch_down(dev, 0, 99, 50);
+	litest_touch_move_to(dev, 0, 99, 50, 99, 40, 10, 0);
+	litest_touch_move_to(dev, 0, 99, 40, 99, 50, 10, 0);
+	litest_assert_empty_queue(li);
+	litest_touch_down(dev, 1, 50, 50);
+	litest_assert_empty_queue(li);
+
+	litest_touch_move_two_touches(dev, 99, 50, 50, 50, 0, -20, 10, 0);
+	litest_assert_only_typed_events(li, LIBINPUT_EVENT_POINTER_AXIS);
+}
+END_TEST
+
+START_TEST(touchpad_palm_detect_both_edges)
+{
+	struct litest_device *dev = litest_current_device();
+	struct libinput *li = dev->libinput;
+
+	if (!touchpad_has_palm_detect_size(dev) ||
+	    !litest_has_2fg_scroll(dev))
+		return;
+
+	litest_enable_2fg_scroll(dev);
+
+	litest_drain_events(li);
+
+	/* two fingers moving up/down in the left/right palm zone must not
+	 * generate events */
+	litest_touch_down(dev, 0, 99, 50);
+	litest_touch_move_to(dev, 0, 99, 50, 99, 40, 10, 0);
+	litest_touch_move_to(dev, 0, 99, 40, 99, 50, 10, 0);
+	litest_assert_empty_queue(li);
+	litest_touch_down(dev, 1, 1, 50);
+	litest_touch_move_to(dev, 1, 1, 50, 1, 40, 10, 0);
+	litest_touch_move_to(dev, 1, 1, 40, 1, 50, 10, 0);
+	litest_assert_empty_queue(li);
+
+	litest_touch_move_two_touches(dev, 99, 50, 1, 50, 0, -20, 10, 0);
+	litest_assert_empty_queue(li);
+}
+END_TEST
+
 START_TEST(touchpad_left_handed)
 {
 	struct litest_device *dev = litest_current_device();
@@ -4102,6 +4158,8 @@ litest_setup_tests(void)
 	litest_add("touchpad:palm", touchpad_palm_detect_tap_softbuttons, LITEST_CLICKPAD, LITEST_ANY);
 	litest_add("touchpad:palm", touchpad_palm_detect_tap_clickfinger, LITEST_CLICKPAD, LITEST_ANY);
 	litest_add("touchpad:palm", touchpad_no_palm_detect_at_edge_for_edge_scrolling, LITEST_TOUCHPAD, LITEST_CLICKPAD);
+	litest_add("touchpad:palm", touchpad_no_palm_detect_2fg_scroll, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
+	litest_add("touchpad:palm", touchpad_palm_detect_both_edges, LITEST_TOUCHPAD, LITEST_SINGLE_TOUCH);
 
 	litest_add("touchpad:left-handed", touchpad_left_handed, LITEST_TOUCHPAD|LITEST_BUTTON, LITEST_CLICKPAD);
 	litest_add("touchpad:left-handed", touchpad_left_handed_clickpad, LITEST_CLICKPAD, LITEST_APPLE_CLICKPAD);
-- 
2.7.4



More information about the wayland-devel mailing list