[PATCH libevdev 5/6] If the tracking ID changes during SYN_DROPPED, terminate the touch first

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


Most clients can't deal with tracking ID changes unless a -1 is sent first. So
if we notice that the tracking ID has changed during the sync process, send a
set of ABS_MT_TRACKING_ID -1 events for each of those, then send the rest of
the events.

Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
 libevdev/libevdev.c         |  26 +++++++
 test/test-libevdev-events.c | 163 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 189 insertions(+)

diff --git a/libevdev/libevdev.c b/libevdev/libevdev.c
index 958580f..29b2ce4 100644
--- a/libevdev/libevdev.c
+++ b/libevdev/libevdev.c
@@ -562,6 +562,8 @@ sync_mt_state(struct libevdev *dev, int create_events)
 		int val[MAX_SLOTS];
 	} mt_state;
 	unsigned long slot_update[NLONGS(MAX_SLOTS * ABS_MT_CNT)] = {0};
+	unsigned long tracking_id_changes[NLONGS(MAX_SLOTS)] = {0};
+	int need_tracking_id_changes = 0;
 
 #define AXISBIT(_slot, _axis) (_slot * ABS_MT_CNT + _axis - ABS_MT_MIN)
 
@@ -591,6 +593,13 @@ sync_mt_state(struct libevdev *dev, int create_events)
 				if (*slot_value(dev, slot, axis) == mt_state.val[slot])
 					continue;
 
+				if (axis == ABS_MT_TRACKING_ID &&
+				    *slot_value(dev, slot, axis) != -1 &&
+				    mt_state.val[slot] != -1) {
+					set_bit(tracking_id_changes, slot);
+					need_tracking_id_changes = 1;
+				}
+
 				*slot_value(dev, slot, axis) = mt_state.val[slot];
 
 				set_bit(slot_update, AXISBIT(slot, axis));
@@ -607,6 +616,23 @@ sync_mt_state(struct libevdev *dev, int create_events)
 		goto out;
 	}
 
+	if (need_tracking_id_changes) {
+		for (slot = 0; slot < min(dev->num_slots, MAX_SLOTS); slot++) {
+			if (!bit_is_set(tracking_id_changes, slot))
+				continue;
+
+			ev = queue_push(dev);
+			init_event(dev, ev, EV_ABS, ABS_MT_SLOT, slot);
+			ev = queue_push(dev);
+			init_event(dev, ev, EV_ABS, ABS_MT_TRACKING_ID, -1);
+
+			last_reported_slot = slot;
+		}
+
+		ev = queue_push(dev);
+		init_event(dev, ev, EV_SYN, SYN_REPORT, 0);
+	}
+
 	for (slot = 0; slot < min(dev->num_slots, MAX_SLOTS); slot++) {
 		if (!bit_is_set(slot_update, AXISBIT(slot, ABS_MT_SLOT)))
 			continue;
diff --git a/test/test-libevdev-events.c b/test/test-libevdev-events.c
index eca8a94..6ead890 100644
--- a/test/test-libevdev-events.c
+++ b/test/test-libevdev-events.c
@@ -884,6 +884,168 @@ START_TEST(test_syn_delta_sw)
 }
 END_TEST
 
+START_TEST(test_syn_delta_tracking_ids)
+{
+	struct uinput_device* uidev;
+	struct libevdev *dev;
+	int rc;
+	struct input_event ev;
+	struct input_absinfo abs[6];
+	int i;
+	const int num_slots = 15;
+	int slot = -1;
+	unsigned long terminated[NLONGS(num_slots)];
+	unsigned long restarted[NLONGS(num_slots)];
+
+	memset(abs, 0, sizeof(abs));
+	abs[0].value = ABS_X;
+	abs[0].maximum = 1000;
+	abs[1].value = ABS_MT_POSITION_X;
+	abs[1].maximum = 1000;
+
+	abs[2].value = ABS_Y;
+	abs[2].maximum = 1000;
+	abs[3].value = ABS_MT_POSITION_Y;
+	abs[3].maximum = 1000;
+
+	abs[4].value = ABS_MT_SLOT;
+	abs[4].maximum = num_slots - 1;
+
+	abs[5].minimum = -1;
+	abs[5].maximum = 255;
+	abs[5].value = ABS_MT_TRACKING_ID;
+
+	rc = test_create_abs_device(&uidev, &dev,
+				    6, abs,
+				    EV_SYN, SYN_REPORT,
+				    -1);
+	ck_assert_msg(rc == 0, "Failed to create device: %s", strerror(-rc));
+
+	/* Test the sync process to make sure we get touches terminated when
+	 * the tracking id changes:
+	 * 1) start a bunch of touch points
+	 * 2) read data into libevdev, make sure state is up-to-date
+	 * 3) change touchpoints
+	 * 3.1) change the tracking ID on some (indicating terminated and
+	 * re-started touchpoint)
+	 * 3.2) change the tracking ID to -1 on some (indicating termianted
+	 * touchpoint)
+	 * 3.3) just update the data on others
+	 * 4) force a sync on the device
+	 * 5) make sure we get the right tracking ID changes in the caller
+	 */
+
+	/* Start a bunch of touch points  */
+	for (i = num_slots; i >= 0; i--) {
+		uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT, i);
+		uinput_device_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, i);
+		uinput_device_event(uidev, EV_ABS, ABS_X, 100 + i);
+		uinput_device_event(uidev, EV_ABS, ABS_Y, 500 + i);
+		uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 100 + i);
+		uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 500 + i);
+		uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0);
+		do {
+			rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev);
+			ck_assert_int_ne(rc, LIBEVDEV_READ_STATUS_SYNC);
+		} while (rc >= 0);
+	}
+
+	/* we have a bunch of touches now, and libevdev knows it. Change all
+	 * touches */
+	for (i = num_slots; i >= 0; i--) {
+		uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT, i);
+		if (i % 3 == 0) {
+			/* change some slots with a new tracking id */
+			uinput_device_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, num_slots + i);
+			uinput_device_event(uidev, EV_ABS, ABS_X, 200 + i);
+			uinput_device_event(uidev, EV_ABS, ABS_Y, 700 + i);
+			uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 200 + i);
+			uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 700 + i);
+		} else if (i % 3 == 1) {
+			/* stop others */
+			uinput_device_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, -1);
+		} else {
+			/* just update */
+			uinput_device_event(uidev, EV_ABS, ABS_X, 200 + i);
+			uinput_device_event(uidev, EV_ABS, ABS_Y, 700 + i);
+			uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 200 + i);
+			uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 700 + i);
+		}
+		uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0);
+	}
+
+	/* Force sync */
+	rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_FORCE_SYNC, &ev);
+	ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC);
+
+	/* now check for the right tracking IDs */
+	memset(terminated, 0, sizeof(terminated));
+	memset(restarted, 0, sizeof(restarted));
+	slot = -1;
+	while ((rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev)) != -EAGAIN) {
+		if (libevdev_event_is_code(&ev, EV_SYN, SYN_REPORT))
+			continue;
+
+		if (libevdev_event_is_code(&ev, EV_ABS, ABS_MT_SLOT)) {
+			slot = ev.value;
+			continue;
+		}
+
+		if (libevdev_event_is_code(&ev, EV_ABS, ABS_X) ||
+		    libevdev_event_is_code(&ev, EV_ABS, ABS_Y))
+			continue;
+
+		ck_assert_int_ne(slot, -1);
+
+		if (libevdev_event_is_code(&ev, EV_ABS, ABS_MT_TRACKING_ID)) {
+			if (slot % 3 == 0) {
+				if (!bit_is_set(terminated, slot)) {
+					ck_assert_int_eq(ev.value, -1);
+					set_bit(terminated, slot);
+				} else {
+					ck_assert_int_eq(ev.value, num_slots + slot);
+					set_bit(restarted, slot);
+				}
+			} else if (slot % 3 == 1) {
+				ck_assert(!bit_is_set(terminated, slot));
+				ck_assert_int_eq(ev.value, -1);
+				set_bit(terminated, slot);
+			} else
+				ck_abort();
+
+			continue;
+		}
+
+		switch(ev.code) {
+			case ABS_MT_POSITION_X:
+				ck_assert_int_eq(ev.value, 200 + slot);
+				break;
+			case ABS_MT_POSITION_Y:
+				ck_assert_int_eq(ev.value, 700 + slot);
+				break;
+			default:
+				ck_abort();
+		}
+	}
+
+	for (i = 0; i < num_slots; i++) {
+		if (i % 3 == 0) {
+			ck_assert(bit_is_set(terminated, i));
+			ck_assert(bit_is_set(restarted, i));
+		} else if (i % 3 == 1) {
+			ck_assert(bit_is_set(terminated, i));
+			ck_assert(!bit_is_set(restarted, i));
+		} else {
+			ck_assert(!bit_is_set(terminated, i));
+			ck_assert(!bit_is_set(restarted, i));
+		}
+	}
+
+	uinput_device_free(uidev);
+	libevdev_free(dev);
+}
+END_TEST
+
 START_TEST(test_syn_delta_fake_mt)
 {
 	struct uinput_device* uidev;
@@ -1686,6 +1848,7 @@ libevdev_events(void)
 	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);
+	tcase_add_test(tc, test_syn_delta_tracking_ids);
 	suite_add_tcase(s, tc);
 
 	tc = tcase_create("skipped syncs");
-- 
1.8.5.3



More information about the Input-tools mailing list