windows_hotkeys/
threadsafe.rs

1use std::{
2    marker::PhantomData,
3    sync::mpsc::{channel, Receiver, Sender},
4    thread::{spawn, JoinHandle},
5};
6
7use crate::{
8    error::HkError,
9    keys::{ModKey, VKey},
10    singlethreaded, HotkeyId, HotkeyManagerImpl, InterruptHandle,
11};
12
13struct Hotkey<T: 'static> {
14    key: VKey,
15    key_modifiers: Vec<ModKey>,
16    extra_keys: Vec<VKey>,
17    callback: Box<dyn Fn() -> T + Send + 'static>,
18}
19
20enum HkMsg<T: 'static> {
21    Register(Sender<Result<HotkeyId, HkError>>, Hotkey<T>),
22    HandleHotkey(Sender<Option<T>>),
23    Unregister(Sender<Result<(), HkError>>, HotkeyId),
24    UnregisterAll(Sender<Result<(), HkError>>),
25    EventLoop(Sender<()>),
26    InterruptHandle(Sender<InterruptHandle>),
27    Exit(Sender<()>),
28}
29
30pub struct HotkeyManager<T: 'static> {
31    no_repeat: bool,
32    _phantom: PhantomData<T>,
33    snd: Sender<HkMsg<T>>,
34    backend_handle: Option<JoinHandle<()>>,
35}
36
37struct TSHotkeyManagerBackend<T: 'static> {
38    hkm: singlethreaded::HotkeyManager<T>,
39    rec: Receiver<HkMsg<T>>,
40}
41
42impl<T: 'static> HotkeyManager<T> {
43    /// Enable or disable the automatically applied `ModKey::NoRepeat` modifier. By default, this
44    /// option is set to `true` which causes all hotkey registration calls to add the `NoRepeat`
45    /// modifier, thereby disabling automatic retriggers of hotkeys when holding down the keys.
46    ///
47    /// When this option is disabled, the `ModKey::NoRepeat` can still be manually added while
48    /// registering hotkeys.
49    ///
50    /// Note: Setting this flag doesn't change previously registered hotkeys. It only applies to
51    /// registrations performed after calling this function.
52    pub fn set_no_repeat(&mut self, no_repeat: bool) {
53        self.no_repeat = no_repeat;
54    }
55}
56
57impl<T> TSHotkeyManagerBackend<T> {
58    /// Create a new HotkeyManager instance. To work around the same-thread limitation of the
59    /// windows event API, this will launch a new background thread to handle hotkey interactions.
60    ///
61    fn new(rec: Receiver<HkMsg<T>>) -> Self {
62        let mut hkm = singlethreaded::HotkeyManager::new();
63        hkm.set_no_repeat(false);
64        Self { hkm, rec }
65    }
66
67    fn backend_loop(&mut self) {
68        while let Ok(msg) = self.rec.recv() {
69            match msg {
70                HkMsg::Register(chan_ret, hk) => {
71                    let ret_val = self.hkm.register_extrakeys(
72                        hk.key,
73                        &hk.key_modifiers,
74                        &hk.extra_keys,
75                        hk.callback,
76                    );
77                    chan_ret.send(ret_val).unwrap();
78                }
79                HkMsg::HandleHotkey(chan_ret) => {
80                    let ret_val = self.hkm.handle_hotkey();
81                    chan_ret.send(ret_val).unwrap();
82                }
83                HkMsg::Unregister(chan_ret, hkid) => {
84                    let ret_val = self.hkm.unregister(hkid);
85                    chan_ret.send(ret_val).unwrap();
86                }
87                HkMsg::UnregisterAll(chan_ret) => {
88                    let ret_val = self.hkm.unregister_all();
89                    chan_ret.send(ret_val).unwrap();
90                }
91                HkMsg::EventLoop(chan_ret) => {
92                    self.hkm.event_loop();
93                    chan_ret.send(()).unwrap();
94                }
95                HkMsg::InterruptHandle(chan_ret) => {
96                    let ret_val = self.hkm.interrupt_handle();
97                    chan_ret.send(ret_val).unwrap();
98                }
99                HkMsg::Exit(chan_ret) => {
100                    chan_ret.send(()).unwrap();
101                    return;
102                }
103            }
104        }
105    }
106}
107
108impl<T: 'static + Send> HotkeyManagerImpl<T> for HotkeyManager<T> {
109    fn new() -> Self {
110        let (snd, rec) = channel();
111        let backend_handle = spawn(move || {
112            let mut backend = TSHotkeyManagerBackend::<T>::new(rec);
113            backend.backend_loop();
114        });
115
116        Self {
117            no_repeat: true,
118            _phantom: PhantomData,
119            snd,
120            backend_handle: Some(backend_handle),
121        }
122    }
123
124    fn register(
125        &mut self,
126        key: VKey,
127        key_modifiers: &[ModKey],
128        callback: impl Fn() -> T + Send + 'static,
129    ) -> Result<HotkeyId, HkError> {
130        self.register_extrakeys(key, key_modifiers, &[], callback)
131    }
132
133    fn register_extrakeys(
134        &mut self,
135        key: VKey,
136        key_modifiers: &[ModKey],
137        extra_keys: &[VKey],
138        callback: impl Fn() -> T + Send + 'static,
139    ) -> Result<HotkeyId, HkError> {
140        let ret_ch = channel();
141
142        let mut key_modifiers = key_modifiers.to_vec();
143        if self.no_repeat {
144            key_modifiers.push(ModKey::NoRepeat);
145        }
146
147        let hk = Hotkey {
148            key,
149            key_modifiers,
150            extra_keys: extra_keys.to_vec(),
151            callback: Box::new(callback),
152        };
153        self.snd.send(HkMsg::Register(ret_ch.0, hk)).unwrap();
154        ret_ch.1.recv().unwrap()
155    }
156
157    fn unregister(&mut self, id: HotkeyId) -> Result<(), HkError> {
158        let ret_ch = channel();
159        self.snd.send(HkMsg::Unregister(ret_ch.0, id)).unwrap();
160        ret_ch.1.recv().unwrap()
161    }
162
163    fn unregister_all(&mut self) -> Result<(), HkError> {
164        let ret_ch = channel();
165        self.snd.send(HkMsg::UnregisterAll(ret_ch.0)).unwrap();
166        ret_ch.1.recv().unwrap()
167    }
168
169    fn handle_hotkey(&self) -> Option<T> {
170        let ret_ch = channel();
171        self.snd.send(HkMsg::HandleHotkey(ret_ch.0)).unwrap();
172        ret_ch.1.recv().unwrap()
173    }
174
175    fn event_loop(&self) {
176        let ret_ch = channel();
177        self.snd.send(HkMsg::EventLoop(ret_ch.0)).unwrap();
178        ret_ch.1.recv().unwrap()
179    }
180
181    fn interrupt_handle(&self) -> InterruptHandle {
182        let ret_ch = channel();
183        self.snd.send(HkMsg::InterruptHandle(ret_ch.0)).unwrap();
184        ret_ch.1.recv().unwrap()
185    }
186}
187
188impl<T> Drop for HotkeyManager<T> {
189    fn drop(&mut self) {
190        let ret_ch = channel();
191        self.snd.send(HkMsg::Exit(ret_ch.0)).unwrap();
192        ret_ch.1.recv().unwrap();
193        self.backend_handle.take().unwrap().join().unwrap();
194    }
195}