win_hotkey/
single_thread.rs1#[cfg(not(target_os = "windows"))]
2compile_error!("Only supported on windows");
3
4use std::collections::HashMap;
5use std::marker::PhantomData;
6
7use windows_sys::core::PCSTR;
8use windows_sys::Win32::Foundation::HWND;
9use windows_sys::Win32::System::LibraryLoader::GetModuleHandleA;
10use windows_sys::Win32::UI::Input::KeyboardAndMouse::RegisterHotKey;
11use windows_sys::Win32::UI::Input::KeyboardAndMouse::UnregisterHotKey;
12use windows_sys::Win32::UI::WindowsAndMessaging::CreateWindowExA;
13use windows_sys::Win32::UI::WindowsAndMessaging::DestroyWindow;
14use windows_sys::Win32::UI::WindowsAndMessaging::GetMessageW;
15use windows_sys::Win32::UI::WindowsAndMessaging::HWND_MESSAGE;
16use windows_sys::Win32::UI::WindowsAndMessaging::MSG;
17use windows_sys::Win32::UI::WindowsAndMessaging::WM_HOTKEY;
18use windows_sys::Win32::UI::WindowsAndMessaging::WM_NULL;
19use windows_sys::Win32::UI::WindowsAndMessaging::WS_DISABLED;
20use windows_sys::Win32::UI::WindowsAndMessaging::WS_EX_NOACTIVATE;
21
22use crate::error::HotkeyError;
23use crate::get_global_keystate;
24use crate::keys::*;
25use crate::HotkeyCallback;
26use crate::HotkeyId;
27use crate::HotkeyManagerImpl;
28use crate::InterruptHandle;
29
30#[derive(Debug, Clone)]
31struct DropHWND(HWND);
32
33unsafe impl Send for DropHWND {}
34unsafe impl Sync for DropHWND {}
35
36impl Drop for DropHWND {
37 fn drop(&mut self) {
38 if !self.0.is_null() {
39 let _ = unsafe { DestroyWindow(self.0) };
40 }
41 }
42}
43
44#[derive(Debug)]
45pub struct HotkeyManager<T> {
46 hwnd: DropHWND,
47 id: u16,
48 handlers: HashMap<HotkeyId, HotkeyCallback<T>>,
49 no_repeat: bool,
50 _unimpl_send_sync: PhantomData<*const u8>,
51}
52
53unsafe impl<T> Send for HotkeyManager<T> {}
54unsafe impl<T> Sync for HotkeyManager<T> {}
55
56impl<T> Default for HotkeyManager<T> {
57 fn default() -> Self {
58 Self::new()
59 }
60}
61
62impl<T> HotkeyManager<T> {
63 pub fn set_no_repeat(&mut self, no_repeat: bool) {
73 self.no_repeat = no_repeat;
74 }
75}
76
77impl<T> HotkeyManagerImpl<T> for HotkeyManager<T> {
78 fn new() -> HotkeyManager<T> {
79 let hwnd = create_hidden_window().unwrap_or(DropHWND(std::ptr::null_mut()));
80 HotkeyManager {
81 hwnd,
82 id: 0,
83 handlers: HashMap::new(),
84 no_repeat: true,
85 _unimpl_send_sync: PhantomData,
86 }
87 }
88
89 fn register_extrakeys(
90 &mut self,
91 virtual_key: VirtualKey,
92 modifiers_key: Option<&[ModifiersKey]>,
93 extra_keys: Option<&[VirtualKey]>,
94 callback: Option<impl Fn() -> T + Send + 'static>,
95 ) -> Result<HotkeyId, HotkeyError> {
96 let register_id = HotkeyId(self.id);
97 self.id += 1;
98
99 let mut modifiers = ModifiersKey::combine(modifiers_key);
100 if self.no_repeat {
101 modifiers |= ModifiersKey::NoRepeat.to_mod_code();
102 }
103
104 let reg_ok = unsafe {
105 RegisterHotKey(
106 self.hwnd.0,
107 register_id.0 as i32,
108 modifiers,
109 virtual_key.to_vk_code() as u32,
110 )
111 };
112
113 if reg_ok == 0 {
114 Err(HotkeyError::RegistrationFailed)
115 } else {
116 let callback = callback.map(|cb| Box::new(cb) as Box<dyn Fn() -> T + 'static>);
118 self.handlers.insert(
119 register_id,
120 HotkeyCallback {
121 callback,
122 extra_keys: extra_keys.map(|keys| keys.to_vec()),
123 },
124 );
125
126 Ok(register_id)
127 }
128 }
129
130 fn register(
131 &mut self,
132 virtual_key: VirtualKey,
133 modifiers_key: Option<&[ModifiersKey]>,
134 callback: Option<impl Fn() -> T + Send + 'static>,
135 ) -> Result<HotkeyId, HotkeyError> {
136 self.register_extrakeys(virtual_key, modifiers_key, None, callback)
137 }
138
139 fn unregister(&mut self, id: HotkeyId) -> Result<(), HotkeyError> {
140 let ok = unsafe { UnregisterHotKey(self.hwnd.0, id.0 as i32) };
141
142 match ok {
143 0 => Err(HotkeyError::UnregistrationFailed),
144 _ => {
145 self.handlers.remove(&id);
146 Ok(())
147 }
148 }
149 }
150
151 fn unregister_all(&mut self) -> Result<(), HotkeyError> {
152 let ids: Vec<_> = self.handlers.keys().copied().collect();
153 for id in ids {
154 self.unregister(id)?;
155 }
156
157 Ok(())
158 }
159
160 fn handle_hotkey(&self) -> Option<T> {
161 loop {
162 let mut msg = std::mem::MaybeUninit::<MSG>::uninit();
163
164 let ok = unsafe { GetMessageW(msg.as_mut_ptr(), self.hwnd.0, WM_NULL, WM_HOTKEY) };
167
168 if ok != 0 {
169 let msg = unsafe { msg.assume_init() };
170
171 if WM_HOTKEY == msg.message {
172 let hk_id = HotkeyId(msg.wParam as u16);
173
174 if let Some(handler) = self.handlers.get(&hk_id) {
176 match &handler.extra_keys {
177 Some(keys) => {
178 if !keys.iter().any(|vk| !get_global_keystate(*vk)) {
179 if let Some(cb) = &handler.callback {
180 return Some(cb());
181 }
182 }
183 }
184 None => {
185 if let Some(cb) = &handler.callback {
186 return Some(cb());
187 }
188 }
189 }
190 }
191 } else if WM_NULL == msg.message {
192 return None;
193 }
194 }
195 }
196 }
197
198 fn event_loop(&self) {
199 while self.handle_hotkey().is_some() {}
200 }
201
202 fn interrupt_handle(&self) -> InterruptHandle {
203 InterruptHandle(self.hwnd.0)
204 }
205}
206
207impl<T> Drop for HotkeyManager<T> {
208 fn drop(&mut self) {
209 let _ = self.unregister_all();
210 }
211}
212
213fn create_hidden_window() -> Result<DropHWND, ()> {
216 let hwnd = unsafe {
217 let hinstance = GetModuleHandleA(std::ptr::null_mut());
219 let lpwindowname = c"".as_ptr() as PCSTR;
220 let lpclassname = c"Static".as_ptr() as PCSTR;
221
222 CreateWindowExA(
223 WS_EX_NOACTIVATE,
224 lpclassname,
227 lpwindowname,
228 WS_DISABLED,
229 0,
230 0,
231 0,
232 0,
233 HWND_MESSAGE,
234 std::ptr::null_mut(),
235 hinstance,
236 std::ptr::null_mut(),
237 )
238 };
239 if hwnd.is_null() {
240 Err(())
241 } else {
242 Ok(DropHWND(hwnd))
243 }
244}