Commit 0dad7408 authored by Peter Hutterer's avatar Peter Hutterer

Calculate the required scroll distance based on the angle

For a mouse with a click angle of 15 degrees things are unchanged. For devices
with angles less than 10, the current code scrolled way too fast. Because the
angle wasn't used anywhere, each tick would count as full scroll wheel event,
a slight movement of the wheel would thus scroll as much as a large movement
on a normal mouse.

Fix this by taking the actual click angle of the device into account. We
calculate some multiple of the angle that's close enough to the default 15
degrees of the wheel and then require that many click events to hit the full
scroll distance. For example, a mouse with a click angle of 3 degrees now
requires 5 clicks to trigger a full legacy scroll button event.

XI2.1 clients get the intermediate events (i.e. in this case five times
one-fifth of the scroll distance) and can thus scroll smoothly, or more
specifically in smaller events than usual. Peter Hutterer's avatarPeter Hutterer <>
Reviewed-by: default avatarHans de Goede <>
parent ea02578a
......@@ -126,6 +126,9 @@ struct xf86libinput {
struct {
int vdist;
int hdist;
double vdist_fraction;
double hdist_fraction;
} scroll;
struct {
......@@ -1323,6 +1326,64 @@ xf86libinput_handle_key(InputInfoPtr pInfo, struct libinput_event_keyboard *even
xf86PostKeyboardEvent(dev, key, is_press);
* The scroll fraction is the value we divide the scroll dist with to
* accommodate for wheels with a small click angle. On these devices,
* multiple clicks of small angle accumulate to the XI 2.1 scroll distance.
* This gives us smooth scrolling on those wheels for small movements, the
* legacy button events are generated whenever the full distance is reached.
* e.g. a 2 degree click angle requires 8 clicks before a legacy event is
* sent, but each of those clicks will send XI2.1 smooth scroll data for
* compatible clients.
static inline double
get_scroll_fraction(struct xf86libinput *driver_data,
struct libinput_event_pointer *event,
enum libinput_pointer_axis axis)
double *fraction;
double f;
double angle;
int discrete;
switch (axis) {
fraction = &driver_data->scroll.hdist_fraction;
fraction = &driver_data->scroll.vdist_fraction;
return 0.0;
if (*fraction != 0.0)
return *fraction;
/* Calculate the angle per single scroll event */
angle = libinput_event_pointer_get_axis_value(event, axis);
discrete = libinput_event_pointer_get_axis_value_discrete(event, axis);
angle /= discrete;
/* We only do magic for click angles smaller than 10 degrees */
if (angle >= 10) {
*fraction = 1.0;
return 1.0;
/* Figure out something that gets close to 15 degrees (the general
* wheel default) with a number of clicks. This formula gives us
* between 12 and and 20 degrees for the range of 1-10. See
* for a
* graph.
f = round(15.0/angle);
*fraction = f;
return f;
static inline bool
calculate_axis_value(struct xf86libinput *driver_data,
enum libinput_pointer_axis axis,
......@@ -1337,8 +1398,11 @@ calculate_axis_value(struct xf86libinput *driver_data,
source = libinput_event_pointer_get_axis_source(event);
double scroll_fraction;
value = libinput_event_pointer_get_axis_value_discrete(event, axis);
value *= driver_data->scroll.vdist;
scroll_fraction = get_scroll_fraction(driver_data, event, axis);
value *= driver_data->scroll.vdist/scroll_fraction;
} else {
value = libinput_event_pointer_get_axis_value(event, axis);
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment