1use async_trait::async_trait;
2use std::{
3 collections::{HashMap, HashSet},
4 fmt::Display,
5};
6
7use input_event::{Event, KeyboardEvent};
8
9pub use self::error::{EmulationCreationError, EmulationError, InputEmulationError};
10
11#[cfg(windows)]
12mod windows;
13
14#[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
15mod x11;
16
17#[cfg(all(unix, feature = "wlroots", not(target_os = "macos")))]
18mod wlroots;
19
20#[cfg(all(unix, feature = "remote_desktop_portal", not(target_os = "macos")))]
21mod xdg_desktop_portal;
22
23#[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
24mod libei;
25
26#[cfg(target_os = "macos")]
27mod macos;
28
29mod dummy;
31mod error;
32
33pub type EmulationHandle = u64;
34
35#[derive(Clone, Copy, Debug, Eq, PartialEq)]
36pub enum Backend {
37 #[cfg(all(unix, feature = "wlroots", not(target_os = "macos")))]
38 Wlroots,
39 #[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
40 Libei,
41 #[cfg(all(unix, feature = "remote_desktop_portal", not(target_os = "macos")))]
42 Xdp,
43 #[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
44 X11,
45 #[cfg(windows)]
46 Windows,
47 #[cfg(target_os = "macos")]
48 MacOs,
49 Dummy,
50}
51
52impl Display for Backend {
53 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54 match self {
55 #[cfg(all(unix, feature = "wlroots", not(target_os = "macos")))]
56 Backend::Wlroots => write!(f, "wlroots"),
57 #[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
58 Backend::Libei => write!(f, "libei"),
59 #[cfg(all(unix, feature = "remote_desktop_portal", not(target_os = "macos")))]
60 Backend::Xdp => write!(f, "xdg-desktop-portal"),
61 #[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
62 Backend::X11 => write!(f, "X11"),
63 #[cfg(windows)]
64 Backend::Windows => write!(f, "windows"),
65 #[cfg(target_os = "macos")]
66 Backend::MacOs => write!(f, "macos"),
67 Backend::Dummy => write!(f, "dummy"),
68 }
69 }
70}
71
72pub struct InputEmulation {
73 emulation: Box<dyn Emulation>,
74 handles: HashSet<EmulationHandle>,
75 pressed_keys: HashMap<EmulationHandle, HashSet<u32>>,
76}
77
78impl InputEmulation {
79 async fn with_backend(backend: Backend) -> Result<InputEmulation, EmulationCreationError> {
80 let emulation: Box<dyn Emulation> = match backend {
81 #[cfg(all(unix, feature = "wlroots", not(target_os = "macos")))]
82 Backend::Wlroots => Box::new(wlroots::WlrootsEmulation::new()?),
83 #[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
84 Backend::Libei => Box::new(libei::LibeiEmulation::new().await?),
85 #[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
86 Backend::X11 => Box::new(x11::X11Emulation::new()?),
87 #[cfg(all(unix, feature = "remote_desktop_portal", not(target_os = "macos")))]
88 Backend::Xdp => Box::new(xdg_desktop_portal::DesktopPortalEmulation::new().await?),
89 #[cfg(windows)]
90 Backend::Windows => Box::new(windows::WindowsEmulation::new()?),
91 #[cfg(target_os = "macos")]
92 Backend::MacOs => Box::new(macos::MacOSEmulation::new()?),
93 Backend::Dummy => Box::new(dummy::DummyEmulation::new()),
94 };
95 Ok(Self {
96 emulation,
97 handles: HashSet::new(),
98 pressed_keys: HashMap::new(),
99 })
100 }
101
102 pub async fn new(backend: Option<Backend>) -> Result<InputEmulation, EmulationCreationError> {
103 if let Some(backend) = backend {
104 let b = Self::with_backend(backend).await;
105 if b.is_ok() {
106 log::info!("using emulation backend: {backend}");
107 }
108 return b;
109 }
110
111 for backend in [
112 #[cfg(all(unix, feature = "wlroots", not(target_os = "macos")))]
113 Backend::Wlroots,
114 #[cfg(all(unix, feature = "libei", not(target_os = "macos")))]
115 Backend::Libei,
116 #[cfg(all(unix, feature = "remote_desktop_portal", not(target_os = "macos")))]
117 Backend::Xdp,
118 #[cfg(all(unix, feature = "x11", not(target_os = "macos")))]
119 Backend::X11,
120 #[cfg(windows)]
121 Backend::Windows,
122 #[cfg(target_os = "macos")]
123 Backend::MacOs,
124 Backend::Dummy,
125 ] {
126 match Self::with_backend(backend).await {
127 Ok(b) => {
128 log::info!("using emulation backend: {backend}");
129 return Ok(b);
130 }
131 Err(e) if e.cancelled_by_user() => return Err(e),
132 Err(e) => log::warn!("{e}"),
133 }
134 }
135
136 Err(EmulationCreationError::NoAvailableBackend)
137 }
138
139 pub async fn consume(
140 &mut self,
141 event: Event,
142 handle: EmulationHandle,
143 ) -> Result<(), EmulationError> {
144 match event {
145 Event::Keyboard(KeyboardEvent::Key { key, state, .. }) => {
146 if self.update_pressed_keys(handle, key, state) {
148 self.emulation.consume(event, handle).await?;
149 }
150 Ok(())
151 }
152 _ => self.emulation.consume(event, handle).await,
153 }
154 }
155
156 pub async fn create(&mut self, handle: EmulationHandle) -> bool {
157 if self.handles.insert(handle) {
158 self.pressed_keys.insert(handle, HashSet::new());
159 self.emulation.create(handle).await;
160 true
161 } else {
162 false
163 }
164 }
165
166 pub async fn destroy(&mut self, handle: EmulationHandle) {
167 let _ = self.release_keys(handle).await;
168 if self.handles.remove(&handle) {
169 self.pressed_keys.remove(&handle);
170 self.emulation.destroy(handle).await
171 }
172 }
173
174 pub async fn terminate(&mut self) {
175 for handle in self.handles.iter().cloned().collect::<Vec<_>>() {
176 self.destroy(handle).await
177 }
178 self.emulation.terminate().await
179 }
180
181 pub async fn release_keys(&mut self, handle: EmulationHandle) -> Result<(), EmulationError> {
182 if let Some(keys) = self.pressed_keys.get_mut(&handle) {
183 let keys = keys.drain().collect::<Vec<_>>();
184 for key in keys {
185 let event = Event::Keyboard(KeyboardEvent::Key {
186 time: 0,
187 key,
188 state: 0,
189 });
190 self.emulation.consume(event, handle).await?;
191 if let Ok(key) = input_event::scancode::Linux::try_from(key) {
192 log::warn!("releasing stuck key: {key:?}");
193 }
194 }
195 }
196
197 let event = Event::Keyboard(KeyboardEvent::Modifiers {
198 depressed: 0,
199 latched: 0,
200 locked: 0,
201 group: 0,
202 });
203 self.emulation.consume(event, handle).await?;
204 Ok(())
205 }
206
207 pub fn has_pressed_keys(&self, handle: EmulationHandle) -> bool {
208 self.pressed_keys
209 .get(&handle)
210 .is_some_and(|p| !p.is_empty())
211 }
212
213 fn update_pressed_keys(&mut self, handle: EmulationHandle, key: u32, state: u8) -> bool {
216 let Some(pressed_keys) = self.pressed_keys.get_mut(&handle) else {
217 return false;
218 };
219
220 if state == 0 {
221 pressed_keys.remove(&key)
223 } else {
224 pressed_keys.insert(key)
226 }
227 }
228}
229
230#[async_trait]
231trait Emulation: Send {
232 async fn consume(
233 &mut self,
234 event: Event,
235 handle: EmulationHandle,
236 ) -> Result<(), EmulationError>;
237 async fn create(&mut self, handle: EmulationHandle);
238 async fn destroy(&mut self, handle: EmulationHandle);
239 async fn terminate(&mut self);
240}