Deprecated: Function get_magic_quotes_gpc() is deprecated in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 99

Deprecated: The each() function is deprecated. This message will be suppressed on further calls in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 619

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1169

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176

Warning: Cannot modify header information - headers already sent by (output started at /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php:99) in /hermes/walnacweb04/walnacweb04ab/b2791/pow.jasaeld/htdocs/De1337/nothing/index.php on line 1176
8000 GitHub - jackielii/skhd.zig: Simple Hotkey Daemon for macOS, ported from skhd by koekeishiya
Nothing Special   »   [go: up one dir, main page]

Skip to content

jackielii/skhd.zig

Repository files navigation

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.

About

Simple Hotkey Daemon for macOS, ported from skhd by koekeishiya

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •  
0