Commit d6c2b3d0 authored by Peter Hutterer's avatar Peter Hutterer

Update the BTN_TOOL bits correctly during SYN_DROPPED handling

Where at least one touch ends during SYN_DROPPED, we send out two event
frames: one with all applicable touch sequences ending (tracking id -1) and
the second one with the whole device state *and* the applicable touch
sequences starting (tracking id != -1).

This requires us to also update the BTN_TOOL_ bits correctly so that they are
correct after the first frame. For that we count the number of previously
known touches and send a 0 event for the matching BTN_TOOL_ bit, together with
a 1 event for the currently known touches.
Signed-off-by: Peter Hutterer's avatarPeter Hutterer <peter.hutterer@who-t.net>
parent 41de1b0e
Pipeline #110122 passed with stages
in 3 minutes and 11 seconds
......@@ -61,6 +61,8 @@ struct slot_change_state {
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)
......@@ -737,24 +739,70 @@ terminate_slots(struct libevdev *dev,
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
......
......@@ -824,6 +824,207 @@ START_TEST(test_syn_delta_tracking_ids)
}
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;
......@@ -1875,6 +2076,7 @@ TEST_SUITE_ROOT_PRIVILEGES(libevdev_events)
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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment