[PATCH v2 libinput] touchpad: disable 2fg scrolling on Synaptics semi-mt touchpads

Peter Hutterer peter.hutterer at who-t.net
Sun Jul 26 21:45:25 PDT 2015


These touchpads have a terrible resolution when two fingers are down, causing
scrolling to jump around a lot. That then turns into bug reports that we can't
do much about, the data is simply garbage.

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

Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
Changes to v1:
- only apply to synaptics semi-mt pads

 doc/scrolling.dox                    |  7 +++++++
 src/evdev-mt-touchpad.c              | 37 +++++++++++++++++++++++++++++-------
 src/evdev.c                          |  1 +
 src/evdev.h                          |  1 +
 test/litest-device-synaptics-hover.c | 10 ++++++++++
 test/touchpad.c                      | 21 +++++++++++++++++---
 udev/libinput-model-quirks.c         | 26 +++++++++++++++++++++++++
 7 files changed, 93 insertions(+), 10 deletions(-)

diff --git a/doc/scrolling.dox b/doc/scrolling.dox
index 658fe4b..0c03c98 100644
--- a/doc/scrolling.dox
+++ b/doc/scrolling.dox
@@ -44,6 +44,13 @@ movements will translate into tiny scroll movements.
 Scrolling in both directions at once is possible by meeting the required
 distance thresholds to enable each direction separately.
 
+Two-finger scrolling requires the touchpad to track both touch points with
+reasonable precision. Unfortunately, some so-called "semi-mt" touchpads can
+only track the bounding box of the two fingers rather than the actual
+position of each finger. In addition, that bounding box usually suffers from
+a low resolution, causing jumpy movement during two-finger scrolling.
+libinput does not provide two-finger scrolling on those touchpads.
+
 @section edge_scrolling Edge scrolling
 
 On some touchpads, edge scrolling is available, triggered by moving a single
diff --git a/src/evdev-mt-touchpad.c b/src/evdev-mt-touchpad.c
index 6718b61..3b514bb 100644
--- a/src/evdev-mt-touchpad.c
+++ b/src/evdev-mt-touchpad.c
@@ -1521,18 +1521,29 @@ tp_init_accel(struct tp_dispatch *tp, double diagonal)
 }
 
 static uint32_t
-tp_scroll_config_scroll_method_get_methods(struct libinput_device *device)
+tp_scroll_get_methods(struct tp_dispatch *tp)
 {
-	struct evdev_device *evdev = (struct evdev_device*)device;
-	struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
 	uint32_t methods = LIBINPUT_CONFIG_SCROLL_EDGE;
 
-	if (tp->ntouches >= 2)
+	/* some Synaptics semi-mt touchpads have a terrible 2fg resolution,
+	 * causing scroll jumps. For all other 2fg touchpads, we enable 2fg
+	 * scrolling */
+	if (tp->ntouches >= 2 &&
+	    (tp->device->model_flags & EVDEV_MODEL_JUMPING_SEMI_MT) == 0)
 		methods |= LIBINPUT_CONFIG_SCROLL_2FG;
 
 	return methods;
 }
 
+static uint32_t
+tp_scroll_config_scroll_method_get_methods(struct libinput_device *device)
+{
+	struct evdev_device *evdev = (struct evdev_device*)device;
+	struct tp_dispatch *tp = (struct tp_dispatch*)evdev->dispatch;
+
+	return tp_scroll_get_methods(tp);
+}
+
 static enum libinput_config_status
 tp_scroll_config_scroll_method_set_method(struct libinput_device *device,
 		        enum libinput_config_scroll_method method)
@@ -1564,10 +1575,22 @@ tp_scroll_config_scroll_method_get_method(struct libinput_device *device)
 static enum libinput_config_scroll_method
 tp_scroll_get_default_method(struct tp_dispatch *tp)
 {
-	if (tp->ntouches >= 2)
-		return LIBINPUT_CONFIG_SCROLL_2FG;
+	uint32_t methods;
+	enum libinput_config_scroll_method method;
+
+	methods = tp_scroll_get_methods(tp);
+
+	if (tp->ntouches >= 2 &&
+	    (methods & LIBINPUT_CONFIG_SCROLL_2FG))
+		method = LIBINPUT_CONFIG_SCROLL_2FG;
 	else
-		return LIBINPUT_CONFIG_SCROLL_EDGE;
+		method = LIBINPUT_CONFIG_SCROLL_EDGE;
+
+	if ((methods & method) == 0)
+		log_bug_libinput(tp_libinput_context(tp),
+				 "Invalid default scroll method %d\n",
+				 method);
+	return method;
 }
 
 static enum libinput_config_scroll_method
diff --git a/src/evdev.c b/src/evdev.c
index 78d1f5d..0fc5a64 100644
--- a/src/evdev.c
+++ b/src/evdev.c
@@ -1542,6 +1542,7 @@ evdev_read_model_flags(struct evdev_device *device)
 		{ "LIBINPUT_MODEL_WACOM_TOUCHPAD", EVDEV_MODEL_WACOM_TOUCHPAD },
 		{ "LIBINPUT_MODEL_ALPS_TOUCHPAD", EVDEV_MODEL_ALPS_TOUCHPAD },
 		{ "LIBINPUT_MODEL_SYNAPTICS_SERIAL_TOUCHPAD", EVDEV_MODEL_SYNAPTICS_SERIAL_TOUCHPAD },
+		{ "LIBINPUT_MODEL_JUMPING_SEMI_MT", EVDEV_MODEL_JUMPING_SEMI_MT },
 		{ NULL, EVDEV_MODEL_DEFAULT },
 	};
 	const struct model_map *m = model_map;
diff --git a/src/evdev.h b/src/evdev.h
index 36eac21..cc61c5f 100644
--- a/src/evdev.h
+++ b/src/evdev.h
@@ -105,6 +105,7 @@ enum evdev_device_model {
 	EVDEV_MODEL_WACOM_TOUCHPAD = (1 << 7),
 	EVDEV_MODEL_ALPS_TOUCHPAD = (1 << 8),
 	EVDEV_MODEL_SYNAPTICS_SERIAL_TOUCHPAD = (1 << 9),
+	EVDEV_MODEL_JUMPING_SEMI_MT = (1 << 10),
 };
 
 struct mt_slot {
diff --git a/test/litest-device-synaptics-hover.c b/test/litest-device-synaptics-hover.c
index 2cc9b72..3c36aff 100644
--- a/test/litest-device-synaptics-hover.c
+++ b/test/litest-device-synaptics-hover.c
@@ -102,6 +102,15 @@ static struct input_absinfo absinfo[] = {
 	{ .value = -1 }
 };
 
+static const char udev_rule[] =
+"ACTION==\"remove\", GOTO=\"synaptics_semi_mt_end\"\n"
+"KERNEL!=\"event*\", GOTO=\"synaptics_semi_mt_end\"\n"
+"\n"
+"ATTRS{name}==\"SynPS/2 Synaptics TouchPad\",\n"
+"    ENV{LIBINPUT_MODEL_JUMPING_SEMI_MT}=\"1\"\n"
+"\n"
+"LABEL=\"synaptics_semi_mt_end\"";
+
 struct litest_test_device litest_synaptics_hover_device = {
 	.type = LITEST_SYNAPTICS_HOVER_SEMI_MT,
 	.features = LITEST_TOUCHPAD | LITEST_SEMI_MT | LITEST_BUTTON,
@@ -114,6 +123,7 @@ struct litest_test_device litest_synaptics_hover_device = {
 	.id = &input_id,
 	.events = events,
 	.absinfo = absinfo,
+	.udev_rule = udev_rule,
 };
 
 static void
diff --git a/test/touchpad.c b/test/touchpad.c
index 7fc8fdc..5db79f8 100644
--- a/test/touchpad.c
+++ b/test/touchpad.c
@@ -91,6 +91,16 @@ enable_buttonareas(struct litest_device *dev)
 	litest_assert_int_eq(status, expected);
 }
 
+static inline int
+is_synaptics_semi_mt(struct litest_device *dev)
+{
+	struct libevdev *evdev = dev->evdev;
+
+	return libevdev_has_property(evdev, INPUT_PROP_SEMI_MT) &&
+		libevdev_get_id_vendor(evdev) == 0x2 &&
+		libevdev_get_id_product(evdev) == 0x7;
+}
+
 START_TEST(touchpad_1fg_motion)
 {
 	struct litest_device *dev = litest_current_device();
@@ -461,10 +471,14 @@ START_TEST(touchpad_scroll_defaults)
 
 	method = libinput_device_config_scroll_get_methods(device);
 	ck_assert(method & LIBINPUT_CONFIG_SCROLL_EDGE);
-	if (libevdev_get_num_slots(evdev) > 1)
+	if (libevdev_get_num_slots(evdev) > 1 &&
+	    !is_synaptics_semi_mt(dev))
 		ck_assert(method & LIBINPUT_CONFIG_SCROLL_2FG);
+	else
+		ck_assert((method & LIBINPUT_CONFIG_SCROLL_2FG) == 0);
 
-	if (libevdev_get_num_slots(evdev) > 1)
+	if (libevdev_get_num_slots(evdev) > 1 &&
+	    !is_synaptics_semi_mt(dev))
 		expected = LIBINPUT_CONFIG_SCROLL_2FG;
 	else
 		expected = LIBINPUT_CONFIG_SCROLL_EDGE;
@@ -480,7 +494,8 @@ START_TEST(touchpad_scroll_defaults)
 	status = libinput_device_config_scroll_set_method(device,
 					  LIBINPUT_CONFIG_SCROLL_2FG);
 
-	if (libevdev_get_num_slots(evdev) > 1)
+	if (libevdev_get_num_slots(evdev) > 1 &&
+	    !is_synaptics_semi_mt(dev))
 		ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_SUCCESS);
 	else
 		ck_assert_int_eq(status, LIBINPUT_CONFIG_STATUS_UNSUPPORTED);
diff --git a/udev/libinput-model-quirks.c b/udev/libinput-model-quirks.c
index fc3dbe8..0e737a4 100644
--- a/udev/libinput-model-quirks.c
+++ b/udev/libinput-model-quirks.c
@@ -69,6 +69,30 @@ handle_touchpad_alps(struct udev_device *device)
 }
 
 static void
+handle_touchpad_synaptics(struct udev_device *device)
+{
+	const char *product, *props;
+	int bus, vid, pid, version;
+	int prop;
+
+	product = prop_value(device, "PRODUCT");
+	if (!product)
+		return;
+
+	if (sscanf(product, "%x/%x/%x/%x", &bus, &vid, &pid, &version) != 4)
+		return;
+
+	if (bus != BUS_I8042 || vid != 0x2 || pid != 0x7)
+		return;
+
+	props = prop_value(device, "PROP");
+	if (sscanf(props, "%x", &prop) != 1)
+		return;
+	if (prop & (1 << INPUT_PROP_SEMI_MT))
+		printf("LIBINPUT_MODEL_JUMPING_SEMI_MT=1\n");
+}
+
+static void
 handle_touchpad(struct udev_device *device)
 {
 	const char *name = NULL;
@@ -79,6 +103,8 @@ handle_touchpad(struct udev_device *device)
 
 	if (strstr(name, "AlpsPS/2 ALPS") != NULL)
 		handle_touchpad_alps(device);
+	if (strstr(name, "Synaptics ") != NULL)
+		handle_touchpad_synaptics(device);
 }
 
 int main(int argc, char **argv)
-- 
2.4.3



More information about the wayland-devel mailing list