A powerful, cross-platform keyboard remapping daemon ported from keyd, written in Rust.
⭐ If you find this project useful, please star it on GitHub!
Features • Prerequisites • Installation • Configuration • Usage • Platform Specifics • Acknowledgments
keydo captures keyboard input at a low level to perform complex stateful transformations, such as multi-purpose keys (e.g., Caps Lock as Escape when tapped, Control when held), custom layers, and shortcuts.
Unlike simple key-swappers, keydo runs as a system daemon and interfaces directly with input systems across Linux, macOS, and Windows.
Note
What does keydo mean?
- keyd oxidised: A tribute to its roots in
keyd, reimagined in Rust. - key do: A direct command, ordering your keys to perform exactly as you wish.
- The Way of the Key: Inspired by the Japanese dō (道), signifying the path or discipline of mastering your input.
- 🎯 Layer Support – Create custom keyboard layers triggered by any key.
- ⚡ Overloads – Assign different behaviors to a key when tapped vs. held.
- 🤝 Chords – Trigger actions by pressing multiple keys simultaneously.
- 📜 Macros – Execute complex sequences of keys and text.
- 🔌 IPC Protocol – Interact with the running daemon to reload configurations, inject input, or monitor state.
- 💻 Cross-Platform Backends:
- Linux: Integrates natively with
uinputand/dev/input. - macOS: Uses
CGEventTapfor capture andCGEventPostfor injection (no kernel extensions needed). - Windows: Uses a
WH_KEYBOARD_LLhook for capture andSendInputfor injection (no drivers needed).
- Linux: Integrates natively with
- OS Support: Linux, macOS 13.0 or later, or Windows 10/11.
- Rust Toolchain: Rust 2024 Edition.
- Windows: Build requires the MSVC toolchain (default) and Visual Studio Build Tools 2019 (or later) with the "Desktop development with C++" workload installed.
- Permissions:
- macOS: Accessibility permissions are required to capture and inject keys.
- Windows: Requires an elevated terminal/daemon to remap input inside admin/elevated windows.
- Linux: Root access is required to access input devices and the system IPC socket.
Compile and install the binary using Cargo:
cargo install --path .Ensure that ~/.cargo/bin (Linux/macOS) or %USERPROFILE%\.cargo\bin (Windows) is added to your system PATH.
Configure keydo to run automatically as a system or user service:
- Copy the binary to your system path:
sudo cp ~/.cargo/bin/keydo /usr/local/bin/ - Automatically register and start the daemon (supports
systemdandrunit):sudo /usr/local/bin/keydo install
- Add your user to the
keydogroup to use the CLI without root (requires logging out and back in to apply):sudo usermod -aG keydo $USER
Register the daemon as a user-level launchd service:
keydo installImportant
You must grant keydo Accessibility permissions. Go to System Settings → Privacy & Security → Accessibility and add the keydo binary (~/.cargo/bin/keydo).
Register the daemon to start automatically at logon:
keydo installkeydo uses the same configuration language as keyd. By default, it reads configuration files from:
- Linux:
/etc/keyd/(system-wide configuration files only) - macOS:
~/.config/keydo/(user-local configurations) - Windows:
%APPDATA%\keydo\(user-local) orC:\ProgramData\keyd\(system-wide)
Tip
Refer to the official keyd configuration documentation for a full reference of the syntax.
Create a configuration file (e.g., default.conf) in your platform's configuration directory:
[ids]
*
[main]
# Maps capslock to escape when tapped, and the 'nav' layer when held
capslock = overload(nav, esc)
[nav]
h = left
j = down
k = up
l = rightStart the daemon manually or use the CLI to interact with a running instance.
# Start the daemon in the foreground
keydo daemon
# Run the daemon with a specific configuration file
keydo daemon --config path/to/config.conf
# Check configuration files for syntax errors
keydo check path/to/config.conf
# Reload configurations without restarting the daemon
keydo reload# Monitor key events in real-time
keydo monitor
# Stream live layer state changes
keydo listen
# List all valid key names for configuration files
keydo list-keys# Inject raw text
keydo input "Hello, World!"
# Execute a macro sequence (e.g., Control+C, then Control+V)
keydo do "C-c C-v"
# Bind a key temporarily
keydo bind "main.j=down"When running as a service, the keydo daemon runs as root to interface with /dev/input and /dev/uinput. The IPC socket /run/keydo/socket is owned by the keydo system group with 0660 permissions.
If setting permissions manually without the installer:
- Create the socket directory with setgid group permissions:
sudo mkdir -p /run/keydo sudo groupadd -f keydo sudo chown root:keydo /run/keydo sudo chmod 2750 /run/keydo
- Add your user to the group:
sudo usermod -aG keydo $USER - Log out and log back in to apply the group changes.
On Windows, a system-wide low-level keyboard hook (WH_KEYBOARD_LL) captures events and SendInput injects them. Input is translated by scancode, making remappings layout-independent. IPC uses the named pipe \\.\pipe\keydo.
Warning
Windows Limitations & Safety Hooks
- Elevated Windows: Hook input is not captured inside admin/elevated programs unless the daemon itself runs in an elevated command prompt.
- Anti-Cheat & Games: Some games/anti-cheat systems block or flag
SendInput-injected events. - Console Window: The daemon currently runs in a visible console window when started.
- Unicode Macros: Unicode composition sequences are currently macOS-only.
- No Device Matching: All keyboards appear as a single device; per-device matching via
[ids]is not supported. - Emergency Panic Sequence: If input locks up or remapping stops under heavy load, press and hold Backspace + Enter + Escape to instantly terminate the daemon and restore default keyboard behavior.
This project is a port of keyd created by Raheman Vaiya. We are incredibly grateful for his work on the design and configuration syntax.