A macOS CLI daemon that translates trackpad gestures into keyboard + scroll wheel events per application, designed to make modern trackpad gestures compatible with legacy X11-forwarded software (XQuartz, remote GUI apps over SSH, etc.).
Only explicitly configured apps are affected. All other apps pass through untouched.
- Per-app mapping — each application has its own gesture→action table; unconfigured apps are never touched
- Interactive config CLI — browse running/recent apps, pick gestures, assign actions, auto-save
- X11/XQuartz compatible — translates pinch/swipe/rotate into Ctrl+scroll, Alt+arrow, PageUp/Down, etc.
- Debug mode —
--list-gesturesshows raw gesture data and Bundle IDs without injecting anything - No Xcode required — built entirely with Swift Package Manager
| Gesture | Description |
|---|---|
pinch_in |
Two-finger pinch (zoom out) |
pinch_out |
Two-finger spread (zoom in) |
swipe_left |
Two-finger swipe left |
swipe_right |
Two-finger swipe right |
rotate_cw |
Two-finger clockwise rotation |
rotate_ccw |
Two-finger counter-clockwise rotation |
smart_magnify |
Two-finger double-tap (smart zoom) |
| Action key | Injects |
|---|---|
ctrl_scroll_up |
Ctrl + Scroll Up (zoom in) |
ctrl_scroll_down |
Ctrl + Scroll Down (zoom out) |
alt_left |
Alt + ← (back / prev page) |
alt_right |
Alt + → (forward / next page) |
page_up |
PageUp |
page_down |
PageDown |
left_bracket |
[ key |
right_bracket |
] key |
ctrl_zero |
Ctrl + 0 (reset zoom) |
ctrl_equal |
Ctrl + = (zoom in) |
ctrl_minus |
Ctrl + - (zoom out) |
none |
Disable this gesture |
- macOS 13 Ventura or later
- Xcode Command Line Tools (
xcode-select --install) - Accessibility permission (System Settings → Privacy & Security → Accessibility)
# Clone
git clone https://github.com/CyrusZhang23/TrackpadBridge.git
cd TrackpadBridge
# Build (release)
swift build -c release
# Install to /usr/local/bin
sudo cp .build/release/TrackpadBridge /usr/local/bin/trackpad-bridgeOn first run, macOS will prompt you to grant Accessibility permission. Grant it, then re-run.
trackpad-bridge config
Launches an interactive menu:
════════════════════════════════════════════════════════════
TrackpadBridge 应用手势配置
────────────────────────────────────────────────────────────
应用列表(序号选择 · q 退出)
1. XQuartz org.xquartz.X11 [✓已配置·运行中]
2. iTerm2 com.googlecode.iterm2 [运行中]
3. Terminal com.apple.Terminal [运行中]
选择应用 > 2
── 🤏 捏合缩小
1. 🚫 禁用此手势
2. ⊕ Ctrl + 滚轮↑ (放大)
3. ⊖ Ctrl + 滚轮↓ (缩小) ← 选这个
...
Changes are saved to ~/.trackpad-bridge.yml immediately.
If you don't know an app's Bundle ID, run:
trackpad-bridge --list-gesturesSwitch to the target app and perform gestures. Output:
🤏 Pinch delta=-0.0431 phase=changed app=XQuartz (org.xquartz.X11)
👆 Swipe dx=-0.48 dy=0.01 phase=ended app=XQuartz (org.xquartz.X11)
Copy the Bundle ID into trackpad-bridge config.
trackpad-bridgeRuns in the foreground. Use a process manager or Login Items to run at startup (see below).
trackpad-bridge --dry-runLocated at ~/.trackpad-bridge.yml (auto-created on first run).
# Only apps listed here are translated; everything else passes through.
apps:
"org.xquartz.X11":
pinch_in: ctrl_scroll_down
pinch_out: ctrl_scroll_up
swipe_left: alt_left
swipe_right: alt_right
swipe_up: page_up
swipe_down: page_down
rotate_cw: right_bracket
rotate_ccw: left_bracket
smart_magnify: ctrl_zero
"com.apple.Terminal":
pinch_in: ctrl_minus
pinch_out: ctrl_equal
swipe_up: page_up
swipe_down: page_downYou can also edit this file directly — it will be picked up on the next daemon start.
System Settings → General → Login Items → add trackpad-bridge.
cat > ~/Library/LaunchAgents/com.cyruszhang.trackpad-bridge.plist << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key> <string>com.cyruszhang.trackpad-bridge</string>
<key>ProgramArguments</key> <array><string>/usr/local/bin/trackpad-bridge</string></array>
<key>RunAtLoad</key> <true/>
<key>KeepAlive</key> <true/>
<key>StandardOutPath</key> <string>/tmp/trackpad-bridge.log</string>
<key>StandardErrorPath</key> <string>/tmp/trackpad-bridge.err</string>
</dict>
</plist>
EOF
launchctl load ~/Library/LaunchAgents/com.cyruszhang.trackpad-bridge.plistmacOS Trackpad
│
▼
CGEventTap ←── TrackpadBridge (this daemon)
│
├── App not in config? ──► pass through unchanged
│
└── App in config? ──► GestureState (accumulate delta)
│
▼
Translator (gesture → KeyCombo)
│
▼
CGEvent inject (keyboard + scroll)
│
▼
Target Application
(XQuartz / X11 / etc.)
The daemon intercepts raw CGEvent gesture events via a session-level event tap. Gestures are accumulated in stateful accumulators (to handle the began/changed/ended phase cycle), then translated and re-injected as standard keyboard+scroll events that X11 programs understand natively.
Sources/TrackpadBridge/
├── main.swift CLI entry point, argument parsing, subcommand routing
├── BridgeConfig.swift Types: KeyCombo, ActionPreset, GestureSlot,
│ AppGestureMap, MultiAppConfig (load/save)
├── AppWatcher.swift Frontmost-app tracker + recent-app persistence
├── GestureState.swift Stateful accumulators (pinch / rotate / swipe)
├── EventBridge.swift CGEventTap core: intercept → translate → inject
├── Injector.swift CGEvent synthesis and posting
└── ConfigCLI.swift Interactive configuration menu
MIT — see LICENSE.