Skip to main content

Crate handy_keys

Crate handy_keys 

Source
Expand description

Cross-platform global keyboard shortcuts library.

handy-keys provides a simple way to register and listen for global keyboard shortcuts across macOS, Windows, and Linux.

§Features

  • Global hotkeys: Register system-wide keyboard shortcuts that work even when your application is not focused
  • Hotkey blocking: Registered hotkeys are blocked from reaching other applications
  • Modifier-only hotkeys: Support for shortcuts like Cmd+Shift without a key
  • String parsing: Parse hotkeys from strings like "Ctrl+Alt+Space"
  • Hotkey recording: Low-level KeyboardListener for implementing “record a hotkey” UI flows
  • Serde support: All types implement Serialize/Deserialize

§Quick Start

use handy_keys::{HotkeyManager, Hotkey, Modifiers, Key};

fn main() -> handy_keys::Result<()> {
    let manager = HotkeyManager::new()?;

    // Register Cmd+Shift+K using the type-safe constructor
    let hotkey = Hotkey::new(Modifiers::CMD | Modifiers::SHIFT, Key::K)?;
    let id = manager.register(hotkey)?;

    // Or parse from a string (useful for UI/config input)
    let hotkey2: Hotkey = "Ctrl+Alt+Space".parse()?;
    let id2 = manager.register(hotkey2)?;

    println!("Registered hotkeys: {:?}, {:?}", id, id2);

    // Wait for hotkey events
    while let Ok(event) = manager.recv() {
        println!("Hotkey triggered: {:?}", event.id);
    }

    Ok(())
}

§Recording Hotkeys

For implementing “press a key to set hotkey” UIs, use KeyboardListener:

use handy_keys::KeyboardListener;

let listener = KeyboardListener::new()?;

// Listen for key events
while let Ok(event) = listener.recv() {
    if event.is_key_down {
        if let Ok(hotkey) = event.as_hotkey() {
            println!("User pressed: {}", hotkey);
            break;
        }
    }
}

§Platform Notes

§macOS

Requires accessibility permissions. Use [check_accessibility] to check if permissions are granted, and [open_accessibility_settings] to prompt the user:

use handy_keys::{check_accessibility, open_accessibility_settings};

if !check_accessibility() {
    open_accessibility_settings()?;
    // User needs to grant permission and restart
}

§Windows

Uses low-level keyboard hooks. No special permissions required.

§Linux

Reads evdev devices (/dev/input/event*) directly, which works the same on Wayland, X11, and the console. Requires read access to the device nodes — membership in the input group, or the udev rule below. Hotkey blocking grabs keyboards exclusively and re-injects non-blocked events through uinput, so it additionally requires write access to /dev/uinput; without it, the blocking constructors fail with an actionable error (the non-blocking listener is unaffected).

§Shipping to Linux users

When distributing an app, don’t ask users to join the input group — ship a udev uaccess rule instead. systemd-logind then grants access to the active seat user (whoever is physically logged in) through device ACLs: effective immediately with no logout, and unlike group membership it does not extend to SSH sessions. One rule file covers both listening and blocking:

# /usr/lib/udev/rules.d/70-yourapp-input.rules
# (uaccess rules must sort before 73-seat-late.rules: keep the number < 73)
KERNEL=="uinput", TAG+="uaccess"
SUBSYSTEM=="input", KERNEL=="event*", TAG+="uaccess"

Packages (deb, rpm, …) install this file and run udevadm control --reload && udevadm trigger in their post-install step, so users never perform any setup. Installer-less formats (AppImage) can write it on first run — e.g. via pkexec — when the constructors report missing access, then retry immediately: the ACL applies without a restart. Degrade gracefully where blocking is unavailable:

use handy_keys::HotkeyManager;

let manager = HotkeyManager::new_with_blocking() // blocks matched hotkeys
    .or_else(|_| HotkeyManager::new());          // read-only: detect but don't block
// If both fail, prompt the user to grant access, then retry.

Read access to /dev/input is inherently keyboard-read capability — the kernel offers nothing finer-grained. Sandboxes that hide /dev/input (e.g. Flatpak) cannot use this backend.

Structs§

Hotkey
A hotkey definition - either a key with modifiers, or modifiers only
HotkeyEvent
Event emitted when a hotkey is pressed or released
HotkeyId
A unique identifier for a registered hotkey
HotkeyManager
Platform-agnostic Hotkey Manager
KeyEvent
Event emitted during key recording
KeyboardListener
Platform-agnostic Keyboard Listener
Modifiers
Modifier keys for hotkey combinations

Enums§

Error
HotkeyState
The state of a hotkey (pressed or released)
Key
Keyboard keys and mouse buttons that can be used in hotkey combinations

Type Aliases§

BlockingHotkeys
Hotkeys that should be blocked when triggered
Result