From 9a9466b6a92cbc738323d73535a1d2f5809c8146 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Mon, 30 Jun 2025 20:44:07 +1000 Subject: [PATCH] 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: --- src/evdev.c | 10 +++++++++- test/test-device.c | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/evdev.c b/src/evdev.c index cc4dc40b..5e52f4fc 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -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) { diff --git a/test/test-device.c b/test/test-device.c index b8872eaa..036f70bc 100644 --- a/test/test-device.c +++ b/test/test-device.c @@ -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 */ }