[PATCH libevdev 3/6] Don't sync the MT state for fake MT devices

Peter Hutterer peter.hutterer at who-t.net
Wed Mar 5 20:44:27 PST 2014


Devices with ABS_MT_SLOT-1 are fake MT devices, they merely overlap the
axis range but don't actually provide slots. The EVIOCGABS ioctl won't work to
retrieve the current value - the kernel does not store values for those axes
and the return value is always 0.

Thus, simply ignore those axes for fake MT devices and instead rely on the
next event to update the caller with the correct state for each axis.

Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
 libevdev/libevdev.c         |  5 +--
 libevdev/libevdev.h         |  6 ++++
 test/test-libevdev-events.c | 82 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 91 insertions(+), 2 deletions(-)

diff --git a/libevdev/libevdev.c b/libevdev/libevdev.c
index d9e2517..7ba4629 100644
--- a/libevdev/libevdev.c
+++ b/libevdev/libevdev.c
@@ -662,8 +662,9 @@ sync_state(struct libevdev *dev)
 		rc = sync_sw_state(dev);
 	if (rc == 0 && libevdev_has_event_type(dev, EV_ABS))
 		rc = sync_abs_state(dev);
-	if (rc == 0 && libevdev_has_event_code(dev, EV_ABS, ABS_MT_SLOT))
-		rc = sync_mt_state(dev, 1);
+	if (rc == 0 && dev->num_slots > -1 &&
+	    libevdev_has_event_code(dev, EV_ABS, ABS_MT_SLOT))
+			rc = sync_mt_state(dev, 1);
 
 	dev->queue_nsync = queue_num_elements(dev);
 
diff --git a/libevdev/libevdev.h b/libevdev/libevdev.h
index 1855cd3..6920a76 100644
--- a/libevdev/libevdev.h
+++ b/libevdev/libevdev.h
@@ -377,6 +377,12 @@ extern "C" {
  * device is not treated as multitouch device. No slot information is
  * available and the ABS_MT axis range for these devices is treated as all
  * other EV_ABS axes.
+ *
+ * Note that because of limitations in the kernel API, such fake multitouch
+ * devices can not be reliably synched after a SYN_DROPPED event. libevdev
+ * ignores all ABS_MT axis values during the sync process and instead
+ * relies on the device to send the current axis value with the first event
+ * after SYN_DROPPED.
  */
 
 /**
diff --git a/test/test-libevdev-events.c b/test/test-libevdev-events.c
index 8d1bd8b..5d38c62 100644
--- a/test/test-libevdev-events.c
+++ b/test/test-libevdev-events.c
@@ -26,6 +26,7 @@
 #include <unistd.h>
 #include <fcntl.h>
 #include <stdio.h>
+#include <libevdev-util.h>
 
 #include "test-common.h"
 
@@ -791,6 +792,86 @@ START_TEST(test_syn_delta_sw)
 }
 END_TEST
 
+START_TEST(test_syn_delta_fake_mt)
+{
+	struct uinput_device* uidev;
+	struct libevdev *dev;
+	int rc;
+	struct input_event ev;
+	struct input_absinfo abs[] = {  { ABS_X, 0, 1000 },
+		{ ABS_Y, 0, 1000 },
+		{ ABS_MT_POSITION_X, 0, 1000 },
+		{ ABS_MT_POSITION_Y, 0, 1000 },
+		{ ABS_MT_SLOT - 1, 0, 2 }};
+		/* don't set ABS_MT_SLOT here, otherwise uinput will init
+		 * slots and the behavior is different to real devices with
+		 * such events */
+	unsigned long received[NLONGS(ABS_CNT)] = {0};
+
+	rc = test_create_abs_device(&uidev, &dev, 5, abs,
+			-1);
+	ck_assert_msg(rc == 0, "Failed to uinput device: %s", strerror(-rc));
+
+	/* first set of events */
+	uinput_device_event(uidev, EV_ABS, ABS_X, 200);
+	uinput_device_event(uidev, EV_ABS, ABS_Y, 400);
+	uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 100);
+	uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 500);
+	uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT - 1, 1);
+	uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0);
+
+	/* second set of events */
+	uinput_device_event(uidev, EV_ABS, ABS_X, 201);
+	uinput_device_event(uidev, EV_ABS, ABS_Y, 401);
+	uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 101);
+	uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 501);
+	uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT - 1, 2);
+	uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0);
+
+	rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_FORCE_SYNC, &ev);
+	ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC);
+
+	while ((rc = libevdev_next_event(dev, LIBEVDEV_READ_STATUS_SYNC, &ev)) != -EAGAIN) {
+		if (ev.type != EV_ABS)
+			continue;
+
+		ck_assert(!bit_is_set(received, ev.code));
+
+		switch(ev.code) {
+			/* see comment below for ABS_MT_POSITION_X
+			 * and ABS_MT_POSITION_Y */
+			case ABS_MT_POSITION_X:
+			case ABS_MT_POSITION_Y:
+				ck_abort();
+				break;
+
+			case ABS_MT_SLOT - 1: ck_assert_int_eq(ev.value, 2); break;
+			case ABS_X: ck_assert_int_eq(ev.value, 201); break;
+			case ABS_Y: ck_assert_int_eq(ev.value, 401); break;
+			default:
+				ck_abort();
+		}
+
+		set_bit(received, ev.code);
+	}
+
+	/* Dont' expect ABS_MT values, they are ignored during the sync
+	 * process */
+	ck_assert(!bit_is_set(received, ABS_MT_POSITION_X));
+	ck_assert(!bit_is_set(received, ABS_MT_POSITION_Y));
+	ck_assert(bit_is_set(received, ABS_MT_SLOT - 1));
+	ck_assert(bit_is_set(received, ABS_X));
+	ck_assert(bit_is_set(received, ABS_Y));
+
+	ck_assert_int_eq(libevdev_get_event_value(dev, EV_ABS, ABS_X), 201);
+	ck_assert_int_eq(libevdev_get_event_value(dev, EV_ABS, ABS_Y), 401);
+	ck_assert_int_eq(libevdev_get_event_value(dev, EV_ABS, ABS_MT_SLOT - 1), 2);
+
+	uinput_device_free(uidev);
+	libevdev_free(dev);
+}
+END_TEST
+
 START_TEST(test_skipped_sync)
 {
 	struct uinput_device* uidev;
@@ -1511,6 +1592,7 @@ libevdev_events(void)
 	tcase_add_test(tc, test_syn_delta_mt_too_many);
 	tcase_add_test(tc, test_syn_delta_led);
 	tcase_add_test(tc, test_syn_delta_sw);
+	tcase_add_test(tc, test_syn_delta_fake_mt);
 	suite_add_tcase(s, tc);
 
 	tc = tcase_create("skipped syncs");
-- 
1.8.5.3



More information about the Input-tools mailing list