windows_hotkeys/lib.rs
1#[cfg(not(target_os = "windows"))]
2compile_error!("Only supported on windows");
3
4#[cfg(windows)]
5pub mod error;
6#[cfg(windows)]
7pub mod keys;
8
9#[cfg(windows)]
10pub mod singlethreaded;
11#[cfg(all(windows, feature = "threadsafe"))]
12pub mod threadsafe;
13
14#[cfg(all(windows, feature = "threadsafe"))]
15pub use threadsafe::HotkeyManager;
16
17#[cfg(all(windows, not(feature = "threadsafe")))]
18pub use singlethreaded::HotkeyManager;
19
20#[cfg(windows)]
21use winapi::shared::windef::HWND;
22#[cfg(windows)]
23use winapi::um::winuser::{GetAsyncKeyState, PostMessageW, WM_NULL};
24
25#[cfg(windows)]
26use crate::{error::HkError, keys::*};
27
28/// Identifier of a registered hotkey. This is returned when registering a hotkey and can be used
29/// to unregister it later.
30///
31#[cfg(windows)]
32#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
33pub struct HotkeyId(i32);
34
35/// HotkeyCallback contains the callback function and a list of extra_keys that need to be pressed
36/// together with the hotkey when executing the callback.
37///
38#[cfg(windows)]
39struct HotkeyCallback<T> {
40 /// Callback function to execute when the hotkey & extrakeys match
41 callback: Box<dyn Fn() -> T + 'static>,
42 /// List of additional VKeys that are required to be pressed to execute
43 /// the callback
44 extra_keys: Vec<VKey>,
45}
46
47#[cfg(windows)]
48pub trait HotkeyManagerImpl<T> {
49 fn new() -> Self;
50
51 /// Register a new hotkey with additional required extra keys.
52 ///
53 /// This will try to register the specified hotkey with windows, but not actively listen for it.
54 /// To listen for hotkeys in order to actually execute the callbacks, the `event_loop` function
55 /// must be called.
56 ///
57 /// # Arguments
58 ///
59 /// * `key` - The main hotkey. For example `VKey::Return` for the CTRL + ALT + ENTER
60 /// combination.
61 ///
62 /// * `key_modifiers` - The modifier keys that need to be combined with the main key. The
63 /// modifier keys are the keys that need to be pressed in addition to the main hotkey in order
64 /// for the hotkey event to fire. For example `&[ModKey::Ctrl, ModKey::Alt]` for the
65 /// CTRL + ALT + ENTER combination.
66 ///
67 /// * `extra_keys` - A list of additional VKeys that also need to be pressed for the hotkey
68 /// callback to be executed. This is enforced after the windows hotkey event is fired, but
69 /// before executing the callback. So these keys need to be pressed before the main hotkey.
70 ///
71 /// * `callback` - A callback function or closure that will be executed when the hotkey is
72 /// triggered. The return type for all callbacks in the same HotkeyManager must be the same.
73 ///
74 /// # Windows API Functions used
75 /// - <https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registerhotkey>
76 ///
77 fn register_extrakeys(
78 &mut self,
79 key: VKey,
80 key_modifiers: &[ModKey],
81 extra_keys: &[VKey],
82 callback: impl Fn() -> T + Send + 'static,
83 ) -> Result<HotkeyId, HkError>;
84
85 /// Same as `register_extrakeys` but without extra keys.
86 ///
87 /// # Windows API Functions used
88 /// - <https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-registerhotkey>
89 ///
90 fn register(
91 &mut self,
92 key: VKey,
93 key_modifiers: &[ModKey],
94 callback: impl Fn() -> T + Send + 'static,
95 ) -> Result<HotkeyId, HkError>;
96
97 /// Unregister a hotkey. This will prevent the hotkey from being triggered in the future.
98 ///
99 /// # Windows API Functions used
100 /// - <https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-unregisterhotkey>
101 ///
102 fn unregister(&mut self, id: HotkeyId) -> Result<(), HkError>;
103
104 /// Unregister all registered hotkeys. This will be called automatically when dropping the
105 /// HotkeyManager instance.
106 ///
107 /// # Windows API Functions used
108 /// - <https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-unregisterhotkey>
109 ///
110 fn unregister_all(&mut self) -> Result<(), HkError>;
111
112 /// Wait for a single a hotkey event and execute the callback if all keys match. This returns
113 /// the callback result if it was not interrupted. The function call will block until a hotkey
114 /// is triggered or it is interrupted.
115 ///
116 /// If the event is interrupted, `None` is returned, otherwise `Some` is returned with the
117 /// return value of the executed callback function.
118 ///
119 /// ## Windows API Functions used
120 /// - <https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getmessagew>
121 ///
122 fn handle_hotkey(&self) -> Option<T>;
123
124 /// Run the event loop, listening for hotkeys. This will run indefinitely until interrupted and
125 /// execute any hotkeys registered before.
126 ///
127 fn event_loop(&self);
128
129 /// Get an `InterruptHandle` for this `HotkeyManager` that can be used to interrupt the event
130 /// loop.
131 ///
132 fn interrupt_handle(&self) -> InterruptHandle;
133}
134
135/// The `InterruptHandle` can be used to interrupt the event loop of the originating `HotkeyManager`.
136/// This handle can be used from any thread and can be used multiple times.
137///
138/// # Note
139/// This handle will technically stay valid even after the `HotkeyManager` is dropped, but it will
140/// simply not do anything.
141///
142#[cfg(windows)]
143pub struct InterruptHandle(HWND);
144
145#[cfg(windows)]
146unsafe impl Sync for InterruptHandle {}
147
148#[cfg(windows)]
149unsafe impl Send for InterruptHandle {}
150
151#[cfg(windows)]
152impl InterruptHandle {
153 /// Interrupt the evet loop of the associated `HotkeyManager`.
154 ///
155 pub fn interrupt(&self) {
156 unsafe {
157 PostMessageW(self.0, WM_NULL, 0, 0);
158 }
159 }
160}
161
162/// Get the global keystate for a given Virtual Key.
163///
164/// Return true if the key is pressed, false otherwise.
165///
166/// ## Windows API Functions used
167/// - <https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getasynckeystate>
168///
169#[cfg(windows)]
170pub fn get_global_keystate(vk: VKey) -> bool {
171 // Most significant bit represents key state (1 => pressed, 0 => not pressed)
172 let key_state = unsafe { GetAsyncKeyState(vk.to_vk_code()) };
173 // Get most significant bit only
174 let key_state = key_state as u32 >> 31;
175
176 key_state == 1
177}