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}