static int sync_mt_state(struct libevdev *dev,
struct slot_change_state *changes_out);
+static int
+update_key_state(struct libevdev *dev, const struct input_event *e);
static inline int*
slot_value(const struct libevdev *dev, int slot, int axis)
const struct slot_change_state changes[dev->num_slots],
int *last_reported_slot)
{
+ const unsigned int map[] = {BTN_TOOL_FINGER, BTN_TOOL_DOUBLETAP,
+ BTN_TOOL_TRIPLETAP, BTN_TOOL_QUADTAP,
+ BTN_TOOL_QUINTTAP};
bool touches_stopped = false;
+ int ntouches_before = 0, ntouches_after = 0;
+ /* For BTN_TOOL_* emulation, we need to know how many touches we had
+ * before and how many we have left once we terminate all the ones
+ * that changed and all the ones that stopped.
+ */
for (int slot = 0; slot < dev->num_slots; slot++) {
- if (changes[slot].state == TOUCH_CHANGED ||
- changes[slot].state == TOUCH_STOPPED) {
+ switch(changes[slot].state) {
+ case TOUCH_OFF:
+ break;
+ case TOUCH_CHANGED:
+ case TOUCH_STOPPED:
queue_push_event(dev, EV_ABS, ABS_MT_SLOT, slot);
queue_push_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
*last_reported_slot = slot;
touches_stopped = true;
+ ntouches_before++;
+ break;
+ case TOUCH_ONGOING:
+ ntouches_before++;
+ ntouches_after++;
+ break;
+ case TOUCH_STARTED:
+ break;
}
}
/* If any of the touches stopped, we need to split the sync state
into two frames - one with all the stopped touches, one with the
new touches starting (if any) */
- if (touches_stopped)
+ if (touches_stopped) {
+ /* Send through the required BTN_TOOL_ 0 and 1 events for
+ * the previous and current number of fingers. And update
+ * our own key state accordingly, so that during the second
+ * sync event frame sync_key_state() sets everything correctly
+ * for the *real* number of touches.
+ */
+ if (ntouches_before <= 5) {
+ struct input_event ev = {
+ .type = EV_KEY,
+ .code = map[ntouches_before - 1],
+ .value = 0,
+ };
+ queue_push_event(dev, ev.type, ev.code, ev.value);
+ update_key_state(dev, &ev);
+ }
+
+ if (ntouches_after <= 5) {
+ struct input_event ev = {
+ .type = EV_KEY,
+ .code = map[ntouches_after - 1],
+ .value = 1,
+ };
+ queue_push_event(dev, ev.type, ev.code, ev.value);
+ update_key_state(dev, &ev);
+ }
+
queue_push_event(dev, EV_SYN, SYN_REPORT, 0);
+ }
}
static int
}
END_TEST
+START_TEST(test_syn_delta_tracking_ids_btntool)
+{
+ struct uinput_device* uidev;
+ struct libevdev *dev;
+ int rc;
+ struct input_event ev;
+ const int num_slots = 5;
+ struct input_absinfo abs[6] = {
+ { .value = ABS_X, .maximum = 1000 },
+ { .value = ABS_Y, .maximum = 1000 },
+ { .value = ABS_MT_POSITION_X, .maximum = 1000 },
+ { .value = ABS_MT_POSITION_Y, .maximum = 1000 },
+ { .value = ABS_MT_SLOT, .maximum = num_slots },
+ { .value = ABS_MT_TRACKING_ID, .minimum = -1, .maximum = 0xffff },
+ };
+ bool have_tripletap = false,
+ have_doubletap = false,
+ have_quadtap = false,
+ have_quinttap = false;
+
+ test_create_abs_device(&uidev, &dev,
+ ARRAY_LENGTH(abs), abs,
+ EV_KEY, BTN_TOOL_FINGER,
+ EV_KEY, BTN_TOOL_DOUBLETAP,
+ EV_KEY, BTN_TOOL_TRIPLETAP,
+ EV_KEY, BTN_TOOL_QUADTAP,
+ EV_KEY, BTN_TOOL_QUINTTAP,
+ EV_SYN, SYN_REPORT,
+ -1);
+
+ /* Test the sync process to make sure we get the BTN_TOOL bits for
+ * touches adjusted correctly 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 BTN_TOOL_ changes in the caller
+ */
+ for (int i = 0; i < num_slots; i++) {
+ uinput_device_event_multiple(uidev,
+ EV_ABS, ABS_MT_SLOT, i,
+ EV_ABS, ABS_MT_TRACKING_ID, 111,
+ EV_ABS, ABS_X, 100 + 10 * i,
+ EV_ABS, ABS_Y, 100 + 10 * i,
+ EV_ABS, ABS_MT_POSITION_X, 100,
+ EV_ABS, ABS_MT_POSITION_Y, 100,
+ -1, -1);
+ switch (i) {
+ case 0:
+ uinput_device_event(uidev, EV_KEY, BTN_TOOL_FINGER, 1);
+ break;
+ case 1:
+ uinput_device_event(uidev, EV_KEY, BTN_TOOL_FINGER, 0);
+ uinput_device_event(uidev, EV_KEY, BTN_TOOL_DOUBLETAP, 1);
+ break;
+ case 2:
+ uinput_device_event(uidev, EV_KEY, BTN_TOOL_DOUBLETAP, 0);
+ uinput_device_event(uidev, EV_KEY, BTN_TOOL_TRIPLETAP, 1);
+ break;
+ case 3:
+ uinput_device_event(uidev, EV_KEY, BTN_TOOL_TRIPLETAP, 0);
+ uinput_device_event(uidev, EV_KEY, BTN_TOOL_QUADTAP, 1);
+ break;
+ case 4:
+ uinput_device_event(uidev, EV_KEY, BTN_TOOL_QUADTAP, 0);
+ uinput_device_event(uidev, EV_KEY, BTN_TOOL_QUINTTAP, 1);
+ break;
+ case 5:
+ uinput_device_event(uidev, EV_KEY, BTN_TOOL_QUINTTAP, 0);
+ break;
+ default:
+ ck_abort();
+ }
+ 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.
+ * - stop touch 0
+ * - stop and restart touch 1 and 4
+ * - leave 2, 3 unchanged
+ */
+ uinput_device_event_multiple(uidev,
+ EV_ABS, ABS_MT_SLOT, 0,
+ EV_ABS, ABS_MT_TRACKING_ID, -1,
+ EV_KEY, BTN_TOOL_QUINTTAP, 0,
+ EV_KEY, BTN_TOOL_QUADTAP, 1,
+ EV_SYN, SYN_REPORT, 0,
+ -1, -1);
+ uinput_device_event_multiple(uidev,
+ EV_ABS, ABS_MT_SLOT, 1,
+ EV_ABS, ABS_MT_TRACKING_ID, -1,
+ EV_KEY, BTN_TOOL_QUADTAP, 0,
+ EV_KEY, BTN_TOOL_TRIPLETAP, 1,
+ EV_SYN, SYN_REPORT, 0,
+ -1, -1);
+ uinput_device_event_multiple(uidev,
+ EV_ABS, ABS_MT_SLOT, 1,
+ EV_ABS, ABS_MT_TRACKING_ID, 666,
+ EV_ABS, ABS_X, 666,
+ EV_ABS, ABS_Y, 666,
+ EV_ABS, ABS_MT_POSITION_X, 666,
+ EV_ABS, ABS_MT_POSITION_Y, 666,
+ EV_KEY, BTN_TOOL_TRIPLETAP, 0,
+ EV_KEY, BTN_TOOL_QUADTAP, 1,
+ EV_SYN, SYN_REPORT, 0,
+ -1, -1);
+ uinput_device_event_multiple(uidev,
+ EV_ABS, ABS_MT_SLOT, 4,
+ EV_ABS, ABS_MT_TRACKING_ID, -1,
+ EV_KEY, BTN_TOOL_QUADTAP, 0,
+ EV_KEY, BTN_TOOL_TRIPLETAP, 1,
+ EV_SYN, SYN_REPORT, 0,
+ -1, -1);
+ uinput_device_event_multiple(uidev,
+ EV_ABS, ABS_MT_SLOT, 4,
+ EV_ABS, ABS_MT_TRACKING_ID, 777,
+ EV_ABS, ABS_X, 777,
+ EV_ABS, ABS_Y, 777,
+ EV_ABS, ABS_MT_POSITION_X, 777,
+ EV_ABS, ABS_MT_POSITION_Y, 777,
+ EV_KEY, BTN_TOOL_QUADTAP, 1,
+ EV_KEY, BTN_TOOL_TRIPLETAP, 0,
+ EV_SYN, SYN_REPORT, 0,
+ -1, -1);
+
+ /* Force sync */
+ rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_FORCE_SYNC, &ev);
+ ck_assert_int_eq(rc, LIBEVDEV_READ_STATUS_SYNC);
+
+ /* In the first sync frame, we expect us to drop to 2 touches - we
+ * started with 5, 1 stopped, 2 stopped+restarted */
+ while ((rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev)) != -EAGAIN) {
+ if (libevdev_event_is_code(&ev, EV_KEY, BTN_TOOL_QUINTTAP)) {
+ ck_assert(!have_quinttap);
+ assert_event(&ev, EV_KEY, BTN_TOOL_QUINTTAP, 0);
+ have_quinttap = true;
+ }
+
+ if (libevdev_event_is_code(&ev, EV_KEY, BTN_TOOL_DOUBLETAP)) {
+ ck_assert(!have_doubletap);
+ assert_event(&ev, EV_KEY, BTN_TOOL_DOUBLETAP, 1);
+ have_doubletap = true;
+ }
+
+ ck_assert(!libevdev_event_is_code(&ev, EV_KEY, BTN_TOOL_TRIPLETAP));
+ ck_assert(!libevdev_event_is_code(&ev, EV_KEY, BTN_TOOL_QUADTAP));
+ ck_assert(!libevdev_event_is_code(&ev, EV_KEY, BTN_TOOL_FINGER));
+
+ if (libevdev_event_is_code(&ev, EV_SYN, SYN_REPORT)) {
+ ck_assert(have_doubletap);
+ ck_assert(have_quinttap);
+ ck_assert(!have_tripletap);
+ break;
+ }
+ }
+
+ have_tripletap = false;
+ have_doubletap = false;
+ have_quadtap = false;
+
+ /* In the second sync frame, we expect to go back to 4 touches,
+ * recovering the two stopped+started touches */
+ while ((rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev)) != -EAGAIN) {
+ if (libevdev_event_is_code(&ev, EV_KEY, BTN_TOOL_QUADTAP)) {
+ ck_assert(!have_quadtap);
+ assert_event(&ev, EV_KEY, BTN_TOOL_QUADTAP, 1);
+ have_quadtap = true;
+ }
+
+ if (libevdev_event_is_code(&ev, EV_KEY, BTN_TOOL_DOUBLETAP)) {
+ ck_assert(!have_doubletap);
+ assert_event(&ev, EV_KEY, BTN_TOOL_DOUBLETAP, 0);
+ have_doubletap = true;
+ }
+
+ ck_assert(!libevdev_event_is_code(&ev, EV_KEY, BTN_TOOL_TRIPLETAP));
+ ck_assert(!libevdev_event_is_code(&ev, EV_KEY, BTN_TOOL_QUINTTAP));
+ ck_assert(!libevdev_event_is_code(&ev, EV_KEY, BTN_TOOL_FINGER));
+
+ if (libevdev_event_is_code(&ev, EV_SYN, SYN_REPORT)) {
+ ck_assert(have_doubletap);
+ ck_assert(have_quadtap);
+ break;
+ }
+ }
+
+ uinput_device_free(uidev);
+ libevdev_free(dev);
+}
+END_TEST
+
START_TEST(test_syn_delta_late_sync)
{
struct uinput_device* uidev;
add_test(s, test_syn_delta_fake_mt);
add_test(s, test_syn_delta_late_sync);
add_test(s, test_syn_delta_tracking_ids);
+ add_test(s, test_syn_delta_tracking_ids_btntool);
add_test(s, test_skipped_sync);
add_test(s, test_incomplete_sync);