From 9e3544b258e0d1a318bac32129c23c426a6e8712 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Wed, 9 Jul 2025 15:54:02 +0200 Subject: [PATCH] Add ScrollFactor option to change mouse/touchpad scroll speed Add xorg option ScrollFactor. Add xinput option "libinput Scrolling Factor". This works well in xi2 applications where they receive the raw scroll value, however applications that use the core x11 protocol only for input receive a scroll button press. The frequency this scroll button press event is sent depends on the value of ScrollFactor, so it works in such applications as well but the scrolling wont be as "smooth" as if they used xi2. gtk, qt, chromium (and electron) applications seem to use xi2 while firefox does not, unless you set the MOZ_USE_XINPUT2=1 environment variable. Signed-off-by: dec05eba --- include/libinput-properties.h | 6 +++ man/libinput.man | 5 +++ src/xf86libinput.c | 79 +++++++++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+) diff --git a/include/libinput-properties.h b/include/libinput-properties.h index bf774d5..2034338 100644 --- a/include/libinput-properties.h +++ b/include/libinput-properties.h @@ -154,6 +154,12 @@ /* Scroll pixel distance: CARD32, 1 value, read-only */ #define LIBINPUT_PROP_SCROLL_PIXEL_DISTANCE_DEFAULT "libinput Scrolling Pixel Distance Default" +/* Scroll factor: FLOAT, 1 value, 32 bit */ +#define LIBINPUT_PROP_SCROLL_FACTOR "libinput Scrolling Factor" + +/* Scroll factor: FLOAT, 1 value, 32 bit, read-only */ +#define LIBINPUT_PROP_SCROLL_FACTOR_DEFAULT "libinput Scrolling Factor 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" diff --git a/man/libinput.man b/man/libinput.man index 053bbf7..2c2a6af 100644 --- a/man/libinput.man +++ b/man/libinput.man @@ -206,6 +206,11 @@ If disabled (the default), the button is considered logically down while held down and up once physically released. .TP 7 +.BI "Option \*qScrollFactor\*q \*q" float \*q +Sets the scroll factor for scroll events. A higher value means faster scrolling. +Applications that don't support xi2 input will instead receive scroll button events +less or more frequently depending on the scroll factor value, instead of scrolling "smoother". +.TP 7 .BI "Option \*qScrollMethod\*q \*q" string \*q Enables a scroll method. Permitted values are .BR none , diff --git a/src/xf86libinput.c b/src/xf86libinput.c index b62173b..ad059be 100644 --- a/src/xf86libinput.c +++ b/src/xf86libinput.c @@ -52,6 +52,8 @@ #define TOUCH_MAX_SLOTS 15 #define XORG_KEYCODE_OFFSET 8 #define SCROLL_INCREMENT 15 +#define SCROLL_FACTOR_MIN 0.0001 +#define SCROLL_FACTOR_MAX 1000.0 #define TOUCHPAD_SCROLL_DIST_MIN 10 /* in libinput pixels */ #define TOUCHPAD_SCROLL_DIST_MAX 50 /* in libinput pixels */ @@ -175,6 +177,7 @@ struct xf86libinput { CARD32 scroll_button; /* xorg button number */ BOOL scroll_buttonlock; uint32_t scroll_pixel_distance; + float scroll_factor; float speed; float matrix[9]; enum libinput_config_scroll_method scroll_method; @@ -1913,6 +1916,7 @@ calculate_axis_value(struct xf86libinput *driver_data, value = value/dist * SCROLL_INCREMENT * 8; } + value *= driver_data->options.scroll_factor; *value_out = value; return true; @@ -3435,6 +3439,20 @@ xf86libinput_parse_scroll_pixel_distance_option(InputInfoPtr pInfo, return dist; } +static inline double +xf86libinput_parse_scroll_factor_option(InputInfoPtr pInfo, + struct libinput_device *device) +{ + const double scroll_factor_default = 1.0; + double scroll_factor = xf86SetRealOption(pInfo->options, "ScrollFactor", scroll_factor_default); + if (scroll_factor < SCROLL_FACTOR_MIN || scroll_factor > SCROLL_FACTOR_MAX) { + xf86IDrvMsg(pInfo, X_ERROR, "Invalid ScrollFactor %f, expected to be between %f and %f\n", + scroll_factor, SCROLL_FACTOR_MIN, SCROLL_FACTOR_MAX); + scroll_factor = scroll_factor_default; + } + return scroll_factor; +} + static inline unsigned int xf86libinput_parse_clickmethod_option(InputInfoPtr pInfo, struct libinput_device *device) @@ -3774,6 +3792,7 @@ xf86libinput_parse_options(InputInfoPtr pInfo, 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->scroll_factor = xf86libinput_parse_scroll_factor_option(pInfo, device); options->click_method = xf86libinput_parse_clickmethod_option(pInfo, device); #if HAVE_LIBINPUT_CLICKFINGER_BUTTON_MAP options->clickfinger_button_map = xf86libinput_parse_clickfinger_map_option(pInfo, device); @@ -4257,6 +4276,8 @@ 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_scroll_factor; +static Atom prop_scroll_factor_default; static Atom prop_click_methods_available; static Atom prop_click_method_enabled; static Atom prop_click_method_default; @@ -5247,6 +5268,34 @@ LibinputSetPropertyScrollPixelDistance(DeviceIntPtr dev, return Success; } +static inline int +LibinputSetPropertyScrollFactor(DeviceIntPtr dev, + Atom atom, + XIPropertyValuePtr val, + BOOL checkonly) +{ + InputInfoPtr pInfo = dev->public.devicePrivate; + struct xf86libinput *driver_data = pInfo->private; + float *scroll_factor; + + if (val->format != 32 || val->size != 1 || val->type != prop_float) + return BadMatch; + + scroll_factor = (float*)val->data; + + if (checkonly) { + if (*scroll_factor < SCROLL_FACTOR_MIN || *scroll_factor > SCROLL_FACTOR_MAX) + return BadValue; + + if (!xf86libinput_check_device(dev, atom)) + return BadMatch; + } else { + driver_data->options.scroll_factor = *scroll_factor; + } + + return Success; +} + static inline int LibinputSetPropertyRotationAngle(DeviceIntPtr dev, Atom atom, @@ -5502,6 +5551,8 @@ LibinputSetProperty(DeviceIntPtr dev, Atom atom, rc = LibinputSetPropertyHorizScroll(dev, atom, val, checkonly); else if (atom == prop_scroll_pixel_distance) rc = LibinputSetPropertyScrollPixelDistance(dev, atom, val, checkonly); + else if (atom == prop_scroll_factor) + rc = LibinputSetPropertyScrollFactor(dev, atom, val, checkonly); else if (atom == prop_mode_groups) { InputInfoPtr pInfo = dev->public.devicePrivate; struct xf86libinput *driver_data = pInfo->private; @@ -5543,6 +5594,7 @@ LibinputSetProperty(DeviceIntPtr dev, Atom atom, atom == prop_scroll_method_default || atom == prop_scroll_methods_available || atom == prop_scroll_pixel_distance_default || + atom == prop_scroll_factor_default || atom == prop_sendevents_available || atom == prop_sendevents_default || atom == prop_serial || @@ -6155,6 +6207,32 @@ LibinputInitScrollPixelDistanceProperty(DeviceIntPtr dev, 1, &dist); } +static void +LibinputInitScrollFactorProperty(DeviceIntPtr dev, + struct xf86libinput *driver_data, + struct libinput_device *device) +{ + float scroll_factor = driver_data->options.scroll_factor; + + if (!subdevice_has_capabilities(dev, CAP_POINTER)) + return; + + prop_scroll_factor = LibinputMakeProperty(dev, + LIBINPUT_PROP_SCROLL_FACTOR, + prop_float, 32, + 1, &scroll_factor); + if (!prop_scroll_factor) + return; + + prop_scroll_factor_default = LibinputMakeProperty(dev, + LIBINPUT_PROP_SCROLL_FACTOR_DEFAULT, + prop_float, 32, + 1, &scroll_factor); + + if (!prop_scroll_factor_default) + return; +} + static void LibinputInitClickMethodsProperty(DeviceIntPtr dev, struct xf86libinput *driver_data, @@ -6700,6 +6778,7 @@ LibinputInitProperty(DeviceIntPtr dev) LibinputInitNaturalScrollProperty(dev, driver_data, device); LibinputInitDisableWhileTypingProperty(dev, driver_data, device); LibinputInitScrollMethodsProperty(dev, driver_data, device); + LibinputInitScrollFactorProperty(dev, driver_data, device); LibinputInitClickMethodsProperty(dev, driver_data, device); LibinputInitClickfingerButtonmapProperty(dev, driver_data, device); LibinputInitMiddleEmulationProperty(dev, driver_data, device);