A userspace daemon that brings Magic Trackpad 2 force-touch clicks and haptics to Linux. The trackpad doesn't physically click — it fakes the feeling with a haptic engine, and on Linux that engine normally sits unused beyond a basic click. LinuxMagicForce drives it directly.
The daemon reads the Bluetooth Magic Trackpad 2 through hidraw, puts it in
host-click mode, and runs its own pressure state machine: it decides when a click happens,
fires the matching haptic reports so the pad clicks under your finger, and emits mouse button
events through /dev/uinput.
On exit the daemon restores the firmware's default click mode, so the trackpad keeps working normally without it.
The trackpad reports per-finger position and pressure over Bluetooth HID. The daemon tracks total pressure and the touch centroid, and runs every click decision through a pressure state machine:
70 starts a normal click; crossing 165 while
held escalates to a force click. A click within the double-click window uses a harder
force threshold (220) so double-click-drags don't accidentally force-click.normal down → force down → force up → normal up
(pressure 70) (pressure 165) (pressure ramps (full release)
back down)65)
or repeat force clicks without fully releasing the first stage (re-arm at 125).Those defaults come from measurement, not guesswork: with firmware clicking active, the
pad's own click-down lands around pressure 64–96 with a median near 69 — so the daemon's normal threshold of 70 makes a host-generated
click feel the same as a firmware one.
The haptic protocol is undocumented, so the interesting parts were found by probing. Two vendor feature reports flip the trackpad between firmware clicking and host control:
f2 21 01 # feature report: enable host-click mode f2 21 00 # feature report: restore firmware clicking
In host-click mode the firmware stops clicking on its own, and each haptic pulse is a
15-byte output report with report ID 0xf2, command 0x53. The
daemon starts from two known-working templates — one for click impact, one for release:
byte: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 down (click): f2 53 01 17 78 02 06 24 30 06 01 06 18 48 12 up (release): f2 53 01 14 78 02 00 24 30 06 01 00 18 48 12
A Python probe swept each byte of the down report across its range (and pairs of bytes in lockstep), one pulse per step, with the results logged to CSV and judged by feel. Of the fifteen bytes, three actually shape the click:
| Byte | Default (down/up) | Effect | Usable range |
|---|---|---|---|
3 | 0x17 / 0x14 | Main strength knob — behaves linearly and predictably | full 0x00–0xff |
6 | 0x06 / 0x00 | Sharper, clickier, louder character — gets aggressive quickly | ≤ 0x20 |
11 | 0x06 / 0x00 | Deeper, more vertical thump | ≤ 0x40 |
One quirk worth knowing: bytes 6 and 11 can fail to produce a clean click when nothing is touching the pad — the actuator behaves differently under load. All tuning has to be judged with a finger actually on the glass.
The config exposes these as packed 0xAABBCCDD params (BB → byte 3, CC → byte 6, DD → byte 11), a layout inherited from the
experimental hid-magicmouse kernel patches where the templates originate — there, the high byte is the pressure
threshold; here the state machine owns thresholds, so it's ignored. MagicTrackpad2ForWindows covers the same hardware from the Windows side and is a useful cross-reference.
Build from source with Cargo:
cargo build --release
Or with Nix:
nix build # binary at result/bin/force-touchd
Run it against the trackpad (root is required for hidraw and /dev/uinput access):
sudo target/release/force-touchd --config config/force-touch-linux.toml
To run it as a service, install the binary, config, and bundled systemd unit:
sudo install -Dm755 target/release/force-touchd /usr/local/bin/force-touchd sudo install -Dm644 config/force-touch-linux.toml /etc/force-touch-linux/config.toml sudo install -Dm644 systemd/force-touchd.service /etc/systemd/system/force-touchd.service sudo systemctl daemon-reload sudo systemctl enable --now force-touchd.service
The flake ships a NixOS module that sets up force-touchd.service with the config
embedded in the Nix store:
{
inputs.linuxMagicForce.url = "github:Isolyth/LinuxMagicForce";
outputs = { nixpkgs, linuxMagicForce, ... }: {
nixosConfigurations.host = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
linuxMagicForce.nixosModules.default
{
services.linuxMagicForce.enable = true;
}
];
};
};
} The module also accepts a local config file, inline config text, and extra daemon flags — the README covers those, along with the full config schema: click thresholds, release detection, drag haptics, ridge haptics, and the raw HID report bytes that shape how each click feels.