evdev: discard any frame with EV_SYN SYN_REPORT 1

When the kernel inserts a repeat frame it does that with EV_KEY code
value of 2 and the frame itself is a SYN_REPORT with value 1. Nothing in
libinput wants those repeat values, so let's discard them here before
anything tries to process them.

This inserted frame causes bugs on touchpads with EV_REP (rare enough)
because while the key event itself is dropped, the timestamp of the
frame still causes the next real frame's delta time to shorten,
resulting in wrong acceleration values.

Closes #1149

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1255>
This commit is contained in:
Peter Hutterer 2025-06-30 20:44:07 +10:00 committed by Marge Bot
parent 06d8750504
commit 9a9466b6a9
2 changed files with 45 additions and 1 deletions

View file

@ -1084,7 +1084,15 @@ evdev_device_dispatch(void *data)
"event frame overflow, discarding events.\n");
}
if (ev.type == EV_SYN && ev.code == SYN_REPORT) {
evdev_device_dispatch_frame(libinput, device, frame);
/* A SYN_REPORT 1 event is a kernel-inserted
* auto-repeat. Nothing in libinput cares about kernel
* repeats and the inserted frame causes issues with
* timestamp deltas (see e.g. #1145)
*/
if (ev.value != 1)
evdev_device_dispatch_frame(libinput,
device,
frame);
evdev_frame_reset(frame);
}
} else if (rc == -ENODEV) {

View file

@ -1656,6 +1656,36 @@ START_TEST(device_button_down_remove)
}
END_TEST
START_TEST(device_ignore_repeat_frames)
{
struct litest_device *dev = litest_current_device();
struct libinput *li = dev->libinput;
unsigned int code = BTN_LEFT;
if (!libevdev_has_event_code(dev->evdev, EV_KEY, code))
code = BTN_0;
if (!libevdev_has_event_code(dev->evdev, EV_KEY, code))
code = KEY_A;
if (!libevdev_has_event_code(dev->evdev, EV_KEY, code))
return LITEST_NOT_APPLICABLE;
litest_drain_events(li);
/* Send a button/key press event with a repeat frame
* (SYN_REPORT value 1). Notably, the actual event in this frame is
* *not* a repeat (value 2), the whole event frame is a repeat frame
* though. */
litest_event(dev, EV_KEY, code, 1);
litest_event(dev, EV_SYN, SYN_REPORT, 1);
litest_dispatch(li);
litest_event(dev, EV_KEY, code, 0);
litest_event(dev, EV_SYN, SYN_REPORT, 1);
litest_dispatch(li);
litest_assert_empty_queue(li);
}
END_TEST
TEST_COLLECTION(device)
{
/* clang-format off */
@ -1743,5 +1773,11 @@ TEST_COLLECTION(device)
litest_add(device_seat_phys_name, LITEST_ANY, LITEST_ANY);
litest_add(device_button_down_remove, LITEST_BUTTON, LITEST_ANY);
/* Run for various combinations of devices to hopefully cover most backends */
litest_add(device_ignore_repeat_frames, LITEST_BUTTON, LITEST_ANY);
litest_add(device_ignore_repeat_frames, LITEST_KEYS, LITEST_ANY);
litest_add(device_ignore_repeat_frames, LITEST_TABLET, LITEST_ANY);
litest_add(device_ignore_repeat_frames, LITEST_TABLET_PAD, LITEST_ANY);
/* clang-format off */
}