[PATCH] HID: input: restore the hi-res scroll emulation to work on the midpoint

From: Peter Hutterer
Date: Tue Oct 30 2018 - 00:10:53 EST


A single notch movement does not always cause exactly $multiplier events in
the hardware. Setting the trigger at the midpoint allows us to slide a few
events either direction while still triggering exactly one scroll event per
multiplier.

Signed-off-by: Peter Hutterer <peter.hutterer@xxxxxxxxx>
---
drivers/hid/hid-input.c | 25 ++++++++++++++++++-------
include/linux/hid.h | 1 +
2 files changed, 19 insertions(+), 7 deletions(-)

diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index a2f74e6adc70..a170a6823072 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -1855,7 +1855,7 @@ EXPORT_SYMBOL_GPL(hidinput_disconnect);
void hid_scroll_counter_handle_scroll(struct hid_scroll_counter *counter,
int hi_res_value)
{
- int low_res_value, remainder, multiplier;
+ int low_res_value, remainder, multiplier, direction;

input_report_rel(counter->dev, REL_WHEEL_HI_RES,
hi_res_value * counter->microns_per_hi_res_unit);
@@ -1865,20 +1865,31 @@ void hid_scroll_counter_handle_scroll(struct hid_scroll_counter *counter,
* but reset if the direction has changed.
*/
remainder = counter->remainder;
- if ((remainder ^ hi_res_value) < 0)
+ direction = hi_res_value > 0 ? 1 : -1;
+ if (direction != counter->direction)
remainder = 0;
+ counter->direction = direction;
remainder += hi_res_value;

/*
* Then just use the resolution multiplier to see if
* we should send a low-res (aka regular wheel) event.
+ * Threshold is at the mid-point because we'll slide a few events
+ * back/forth when the mouse gives us more or less than multiplier
+ * events for a single notch movement.
*/
multiplier = counter->resolution_multiplier;
- low_res_value = remainder / multiplier;
- remainder -= low_res_value * multiplier;
- counter->remainder = remainder;
-
- if (low_res_value)
+ if (abs(remainder) >= multiplier/2) {
+ low_res_value = remainder / multiplier;
+ /* Move at minimum 1/-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).
+ */
+ if (low_res_value == 0)
+ low_res_value = (hi_res_value > 0 ? 1 : -1);
+ remainder -= low_res_value * multiplier;
input_report_rel(counter->dev, REL_WHEEL, low_res_value);
+ }
+ counter->remainder = remainder;
}
EXPORT_SYMBOL_GPL(hid_scroll_counter_handle_scroll);
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 2827b87590d8..9752b8a2ee42 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -1162,6 +1162,7 @@ struct hid_scroll_counter {
int resolution_multiplier;

int remainder;
+ int direction;
};

void hid_scroll_counter_handle_scroll(struct hid_scroll_counter *counter,
--
2.19.1