Note: Configure Magic Trackpad 2 on Linux

Published on

Original language: Chinese . AI translations: English , Japanese .


For my wrist health


If I use a mouse for too long, my wrist and forearm start to feel uncomfortable.
Switching to a drawing tablet solved it.

But recently the tablet’s battery seems to be swelling (a bit scary…), and I’ve been eyeing the Magic Trackpad 2 for a long time.
For my wrist health, I spent a lot bought Apple’s Magic Trackpad 2.

Quick unboxing: it’s smaller than I expected, a pretty delicate little slab.
Over Bluetooth, it works out of the box on both Windows and Linux, but only as basic left/right click. Multi-finger gestures (like Windows’ three-finger task switch) don’t work by default.
So I had to do some extra configuration.


Windows Setup

Setup on Windows is very easy: someone has already made a driver. Just install it.
Project page: Github - imbushuo/mac-precision-touchpad

After installation, the trackpad supports multi-finger gestures and behaves like a laptop’s built-in touchpad.

Linux Setup

On Linux it’s slightly more annoying. I hit a few many pitfalls, so I’m writing this down.

First Attempt (Failed)

Manjaro’s Settings app has some touchpad options, but only basic stuff; there’s no way to configure multi-finger gestures.
And for some reason, the built-in settings often “forget” themselves: after waking the PC from sleep, the trackpad resets to defaults (pointer speed, scroll direction, etc.).
Changing them again in Settings sometimes doesn’t take effect either. Weird.

So I gave up on the GUI settings and configured it manually.
I found this tutorial: Configure Apple Magic Trackpad 2 Gestures on Manjaro KDE
It seemed to match what I wanted, so I skimmed it and followed the steps.

  • sudo pacman -S libinput-gestures
    Install libinput-gestures, which lets us map multi-finger gestures to actions.

  • sudo gpasswd -a $USER input
    Add your user to the input group.

  • Then you can start the libinput-gestures service

    • libinput-gestures-setup start start
    • libinput-gestures-setup stop stop
    • also commonly used: restart|autostart|status
  • The service seemed to start fine. Next, configure gestures.
    The config file can live in two places:
    /etc/libinput-gestures.conf is the system-level config (with some defaults).
    ~/.config/libinput-gestures.conf is per-user and you write it yourself.
    Typically you put your custom mappings in ~/.config/libinput-gestures.conf.

  • After editing the config, restart the service: libinput-gestures-setup restart

But after doing all that, none of the gestures worked.
I ran libinput-gestures -d for debugging; no matter what I did on the trackpad, libinput-gestures printed nothing. It looked like it wasn’t receiving any events.

I tried for a while with no result and gave up for the moment. (I briefly thought I just couldn’t make this thing work.)


Second Attempt (Found the Cause)

Two weeks later, I happened to look at the libinput package again. I realized libinput-gestures probably reads trackpad events from libinput, then triggers actions according to the config.
So if libinput-gestures wasn’t seeing any events, what about libinput itself?

  • First, check how to use libinput

    Terminal window
    $ libinput -h
    Usage: libinput [--help|--version] <command> [<args>]
    Global options:
    --help ...... show this help and exit
    --version ... show version information and exit
    Commands:
    list-devices
    List all devices with their default configuration options
    debug-events
    Print events to stdout
    debug-gui
    Display a simple GUI to visualize libinput's events.
    measure <feature>
    Measure various device properties. See the man page for more info
    analyze <feature>
    Analyze device events. See the man page for more info
    record
    Record event stream from a device node. See the man page for more info
    replay
    Replay a previously recorded event stream. See the man page for more info
  • Next, see whether libinput can even recognize the trackpad:
    libinput list-devices
    It prints a bunch of devices. Find the trackpad and you’ll see something like:

    Terminal window
    Device: Apple Inc. Magic Trackpad 2
    Kernel: /dev/input/event11
    Group: 4
    Seat: seat0, default
    Size: 162x115mm
    Capabilities: pointer gesture
    Tap-to-click: disabled
    Tap-and-drag: enabled
    Tap drag lock: disabled
    Left-handed: disabled
    Nat.scrolling: disabled
    Middle emulation: disabled
    Calibration: n/a
    Scroll methods: *two-finger edge
    Click methods: button-areas *clickfinger
    Disable-w-typing: n/a
    Disable-w-trackpointing: n/a
    Accel profiles: flat *adaptive custom
    Rotation: n/a
    Device: Apple Inc. Magic Trackpad 2
    Kernel: /dev/input/event13
    Group: 4
    Seat: seat0, default
    Size: 162x115mm
    Capabilities: pointer gesture
    Tap-to-click: disabled
    Tap-and-drag: enabled
    Tap drag lock: disabled
    Left-handed: disabled
    Nat.scrolling: disabled
    Middle emulation: disabled
    Calibration: n/a
    Scroll methods: *two-finger edge
    Click methods: button-areas *clickfinger
    Disable-w-typing: n/a
    Disable-w-trackpointing: n/a
    Accel profiles: flat *adaptive custom
    Rotation: n/a

    But weirdly, it showed two Magic Trackpad 2 devices, even though I only connected one.

    In hindsight, this was the culprit.

  • Then check whether libinput can receive trackpad events:
    libinput debug-events /dev/input/event11, then interact with the trackpad a bit.

    Terminal window
    event11 DEVICE_ADDED Apple Inc. Magic Trackpad 2 seat0 default group1 cap:pg size 162x115mm tap(dl off)
    event11 POINTER_MOTION +0.012s 0.00/ 1.04 ( +0.00/ +5.34)
    event11 POINTER_MOTION +0.022s 0.00/ 0.72 ( +0.00/ +3.20)
    event11 POINTER_MOTION +0.034s 0.00/ 1.20 ( +0.00/ +5.34)
    event11 GESTURE_HOLD_BEGIN +0.040s 1
    event11 POINTER_MOTION +0.045s -0.22/ 1.44 ( -1.00/ +6.41)
    event11 POINTER_MOTION +0.056s -0.90/ 1.67 ( -4.00/ +7.48)
    event11 GESTURE_HOLD_END +0.056s 1 cancelled
    event11 POINTER_MOTION +0.067s -1.79/ 2.39 ( -8.00/+10.68)
    event11 POINTER_MOTION +0.079s -2.69/ 2.39 (-12.00/+10.68)
    event11 POINTER_MOTION +0.090s -3.36/ 2.87 (-15.00/+12.82)
    event11 POINTER_MOTION +0.101s -6.49/ 5.74 (-29.00/+25.64)
    event11 POINTER_MOTION +0.112s -7.16/ 4.78 (-32.00/+21.36)
    event11 POINTER_MOTION +0.124s -8.51/ 5.26 (-38.00/+23.50)
    event11 POINTER_MOTION +0.135s -9.18/ 5.02 (-41.00/+22.43)
    event11 POINTER_MOTION +0.146s -6.72/ 2.87 (-30.00/+12.82)
    event11 POINTER_MOTION +0.157s -9.63/ 4.31 (-43.00/+19.23)
    event11 POINTER_MOTION +0.169s -9.40/ 3.35 (-42.00/+14.95)
    event11 POINTER_MOTION +0.180s -8.51/ 2.87 (-38.00/+12.82)
    event11 POINTER_MOTION +0.191s -4.70/ 0.72 (-21.00/ +3.20)
    ...

    Great: libinput printed a bunch of trackpad events.

  • So the problem became: libinput can read trackpad events, but libinput-gestures can’t.
    Next, I checked libinput-gestures --help to understand how it works.

    Terminal window
    $ libinput-gestures --help
    usage: libinput-gestures [-h] [-c CONFFILE] [-v] [-d] [-r] [-l] [--device DEVICE]
    Read gestures from libinput touchpad and action shell commands.
    options:
    -h, --help show this help message and exit
    -c CONFFILE, --conffile CONFFILE
    alternative configuration file
    -v, --verbose output diagnostic messages
    -d, --debug output diagnostic messages only, do not action gestures
    -r, --raw output raw libinput debug-event messages only, do not action gestures
    -l, --list just list out environment and configuration
    --device DEVICE explicit device name to use (or path if starts with /)

    Roughly:
    -d shows whether your configured gestures are being triggered
    -r prints the raw events received from libinput
    If you want to see whether libinput-gestures is receiving events at all, -r is the right choice.
    (I only understood this clearly after everything worked. While debugging I just tried every flag I could find.)

    libinput-gestures-setup stop to stop the current libinput-gestures service
    libinput-gestures -r --device /dev/input/event11 to check whether libinput-gestures can read events from event11

    Terminal window
    $ libinput-gestures -r --device /dev/input/event11
    libinput-gestures: session KDE+x11 on Linux-6.1.**
    Hash: **
    device /dev/input/event11
    libinput-gestures: device /dev/input/event11: Apple Inc. Magic Trackpad 2
    -event11 DEVICE_ADDED Apple Inc. Magic Trackpad 2 seat0 default group1 cap:pg size 162x115mm tap(dl off)
    event11 POINTER_MOTION +0.867s 2.59/ 0.00 (+13.00/ +0.00)
    event11 POINTER_MOTION +0.878s 3.58/ 0.24 (+16.00/ +1.07)
    event11 POINTER_MOTION +0.889s 3.81/ 0.24 (+17.00/ +1.07)
    event11 POINTER_MOTION +0.900s 3.81/ 0.72 (+17.00/ +3.20)
    event11 POINTER_MOTION +0.912s 3.58/ 0.48 (+16.00/ +2.14)
    event11 POINTER_MOTION +0.923s 3.13/ 0.48 (+14.00/ +2.14)
    event11 POINTER_MOTION +0.934s 2.69/ 0.00 (+12.00/ +0.00)
    event11 POINTER_MOTION +0.945s 2.24/ 0.24 (+10.00/ +1.07)
    event11 POINTER_MOTION +0.957s 1.79/ 0.00 ( +8.00/ +0.00)
    event11 POINTER_MOTION +0.968s 2.46/ 0.24 (+11.00/ +1.07)

    Just like libinput, libinput-gestures printed a bunch of events here. So event11 was fine.

    Next, I tried libinput-gestures -r --device /dev/input/event13 to see what the other “trackpad” device was.

    Terminal window
    $ libinput-gestures -r --device /dev/input/event13
    libinput-gestures: session KDE+x11 on Linux-6.1.**
    Hash: **
    device /dev/input/event13
    libinput-gestures: device /dev/input/event13: Apple Inc. Magic Trackpad 2
    -event13 DEVICE_ADDED Apple Inc. Magic Trackpad 2 seat0 default group1 cap:pg size 162x115mm tap(dl off)

    No matter what I did on the physical trackpad, this device produced zero events.

  • So the situation was: for some reason my computer had two trackpad devices, /dev/input/event11 and /dev/input/event13.
    The former worked; the latter was dead.

    I also checked libinput-gestures -d:

    Terminal window
    $ libinput-gestures -d
    libinput-gestures: session KDE+x11 on Linux-6.1.**
    Hash: **
    Gestures configured in ~/.config/libinput-gestures.conf:
    swipe up 4 xdotool key ctrl+super+Down
    swipe down 4 xdotool key ctrl+super+Up
    swipe left 4 xdotool key ctrl+super+Right
    swipe right 4 xdotool key ctrl+super+Left
    libinput-gestures: device /dev/input/event13: Apple Inc. Magic Trackpad 2

Case closed: libinput-gestures was defaulting to /dev/input/event13, the device that receives no events.

As for why the system saw two trackpads and the second one had no events:
My guess is that I tested it via cable first (Lightning), then later switched to Bluetooth.
For some reason, the old “wired” device node didn’t disappear and was left behind.
And libinput-gestures happened to pick that zombie device… sigh.


Make libinput-gestures Use the Right Device

So the fix is to tell libinput-gestures to use the device that actually receives events: /dev/input/event11.

I vaguely remembered seeing this in the system-level config file /etc/libinput-gestures.conf, so I opened it and read carefully.

###############################################################################
This application normally determines your touchpad device
automatically. Some users may have multiple touchpads but by default
we use only the first one found. However, you can choose to specify
the explicit device name to use. Run "libinput list-devices" to work
out the name of your device (from the "Device:" field). Then add a
device line specifying that name, e.g:
#
device DLL0665:01 06CB:76AD Touchpad
#
If the device name starts with a '/' then it is instead considered as
the explicit device path although since device paths can change
through reboots this is best to be a symlink. E.g. instead of specifying
/dev/input/event12, you should use the corresponding full path link
under /dev/input/by-path/ or /dev/input/by-id/.
#
You can choose to use ALL touchpad devices by setting the device name
to "all". E.g. Do this if you have multiple touchpads which you want
to use in parallel. This reduces performance slightly so only set this
if you have to.
#
device all

Turns out the author already explained it clearly: by default, libinput-gestures uses the first touchpad device it finds.
If you have multiple devices, you can explicitly specify which one to use.

Terminal window
device /dev/input/event11
device /dev/input/by-path/pci-0000:00:****-event-mouse
device /dev/input/by-id/usb-Apple_Inc._Magic_Trackpad_2_****-event-mouse

I tested it: all three approaches above can pin to a specific device.
But as the author notes, event11 is not stable across reboots, so it’s better to use a stable symlink under by-path or by-id.

There’s also a simple brute-force option:

Terminal window
device all

Use all touchpad devices (at the cost of a bit of performance).

After thinking about it, I decided to use device all.
Sometimes I need to plug the trackpad in to charge, or switch to wired mode when Bluetooth is flaky.
The system treats wired and Bluetooth as two devices. If I pin to the Bluetooth device via by-path/by-id, then the config breaks when I switch to wired.
So I went with the “no-brain” device all.


Change Trackpad Settings via xinput

Now that libinput-gestures was fixed, multi-finger gestures started working.
Next I wanted to set trackpad options like pointer speed and natural scrolling.

In theory these can be changed in the GUI settings.
But as mentioned earlier, after waking from sleep these settings sometimes reset to defaults and can’t be changed again via the GUI.

So I looked for a CLI way to configure the trackpad.
After some searching, xinput seemed the simplest.
Usage reference: ArchWiki - xinput

  • xinput list to find the device
  • xinput list-props device to see available properties
  • xinput set-prop device property values to set properties

And here I hit the same issue again: xinput also showed two trackpads.

Terminal window
$ xinput list
Virtual core pointer id=2 [master pointer (3)]
Virtual core XTEST pointer id=4 [slave pointer (2)]
Logi K855 Keyboard id=11 [slave pointer (2)]
Apple Inc. Magic Trackpad 2 id=9 [slave pointer (2)]
Apple Inc. Magic Trackpad 2 id=13 [slave pointer (2)]
Virtual core keyboard id=3 [master keyboard (2)]
Virtual core XTEST keyboard id=5 [slave keyboard (3)]
Power Button id=6 [slave keyboard (3)]
Power Button id=7 [slave keyboard (3)]
Sleep Button id=8 [slave keyboard (3)]
CSCTEK USB Audio and HID id=10 [slave keyboard (3)]
Logi K855 Keyboard id=12 [slave keyboard (3)]

I decided to do the same thing as libinput-gestures: since I didn’t know which one was the “real” device, just configure both.

I borrowed ideas from:
Stack Overflow - ‘xinput list’ shows same device twice & device IDs change: how to use ‘set-prop’ in a script?
Stack Overflow - How to make a program that finds id’s of xinput devices and sets xinput some settings

And stitched together a script that works:

Terminal window
# Setup Apple Magic Trackpad 2
# Get all trackpad device ids
ids=$(xinput --list | grep "Trackpad" | cut -d '=' -f 2 | cut -f 1)
for id in $ids
do
xinput set-prop $id "libinput Accel Speed" 0.5
xinput set-prop $id "libinput Natural Scrolling Enabled" 1
done
libinput-gestures-setup restart
systemctl --user restart libinput-gestures.service

I put this into .bashrc. Whenever the trackpad feels “off”, I can hit Ctrl+Alt+T to open a terminal and re-run it, and the trackpad becomes smooth again.

The last two lines are there because the libinput-gestures service sometimes dies and needs a restart.
You can restart with libinput-gestures-setup restart.
Or register libinput-gestures as a systemd user service and restart it via systemctl as in the script.
For systemd setup, see: Github - bulletmark/libinput-gestures - SYSTEMD USER SERVICE

Summary

So the root cause was: libinput saw two trackpad devices, and libinput-gestures defaulted to the dead one, so none of my configuration worked.
After a lot of searching, I found the fix was clearly written in the documentation. I just didn’t read it carefully at the beginning… and wasted a lot of time.
Lesson learned: read the docs properly.

Trackpad 2 Experience

Overall it’s pretty good. Over Bluetooth it drops around ~20% battery per week, maybe less (I didn’t measure precisely). Battery life is decent.
Bluetooth latency feels a bit higher and it’s less stable than wired: occasionally it stops responding and reconnects after a few seconds.
Other than that, I’m happy with it. Money well spent.

Tray Icons Disappearing

After using the trackpad for a while, I noticed that sometimes some tray icons randomly disappear, and the keyboard layout resets to the default US layout and can’t be changed back. I’m not sure if it’s related to the trackpad disconnecting/reconnecting.
I searched for a command to restart the tray and added an alias to .bashrc:

Terminal window
alias tray_restart='kded5 --replace > /dev/null 2>&1 &'

When the tray icons are gone, running tray_restart fixes it.