Avoiding Libinput Hysteresis on a ThinkPad

20201220
~05 mins
tl;dr: Avoid triggering hysteresis with this one dirty trick.

Making a touchpad work on Linux as well as it does on macOS/Windows. It’s a problem as old as time itself, or at least as old as the “year of Linux on the desktop” meme.

The days of calibrating obscure values that I don’t fully understand on the old X11 synaptics driver were supposed to be a thing of the past with libinput, and to be fair they have been, for the most part. Things are definitely more in tune with what you’ve come to expect from other OSes out of the box.

Unfortunately I seem to have landed myself a ThinkPad with a touchpad (clickpad) revision that triggers a hysteresis in libinput unnecessarily, making it difficult to conduct precise, narrow gestures (like carefully circling a small number of pixels for example).

This is a core issue being tracked by the libinput developers that is prevalent in various ThinkPad revisions but not yet resolved. The gist seems to be yet unexplained heuristics causing wobbling detection to trigger a hysteresis instantly. This is probably a hard thing to fix and way beyond me. However, turning off wobbling detection altogether is not. And while a quick, temporary and dirty fix, it does seem to help!

Disable wobbling detection

To do so requires a very simple patch.

--- a/src/evdev-mt-touchpad.c 2020-12-20 12:16:11.039665884 +1100
+++ b/src/evdev-mt-touchpad.c 2020-12-20 12:16:02.846795394 +1100
@@ -1754,7 +1754,7 @@

 		tp_thumb_update_touch(tp, t, time);
 		tp_palm_detect(tp, t, time);
-		tp_detect_wobbling(tp, t, time);
+		// tp_detect_wobbling(tp, t, time);
 		tp_motion_hysteresis(tp, t);
 		tp_motion_history_push(t);

With this in place I no longer have hysteresis issues and haven’t noticed any negative effects of disabled wobbling protection.

Apply the above patch to a recent libinput source and compile then install in a way that suits your OS.

Nix

Below is an excerpt from how I do both in my NixOS package overlays.

nixpkgs.overlays = [
    (self: super: {
        libinput = super.libinput.overrideAttrs
            (o: { patches = o.patches ++ [ libinput.patch ]; });
    })
];

This means any Nix expression in my system making use of libinput gets my patched version.

A note on Synaptics RMI4 over SMBus

As I was digging into this problem I realised that my ThinkPad’s Synaptics clickpad runs their RMI4 protocol. This is a native protocol that has had Linux kernel support as of 4.6 and means you can ditch the HID/PS2 emulation (psmouse module).

This should have been automatic for me but it seems my device’s PnP ID is not in the current kernel’s list. I can force it with the kernel parameter psmouse.synaptics_intertouch=1 but then it seems I then hit another issue in libinput that causes my clickpad buttons to not get discovered by the probing code.

Alas, the situation is still good enough that I don’t need to reach for the synaptics driver, and there is better support landing in the 5.10 kernels so I’m happy to hold out for now.