Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

HID: input: simplify/fix high-res scroll event handling

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>

+21 -22
+21 -22
drivers/hid/hid-input.c
··· 1855 1855 void hid_scroll_counter_handle_scroll(struct hid_scroll_counter *counter, 1856 1856 int hi_res_value) 1857 1857 { 1858 - int low_res_scroll_amount; 1859 - /* Some wheels will rest 7/8ths of a notch from the previous notch 1860 - * after slow movement, so we want the threshold for low-res events to 1861 - * be in the middle of the notches (e.g. after 4/8ths) as opposed to on 1862 - * the notches themselves (8/8ths). 1863 - */ 1864 - int threshold = counter->resolution_multiplier / 2; 1858 + int low_res_value, remainder, multiplier; 1865 1859 1866 1860 input_report_rel(counter->dev, REL_WHEEL_HI_RES, 1867 1861 hi_res_value * counter->microns_per_hi_res_unit); 1868 1862 1869 - counter->remainder += hi_res_value; 1870 - if (abs(counter->remainder) >= threshold) { 1871 - /* Add (or subtract) 1 because we want to trigger when the wheel 1872 - * is half-way to the next notch (i.e. scroll 1 notch after a 1873 - * 1/2 notch movement, 2 notches after a 1 1/2 notch movement, 1874 - * etc.). 1875 - */ 1876 - low_res_scroll_amount = 1877 - counter->remainder / counter->resolution_multiplier 1878 - + (hi_res_value > 0 ? 1 : -1); 1879 - input_report_rel(counter->dev, REL_WHEEL, 1880 - low_res_scroll_amount); 1881 - counter->remainder -= 1882 - low_res_scroll_amount * counter->resolution_multiplier; 1883 - } 1863 + /* 1864 + * Update the low-res remainder with the high-res value, 1865 + * but reset if the direction has changed. 1866 + */ 1867 + remainder = counter->remainder; 1868 + if ((remainder ^ hi_res_value) < 0) 1869 + remainder = 0; 1870 + remainder += hi_res_value; 1871 + 1872 + /* 1873 + * Then just use the resolution multiplier to see if 1874 + * we should send a low-res (aka regular wheel) event. 1875 + */ 1876 + multiplier = counter->resolution_multiplier; 1877 + low_res_value = remainder / multiplier; 1878 + remainder -= low_res_value * multiplier; 1879 + counter->remainder = remainder; 1880 + 1881 + if (low_res_value) 1882 + input_report_rel(counter->dev, REL_WHEEL, low_res_value); 1884 1883 } 1885 1884 EXPORT_SYMBOL_GPL(hid_scroll_counter_handle_scroll);