1use std::sync::atomic::{AtomicBool, Ordering};
14use std::sync::mpsc::{Receiver, TryRecvError};
15use std::sync::Arc;
16use std::thread::JoinHandle;
17use std::time::Duration;
18
19use crate::error::{Error, Result};
20use crate::types::KeyEvent;
21
22pub use crate::platform::state::BlockingHotkeys;
23
24pub struct KeyboardListener {
29 event_receiver: Receiver<KeyEvent>,
30 _thread_handle: Option<JoinHandle<()>>,
31 running: Arc<AtomicBool>,
32 blocking_hotkeys: Option<BlockingHotkeys>,
33}
34
35impl KeyboardListener {
36 pub fn new() -> Result<Self> {
42 Self::new_internal(None)
43 }
44
45 pub fn new_with_blocking(blocking_hotkeys: BlockingHotkeys) -> Result<Self> {
53 Self::new_internal(Some(blocking_hotkeys))
54 }
55
56 fn new_internal(blocking_hotkeys: Option<BlockingHotkeys>) -> Result<Self> {
57 #[cfg(target_os = "macos")]
58 {
59 use crate::platform::macos::listener;
60 let state = listener::spawn(blocking_hotkeys)?;
61 Ok(KeyboardListener {
62 event_receiver: state.event_receiver,
63 _thread_handle: state.thread_handle,
64 running: state.running,
65 blocking_hotkeys: state.blocking_hotkeys,
66 })
67 }
68
69 #[cfg(target_os = "windows")]
70 {
71 use crate::platform::windows::listener;
72 let state = listener::spawn(blocking_hotkeys)?;
73 Ok(KeyboardListener {
74 event_receiver: state.event_receiver,
75 _thread_handle: state.thread_handle,
76 running: state.running,
77 blocking_hotkeys: state.blocking_hotkeys,
78 })
79 }
80
81 #[cfg(target_os = "linux")]
82 {
83 use crate::platform::linux::listener;
84 let state = listener::spawn(blocking_hotkeys)?;
85 Ok(KeyboardListener {
86 event_receiver: state.event_receiver,
87 _thread_handle: state.thread_handle,
88 running: state.running,
89 blocking_hotkeys: state.blocking_hotkeys,
90 })
91 }
92 }
93
94 pub fn blocking_hotkeys(&self) -> Option<&BlockingHotkeys> {
96 self.blocking_hotkeys.as_ref()
97 }
98
99 pub fn recv(&self) -> Result<KeyEvent> {
103 self.event_receiver
104 .recv()
105 .map_err(|_| Error::EventLoopNotRunning)
106 }
107
108 pub fn recv_timeout(&self, timeout: Duration) -> Result<KeyEvent> {
112 self.event_receiver
113 .recv_timeout(timeout)
114 .map_err(|e| match e {
115 std::sync::mpsc::RecvTimeoutError::Timeout => Error::Timeout,
116 std::sync::mpsc::RecvTimeoutError::Disconnected => Error::EventLoopNotRunning,
117 })
118 }
119
120 pub fn try_recv(&self) -> Option<KeyEvent> {
124 match self.event_receiver.try_recv() {
125 Ok(event) => Some(event),
126 Err(TryRecvError::Empty) => None,
127 Err(TryRecvError::Disconnected) => None,
128 }
129 }
130}
131
132impl Drop for KeyboardListener {
133 fn drop(&mut self) {
134 self.running.store(false, Ordering::SeqCst);
135
136 #[cfg(any(target_os = "macos", target_os = "windows"))]
140 if let Some(handle) = self._thread_handle.take() {
141 let _ = handle.join();
142 }
143 }
144}