diff options
author | Peter Hutterer <peter.hutterer@who-t.net> | 2014-04-07 15:16:28 +1000 |
---|---|---|
committer | Peter Hutterer <peter.hutterer@who-t.net> | 2014-04-24 08:17:01 +1000 |
commit | 050bca91a1e8dd2e46c219c0b282092cb053166c (patch) | |
tree | 64aa1d91e1ec7768fcaf034f4087868ee2393ca7 /test/test-libevdev-events.c | |
parent | 8b01184404dafb0dc121a1cdef066ea78ba6d21c (diff) | |
download | libevdev-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.c | 219 |
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"); |