HID: input: simplify/fix high-res scroll event handling
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 29 Oct 2018 18:25:24 +0000 (11:25 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 29 Oct 2018 18:25:24 +0000 (11:25 -0700)
Commit 1ff2e1a44e02 ("HID: input: Create a utility class for counting
scroll events") created the helper function

    hid_scroll_counter_handle_scroll()

to handle high-res scroll events and also expose them as regular wheel
events.

But the resulting algorithm was unstable, and causes scrolling to be
very unreliable.  When you hit the half-way mark of the highres
multiplier, small highres movements will incorrectly translate into big
traditional wheel movements, causing odd jitters.

Simplify the code and make the output stable.

NOTE! I'm pretty sure this will need further tweaking.  But this at
least turns a unusable mouse wheel on my Logitech MX Anywhere 2S into
a usable one.

Cc: Jiri Kosina <jikos@kernel.org>
Cc: Harry Cutts <hcutts@chromium.org>
Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
drivers/hid/hid-input.c

index 567c3bf64515d3599946c1d74aa79d52d9a61620..a2f74e6adc70d162adf641ac3f02ae452e3552a3 100644 (file)
@@ -1855,31 +1855,30 @@ EXPORT_SYMBOL_GPL(hidinput_disconnect);
 void hid_scroll_counter_handle_scroll(struct hid_scroll_counter *counter,
                                      int hi_res_value)
 {
 void hid_scroll_counter_handle_scroll(struct hid_scroll_counter *counter,
                                      int hi_res_value)
 {
-       int low_res_scroll_amount;
-       /* Some wheels will rest 7/8ths of a notch from the previous notch
-        * after slow movement, so we want the threshold for low-res events to
-        * be in the middle of the notches (e.g. after 4/8ths) as opposed to on
-        * the notches themselves (8/8ths).
-        */
-       int threshold = counter->resolution_multiplier / 2;
+       int low_res_value, remainder, multiplier;
 
        input_report_rel(counter->dev, REL_WHEEL_HI_RES,
                         hi_res_value * counter->microns_per_hi_res_unit);
 
 
        input_report_rel(counter->dev, REL_WHEEL_HI_RES,
                         hi_res_value * counter->microns_per_hi_res_unit);
 
-       counter->remainder += hi_res_value;
-       if (abs(counter->remainder) >= threshold) {
-               /* Add (or subtract) 1 because we want to trigger when the wheel
-                * is half-way to the next notch (i.e. scroll 1 notch after a
-                * 1/2 notch movement, 2 notches after a 1 1/2 notch movement,
-                * etc.).
-                */
-               low_res_scroll_amount =
-                       counter->remainder / counter->resolution_multiplier
-                       + (hi_res_value > 0 ? 1 : -1);
-               input_report_rel(counter->dev, REL_WHEEL,
-                                low_res_scroll_amount);
-               counter->remainder -=
-                       low_res_scroll_amount * counter->resolution_multiplier;
-       }
+       /*
+        * Update the low-res remainder with the high-res value,
+        * but reset if the direction has changed.
+        */
+       remainder = counter->remainder;
+       if ((remainder ^ hi_res_value) < 0)
+               remainder = 0;
+       remainder += hi_res_value;
+
+       /*
+        * Then just use the resolution multiplier to see if
+        * we should send a low-res (aka regular wheel) event.
+        */
+       multiplier = counter->resolution_multiplier;
+       low_res_value = remainder / multiplier;
+       remainder -= low_res_value * multiplier;
+       counter->remainder = remainder;
+
+       if (low_res_value)
+               input_report_rel(counter->dev, REL_WHEEL, low_res_value);
 }
 EXPORT_SYMBOL_GPL(hid_scroll_counter_handle_scroll);
 }
 EXPORT_SYMBOL_GPL(hid_scroll_counter_handle_scroll);