win_hotkey/
global.rs

1use rustc_hash::FxHashMap;
2
3use crate::{HotkeyId, HotkeyManager, HotkeyManagerImpl, ModifiersKey, VirtualKey};
4use core::fmt;
5use std::sync::{
6    atomic::{AtomicBool, Ordering},
7    Arc, Mutex,
8};
9
10#[derive(Clone)]
11pub struct GlobalHotkey<T> {
12    key: VirtualKey,
13    modifiers: Option<Vec<ModifiersKey>>,
14    extras: Option<Vec<VirtualKey>>,
15    action: Option<Arc<Mutex<dyn Fn() -> T + Send + 'static>>>, // Callback needs to be Send too
16}
17
18impl<T> fmt::Debug for GlobalHotkey<T>
19where
20    T: fmt::Debug, // Ensures that T can be printed if necessary
21{
22    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23        f.debug_struct("GlobalHotkey")
24            .field("key", &self.key)
25            .field("modifiers", &self.modifiers)
26            .field("extras", &self.extras)
27            .field(
28                "action",
29                &self.action.as_ref().map_or_else(
30                    || "None".to_string(),
31                    |_| "Some(Fn() -> T + Send)".to_string(),
32                ),
33            )
34            .finish()
35    }
36}
37
38#[derive(Clone, Debug)]
39pub struct GlobalHotkeyManager<T: Send + 'static> {
40    hotkeys: Arc<Mutex<FxHashMap<String, GlobalHotkey<T>>>>,
41    manager: Arc<Mutex<HotkeyManager<T>>>,
42    listening: Arc<AtomicBool>,
43    key_ids: Arc<Mutex<Vec<HotkeyId>>>,
44}
45
46impl<T: Send + 'static> GlobalHotkey<T> {
47    pub fn set_action(&mut self, action: impl Fn() -> T + Send + 'static) {
48        self.action = Some(Arc::new(Mutex::new(action)));
49    }
50}
51
52impl<T: Send + 'static> Default for GlobalHotkeyManager<T> {
53    fn default() -> Self {
54        let mut hkm = HotkeyManager::new();
55        hkm.set_no_repeat(false);
56        Self {
57            manager: Arc::new(Mutex::new(hkm)),
58            listening: Arc::new(AtomicBool::new(false)),
59            hotkeys: Arc::new(Mutex::new(FxHashMap::default())),
60            key_ids: Arc::new(Mutex::new(Vec::new())),
61        }
62    }
63}
64
65pub trait GlobalHotkeyManagerImpl<T> {
66    fn new() -> Self;
67    fn register_hotkey(
68        &self,
69        name: String,
70        key: VirtualKey,
71        modifiers: Option<Vec<ModifiersKey>>,
72        extras: Option<Vec<VirtualKey>>,
73        callback: Option<impl Fn() -> T + Send + 'static>,
74    );
75    fn add_hotkey(&self, name: String, hotkey: GlobalHotkey<T>);
76    fn remove_hotkey(&self, name: String) -> Option<GlobalHotkey<T>>;
77    fn start(&self);
78    fn stop(&self) -> bool;
79    #[cfg(feature = "upcoming_update")]
80    fn update(&mut self);
81}
82
83impl<T: Send + 'static> GlobalHotkeyManagerImpl<T> for GlobalHotkeyManager<T> {
84    fn new() -> Self {
85        Self::default()
86    }
87
88    fn register_hotkey(
89        &self,
90        name: String,
91        key: VirtualKey,
92        modifiers: Option<Vec<ModifiersKey>>,
93        extras: Option<Vec<VirtualKey>>,
94        callback: Option<impl Fn() -> T + Send + 'static>,
95    ) {
96        let mut hotkeys = self.hotkeys.lock().unwrap();
97        hotkeys.insert(
98            name,
99            GlobalHotkey {
100                key,
101                modifiers,
102                extras,
103                action: callback.map(|cb| {
104                    Arc::new(Mutex::new(cb)) as Arc<Mutex<dyn Fn() -> T + Send + 'static>>
105                }),
106            },
107        );
108    }
109
110    fn add_hotkey(&self, name: String, hotkey: GlobalHotkey<T>) {
111        let mut hotkeys = self.hotkeys.lock().unwrap();
112        hotkeys.insert(name, hotkey);
113    }
114
115    fn remove_hotkey(&self, key: String) -> Option<GlobalHotkey<T>> {
116        let mut hotkeys = self.hotkeys.lock().unwrap();
117        hotkeys.remove(&key)
118    }
119
120    #[cfg(feature = "upcoming_update")]
121    fn update(&mut self) {
122        let listening = self.listening.clone();
123        let hotkey_manager = self.manager.clone();
124
125        // Lock bindings to access keybindings
126        let mut hotkey_manager_mut = hotkey_manager.lock().unwrap();
127        let hotkeys = self.hotkeys.lock().unwrap();
128        let mut key_ids = self.key_ids.lock().unwrap();
129
130        if let Err(e) = hotkey_manager_mut.unregister_all() {
131            eprintln!("failed to unregister all keybindings: {}", e);
132        }
133
134        let handle = hotkey_manager_mut.interrupt_handle();
135        handle.interrupt();
136        key_ids.clear();
137
138        let mut new_hk = HotkeyManager::new();
139        new_hk.set_no_repeat(false);
140        let new_hkm = Arc::new(Mutex::new(new_hk));
141        self.manager = new_hkm.clone();
142
143        let hotkey_manager = self.manager.clone();
144        let mut hotkey_manager_mut = hotkey_manager.lock().unwrap();
145
146        // Collect hotkeys and their actions upfront
147        for hotkey in hotkeys.values() {
148            let action = hotkey.action.clone();
149            let result = if let Some(action) = action {
150                // Register with an action if present
151                hotkey_manager_mut.register_extrakeys(
152                    hotkey.key,
153                    hotkey.modifiers.as_deref(),
154                    hotkey.extras.as_deref(),
155                    Some(move || {
156                        let action = action.clone();
157                        let action = action.lock().unwrap();
158                        action()
159                    }),
160                )
161            } else {
162                // Register without an action if None
163                hotkey_manager_mut.register_extrakeys(
164                    hotkey.key,
165                    hotkey.modifiers.as_deref(),
166                    hotkey.extras.as_deref(),
167                    None::<fn() -> T>,
168                )
169            };
170
171            match result {
172                Ok(hotkey_id) => key_ids.push(hotkey_id),
173                Err(e) => {
174                    eprintln!("failed to register keybinding {:?}: {}", hotkey.key, e);
175                }
176            }
177        }
178
179        let hkm = hotkey_manager.clone();
180
181        std::thread::spawn(move || {
182            // Lock the Mutex inside the thread, instead of moving the MutexGuard
183            while listening.load(Ordering::SeqCst) {
184                hkm.lock().unwrap().event_loop();
185            }
186        });
187    }
188
189    fn start(&self) {
190        if self.listening.load(Ordering::SeqCst) {
191            eprintln!("already listening for hotkeys.");
192            return;
193        }
194
195        let hotkey_manager = self.manager.clone();
196        let listening = self.listening.clone();
197
198        listening.store(true, Ordering::SeqCst);
199
200        // Lock bindings to access keybindings
201        let mut hotkey_manager_mut = hotkey_manager.lock().unwrap();
202        let hotkeys = self.hotkeys.lock().unwrap();
203        let mut key_ids = self.key_ids.lock().unwrap();
204
205        // Collect hotkeys and their actions upfront
206        for hotkey in hotkeys.values() {
207            let action = hotkey.action.clone();
208            let result = if let Some(action) = action {
209                // Register with an action if present
210                hotkey_manager_mut.register_extrakeys(
211                    hotkey.key,
212                    hotkey.modifiers.as_deref(),
213                    hotkey.extras.as_deref(),
214                    Some(move || {
215                        let action = action.clone();
216                        let action = action.lock().unwrap();
217                        action()
218                    }),
219                )
220            } else {
221                // Register without an action if None
222                hotkey_manager_mut.register_extrakeys(
223                    hotkey.key,
224                    hotkey.modifiers.as_deref(),
225                    hotkey.extras.as_deref(),
226                    None::<fn() -> T>,
227                )
228            };
229
230            match result {
231                Ok(hotkey_id) => key_ids.push(hotkey_id),
232                Err(e) => {
233                    eprintln!("failed to register keybinding {:?}: {}", hotkey.key, e);
234                }
235            }
236        }
237
238        let hkm = hotkey_manager.clone();
239
240        std::thread::spawn(move || {
241            // Lock the Mutex inside the thread, instead of moving the MutexGuard
242            while listening.load(Ordering::SeqCst) {
243                hkm.lock().unwrap().event_loop();
244            }
245        });
246    }
247
248    fn stop(&self) -> bool {
249        if !self.listening.load(Ordering::SeqCst) {
250            return false;
251        }
252
253        self.listening.store(false, Ordering::SeqCst);
254
255        true
256    }
257}
258
259#[derive(Debug)]
260pub enum HotKeyParseError {
261    UnsupportedKey(String),
262    EmptyToken(String),
263    InvalidFormat(String),
264}
265
266impl std::fmt::Display for HotKeyParseError {
267    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
268        match *self {
269            HotKeyParseError::UnsupportedKey(ref key) => {
270                write!(
271                    f,
272                    "Couldn't recognize \"{}\" as a valid key for hotkey",
273                    key
274                )
275            }
276            HotKeyParseError::EmptyToken(ref token) => {
277                write!(f, "Found empty token while parsing hotkey: {}", token)
278            }
279            HotKeyParseError::InvalidFormat(ref format) => {
280                write!(
281                    f,
282                    "Invalid hotkey format: \"{}\", a hotkey should have the modifiers first and only one main key, for example: \"Shift + Alt + K\"",
283                    format
284                )
285            }
286        }
287    }
288}
289
290impl std::error::Error for HotKeyParseError {
291    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
292        // No underlying error, so we return None.
293        None
294    }
295}
296
297impl<T: Send + 'static> TryInto<GlobalHotkey<T>> for &str {
298    type Error = HotKeyParseError;
299
300    fn try_into(self) -> Result<GlobalHotkey<T>, Self::Error> {
301        let tokens = self.split('+').collect::<Vec<&str>>();
302        let mut modifiers: Vec<ModifiersKey> = Vec::new();
303        let mut key = None;
304        let mut extras: Vec<VirtualKey> = Vec::new();
305
306        match tokens.len() {
307            1 => {
308                // Only a key, no modifiers or extras
309                key = Some(
310                    VirtualKey::try_from(tokens[0].trim())
311                        .map_err(|e| HotKeyParseError::UnsupportedKey(e.to_string()))?,
312                );
313            }
314            _ => {
315                let mut found_key = false;
316
317                for raw in tokens {
318                    let token = raw.trim();
319
320                    if token.is_empty() {
321                        return Err(HotKeyParseError::EmptyToken(self.to_string()));
322                    }
323
324                    // If we have already found the key, treat the rest as extras
325                    if found_key {
326                        let extra_key = VirtualKey::try_from(token)
327                            .map_err(|e| HotKeyParseError::UnsupportedKey(e.to_string()))?;
328                        extras.push(extra_key);
329                    } else {
330                        if key.is_some() {
331                            return Err(HotKeyParseError::InvalidFormat(self.to_string()));
332                        }
333
334                        let temp_key = VirtualKey::try_from(token)
335                            .map_err(|e| HotKeyParseError::UnsupportedKey(e.to_string()))?;
336
337                        // If the token is a valid modifier, add it to the modifiers
338                        if let Ok(modifier) = temp_key.try_into() {
339                            modifiers.push(modifier);
340                        } else {
341                            // Otherwise, treat it as the main key
342                            key = Some(temp_key);
343                            found_key = true; // Mark that the key has been found
344                        }
345                    }
346                }
347            }
348        }
349
350        // If no key was found, return an error
351        let key = key.ok_or_else(|| HotKeyParseError::InvalidFormat(self.to_string()))?;
352
353        Ok(GlobalHotkey {
354            key,
355            modifiers: if modifiers.is_empty() {
356                None
357            } else {
358                Some(modifiers)
359            },
360            extras: if extras.is_empty() {
361                None
362            } else {
363                Some(extras)
364            },
365            action: None, // action is still None
366        })
367    }
368}