mirror of
https://gitlab.freedesktop.org/libevdev/libevdev.git
synced 2025-12-25 08:10:05 +01:00
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 <peter.hutterer@who-t.net>
This commit is contained in:
parent
41de1b0e1a
commit
d6c2b3d0de
2 changed files with 253 additions and 3 deletions
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue