1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
//! 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
//!
//! ```no_run
//! 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`]:
//!
//! ```no_run
//! 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;
//! }
//! }
//! }
//! # Ok::<(), handy_keys::Error>(())
//! ```
//!
//! # Platform Notes
//!
//! ## macOS
//!
//! Requires accessibility permissions. Use [`check_accessibility`] to check if
//! permissions are granted, and [`open_accessibility_settings`] to prompt the user:
//!
//! ```no_run
//! # #[cfg(target_os = "macos")]
//! # fn main() -> handy_keys::Result<()> {
//! use handy_keys::{check_accessibility, open_accessibility_settings};
//!
//! if !check_accessibility() {
//! open_accessibility_settings()?;
//! // User needs to grant permission and restart
//! }
//! # Ok(())
//! # }
//! # #[cfg(not(target_os = "macos"))]
//! # fn main() {}
//! ```
//!
//! ## 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:
//!
//! ```text
//! # /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:
//!
//! ```no_run
//! 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.
//! # drop(manager);
//! ```
//!
//! 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.
pub use ;
pub use ;
pub use HotkeyManager;
pub use ;
pub use ;