[← Back (Home)](/) # LinuxMagicForce [](https://github.com/Isolyth/LinuxMagicForce) June 11, 2026 project linux hardware 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. ## What It Does 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. - One-finger click emits left click, two-finger right, three-or-more middle. - Pressing harder triggers a force click — a second, deeper click with its own haptic — bound to a configurable button. - Double-click-drag and click-drag use separate thresholds so drags don't accidentally release or escalate into force clicks. - Optional scroll haptics add texture during two- and three-finger scrolling. - Every threshold, timing window, and haptic waveform byte is tunable via a TOML config. On exit the daemon restores the firmware's default click mode, so the trackpad keeps working normally without it. ## How It Works 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: - Pressure crossing 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. - Release haptics don't wait for full release — they fire when pressure drops a configured distance from its peak, or faster than a configured rate. That's what makes the "click" feel like it happens at your finger instead of lagging behind it. - A force click releases in two stages, mirroring the two-stage press: normal down → force down → force up → normal up (pressure 70) (pressure 165) (pressure ramps (full release) back down) - Separate re-arm thresholds let you rapid-fire normal clicks (re-arm at 65) or repeat force clicks without fully releasing the first stage (re-arm at 125). - Centroid movement past a threshold marks a click as a drag, which switches to a lower release pressure so held drags don't drop mid-movement. 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 Bytes 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: ByteDefault (down/up)EffectUsable range30x17 / 0x14Main strength knob — behaves linearly and predictablyfull 0x00–0xff60x06 / 0x00Sharper, clickier, louder character — gets aggressive quickly≤ 0x20110x06 / 0x00Deeper, 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](https://github.com/nexustar/linux-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](https://github.com/vitoplantamura/MagicTrackpad2ForWindows) covers the same hardware from the Windows side and is a useful cross-reference. ## Install 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 ## NixOS 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](https://github.com/Isolyth/LinuxMagicForce) 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. [LLM-readable markdown version of this page](/projects/linux-magic-force/llms.txt)