Skhd.zig
Simple Hotkey Daemon for macOS, ported from skhd by koekeishiya
Install / Use
/learn @jackielii/Skhd.zigREADME
SKHD in Zig
Simple Hotkey Daemon for macOS, ported from skhd to Zig.
This implementation is fully compatible with the original skhd configuration format - your existing .skhdrc files will work without modification. Additionally, it includes new features like process groups and command definitions (.define) for cleaner configs, key forwarding/remapping, and improved error reporting.
Installation
Homebrew
The easiest way to install skhd.zig:
brew install jackielii/tap/skhd-zig
Pre-built Binaries
Download the latest release for your architecture:
skhd-arm64-macos.tar.gz- For Apple Silicon Macsskhd-x86_64-macos.tar.gz- For Intel Macs
Extract and install:
tar -xzf skhd-*.tar.gz
sudo cp skhd /usr/local/bin/
Development Builds from GitHub Actions
If you need builds with different optimization levels (Debug, ReleaseSafe, ReleaseFast, ReleaseSmall), you can download them directly from GitHub Actions:
- Go to the CI workflow in Actions tab. Filter by branch
main. - Click on the latest successful run
- Scroll down to the "Artifacts" section
- Download the build artifact for your desired optimization level:
skhd-Debug- Debug build with full debugging symbolsskhd-ReleaseSafe- Release build with safety checks and runtime safetyskhd-ReleaseFast- Optimized for performance (recommended for daily use)skhd-ReleaseSmall- Optimized for binary size
Build from Source
# Clone the repository
git clone https://github.com/jackielii/skhd.zig
cd skhd.zig
# Build in release mode
zig build -Doptimize=ReleaseFast
# Install (copy to /usr/local/bin)
sudo cp zig-out/bin/skhd /usr/local/bin/
Running as Service
After installation, run skhd as a service for automatic startup:
# Install and start the service
skhd --install-service
skhd --start-service
# Check if skhd is running properly
skhd --status
# Restart service (useful for restarting after giving accessibility permissions)
skhd --restart-service
# Stop service
skhd --stop-service
# Uninstall service
skhd --uninstall-service
The service will:
- Start automatically on login
- Create logs at
/tmp/skhd_$USER.log - Use your config from
~/.config/skhd/skhdrcor~/.skhdrc - Automatically reload on config changes
Features
Core Functionality
- Event capturing: Uses macOS Core Graphics Event Tap for system-wide keyboard event interception
- Hotkey mapping: Maps key combinations to shell commands with full modifier support
- Process-specific bindings: Different commands for different applications
- Key forwarding/remapping: Remap keys to other key combinations
- Modal system: Multi-level modal hotkey system with capture modes
- Configuration file: Compatible with original skhd configuration format
- Hot reloading: Automatic config reload on file changes
Additional Features (New in skhd.zig!)
- Process groups: Define named groups of applications for cleaner configs
- Command definitions: Define reusable commands with placeholders to reduce repetition
- Key Forwarding: Forward / remap key binding to another key binding
- Mode activation with command: Execute a command when switching modes (e.g.,
cmd - w ; window : echo "Window mode")
Command-Line Interface
--version/-v- Display version information--help- Show usage information-c/--config- Specify config file location-o/--observe- Observe mode (echo keycodes and modifiers)-V/--verbose- Debug output with detailed logging-k/--key- Synthesize keypress for testing-t/--text- Synthesize text input-r/--reload- Signal reload to running instance-h/--no-hotload- Disable hotloading-P/--profile- Profile event handling (Debug and ReleaseSafe builds only)
Service Management
--install-service- Install launchd service--uninstall-service- Remove launchd service--start-service- Start as service--restart-service- Restart service--stop-service- Stop service- PID file management (
/tmp/skhd_$USER.pid) - Service logging (
/tmp/skhd_$USER.log)
Advanced Features
- Blacklisting: Exclude applications from hotkey processing
- Shell customization: Use custom shell for command execution
- Left/right modifier distinction: Support for lcmd, rcmd, lalt, ralt, etc.
- Special key support: Function keys, media keys, arrow keys
- Passthrough mode: Execute command but still send keypress to application
- Config includes: Load additional config files with
.loaddirective - Comprehensive error reporting: Detailed error messages with line numbers
Build Commands
# Build the project (creates executable in zig-out/bin/)
zig build
# Build in release mode with optimizations
zig build -Doptimize=ReleaseFast
# Run the application
zig build run
# Run with arguments
zig build run -- -V -c ~/.config/skhd/skhdrc
# Run tests
zig build test
Configuration & Usage
Default Configuration Locations
skhd.zig looks for configuration files in the following order:
- Path specified with
-cflag ~/.config/skhd/skhdrc~/.skhdrc
The configuration syntax is fully compatible with the original skhd. See SYNTAX.md for the complete syntax reference and grammar.
Configuration Directives
# Use custom shell (skips interactive shell overhead)
.shell "/bin/dash"
# Blacklist applications (skip hotkey processing)
.blacklist [
"dota2"
"Microsoft Remote Desktop"
"VMware Fusion"
]
# Load additional config files
.load "~/.config/skhd/extra.skhdrc"
# Define process groups for reuse (New in skhd.zig!)
.define terminal_apps ["kitty", "wezterm", "terminal"]
.define native_apps ["kitty", "wezterm", "chrome", "whatsapp"]
.define browser_apps ["chrome", "safari", "firefox", "edge"]
# Define reusable commands with placeholders (New in skhd.zig!)
.define yabai_focus : yabai -m window --focus {{1}} || yabai -m display --focus {{1}}
.define yabai_swap : yabai -m window --swap {{1}} || (yabai -m window --display {{1}} && yabai -m display --focus {{1}})
.define toggle_app : open -a "{{1}}" || osascript -e 'tell app "{{1}}" to quit'
.define resize_window : yabai -m window --resize {{1}}:{{2}}:{{3}}
.define toggle_scratchpad : yabai -m window --toggle {{1}} || open -a "{{2}}"
Basic Hotkey Syntax
# Basic format: modifier - key : command
cmd - a : echo "Command+A pressed"
# Multiple modifiers
cmd + shift - t : open -a Terminal
# Different modifier combinations
ctrl - h : echo "Control+H"
alt - space : echo "Alt+Space"
shift - f1 : echo "Shift+F1"
Supported Modifiers
# Basic modifiers
cmd # Command key
ctrl # Control key
alt # Alt/Option key
shift # Shift key
fn # Function key
# Left/right specific modifiers
lcmd, rcmd # Left/right Command
lctrl, rctrl # Left/right Control
lalt, ralt # Left/right Alt
lshift, rshift # Left/right Shift
# Special modifier combinations
hyper # cmd + shift + alt + ctrl
meh # shift + alt + ctrl
Special Keys
# Navigation keys
cmd - left : echo "Left arrow"
cmd - right : echo "Right arrow"
cmd - up : echo "Up arrow"
cmd - down : echo "Down arrow"
# Special keys
cmd - space : echo "Space"
cmd - return : echo "Return/Enter"
cmd - tab : echo "Tab"
cmd - escape : echo "Escape"
cmd - delete : echo "Delete/Backspace"
cmd - home : echo "Home"
cmd - end : echo "End"
cmd - pageup : echo "Page Up"
cmd - pagedown : echo "Page Down"
# Function keys
cmd - f1 : echo "F1"
cmd - f12 : echo "F12"
# Media keys
sound_up : echo "Volume Up"
sound_down : echo "Volume Down"
mute : echo "Mute"
brightness_up : echo "Brightness Up"
brightness_down : echo "Brightness Down"
Process-Specific Bindings
# Different commands for different applications
cmd - n [
"terminal" : echo "New terminal window"
"safari" : echo "New safari window"
"finder" : echo "New finder window"
* : echo "New window in other apps"
]
Key Forwarding/Remapping
# Keyboard layout fixes
0xa | 0x32 # UK keyboard § to `
shift - 0xa | shift - 0x32 # shift - § to ~
# Function key navigation (for laptop keyboards)
fn - j | down
fn - k | up
fn - h | left
fn - l | right
# When you have cmd - number for yabai spaces,
# and you still want the cmd - number to work in applications
ctrl - 1 | cmd - 1
ctrl - 2 | cmd - 2
ctrl - 3 | cmd - 3
Passthrough Mode
# Execute command but still send keypress to application
cmd - p -> : echo "This runs but Cmd+P still goes to app"
Modal Workflow with Visual Indicators
# Window management mode with anybar visual indicator
# Install anybar: brew install --cask anybar
# Define window management mode for warp/stack operations
# Use anybar to indicate the mode: https://github.com/tonsky/AnyBar
:: winmode @ : echo -n "red" | nc -4u -w0 localhost 1738
:: default : echo -n "hollow" | nc -4u -w0 localhost 1738
# Enter window mode with meh + m (shift + alt + ctrl + m)
meh - w ; winmode
winmode < escape ; default
winmode < meh - w ; default
# Alternative: Enter window mode AND show notification (New in skhd.zig!)
# This executes the command when switching to the mode
# It allows for different commands to execute and switch to another mode
meh - w ; winmode : osascript -e 'display notification "Window mode active" with title "skhd"'
winmode < escape ; default : osascript -e 'display notification "Normal mode" with title "skhd"'
# Focus operations - basic hjkl for focus
winmode < h : yabai -m window --focus west || yabai -m display --focus west
winmode < j : yabai -m window --focus south || yabai -m display --focus south
winmode <
