From d3ae3da90f871320de968924ef11ef1a02abc608 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Wed, 5 Mar 2014 12:03:57 +1000 Subject: [PATCH] Send an extra ABS_MT_SLOT event to sync the client up with the current slot If multiple slots have changed during the sync handling, the client must be re-set to the current slot before continuing with normal events. Signed-off-by: Benjamin Tissoires Signed-off-by: Peter Hutterer Reviewed-by: Benjamin Tissoires --- libevdev/libevdev.c | 25 +++++++++++- test/test-libevdev-events.c | 93 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+), 2 deletions(-) diff --git a/libevdev/libevdev.c b/libevdev/libevdev.c index 7ba4629..958580f 100644 --- a/libevdev/libevdev.c +++ b/libevdev/libevdev.c @@ -551,9 +551,12 @@ out: static int sync_mt_state(struct libevdev *dev, int create_events) { + struct input_event *ev; + struct input_absinfo abs_info; int rc; int axis, slot; int ioctl_success = 0; + int last_reported_slot = 0; struct mt_state { int code; int val[MAX_SLOTS]; @@ -599,14 +602,18 @@ sync_mt_state(struct libevdev *dev, int create_events) } } - for (slot = 0; create_events && slot < min(dev->num_slots, MAX_SLOTS); slot++) { - struct input_event *ev; + if (!create_events) { + rc = 0; + goto out; + } + for (slot = 0; slot < min(dev->num_slots, MAX_SLOTS); slot++) { if (!bit_is_set(slot_update, AXISBIT(slot, ABS_MT_SLOT))) continue; ev = queue_push(dev); init_event(dev, ev, EV_ABS, ABS_MT_SLOT, slot); + last_reported_slot = slot; for (axis = ABS_MT_MIN; axis <= ABS_MT_MAX; axis++) { if (axis == ABS_MT_SLOT || @@ -620,6 +627,20 @@ sync_mt_state(struct libevdev *dev, int create_events) } } + /* add one last slot event to make sure the client is on the same + slot as the kernel */ + + rc = ioctl(dev->fd, EVIOCGABS(ABS_MT_SLOT), &abs_info); + if (rc < 0) + goto out; + + dev->current_slot = abs_info.value; + + if (dev->current_slot != last_reported_slot) { + ev = queue_push(dev); + init_event(dev, ev, EV_ABS, ABS_MT_SLOT, dev->current_slot); + } + #undef AXISBIT rc = 0; diff --git a/test/test-libevdev-events.c b/test/test-libevdev-events.c index 5d38c62..eca8a94 100644 --- a/test/test-libevdev-events.c +++ b/test/test-libevdev-events.c @@ -585,6 +585,93 @@ START_TEST(test_syn_delta_mt) } END_TEST +START_TEST(test_syn_delta_mt_reset_slot) +{ + struct uinput_device* uidev; + struct libevdev *dev; + int rc; + struct input_event ev, + last_slot_event = { .type = 0}; + struct input_absinfo abs[6]; + + 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 = 1; + abs[5].value = ABS_MT_TRACKING_ID; + abs[5].minimum = -1; + abs[5].maximum = 2; + + 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)); + + uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT, 1); + 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_TRACKING_ID, 1); + uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT, 0); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 1); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 5); + uinput_device_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, 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); + + do { + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + if (libevdev_event_is_code(&ev, EV_ABS, ABS_MT_SLOT)) + last_slot_event = ev; + } while (rc != -EAGAIN); + + ck_assert(libevdev_event_is_code(&last_slot_event, EV_ABS, ABS_MT_SLOT)); + ck_assert_int_eq(last_slot_event.value, 0); + ck_assert_int_eq(libevdev_get_current_slot(dev), 0); + + last_slot_event.type = 0; + + /* same thing again, this time swap the numbers */ + uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT, 0); + 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_TRACKING_ID, 1); + uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT, 1); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 1); + uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 5); + uinput_device_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, 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); + + do { + rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + if (libevdev_event_is_code(&ev, EV_ABS, ABS_MT_SLOT)) + last_slot_event = ev; + } while (rc != -EAGAIN); + + ck_assert(libevdev_event_is_code(&last_slot_event, EV_ABS, ABS_MT_SLOT)); + ck_assert_int_eq(last_slot_event.value, 1); + ck_assert_int_eq(libevdev_get_current_slot(dev), 1); + + uinput_device_free(uidev); + libevdev_free(dev); +} +END_TEST + START_TEST(test_syn_delta_mt_too_many) { struct uinput_device* uidev; @@ -656,6 +743,11 @@ START_TEST(test_syn_delta_mt_too_many) ck_assert_int_lt(slot, MAX_SLOTS); rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev); + + /* last MT_SLOT event is on its own */ + if (libevdev_event_is_code(&ev, EV_SYN, SYN_REPORT)) + break; + ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC); ck_assert_int_eq(ev.type, EV_ABS); ck_assert_int_eq(ev.code, ABS_MT_POSITION_X); @@ -1590,6 +1682,7 @@ libevdev_events(void) tcase_add_test(tc, test_syn_delta_abs); tcase_add_test(tc, test_syn_delta_mt); tcase_add_test(tc, test_syn_delta_mt_too_many); + tcase_add_test(tc, test_syn_delta_mt_reset_slot); 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); -- 2.7.4