diff options
author | José Expósito <jose.exposito89@gmail.com> | 2021-09-24 18:08:01 +0200 |
---|---|---|
committer | Peter Hutterer <peter.hutterer@who-t.net> | 2021-09-27 22:43:22 +0000 |
commit | 5bda716ebfdfbce2017efa5e87b91a65ed9fa4a5 (patch) | |
tree | b7d021544948c358d94e71c40671d86d2a7d0311 | |
parent | e0aa946e39887f6ff1ac825d99c6a0295de937b4 (diff) | |
download | libinput-5bda716ebfdfbce2017efa5e87b91a65ed9fa4a5.tar.gz |
fallback: hires scroll heuristics for buggy devices
Some devices might announce support for high-resolution scroll wheel
by enabling REL_WHEEL_HI_RES and/or REL_HWHEEL_HI_RES but never send
a high-resolution scroll event.
When the first low-resolution scroll event is received without any
previous high-resolution event, print a kernel bug warning and start
emulating high-resolution scroll events.
Fix #668
Signed-off-by: José Expósito <jose.exposito89@gmail.com>
-rw-r--r-- | doc/user/incorrectly-enabled-hires.rst | 62 | ||||
-rw-r--r-- | doc/user/meson.build | 2 | ||||
-rw-r--r-- | doc/user/troubleshooting.rst | 1 | ||||
-rw-r--r-- | src/evdev-fallback.c | 14 | ||||
-rw-r--r-- | src/evdev-fallback.h | 1 | ||||
-rw-r--r-- | test/test-pointer.c | 66 |
6 files changed, 146 insertions, 0 deletions
diff --git a/doc/user/incorrectly-enabled-hires.rst b/doc/user/incorrectly-enabled-hires.rst new file mode 100644 index 00000000..7dffca79 --- /dev/null +++ b/doc/user/incorrectly-enabled-hires.rst @@ -0,0 +1,62 @@ +.. _incorrectly_enabled_hires: + +============================================================================== +Incorrectly enabled high-resolution scroll +============================================================================== + +Some devices might announce support for high-resolution scroll wheel by enabling +``REL_WHEEL_HI_RES`` and/or ``REL_HWHEEL_HI_RES`` but never send a +high-resolution scroll event. + +When the first low-resolution scroll event is received without any previous +high-resolution event, libinput prints a bug warning with the text **"device +supports high-resolution scroll but only low-resolution events have been +received"** and a link to this page. + +.. note:: This warning will be printed only once + +In most cases this is a bug on the device firmware, the kernel driver or in a +software used to create user-space devices through uinput. + +Once the bug is detected, libinput will start emulating high-resolution scroll +events. + +------------------------------------------------------------------------------ +Detecting and fixing the issue +------------------------------------------------------------------------------ + +Events sent by a buggy device can be shown in the +:ref:`libinput record <libinput-record>` output for the device. Notice that +``REL_WHEEL_HI_RES`` and ``REL_HWHEEL_HI_RES`` are set but only ``REL_WHEEL`` +events are sent: :: + + # Supported Events: + # Event type 0 (EV_SYN) + # Event type 1 (EV_KEY) + # Event code 272 (BTN_LEFT) + # Event type 2 (EV_REL) + # Event code 0 (REL_X) + # Event code 1 (REL_Y) + # Event code 6 (REL_HWHEEL) + # Event code 8 (REL_WHEEL) + # Event code 11 (REL_WHEEL_HI_RES) + # Event code 12 (REL_HWHEEL_HI_RES) + [...] + quirks: + events: + - evdev: + - [ 0, 0, 2, 8, 1] # EV_REL / REL_WHEEL 1 + - [ 0, 0, 0, 0, 0] # ------------ SYN_REPORT (0) ---------- +0ms + - evdev: + - [ 0, 15126, 2, 8, 1] # EV_REL / REL_WHEEL 1 + - [ 0, 15126, 0, 0, 0] # ------------ SYN_REPORT (0) ---------- +15ms + - evdev: + - [ 0, 30250, 2, 8, 1] # EV_REL / REL_WHEEL 1 + - [ 0, 30250, 0, 0, 0] # ------------ SYN_REPORT (0) ---------- +15ms + +The issue can be fixed by adding a quirk to unset the ``REL_WHEEL_HI_RES`` and +``REL_HWHEEL_HI_RES`` event codes: :: + + AttrEventCodeDisable=REL_WHEEL_HI_RES;REL_HWHEEL_HI_RES; + +Please see :ref:`device-quirks` for details. diff --git a/doc/user/meson.build b/doc/user/meson.build index 332f67b9..a5e7737d 100644 --- a/doc/user/meson.build +++ b/doc/user/meson.build @@ -50,6 +50,7 @@ src_404s = [ [ 'faqs.rst', 'faq.html'], [ 'features.rst', 'features.html'], [ 'gestures.rst', 'gestures.html'], + [ 'incorrectly-enabled-hires.rst', 'incorrectly-enabled-hires.html'], [ 'middle-button-emulation.rst', 'middle_button_emulation.html'], [ 'normalization-of-relative-motion.rst', 'motion_normalization.html'], [ 'palm-detection.rst', 'palm_detection.html'], @@ -143,6 +144,7 @@ src_rst = files( 'device-quirks.rst', 'faqs.rst', 'gestures.rst', + 'incorrectly-enabled-hires.rst', 'middle-button-emulation.rst', 'normalization-of-relative-motion.rst', 'palm-detection.rst', diff --git a/doc/user/troubleshooting.rst b/doc/user/troubleshooting.rst index a66f96c2..9ca9b9a2 100644 --- a/doc/user/troubleshooting.rst +++ b/doc/user/troubleshooting.rst @@ -14,3 +14,4 @@ Troubleshooting touchpad-pressure-debugging.rst trackpoint-configuration.rst tablet-debugging.rst + incorrectly-enabled-hires.rst diff --git a/src/evdev-fallback.c b/src/evdev-fallback.c index 85cf6ca9..785ac6e2 100644 --- a/src/evdev-fallback.c +++ b/src/evdev-fallback.c @@ -235,6 +235,18 @@ fallback_flush_wheels(struct fallback_dispatch *dispatch, if (!(device->seat_caps & EVDEV_DEVICE_POINTER)) return; + if (!dispatch->wheel.emulate_hi_res_wheel && + !dispatch->wheel.hi_res_event_received && + (dispatch->wheel.lo_res.x != 0 || dispatch->wheel.lo_res.y != 0)) { + evdev_log_bug_kernel(device, + "device supports high-resolution scroll but only low-resolution events have been received.\n" + "See %s/incorrectly-enabled-hires.html for details\n", + HTTP_DOC_LINK); + dispatch->wheel.emulate_hi_res_wheel = true; + dispatch->wheel.hi_res.x = dispatch->wheel.lo_res.x * 120; + dispatch->wheel.hi_res.y = dispatch->wheel.lo_res.y * 120; + } + if (dispatch->wheel.is_inhibited) { dispatch->wheel.hi_res.x = 0; dispatch->wheel.hi_res.y = 0; @@ -897,10 +909,12 @@ fallback_process_relative(struct fallback_dispatch *dispatch, break; case REL_WHEEL_HI_RES: dispatch->wheel.hi_res.y += e->value; + dispatch->wheel.hi_res_event_received = true; dispatch->pending_event |= EVDEV_WHEEL; break; case REL_HWHEEL_HI_RES: dispatch->wheel.hi_res.x += e->value; + dispatch->wheel.hi_res_event_received = true; dispatch->pending_event |= EVDEV_WHEEL; break; } diff --git a/src/evdev-fallback.h b/src/evdev-fallback.h index fafd1821..e8c20631 100644 --- a/src/evdev-fallback.h +++ b/src/evdev-fallback.h @@ -102,6 +102,7 @@ struct fallback_dispatch { struct device_coords hi_res; bool emulate_hi_res_wheel; bool is_inhibited; + bool hi_res_event_received; } wheel; struct { diff --git a/test/test-pointer.c b/test/test-pointer.c index 2be64ee2..218fe074 100644 --- a/test/test-pointer.c +++ b/test/test-pointer.c @@ -803,6 +803,70 @@ START_TEST(pointer_scroll_wheel_hires) } END_TEST +START_TEST(pointer_scroll_wheel_hires_send_only_lores_vertical) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + + if (!libevdev_has_event_code(dev->evdev, EV_REL, REL_WHEEL_HI_RES) && + !libevdev_has_event_code(dev->evdev, EV_REL, REL_HWHEEL_HI_RES)) + return; + + litest_drain_events(dev->libinput); + litest_set_log_handler_bug(li); + + litest_event(dev, EV_REL, REL_WHEEL, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + test_high_and_low_wheel_events_value(dev, REL_WHEEL, -120); + + litest_event(dev, EV_REL, REL_WHEEL, -1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + test_high_and_low_wheel_events_value(dev, REL_WHEEL, 120); + + litest_event(dev, EV_REL, REL_HWHEEL, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + test_high_and_low_wheel_events_value(dev, REL_HWHEEL, 120); + + litest_assert_empty_queue(li); + litest_restore_log_handler(li); +} +END_TEST + +START_TEST(pointer_scroll_wheel_hires_send_only_lores_horizontal) +{ + struct litest_device *dev = litest_current_device(); + struct libinput *li = dev->libinput; + + if (!libevdev_has_event_code(dev->evdev, EV_REL, REL_WHEEL_HI_RES) && + !libevdev_has_event_code(dev->evdev, EV_REL, REL_HWHEEL_HI_RES)) + return; + + litest_drain_events(dev->libinput); + litest_set_log_handler_bug(li); + + litest_event(dev, EV_REL, REL_HWHEEL, 2); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + test_high_and_low_wheel_events_value(dev, REL_HWHEEL, 240); + + litest_event(dev, EV_REL, REL_WHEEL, -1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + test_high_and_low_wheel_events_value(dev, REL_WHEEL, 120); + + litest_event(dev, EV_REL, REL_HWHEEL, 1); + litest_event(dev, EV_SYN, SYN_REPORT, 0); + libinput_dispatch(li); + test_high_and_low_wheel_events_value(dev, REL_HWHEEL, 120); + + litest_assert_empty_queue(li); + litest_restore_log_handler(li); +} +END_TEST + START_TEST(pointer_scroll_natural_defaults) { struct litest_device *dev = litest_current_device(); @@ -3429,6 +3493,8 @@ TEST_COLLECTION(pointer) litest_add_for_device(pointer_scroll_wheel_pressed_noscroll, LITEST_MOUSE); litest_add_for_device(pointer_scroll_hi_res_wheel_pressed_noscroll, LITEST_MOUSE); litest_add(pointer_scroll_wheel_hires, LITEST_WHEEL, LITEST_TABLET); + litest_add(pointer_scroll_wheel_hires_send_only_lores_vertical, LITEST_WHEEL, LITEST_TABLET); + litest_add(pointer_scroll_wheel_hires_send_only_lores_horizontal, LITEST_WHEEL, LITEST_TABLET); litest_add(pointer_scroll_button, LITEST_RELATIVE|LITEST_BUTTON, LITEST_ANY); litest_add(pointer_scroll_button_noscroll, LITEST_ABSOLUTE|LITEST_BUTTON, LITEST_RELATIVE); litest_add(pointer_scroll_button_noscroll, LITEST_ANY, LITEST_RELATIVE|LITEST_BUTTON); |