[PATCH libinput] touchpad: be smarter about clickfinger thumb detection

Peter Hutterer peter.hutterer at who-t.net
Sun Jul 5 21:34:53 PDT 2015


Watching a colleague try clickfinger right-click after enabling it the first
time showed that the vertical distance is too small. Increase it to 30mm
instead.

Increase the allowed spread between fingers to 40x30mm, but check if one of
the fingers is in the bottom-most 20mm of the touchpad. If that's the case,
and the touchpad is large enough to be feasable for resting a thumb on it,
discard the finger for clickfinger count.

If both fingers are in that area or one finger is in the area and they're
really close together, the fingers count separately and are not regarded as
thumb.

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

Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
 src/evdev-mt-touchpad-buttons.c | 50 +++++++++++++++++++------
 test/touchpad.c                 | 82 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 121 insertions(+), 11 deletions(-)

diff --git a/src/evdev-mt-touchpad-buttons.c b/src/evdev-mt-touchpad-buttons.c
index f085774..5fd8a06 100644
--- a/src/evdev-mt-touchpad-buttons.c
+++ b/src/evdev-mt-touchpad-buttons.c
@@ -825,6 +825,9 @@ tp_check_clickfinger_distance(struct tp_dispatch *tp,
 			      struct tp_touch *t2)
 {
 	double x, y;
+	int within_distance = 0;
+	int xres, yres;
+	int bottom_threshold;
 
 	if (!t1 || !t2)
 		return 0;
@@ -839,19 +842,44 @@ tp_check_clickfinger_distance(struct tp_dispatch *tp,
 		w = tp->device->abs.dimensions.x;
 		h = tp->device->abs.dimensions.y;
 
-		return (x < w * 0.3 && y < h * 0.3) ? 1 : 0;
-	} else {
-		/* maximum spread is 40mm horiz, 20mm vert. Anything wider than that
-		 * is probably a gesture. The y spread is small so we ignore clicks
-		 * with thumbs at the bottom of the touchpad while the pointer
-		 * moving finger is still on the pad */
-
-		x /= tp->device->abs.absinfo_x->resolution;
-		y /= tp->device->abs.absinfo_y->resolution;
-
-		return (x < 40 && y < 20) ? 1 : 0;
+		within_distance = (x < w * 0.3 && y < h * 0.3) ? 1 : 0;
+		goto out;
 	}
 
+	xres = tp->device->abs.absinfo_x->resolution;
+	yres = tp->device->abs.absinfo_y->resolution;
+	x /= xres;
+	y /= yres;
+
+	/* maximum horiz spread is 40mm horiz, 30mm vert, anything wider
+	 * than that is probably a gesture. */
+	if (x > 40 || y > 30)
+		goto out;
+
+	within_distance = 1;
+
+	/* if y spread is <= 20mm, they're definitely together. */
+	if (y <= 20)
+		goto out;
+
+	/* if they're vertically spread between 20-40mm, they're not
+	 * together if:
+	 * - the touchpad's vertical size is >50mm, anything smaller is
+	 *   unlikely to have a thumb resting on it
+	 * - and one of the touches is in the bottom 20mm of the touchpad
+	 *   and the other one isn't
+	 */
+
+	if (tp->device->abs.dimensions.y/yres < 50)
+		goto out;
+
+	bottom_threshold = tp->device->abs.absinfo_y->maximum - 20 * yres;
+	if ((t1->point.y > bottom_threshold) !=
+		    (t2->point.y > bottom_threshold))
+		within_distance = 0;
+
+out:
+	return within_distance;
 }
 
 static uint32_t
diff --git a/test/touchpad.c b/test/touchpad.c
index 1f11cfa..6ba0694 100644
--- a/test/touchpad.c
+++ b/test/touchpad.c
@@ -263,6 +263,13 @@ START_TEST(touchpad_2fg_clickfinger_distance)
 {
 	struct litest_device *dev = litest_current_device();
 	struct libinput *li = dev->libinput;
+	double w, h;
+	bool small_touchpad = false;
+	unsigned int expected_button;
+
+	if (libinput_device_get_size(dev->libinput_device, &w, &h) == 0 &&
+	    h < 50.0)
+		small_touchpad = true;
 
 	libinput_device_config_click_set_method(dev->libinput_device,
 						LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER);
@@ -295,12 +302,86 @@ START_TEST(touchpad_2fg_clickfinger_distance)
 	litest_touch_up(dev, 0);
 	litest_touch_up(dev, 1);
 
+	/* if the touchpad is small enough, we expect all fingers to count
+	 * for clickfinger */
+	if (small_touchpad)
+		expected_button = BTN_RIGHT;
+	else
+		expected_button = BTN_LEFT;
+
+	litest_assert_button_event(li,
+				   expected_button,
+				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li,
+				   expected_button,
+				   LIBINPUT_BUTTON_STATE_RELEASED);
+}
+END_TEST
+
+START_TEST(touchpad_2fg_clickfinger_bottom)
+{
+	struct litest_device *dev = litest_current_device();
+	struct libinput *li = dev->libinput;
+
+	/* this test is run for the T440s touchpad only, makes getting the
+	 * mm correct easier */
+
+	libinput_device_config_click_set_method(dev->libinput_device,
+						LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER);
+	litest_drain_events(li);
+
+	/* one above, one below the magic line, vert spread ca 27mm */
+	litest_touch_down(dev, 0, 40, 60);
+	litest_touch_down(dev, 1, 60, 100);
+	litest_event(dev, EV_KEY, BTN_LEFT, 1);
+	litest_event(dev, EV_SYN, SYN_REPORT, 0);
+	litest_event(dev, EV_KEY, BTN_LEFT, 0);
+	litest_event(dev, EV_SYN, SYN_REPORT, 0);
+	litest_touch_up(dev, 0);
+	litest_touch_up(dev, 1);
+
 	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);
+
+	/* both below the magic line */
+	litest_touch_down(dev, 0, 40, 100);
+	litest_touch_down(dev, 1, 60, 95);
+	litest_event(dev, EV_KEY, BTN_LEFT, 1);
+	litest_event(dev, EV_SYN, SYN_REPORT, 0);
+	litest_event(dev, EV_KEY, BTN_LEFT, 0);
+	litest_event(dev, EV_SYN, SYN_REPORT, 0);
+	litest_touch_up(dev, 0);
+	litest_touch_up(dev, 1);
+
+	litest_assert_button_event(li,
+				   BTN_RIGHT,
+				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li,
+				   BTN_RIGHT,
+				   LIBINPUT_BUTTON_STATE_RELEASED);
+
+	/* one above, one below the magic line, vert spread 17mm */
+	litest_touch_down(dev, 0, 50, 75);
+	litest_touch_down(dev, 1, 55, 100);
+	litest_event(dev, EV_KEY, BTN_LEFT, 1);
+	litest_event(dev, EV_SYN, SYN_REPORT, 0);
+	litest_event(dev, EV_KEY, BTN_LEFT, 0);
+	litest_event(dev, EV_SYN, SYN_REPORT, 0);
+	litest_touch_up(dev, 0);
+	litest_touch_up(dev, 1);
+
+	litest_assert_button_event(li,
+				   BTN_RIGHT,
+				   LIBINPUT_BUTTON_STATE_PRESSED);
+	litest_assert_button_event(li,
+				   BTN_RIGHT,
+				   LIBINPUT_BUTTON_STATE_RELEASED);
 }
 END_TEST
 
@@ -3489,6 +3570,7 @@ litest_setup_tests(void)
 	litest_add("touchpad:clickfinger", touchpad_1fg_clickfinger_no_touch, LITEST_CLICKPAD, LITEST_ANY);
 	litest_add("touchpad:clickfinger", touchpad_2fg_clickfinger, LITEST_CLICKPAD, LITEST_ANY);
 	litest_add("touchpad:clickfinger", touchpad_2fg_clickfinger_distance, LITEST_CLICKPAD, LITEST_ANY);
+	litest_add_for_device("touchpad:clickfinger", touchpad_2fg_clickfinger_bottom, LITEST_SYNAPTICS_TOPBUTTONPAD);
 	litest_add("touchpad:clickfinger", touchpad_clickfinger_to_area_method, LITEST_CLICKPAD, LITEST_ANY);
 	litest_add("touchpad:clickfinger",
 		   touchpad_clickfinger_to_area_method_while_down, LITEST_CLICKPAD, LITEST_ANY);
-- 
2.4.3



More information about the wayland-devel mailing list