diff options
author | Peter Hutterer <peter.hutterer@who-t.net> | 2014-03-06 11:54:00 +1000 |
---|---|---|
committer | Peter Hutterer <peter.hutterer@who-t.net> | 2014-03-07 10:16:16 +1000 |
commit | 41334b5b40cd5456f5f584b55d8888aaafa1f26e (patch) | |
tree | 1946d38c764bad82791f961d5432104b40e64eca | |
parent | d3ae3da90f871320de968924ef11ef1a02abc608 (diff) | |
download | libevdev-41334b5b40cd5456f5f584b55d8888aaafa1f26e.tar.gz |
If the tracking ID changes during SYN_DROPPED, terminate the touch first
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@who-t.net>
Reviewed-by: Benjamin Tissoires <benjamin.tissoires@gmail.com>
-rw-r--r-- | libevdev/libevdev.c | 26 | ||||
-rw-r--r-- | test/test-libevdev-events.c | 163 |
2 files changed, 189 insertions, 0 deletions
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"); |