Skip to content

jhickner/tapd

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

tapd

skhd for your trackpad. Define taps and swipes in particular quadrants of your trackpad and link them to hotkeys or shell scripts. Also define knocks, tap/swipe sequences, (think: up, up, down, down, left, right, left, right).

Example config:

# Two-finger tap in the top-right corner -> switch apps.
tap     fingers=2  zone=top-right             key=cmd+tab>return

# Three-finger tap in the bottom-right corner -> launch/open Ghostty
tap     fingers=3  zone=bottom-right          shell=/usr/bin/open -a Ghostty

# A short one-finger flick to the right, anywhere -> Spotlight.
swipe   fingers=1  dir=right                  key=cmd+space

# Only while Ghostty is frontmost: flick down -> tmux prefix, then l.
swipe   fingers=2  dir=down  app=Ghostty      key=ctrl+a>l

# A four-finger tap arms knock mode for ~0.5s, with a sound as feedback.
unlock  fingers=4  shell=/usr/bin/afplay /System/Library/Sounds/Tink.aiff

# Knocks — sequences of taps/swipes, only live in that window after the unlock.
knock   steps=1,2                             key=cmd+space
knock   steps=1@top-left,1@bottom-right       shell=/usr/bin/open -a Ghostty
knock   steps=1@top-left,1:down               shell=/usr/bin/pmset displaysleepnow

Layout (skhd/yabai-style)

Thing Location
Binary /usr/local/bin/tapd
Config ~/.config/tapd/tapd.conf
LaunchAgent ~/Library/LaunchAgents/com.jhickner.tapd.plist
Logs ~/Library/Logs/tapd.{out,err}.log

Build

make            # builds ./tapd and code-signs it (see First-time setup below)

Requires the Xcode command-line tools (clang). Builds a universal binary for Apple Silicon and Intel.

First-time setup

  1. Code-signing identity (once) — gives the binary a stable signature so the Accessibility grant survives rebuilds:

    ./sign/setup-signing.sh

    Follow its prompts; it may point you at Keychain Access to mark the tapd-dev cert Code Signing → Always Trust (the trust API can't be driven from the CLI).

  2. Install:

    make install                 # binary -> /usr/local/bin (uses sudo), config -> ~/.config
    # no-sudo alternative: make install PREFIX=~/.local
  3. Grant permissions in System Settings → Privacy & Security:

    • Accessibility → add /usr/local/bin/tapd and enable it. Required for key= actions (synthesizing keystrokes). Not needed for shell= actions.
    • Reading the trackpad needs no grant on macOS 14. If touches never register on a newer macOS, also add it under Input Monitoring.
  4. Run at login:

    /usr/local/bin/tapd --install-service    # writes the LaunchAgent plist
    /usr/local/bin/tapd --start-service       # loads + launches it now

Service commands

tapd --install-service     # write the LaunchAgent plist
tapd --start-service       # load and launch
tapd --stop-service        # unload
tapd --restart-service     # after replacing the binary (config reloads on its own)
tapd --uninstall-service   # stop and remove the plist

Running by hand / debugging

tapd ~/.config/tapd/tapd.conf   # foreground; Ctrl-C to quit
tapd --log                      # dump raw contacts (calibration)
tapd -v <config>                # log every recognized gesture, matched or not

Matched gestures and dispatched actions are logged to stderr (→ the err log under launchd). Unmatched gestures are silent unless -v is given, so normal scrolling doesn't flood the log.

Config

One trigger per line; see tapd.conf for the full annotated syntax. Summary:

tap    fingers=2  zone=top-right              key=cmd+tab
tap    fingers=3  zone=bottom-right           shell=/usr/bin/open -a Terminal
swipe  fingers=1  dir=right                   key=cmd+space
knock  steps=1,2                              key=cmd+space
knock  steps=1@top-left,1@bottom-right        shell=/usr/bin/open -a Terminal
  • zones (origin bottom-left, so "top" = upper edge): top-left top-right bottom-left bottom-right top bottom left right center any
  • taps use zone=
  • swipes use any of dir=<left|right|up|down> (a short directional flick), from=<zone>, and to=<zone> — all optional and combinable. A bare dir=right fires on a short flick anywhere; from=top-left to=bottom-right still works for a full corner-to-corner drag.
  • knocks are a sequence of steps (≥ 1) via steps=, comma-separated, each <fingers>[@<zone>][:<dir>]. Without :dir a step is a tap (1, 2@top-left); with it, a swipe (1:down, 1@top-left:down). E.g. steps=1,2, steps=1@top-left,1@bottom-right, or steps=1@top-left,1:down (tap top-left, then swipe down). Steps must occur within ~0.6s of each other.
  • actions: key=<combo> (mods cmd/shift/alt/ctrl/fn + a key), or a >-separated sequence of combos for leader-key pairs — e.g. key=ctrl+a>l presses Ctrl-A then L (a tmux prefix binding); or shell=<command> (runs to end of line via /bin/sh -c)
  • app gate (any trigger): add app=<name> to fire only when that app is frontmost, matched case-insensitively against the app's name, e.g. app=Ghostty. Find the exact name with tapd --frontapp (focus the app and read what it prints). A gated knock also won't cause tap-deferral outside its app.

By default a single tap fires immediately unless it could begin a defined knock, in which case it waits up to ~0.6s to see whether the rest of the knock follows.

Unlock (leader) gesture

To keep every tap instant, define an unlock gesture:

unlock  fingers=4  shell=/usr/bin/afplay /System/Library/Sounds/Tink.aiff

With an unlock set, taps never defer — knocks are matched only during a ~0.5s window after the unlock tap (like a leader key). The optional action runs on arm as feedback (e.g. a sound). fingers=N, optional zone=<zone>, and an optional key=/shell= action. At most one unlock line.

This pairs well with single-step knocks: knock steps=1:right ... does nothing on its own, but after the unlock it fires on one swipe right — a gesture that can't trigger accidentally because it only exists while armed.

Edits are picked up automatically — the daemon polls the config file and reloads within ~1s of a save (it logs config reloaded: N trigger(s)). No restart needed unless you replace the binary itself.

Notes / limits

  • macOS Tahoe (26.x): WindowServer may drop synthesized global shortcuts (Cmd+Tab, Cmd+Space) from a signed-but-not-Apple binary. shell= actions are unaffected and are the robust choice there.
  • MultitouchSupport is a private, undocumented framework — a future macOS could change or remove it. The whole private surface is isolated in src/MultitouchSupport.h + src/mt_input.c.
  • The keyboard-key input source (trapping physical keys via a CGEventTap) is not built yet; src/input_source.h marks the seam for it.

License

MIT — see LICENSE.

About

like skhd for your trackpad

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors