1use crate::error::Result;
14use crate::hotkeys::{Key, Modifiers};
15use serde::{Deserialize, Serialize};
16use std::sync::{Arc, Mutex};
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
20pub enum KeyState {
21 Pressed,
22 Released,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
27pub enum MouseButton {
28 Left,
29 Right,
30 Middle,
31 X1,
33 X2,
35}
36
37#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
39pub enum GlobalInputEvent {
40 Keyboard {
41 key: Key,
42 state: KeyState,
43 modifiers: Modifiers,
44 },
45 Mouse {
46 button: MouseButton,
47 state: KeyState,
48 x: i32,
49 y: i32,
50 },
51 MouseMove {
52 x: i32,
53 y: i32,
54 },
55}
56
57pub trait GlobalInputHandler: Send + Sync {
59 fn on_event(&self, event: GlobalInputEvent);
60}
61
62pub struct GlobalInput {
64 handler: Arc<dyn GlobalInputHandler>,
65 inner: Mutex<Option<PlatformInput>>,
66 include_mouse_move: bool,
68}
69
70impl GlobalInput {
71 pub fn new(handler: Arc<dyn GlobalInputHandler>) -> Self {
72 Self {
73 handler,
74 inner: Mutex::new(None),
75 include_mouse_move: false,
76 }
77 }
78
79 pub fn with_mouse_move(mut self, on: bool) -> Self {
81 self.include_mouse_move = on;
82 self
83 }
84
85 pub fn start(&self) -> Result<()> {
87 let mut guard = self.inner.lock().unwrap();
88 if guard.is_some() {
89 return Ok(());
90 }
91 let p = PlatformInput::start(self.handler.clone(), self.include_mouse_move)?;
92 *guard = Some(p);
93 Ok(())
94 }
95
96 pub fn stop(&self) {
98 if let Some(p) = self.inner.lock().unwrap().take() {
99 p.stop();
100 }
101 }
102}
103
104impl Drop for GlobalInput {
105 fn drop(&mut self) {
106 if let Some(p) = self.inner.lock().unwrap().take() {
107 p.stop();
108 }
109 }
110}
111
112enum PlatformInput {
113 #[cfg(windows)]
114 Windows(crate::input_win::WinInput),
115 #[cfg(target_os = "macos")]
116 Macos(crate::input_mac::MacInput),
117 #[cfg(not(any(windows, target_os = "macos")))]
118 Unsupported,
119}
120
121impl PlatformInput {
122 fn start(handler: Arc<dyn GlobalInputHandler>, include_mouse_move: bool) -> Result<Self> {
123 #[cfg(windows)]
124 {
125 Ok(PlatformInput::Windows(crate::input_win::WinInput::start(
126 handler,
127 include_mouse_move,
128 )?))
129 }
130 #[cfg(target_os = "macos")]
131 {
132 Ok(PlatformInput::Macos(crate::input_mac::MacInput::start(
133 handler,
134 include_mouse_move,
135 )?))
136 }
137 #[cfg(not(any(windows, target_os = "macos")))]
138 {
139 let _ = (handler, include_mouse_move);
140 Err(crate::error::RdesktopError::UnsupportedPlatform(
141 "global input hooks are only supported on Windows and macOS".into(),
142 ))
143 }
144 }
145
146 fn stop(self) {
147 match self {
148 #[cfg(windows)]
149 PlatformInput::Windows(w) => w.stop(),
150 #[cfg(target_os = "macos")]
151 PlatformInput::Macos(m) => m.stop(),
152 #[cfg(not(any(windows, target_os = "macos")))]
153 PlatformInput::Unsupported => {}
154 }
155 }
156}