handy_keys/lib.rs
1//! Cross-platform global keyboard shortcuts library.
2//!
3//! `handy-keys` provides a simple way to register and listen for global keyboard
4//! shortcuts across macOS, Windows, and Linux.
5//!
6//! # Features
7//!
8//! - **Global hotkeys**: Register system-wide keyboard shortcuts that work even
9//! when your application is not focused
10//! - **Hotkey blocking**: Registered hotkeys are blocked from reaching other applications
11//! - **Modifier-only hotkeys**: Support for shortcuts like `Cmd+Shift` without a key
12//! - **String parsing**: Parse hotkeys from strings like `"Ctrl+Alt+Space"`
13//! - **Hotkey recording**: Low-level [`KeyboardListener`] for implementing
14//! "record a hotkey" UI flows
15//! - **Serde support**: All types implement `Serialize`/`Deserialize`
16//!
17//! # Quick Start
18//!
19//! ```no_run
20//! use handy_keys::{HotkeyManager, Hotkey, Modifiers, Key};
21//!
22//! fn main() -> handy_keys::Result<()> {
23//! let manager = HotkeyManager::new()?;
24//!
25//! // Register Cmd+Shift+K using the type-safe constructor
26//! let hotkey = Hotkey::new(Modifiers::CMD | Modifiers::SHIFT, Key::K)?;
27//! let id = manager.register(hotkey)?;
28//!
29//! // Or parse from a string (useful for UI/config input)
30//! let hotkey2: Hotkey = "Ctrl+Alt+Space".parse()?;
31//! let id2 = manager.register(hotkey2)?;
32//!
33//! println!("Registered hotkeys: {:?}, {:?}", id, id2);
34//!
35//! // Wait for hotkey events
36//! while let Ok(event) = manager.recv() {
37//! println!("Hotkey triggered: {:?}", event.id);
38//! }
39//!
40//! Ok(())
41//! }
42//! ```
43//!
44//! # Recording Hotkeys
45//!
46//! For implementing "press a key to set hotkey" UIs, use [`KeyboardListener`]:
47//!
48//! ```no_run
49//! use handy_keys::KeyboardListener;
50//!
51//! let listener = KeyboardListener::new()?;
52//!
53//! // Listen for key events
54//! while let Ok(event) = listener.recv() {
55//! if event.is_key_down {
56//! if let Ok(hotkey) = event.as_hotkey() {
57//! println!("User pressed: {}", hotkey);
58//! break;
59//! }
60//! }
61//! }
62//! # Ok::<(), handy_keys::Error>(())
63//! ```
64//!
65//! # Platform Notes
66//!
67//! ## macOS
68//!
69//! Requires accessibility permissions. Use [`check_accessibility`] to check if
70//! permissions are granted, and [`open_accessibility_settings`] to prompt the user:
71//!
72//! ```no_run
73//! # #[cfg(target_os = "macos")]
74//! # fn main() -> handy_keys::Result<()> {
75//! use handy_keys::{check_accessibility, open_accessibility_settings};
76//!
77//! if !check_accessibility() {
78//! open_accessibility_settings()?;
79//! // User needs to grant permission and restart
80//! }
81//! # Ok(())
82//! # }
83//! # #[cfg(not(target_os = "macos"))]
84//! # fn main() {}
85//! ```
86//!
87//! ## Windows
88//!
89//! Uses low-level keyboard hooks. No special permissions required.
90//!
91//! ## Linux
92//!
93//! Uses [rdev](https://crates.io/crates/rdev). On Wayland, hotkey blocking may not
94//! work due to compositor restrictions.
95
96mod error;
97mod listener;
98mod manager;
99mod platform;
100mod types;
101
102pub use error::{Error, Result};
103pub use listener::{BlockingHotkeys, KeyboardListener};
104pub use manager::HotkeyManager;
105pub use types::{Hotkey, HotkeyEvent, HotkeyId, HotkeyState, Key, KeyEvent, Modifiers};
106
107#[cfg(target_os = "macos")]
108pub use platform::macos::{check_accessibility, open_accessibility_settings};