summaryrefslogtreecommitdiff
path: root/test/test-libevdev-events.c
diff options
context:
space:
mode:
authorPeter Hutterer <peter.hutterer@who-t.net>2014-04-07 15:16:28 +1000
committerPeter Hutterer <peter.hutterer@who-t.net>2014-04-24 08:17:01 +1000
commit050bca91a1e8dd2e46c219c0b282092cb053166c (patch)
tree64aa1d91e1ec7768fcaf034f4087868ee2393ca7 /test/test-libevdev-events.c
parent8b01184404dafb0dc121a1cdef066ea78ba6d21c (diff)
downloadlibevdev-050bca91a1e8dd2e46c219c0b282092cb053166c.tar.gz
Drain all events before synchronizing after SYN_DROPPED
The kernel ring buffer drops all events on SYN_DROPPED, but then continues to fill up again. So by the time we read the events, the kernel's client buffer is essentially like this: SYN_DROPPED, ev1, ev2, ev3, ...., evN The kernel's device state represents the device after evN, and that is what the ioctls return. For EV_KEY, EV_SND, EV_LED and EV_SW the kernel removes potential duplicates from the client buffer [1], it doesn't do so for EV_ABS. So we can't actually sync while there are events on the wire because the events represent an earlier state. So simply discard all events in the kernel buffer, synchronize, and then start processing again. We lose some granularity but at least the events are correct. [1] http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/drivers/input/evdev.c?id=483180281f0ac60d1138710eb21f4b9961901294 Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Diffstat (limited to 'test/test-libevdev-events.c')
-rw-r--r--test/test-libevdev-events.c219
1 files changed, 219 insertions, 0 deletions
diff --git a/test/test-libevdev-events.c b/test/test-libevdev-events.c
index dc412a7..ddc509d 100644
--- a/test/test-libevdev-events.c
+++ b/test/test-libevdev-events.c
@@ -927,6 +927,224 @@ START_TEST(test_syn_delta_tracking_ids)
}
END_TEST
+START_TEST(test_syn_delta_late_sync)
+{
+ struct uinput_device* uidev;
+ struct libevdev *dev;
+ int rc;
+ struct input_event ev;
+ struct input_absinfo abs[6];
+ int i, slot;
+
+ 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].minimum = -1;
+ abs[5].maximum = 255;
+ abs[5].value = ABS_MT_TRACKING_ID;
+
+ test_create_abs_device(&uidev, &dev,
+ 6, abs,
+ EV_SYN, SYN_REPORT,
+ -1);
+
+ /* emulate a touch down, make sure libevdev sees it */
+ uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT, 0);
+ uinput_device_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, 1);
+ uinput_device_event(uidev, EV_ABS, ABS_X, 100);
+ uinput_device_event(uidev, EV_ABS, ABS_Y, 500);
+ 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_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);
+
+ /* force enough events to trigger a SYN_DROPPED */
+ for (i = 0; i < 100; 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);
+ }
+
+ rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev);
+ ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC);
+
+ /* trigger the tracking ID change after getting the SYN_DROPPED */
+ uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT, 0);
+ uinput_device_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, -1);
+ uinput_device_event(uidev, EV_ABS, ABS_X, 200);
+ uinput_device_event(uidev, EV_ABS, ABS_Y, 600);
+ uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 200);
+ uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 600);
+ uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0);
+
+ slot = 0;
+
+ /* Now sync the device, expect the data to be equal to the last event*/
+ while ((rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev)) != -EAGAIN) {
+ if (ev.type == EV_SYN)
+ continue;
+
+ ck_assert_int_eq(ev.type, EV_ABS);
+ switch(ev.code) {
+ case ABS_MT_SLOT:
+ slot = ev.value;
+ break;
+ case ABS_MT_TRACKING_ID:
+ if (slot == 0)
+ ck_assert_int_eq(ev.value, -1);
+ break;
+ case ABS_X:
+ case ABS_MT_POSITION_X:
+ ck_assert_int_eq(ev.value, 200);
+ break;
+ case ABS_Y:
+ case ABS_MT_POSITION_Y:
+ ck_assert_int_eq(ev.value, 600);
+ break;
+ }
+ }
+
+ /* And a new tracking ID */
+ uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT, 0);
+ uinput_device_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, 2);
+ uinput_device_event(uidev, EV_ABS, ABS_X, 201);
+ uinput_device_event(uidev, EV_ABS, ABS_Y, 601);
+ uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 201);
+ uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 601);
+ uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0);
+
+ while ((rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev)) != -EAGAIN) {
+ ck_assert_int_ne(rc, LIBEVDEV_READ_STATUS_SYNC);
+
+ if (ev.type == EV_SYN)
+ continue;
+
+ ck_assert_int_eq(ev.type, EV_ABS);
+
+ switch(ev.code) {
+ case ABS_MT_SLOT:
+ ck_assert_int_eq(ev.value, 0);
+ break;
+ case ABS_MT_TRACKING_ID:
+ ck_assert_int_eq(ev.value, 2);
+ break;
+ case ABS_X:
+ case ABS_MT_POSITION_X:
+ ck_assert_int_eq(ev.value, 201);
+ break;
+ case ABS_Y:
+ case ABS_MT_POSITION_Y:
+ ck_assert_int_eq(ev.value, 601);
+ break;
+ }
+ }
+
+
+ /* Now we basically re-do the exact same test, just with the
+ tracking ID order inverted */
+
+ /* drop the tracking ID, make sure libevdev sees it */
+ uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT, 0);
+ uinput_device_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, -1);
+ 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);
+
+ /* force enough events to trigger a SYN_DROPPED */
+ for (i = 0; i < 100; 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);
+ }
+
+ rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev);
+ ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC);
+
+ /* trigger the new tracking ID after getting the SYN_DROPPED */
+ uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT, 0);
+ uinput_device_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, 5);
+ uinput_device_event(uidev, EV_ABS, ABS_X, 200);
+ uinput_device_event(uidev, EV_ABS, ABS_Y, 600);
+ uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_X, 200);
+ uinput_device_event(uidev, EV_ABS, ABS_MT_POSITION_Y, 600);
+ uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0);
+
+ slot = 0;
+
+ /* Now sync the device, expect the data to be equal to the last event*/
+ while ((rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev)) != -EAGAIN) {
+ if (ev.type == EV_SYN)
+ continue;
+
+ ck_assert_int_eq(ev.type, EV_ABS);
+ switch(ev.code) {
+ case ABS_MT_SLOT:
+ slot = ev.value;
+ break;
+ case ABS_MT_TRACKING_ID:
+ if (slot == 0)
+ ck_assert_int_eq(ev.value, 5);
+ break;
+ case ABS_X:
+ case ABS_MT_POSITION_X:
+ ck_assert_int_eq(ev.value, 200);
+ break;
+ case ABS_Y:
+ case ABS_MT_POSITION_Y:
+ ck_assert_int_eq(ev.value, 600);
+ break;
+ }
+ }
+
+ /* Drop the tracking ID */
+ uinput_device_event(uidev, EV_ABS, ABS_MT_SLOT, 0);
+ uinput_device_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, -1);
+ uinput_device_event(uidev, EV_SYN, SYN_REPORT, 0);
+
+ while ((rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev)) != -EAGAIN) {
+ ck_assert_int_ne(rc, LIBEVDEV_READ_STATUS_SYNC);
+
+ if (ev.type == EV_SYN)
+ continue;
+
+ ck_assert_int_eq(ev.type, EV_ABS);
+
+ switch(ev.code) {
+ case ABS_MT_SLOT:
+ ck_assert_int_eq(ev.value, 0);
+ break;
+ case ABS_MT_TRACKING_ID:
+ ck_assert_int_eq(ev.value, -1);
+ break;
+ }
+ }
+
+
+ uinput_device_free(uidev);
+ libevdev_free(dev);
+}
+END_TEST
+
START_TEST(test_syn_delta_fake_mt)
{
struct uinput_device* uidev;
@@ -1863,6 +2081,7 @@ libevdev_events(void)
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);
+ tcase_add_test(tc, test_syn_delta_late_sync);
suite_add_tcase(s, tc);
tc = tcase_create("skipped syncs");