summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosé Expósito <jose.exposito89@gmail.com>2021-09-24 18:08:01 +0200
committerPeter Hutterer <peter.hutterer@who-t.net>2021-09-27 22:43:22 +0000
commit5bda716ebfdfbce2017efa5e87b91a65ed9fa4a5 (patch)
treeb7d021544948c358d94e71c40671d86d2a7d0311
parente0aa946e39887f6ff1ac825d99c6a0295de937b4 (diff)
downloadlibinput-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.rst62
-rw-r--r--doc/user/meson.build2
-rw-r--r--doc/user/troubleshooting.rst1
-rw-r--r--src/evdev-fallback.c14
-rw-r--r--src/evdev-fallback.h1
-rw-r--r--test/test-pointer.c66
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);