Commit 67a2a49f authored by Peter Hutterer's avatar Peter Hutterer
Browse files

Implement a touchpad scroll distance property

To be used for touchpads and continuous (i.e. button-based scrolling).

libinput provides us with pixel data for finger-based and button-based
scrolling but the X server does support this - XI2.1 smooth scrolling is
merely centered around a logical scroll click (defined as "increment"), with
smooth scrolling being a fraction of that increment. For example, in the old
synaptics driver that value was in device-specific units and thus different
for every device.

The increment is a constant value set in the ScrollClass and cannot be changed
at device runtime. So we simply initialize with a random default (15, because
that works well for wheels) and then scale our pixel delta in to that range.

With the default value, a 15 pixel movement would result in a logical scroll
click, if the distance is set to 30 the users has to move 30 pixels to trigger
that scroll click.

From the client's perspective nothing changes, the increment is still the

Range checks are quite restrictive, this option is supposed to improve
usability, not as a workaround around other bugs.
Signed-off-by: Peter Hutterer's avatarPeter Hutterer <>
parent b39abb25
Pipeline #308938 passed with stages
in 47 seconds
......@@ -123,6 +123,12 @@
/* Scroll button lock: BOOL, 1 value, TRUE for enabled, FALSE otherwise, read-only*/
#define LIBINPUT_PROP_SCROLL_BUTTON_LOCK_DEFAULT "libinput Button Scrolling Button Lock Enabled Default"
/* Scroll pixel distance: CARD32, 1 value (with implementation-defined limits) */
#define LIBINPUT_PROP_SCROLL_PIXEL_DISTANCE "libinput Scrolling Pixel Distance"
/* Scroll pixel distance: CARD32, 1 value, read-only */
#define LIBINPUT_PROP_SCROLL_PIXEL_DISTANCE_DEFAULT "libinput Scrolling Pixel Distance Default"
/* Click method: BOOL read-only, 2 values in order buttonareas, clickfinger
shows available click methods */
#define LIBINPUT_PROP_CLICK_METHODS_AVAILABLE "libinput Click Methods Available"
......@@ -160,6 +160,13 @@ Enables a scroll method. Permitted values are
Not all devices support all options, if an option is unsupported, the
default scroll option for this device is used.
.TP 7
.BI "Option \*qScrollPixelDistance\*q \*q" int \*q
Sets the movement distance in pixels required to trigger one logical wheel
click. This option only applies to the scroll methods
.BI twofinger,
.BI edge,
.BI button.
.TP 7
.BI "Option \*qSendEventsMode\*q \*q" (disabled|enabled|disabled-on-external-mouse) \*q
Sets the send events mode to disabled, enabled, or "disable when an external
mouse is connected".
......@@ -279,6 +286,10 @@ Indicates which scroll methods are available on this device.
3 boolean values (8 bit, 0 or 1), in order "two-finger", "edge", "button".
Indicates which scroll method is currently enabled on this device.
.TP 7
.BI "libinput Scroll Pixel Distance"
1 32-bit value (nonzero, with additional implementation-defined range checks).
Changes the movement distance required to trigger one logical wheel click.
.TP 7
.BI "libinput Send Events Modes Available"
2 boolean values (8 bit, 0 or 1), in order "disabled" and
"disabled-on-external-mouse". Indicates which send-event modes are available
......@@ -396,6 +407,12 @@ it takes left-handed-ness into account.
This feature is provided by this driver, not by libinput.
The X server does not support per-pixel scrolling, its smooth scroll
implementation is based around a logical unit of scrolling (traditionally
corresponding to a wheel click). For finger-based or continuous scrolling
(button scrolling), a movement distance has to be converted to a logical
click. The \fBScrollPixelDistance\fR option adjusts this distance.
This driver does not work with \fBOption \*qDevice\*q\fR set to an event
node in \fI/dev/input/by-id\fR and \fI/dev/input/by-path\fR. This can be
......@@ -52,6 +52,9 @@
#define TABLET_NUM_BUTTONS 7 /* we need scroll buttons */
#define TOUCH_MAX_SLOTS 15
#define TOUCHPAD_SCROLL_DIST_MIN 10 /* in libinput pixels */
#define TOUCHPAD_SCROLL_DIST_MAX 50 /* in libinput pixels */
#define streq(a, b) (strcmp(a, b) == 0)
#define strneq(a, b, n) (strncmp(a, b, n) == 0)
......@@ -146,6 +149,7 @@ struct xf86libinput {
CARD32 sendevents;
CARD32 scroll_button; /* xorg button number */
BOOL scroll_buttonlock;
uint32_t scroll_pixel_distance;
float speed;
float matrix[9];
enum libinput_config_scroll_method scroll_method;
......@@ -1621,7 +1625,17 @@ calculate_axis_value(struct xf86libinput *driver_data,
value = get_wheel_scroll_value(driver_data, event, axis);
} else {
double dist = driver_data->options.scroll_pixel_distance;
assert(dist != 0.0);
value = libinput_event_pointer_get_axis_value(event, axis);
/* We need to scale this value into our scroll increment range
* because that one is constant for the lifetime of the
* device. The user may change the ScrollPixelDistance
* though, so where we have a dist of 10 but an increment of
* 15, we need to scale from 0..10 into 0..15.
value = value/dist * SCROLL_INCREMENT;
*value_out = value;
......@@ -2826,6 +2840,29 @@ xf86libinput_parse_scrollbuttonlock_option(InputInfoPtr pInfo,
return buttonlock;
static inline uint32_t
xf86libinput_parse_scroll_pixel_distance_option(InputInfoPtr pInfo,
struct libinput_device *device)
uint32_t dflt = SCROLL_INCREMENT;
uint32_t dist;
uint32_t methods =
if ((libinput_device_config_scroll_get_methods(device) & methods) == 0)
return dflt;
dist = xf86SetIntOption(pInfo->options, "ScrollPixelDistance", dflt);
xf86IDrvMsg(pInfo, X_ERROR,
"Invalid ScrollPixelDistance %d\n", dist);
dist = dflt;
return dist;
static inline unsigned int
xf86libinput_parse_clickmethod_option(InputInfoPtr pInfo,
struct libinput_device *device)
......@@ -3107,6 +3144,7 @@ xf86libinput_parse_options(InputInfoPtr pInfo,
options->scroll_method = xf86libinput_parse_scroll_option(pInfo, device);
options->scroll_button = xf86libinput_parse_scrollbutton_option(pInfo, device);
options->scroll_buttonlock = xf86libinput_parse_scrollbuttonlock_option(pInfo, device);
options->scroll_pixel_distance = xf86libinput_parse_scroll_pixel_distance_option(pInfo, device);
options->click_method = xf86libinput_parse_clickmethod_option(pInfo, device);
options->middle_emulation = xf86libinput_parse_middleemulation_option(pInfo, device);
options->disable_while_typing = xf86libinput_parse_disablewhiletyping_option(pInfo, device);
......@@ -3420,8 +3458,8 @@ xf86libinput_pre_init(InputDriverPtr drv,
* affect touchpad scroll speed. For wheels it doesn't matter as
* we're using the discrete value only.
driver_data->scroll.v.dist = 15;
driver_data->scroll.h.dist = 15;
driver_data->scroll.v.dist = SCROLL_INCREMENT;
driver_data->scroll.h.dist = SCROLL_INCREMENT;
if (!is_subdevice) {
if (libinput_device_has_capability(device, LIBINPUT_DEVICE_CAP_POINTER))
......@@ -3563,6 +3601,8 @@ static Atom prop_scroll_button;
static Atom prop_scroll_button_default;
static Atom prop_scroll_buttonlock;
static Atom prop_scroll_buttonlock_default;
static Atom prop_scroll_pixel_distance;
static Atom prop_scroll_pixel_distance_default;
static Atom prop_click_methods_available;
static Atom prop_click_method_enabled;
static Atom prop_click_method_default;
......@@ -4380,6 +4420,34 @@ LibinputSetPropertyHorizScroll(DeviceIntPtr dev,
return Success;
static inline int
LibinputSetPropertyScrollPixelDistance(DeviceIntPtr dev,
Atom atom,
XIPropertyValuePtr val,
BOOL checkonly)
InputInfoPtr pInfo = dev->public.devicePrivate;
struct xf86libinput *driver_data = pInfo->private;
uint32_t dist;
if (val->format != 32 || val->type != XA_CARDINAL || val->size != 1)
return BadMatch;
dist = *(BOOL*)val->data;
if (checkonly) {
return BadValue;
if (!xf86libinput_check_device(dev, atom))
return BadMatch;
} else {
driver_data->options.scroll_pixel_distance = dist;
return Success;
static inline int
LibinputSetPropertyRotationAngle(DeviceIntPtr dev,
Atom atom,
......@@ -4557,6 +4625,8 @@ LibinputSetProperty(DeviceIntPtr dev, Atom atom, XIPropertyValuePtr val,
rc = LibinputSetPropertyDragLockButtons(dev, atom, val, checkonly);
else if (atom == prop_horiz_scroll)
rc = LibinputSetPropertyHorizScroll(dev, atom, val, checkonly);
else if (atom == prop_scroll_pixel_distance)
rc = LibinputSetPropertyScrollPixelDistance(dev, atom, val, checkonly);
else if (atom == prop_mode_groups) {
InputInfoPtr pInfo = dev->public.devicePrivate;
struct xf86libinput *driver_data = pInfo->private;
......@@ -4588,6 +4658,7 @@ LibinputSetProperty(DeviceIntPtr dev, Atom atom, XIPropertyValuePtr val,
atom == prop_scroll_methods_available ||
atom == prop_scroll_button_default ||
atom == prop_scroll_buttonlock_default ||
atom == prop_scroll_pixel_distance_default ||
atom == prop_click_method_default ||
atom == prop_click_methods_available ||
atom == prop_middle_emulation_default ||
......@@ -5114,6 +5185,36 @@ LibinputInitScrollMethodsProperty(DeviceIntPtr dev,
static void
LibinputInitScrollPixelDistanceProperty(DeviceIntPtr dev,
struct xf86libinput *driver_data,
struct libinput_device *device)
uint32_t scroll_methods;
CARD32 dist = driver_data->options.scroll_pixel_distance;
if (!subdevice_has_capabilities(dev, CAP_POINTER))
scroll_methods = libinput_device_config_scroll_get_methods(device);
prop_scroll_pixel_distance = LibinputMakeProperty(dev,
1, &dist);
if (!prop_scroll_pixel_distance)
prop_scroll_pixel_distance_default = LibinputMakeProperty(dev,
1, &dist);
static void
LibinputInitClickMethodsProperty(DeviceIntPtr dev,
struct xf86libinput *driver_data,
......@@ -5555,6 +5656,7 @@ LibinputInitProperty(DeviceIntPtr dev)
LibinputInitDragLockProperty(dev, driver_data);
LibinputInitHorizScrollProperty(dev, driver_data);
LibinputInitScrollPixelDistanceProperty(dev, driver_data, device);
LibinputInitPressureCurveProperty(dev, driver_data);
LibinputInitTabletAreaRatioProperty(dev, driver_data);
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