SKHD.ZIG(1)

NAME

skhd.zigSimple Hotkey Daemon for macOS, ported from skhd by koekeishiya

SYNOPSIS

$brew install jackielii/tap/skhd-zig

INFO

481 stars
12 forks
0 views

DESCRIPTION

Simple Hotkey Daemon for macOS, ported from skhd by koekeishiya

README

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.

📋 View Changelog

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 Macs
  • skhd-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:

  1. Go to the CI workflow in Actions tab. Filter by branch main.
  2. Click on the latest successful run
  3. Scroll down to the "Artifacts" section
  4. Download the build artifact for your desired optimization level:
    • skhd-Debug - Debug build with full debugging symbols
    • skhd-ReleaseSafe - Release build with safety checks and runtime safety
    • skhd-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/skhdrc or ~/.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 .load directive
  • 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:

  1. Path specified with -c flag
  2. ~/.config/skhd/skhdrc
  3. ~/.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 < k : yabai -m window --focus north || yabai -m display --focus north winmode < l : yabai -m window --focus east || yabai -m display --focus east

Move operations - shift + hjkl for moving

winmode < shift - h : yabai -m window --move rel:-80:0 winmode < shift - j : yabai -m window --move rel:0:80 winmode < shift - k : yabai -m window --move rel:0:-80 winmode < shift - l : yabai -m window --move rel:80:0

Warp operations - alt + shift + hjkl for warping

winmode < alt + shift - h : yabai -m window --warp west winmode < alt + shift - j : yabai -m window --warp south winmode < alt + shift - k : yabai -m window --warp north winmode < alt + shift - l : yabai -m window --warp east

Stack operations - ctrl + shift + hjkl for stacking

winmode < ctrl + shift - h : yabai -m window --stack west winmode < ctrl + shift - j : yabai -m window --stack south winmode < ctrl + shift - k : yabai -m window --stack north winmode < ctrl + shift - l : yabai -m window --stack east

Stack management shortcuts

winmode < s : yabai -m window --insert stack # Toggle stack mode winmode < u : yabai -m window --toggle float; yabai -m window --toggle float # Unstack window winmode < n : yabai -m window --focus stack.next # Navigate stack next winmode < p : yabai -m window --focus stack.prev # Navigate stack prev

Resize submode

winmode < r ; resize :: resize @ : echo -n "orange" | nc -4u -w0 localhost 1738 resize < h : yabai -m window --resize left:-20:0 resize < j : yabai -m window --resize bottom:0:20 resize < k : yabai -m window --resize top:0:-20 resize < l : yabai -m window --resize right:20:0 resize < escape ; winmode

Window Management Example

# Focus windows using command definitions (New in skhd.zig!)
cmd - h : @yabai_focus("west")
cmd - j : @yabai_focus("south")
cmd - k : @yabai_focus("north")
cmd - l : @yabai_focus("east")

Move/swap windows using command definitions

cmd + shift - h : @yabai_swap("west") cmd + shift - j : @yabai_swap("south") cmd + shift - k : @yabai_swap("north") cmd + shift - l : @yabai_swap("east")

Resize windows using command definitions

cmd + ctrl - h : @resize_window("left", "-20", "0") cmd + ctrl - l : @resize_window("right", "20", "0")

Switch spaces

cmd - 1 : yabai -m space --focus 1 cmd - 2 : yabai -m space --focus 2

Application Launching Example

# Quick app launching (traditional way)
alt - return : open -a Terminal
alt - b : open -a Safari

Toggle apps using command definitions (New in skhd.zig!)

alt - f : @toggle_app("Finder") alt - c : @toggle_app("Visual Studio Code")

Scratchpad apps with yabai (New in skhd.zig!)

In yabairc: yabai -m rule --add app="^YouTube Music$" scratchpad=music grid=11:11:1:1:9:9

alt - m : @toggle_scratchpad("music", "YouTube Music") alt - n : @toggle_scratchpad("notes", "Notes")

Text Editing Enhancements Example

# Linux-style word navigation and deletion
ctrl - backspace [
    @native_apps ~         # Terminal apps handle natively
    *            | alt - backspace  # Other apps: delete word
]

ctrl - left [ @native_apps ~ # Terminal apps handle natively * | alt - left # Other apps: move word left ]

ctrl - right [ @native_apps ~ # Terminal apps handle natively * | alt - right # Other apps: move word right ]

Home/End key behavior (with shift for selection)

home [ @native_apps ~ # Terminal apps handle natively * | cmd - left # Other apps: line start ]

shift - home [ @native_apps ~ # Terminal apps handle natively * | cmd + shift - left # Other apps: select to line start ]

Ctrl+Home/End for document navigation

ctrl - home [ @native_apps ~ # Terminal apps handle natively * | cmd - up # Other apps: document start ]

ctrl - end [ @native_apps ~ # Terminal apps handle natively * | cmd - down # Other apps: document end ]

Testing and Debugging

Debug vs Release Builds

Important: The logging and profiling behavior differs between build modes:

  • ReleaseFast builds (installed via Homebrew or built with -Doptimize=ReleaseFast):
    • Only show errors and warnings, even with -V/--verbose flag
    • Profiling (-P/--profile) is disabled - all tracing code is compiled out for maximum performance
  • ReleaseSafe builds (built with -Doptimize=ReleaseSafe):
    • Show errors, warnings, and info messages with -V/--verbose flag
    • Profiling (-P/--profile) is available for production debugging
  • Debug builds (default zig build):
    • Show all log levels including debug messages with -V/--verbose flag
    • Profiling (-P/--profile) is available with full trace details

However, command output will be shown if verbose flag is specified in release builds.

This is a trade-off between convenience and performance:

  • Performance mode (default): Command output is discarded for faster execution
  • Verbose mode (-V): Command output is preserved, which may add slight overhead but helps with trouble shooting

To debug hotkey events and see detailed logging:

# Verbose logging for troubleshooting config issues
# Note: In release builds, verbose mode only shows errors and warnings.
# To see debug/info logs, use a debug build:
zig build run -- -V

Performance: The event loop is allocation-free in release builds, ensuring consistent low-latency hotkey processing.

Testing Commands

# Test key combinations and hex code (observe mode)
skhd -o

Profile event handling (show after CTRL+C)

Note: Profiling works in Debug and ReleaseSafe builds only

zig build && ./zig-out/bin/skhd -P

or for production debugging:

zig build -Doptimize=ReleaseSafe && ./zig-out/bin/skhd -P

Test specific keypress

skhd -k "cmd + shift - t"

Test text synthesis

skhd -t "hello world"

Reload config of running instance

skhd -r

Debug memory allocations with real-time tracking

zig build alloc -- -V

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Run tests: zig build test
  5. Submit a pull request

License

This project maintains compatibility with the original skhd license.

SEE ALSO

clihub3/4/2026SKHD.ZIG(1)