Tuesday, November 9, 2021

Libinput - alternate acceleration profile for touchpad

I don't like the libinput acceleration profile for my laptop touchpad.

I have Debian 11 (bullseye) on my laptop with xfce4 desktop. It defaults to libinput for the touchpad but I didn't like the acceleration profile. It was either too fast for accurate fine movements or too slow for traversing the display. While I could adjust the speed, I couldn't find any setting that was satisfying. I don't recall when the driver changed but it has been a long time now, maybe since I replace Mint with Debian a year or two ago. In any case, it is long enough that I am not merely suffering from a slight change to the acceleration profile conflicting with my muscle memory: even given plenty of time to adjust, I still don't like the libinput acceleration profile.

Recently, I decided to do something about it. I tried the Synaptics driver and it was better but still not what I wanted. I tried the evdev driver but it didn't work at all: the cursor didn't move no matter what I tried. No error message: just no movement. I really wanted to try some of the acceleration profiles that evdev provides but eventually I gave up.

It seems that the Synaptics and evdev drivers are deprecated and the libinput driver will predominate in the future, so I decided to focus on it. Others have criticized its acceleration profile, with sentiments like mine. The libinput FAQ includes:

Why is libinput’s pointer acceleration worse than synaptics/evdev

This is a known problem affecting some devices and/or use-case but the exact cause is still unknown. It may be a device-specific issue, it may be a bug in libinput’s acceleration code, it may be a disagreement about how pointer acceleration should feel. Unfortunately this is something that affected users need to investigate and analyze.

As I had tried all sorts of settings and given it lots of time to get used to it but was still bothered by it, I decided to try implementing an alternate acceleration profile. I am not hopeful that this will ever be accepted into libinput. For good reason, the developers need to keep it simple and robust with minimal support resources. Adding features adds support burden. But I only care about support for me and my one system, and it is, after all, free software.

It took me a couple of days to learn enough to get started but once I familiarized with the libinput code a bit, it turned out to be easy to implement an alternate acceleration profile with hard coded parameters: just one function to change. Adding configuration parameters is a bigger challenge for another day. But as I am recompiling libinput anyway, changing the parameters in the code isn't a problem, and I don't want to be changing them anyway, once I get them right.

I built libinput per Building. This gave me a local repository.

I created a new branch: wip/alternate_profile.

I modified src/filter-touchpad.c, which contains the 'adaptive' profile acceleration code. I created a new function, touchpad_accel_profile_constrained_linear, modeled after the original touchpad_accel_profile_linear (which is, in fact, not a linear transfer function) that implements the adaptive profile (the default profile) and changed the pointer to the transfer function to the new one. I don't yet know how to add the new profile selectable by configuration but I don't want to use the adaptive profile so replacing it is OK for now. It's a crude hack, but fixes my immediate problem and is a step towards adding a new profile.

The new profile has four hard-coded configuration parameters:

  const double minimum_factor = 0.05; /* unitless */
  const double maximum_factor = 0.75; /* unitless */
  const double lower_threshold = 15.0; /* mm/s */
  const double upper_threshold = 100.0; /* mm/s */

The acceleration factor is a simple function of speed:

   accel
   factor
     ^
     |       --------
     |      /
     |     /
     |----/
     +-------------> speed in


At speeds below lower_threshold, factor is minimum_factor.

At speeds above upper_threshold, factor is maximum_factor.

At speeds between the two thresholds, factor is a linear ramp from minimum_factor to maximum_factor.

This gives me precise control at slow speeds and a gradual transition to rapid movement at high speed. The transition is sufficiently smooth that it feels predictable. I may be wrong, but I think the linear ramp is fine: no need for any fancy curve, as long as the transition from slow to fast isn't too abrupt, i don't think the details of the curve matter. If it were a mouse, I might care about the details of the curve more, but a touchpad is so crude, it doesn't matter.

Otherwise, there are no 'magic' numbers, as there are in touchpad_accel_profile_linear, but there may be elsewhere. I haven't reviewed all the code of libinput, and much less so the Xorg libinput driver or Xorg server themselves.

The parameters are hard coded. There is no provision to set them by Xorg configuration files, xinput, xset or whatever. I don't yet know how to add such capability.

But it is easy enough to recompile and reinstall, so not too difficult to change the parameters.

Of course, installing this modified libinput affects the entire system and there is no provision for per-user configuration at this point. I am using it on my personal laptop, so there are no other users.

I am using this on an old Toshiba Satellite Pro C665. I also added a hwdb entry for it, as the default touchpad dimensions were incorrect. In  /etc/udev/hwdb.d/61-evdev-local.hwdb:

# Toshiba Sattelite Pro C665
evdev:name:SynPS/2 Synaptics TouchPad:dmi:*svnTOSHIBA:*pnSatelliteProC665**
 EVDEV_ABS_00=1122:5863:58
 EVDEV_ABS_01=951:5118:99
 EVDEV_ABS_35=1122:5863:58
 EVDEV_ABS_36=951:5118:99

I followed the instructions at Coordinate ranges for absolute axes. I was surprised to find that hwdb only had entries for two Toshiba systems. You might check your own touchpad to make sure it is correct.

If you are interested, you can clone it from wip/alternate_profile

To build and try the new profile:

  1. $ git clone https://github.com/ig3/libinput.git
  2. $ cd libinput
  3. $ git checkout --track  origin/wip/alternate_profile
  4. $ meson --prefix=/usr builddir/
  5. $ ninja -C builddir/
  6. $ sudo ninja -C builddir/ install
  7. restart your X server (e.g. logout and login or reboot)

I have been using it for a few days now and it feels much more comfortable than the libinput 'adaptive' profile. I have good control at slow speeds and I can easily move the cursor across the screen. In summary, it works as I expected and I like it.

No comments:

Labels