Rust · 18775 bytes Raw Blame History
1 //! Wayland input emulation
2 //!
3 //! Injects keyboard and mouse events via virtual input protocols.
4 //! Uses wlr-virtual-pointer and virtual-keyboard Wayland protocols.
5
6 use std::os::unix::io::{AsFd, OwnedFd};
7 use std::sync::Arc;
8
9 use wayland_client::{
10 globals::{registry_queue_init, GlobalListContents},
11 protocol::{wl_registry, wl_seat},
12 Connection, Dispatch, EventQueue, QueueHandle,
13 };
14 use wayland_protocols_wlr::virtual_pointer::v1::client::{
15 zwlr_virtual_pointer_manager_v1::ZwlrVirtualPointerManagerV1,
16 zwlr_virtual_pointer_v1::ZwlrVirtualPointerV1,
17 };
18 use wayland_protocols_misc::zwp_virtual_keyboard_v1::client::{
19 zwp_virtual_keyboard_manager_v1::ZwpVirtualKeyboardManagerV1,
20 zwp_virtual_keyboard_v1::ZwpVirtualKeyboardV1,
21 };
22 use wayland_client::protocol::wl_pointer::{
23 Axis as WlAxis,
24 ButtonState as WlButtonState,
25 };
26
27 use hyprkvm_common::{ButtonState, KeyState};
28
29 /// Virtual pointer for mouse injection
30 pub struct VirtualPointer {
31 pointer: ZwlrVirtualPointerV1,
32 connection: Arc<Connection>,
33 }
34
35 impl VirtualPointer {
36 /// Send relative motion
37 pub fn motion(&self, dx: f64, dy: f64) {
38 // Time in milliseconds (monotonic, we just use 0 for simplicity)
39 let time = std::time::SystemTime::now()
40 .duration_since(std::time::UNIX_EPOCH)
41 .unwrap()
42 .as_millis() as u32;
43
44 self.pointer.motion(time, dx, dy);
45 self.pointer.frame();
46 let _ = self.connection.flush(); // Flush immediately for low latency
47 }
48
49 /// Send absolute motion (normalized 0.0-1.0)
50 pub fn motion_absolute(&self, x: f64, y: f64, width: u32, height: u32) {
51 let time = std::time::SystemTime::now()
52 .duration_since(std::time::UNIX_EPOCH)
53 .unwrap()
54 .as_millis() as u32;
55
56 // Convert to fixed-point (wl_fixed)
57 let x_fixed = (x * width as f64) as u32;
58 let y_fixed = (y * height as f64) as u32;
59
60 self.pointer.motion_absolute(time, x_fixed, y_fixed, width, height);
61 self.pointer.frame();
62 let _ = self.connection.flush();
63 }
64
65 /// Send button event
66 pub fn button(&self, button: u32, state: ButtonState) {
67 let time = std::time::SystemTime::now()
68 .duration_since(std::time::UNIX_EPOCH)
69 .unwrap()
70 .as_millis() as u32;
71
72 let wl_state = match state {
73 ButtonState::Pressed => WlButtonState::Pressed,
74 ButtonState::Released => WlButtonState::Released,
75 };
76
77 self.pointer.button(time, button, wl_state);
78 self.pointer.frame();
79 let _ = self.connection.flush();
80 }
81
82 /// Send scroll (axis) event
83 pub fn scroll(&self, horizontal: f64, vertical: f64) {
84 let time = std::time::SystemTime::now()
85 .duration_since(std::time::UNIX_EPOCH)
86 .unwrap()
87 .as_millis() as u32;
88
89 tracing::debug!("INJECT SCROLL: h={}, v={}", horizontal, vertical);
90
91 // Set axis source to wheel
92 use wayland_client::protocol::wl_pointer::AxisSource;
93 self.pointer.axis_source(AxisSource::Wheel);
94
95 if vertical.abs() > 0.001 {
96 // Calculate discrete steps (each notch is typically 15 units)
97 let discrete = (vertical / 15.0).round() as i32;
98 if discrete != 0 {
99 // Send discrete scroll for wheel mice
100 self.pointer.axis_discrete(time, WlAxis::VerticalScroll, vertical, discrete);
101 } else {
102 // Fallback to smooth scroll for small values
103 self.pointer.axis(time, WlAxis::VerticalScroll, vertical);
104 }
105 }
106 if horizontal.abs() > 0.001 {
107 let discrete = (horizontal / 15.0).round() as i32;
108 if discrete != 0 {
109 self.pointer.axis_discrete(time, WlAxis::HorizontalScroll, horizontal, discrete);
110 } else {
111 self.pointer.axis(time, WlAxis::HorizontalScroll, horizontal);
112 }
113 }
114 self.pointer.frame();
115 let _ = self.connection.flush();
116 }
117 }
118
119 /// Virtual keyboard for key injection
120 pub struct VirtualKeyboard {
121 keyboard: ZwpVirtualKeyboardV1,
122 connection: Arc<Connection>,
123 keymap_set: bool,
124 /// Track pressed modifier keys for state updates
125 modifier_state: ModifierTracker,
126 /// Track all currently pressed keys for cleanup
127 pressed_keys: std::collections::HashSet<u32>,
128 }
129
130 /// Tracks which modifier keys are currently pressed
131 #[derive(Default)]
132 struct ModifierTracker {
133 left_shift: bool,
134 right_shift: bool,
135 left_ctrl: bool,
136 right_ctrl: bool,
137 left_alt: bool,
138 right_alt: bool,
139 left_super: bool,
140 right_super: bool,
141 caps_lock: bool,
142 }
143
144 impl ModifierTracker {
145 /// Update state based on key event, returns true if this was a modifier key
146 fn update(&mut self, keycode: u32, pressed: bool) -> bool {
147 match keycode {
148 42 => { self.left_shift = pressed; true } // KEY_LEFTSHIFT
149 54 => { self.right_shift = pressed; true } // KEY_RIGHTSHIFT
150 29 => { self.left_ctrl = pressed; true } // KEY_LEFTCTRL
151 97 => { self.right_ctrl = pressed; true } // KEY_RIGHTCTRL
152 56 => { self.left_alt = pressed; true } // KEY_LEFTALT
153 100 => { self.right_alt = pressed; true } // KEY_RIGHTALT
154 125 => { self.left_super = pressed; true } // KEY_LEFTMETA
155 126 => { self.right_super = pressed; true } // KEY_RIGHTMETA
156 58 => { self.caps_lock = pressed; true } // KEY_CAPSLOCK
157 _ => false,
158 }
159 }
160
161 /// Get XKB modifier mask for depressed modifiers
162 fn depressed(&self) -> u32 {
163 let mut mask = 0u32;
164 if self.left_shift || self.right_shift { mask |= 1; } // Shift
165 if self.left_ctrl || self.right_ctrl { mask |= 4; } // Control
166 if self.left_alt || self.right_alt { mask |= 8; } // Mod1 (Alt)
167 if self.left_super || self.right_super { mask |= 64; } // Mod4 (Super)
168 mask
169 }
170
171 /// Get XKB modifier mask for locked modifiers (Caps Lock)
172 fn locked(&self) -> u32 {
173 if self.caps_lock { 2 } else { 0 } // Lock bit
174 }
175 }
176
177 impl VirtualKeyboard {
178 /// Send key event
179 pub fn key(&mut self, keycode: u32, state: KeyState) {
180 if !self.keymap_set {
181 tracing::warn!("Keymap not set, key event may not work correctly");
182 }
183
184 let time = std::time::SystemTime::now()
185 .duration_since(std::time::UNIX_EPOCH)
186 .unwrap()
187 .as_millis() as u32;
188
189 let pressed = state == KeyState::Pressed;
190 let wl_state = match state {
191 KeyState::Pressed => wl_keyboard_key_state::PRESSED,
192 KeyState::Released => wl_keyboard_key_state::RELEASED,
193 };
194
195 // Log the key event with modifier state for debugging
196 let key_name = keycode_name(keycode);
197 tracing::debug!(
198 "INJECT KEY: {} {} (keycode={}, modifiers: depressed={:#x}, super={})",
199 key_name,
200 if pressed { "DOWN" } else { "UP" },
201 keycode,
202 self.modifier_state.depressed(),
203 self.modifier_state.left_super || self.modifier_state.right_super
204 );
205
206 // Track pressed keys for cleanup
207 if pressed {
208 self.pressed_keys.insert(keycode);
209 } else {
210 self.pressed_keys.remove(&keycode);
211 }
212
213 // Send the key event
214 self.keyboard.key(time, keycode, wl_state);
215
216 // If this is a modifier key, update and send modifier state
217 if self.modifier_state.update(keycode, pressed) {
218 tracing::debug!(
219 "INJECT MODIFIERS: depressed={:#x} (super={})",
220 self.modifier_state.depressed(),
221 self.modifier_state.left_super || self.modifier_state.right_super
222 );
223 self.keyboard.modifiers(
224 self.modifier_state.depressed(),
225 0, // latched
226 self.modifier_state.locked(),
227 0, // group
228 );
229 }
230
231 let _ = self.connection.flush();
232 }
233
234 /// Send modifier state directly
235 pub fn modifiers(&self, depressed: u32, latched: u32, locked: u32, group: u32) {
236 self.keyboard.modifiers(depressed, latched, locked, group);
237 let _ = self.connection.flush();
238 }
239
240 /// Release all pressed keys and reset internal state
241 /// Call this when stopping injection to ensure clean state for next session
242 pub fn reset_all_keys(&mut self) {
243 let time = std::time::SystemTime::now()
244 .duration_since(std::time::UNIX_EPOCH)
245 .unwrap()
246 .as_millis() as u32;
247
248 // Release ALL pressed keys (including arrow keys, not just modifiers)
249 let keys_to_release: Vec<u32> = self.pressed_keys.iter().copied().collect();
250 for keycode in keys_to_release {
251 tracing::debug!("RESET: Releasing key {} ({})", keycode, keycode_name(keycode));
252 self.keyboard.key(time, keycode, wl_keyboard_key_state::RELEASED);
253 }
254 self.pressed_keys.clear();
255
256 // Reset modifier tracking
257 self.modifier_state = ModifierTracker::default();
258
259 // Send clean modifier state to compositor
260 self.keyboard.modifiers(0, 0, 0, 0);
261 let _ = self.connection.flush();
262
263 tracing::debug!("All keys reset complete");
264 }
265 }
266
267 // Key state constants
268 mod wl_keyboard_key_state {
269 pub const RELEASED: u32 = 0;
270 pub const PRESSED: u32 = 1;
271 }
272
273 /// State for input emulation setup
274 struct EmulationState {
275 pointer_manager: Option<ZwlrVirtualPointerManagerV1>,
276 keyboard_manager: Option<ZwpVirtualKeyboardManagerV1>,
277 seat: Option<wl_seat::WlSeat>,
278 }
279
280 impl Dispatch<wl_registry::WlRegistry, GlobalListContents> for EmulationState {
281 fn event(
282 state: &mut Self,
283 registry: &wl_registry::WlRegistry,
284 event: wl_registry::Event,
285 _data: &GlobalListContents,
286 _conn: &Connection,
287 qh: &QueueHandle<Self>,
288 ) {
289 if let wl_registry::Event::Global { name, interface, version } = event {
290 match interface.as_str() {
291 "zwlr_virtual_pointer_manager_v1" => {
292 state.pointer_manager = Some(registry.bind(name, version.min(2), qh, ()));
293 }
294 "zwp_virtual_keyboard_manager_v1" => {
295 state.keyboard_manager = Some(registry.bind(name, version.min(1), qh, ()));
296 }
297 "wl_seat" => {
298 state.seat = Some(registry.bind(name, version.min(7), qh, ()));
299 }
300 _ => {}
301 }
302 }
303 }
304 }
305
306 // Implement empty dispatchers for the protocols we use
307 impl Dispatch<ZwlrVirtualPointerManagerV1, ()> for EmulationState {
308 fn event(
309 _state: &mut Self,
310 _proxy: &ZwlrVirtualPointerManagerV1,
311 _event: <ZwlrVirtualPointerManagerV1 as wayland_client::Proxy>::Event,
312 _data: &(),
313 _conn: &Connection,
314 _qh: &QueueHandle<Self>,
315 ) {
316 }
317 }
318
319 impl Dispatch<ZwlrVirtualPointerV1, ()> for EmulationState {
320 fn event(
321 _state: &mut Self,
322 _proxy: &ZwlrVirtualPointerV1,
323 _event: <ZwlrVirtualPointerV1 as wayland_client::Proxy>::Event,
324 _data: &(),
325 _conn: &Connection,
326 _qh: &QueueHandle<Self>,
327 ) {
328 }
329 }
330
331 impl Dispatch<ZwpVirtualKeyboardManagerV1, ()> for EmulationState {
332 fn event(
333 _state: &mut Self,
334 _proxy: &ZwpVirtualKeyboardManagerV1,
335 _event: <ZwpVirtualKeyboardManagerV1 as wayland_client::Proxy>::Event,
336 _data: &(),
337 _conn: &Connection,
338 _qh: &QueueHandle<Self>,
339 ) {
340 }
341 }
342
343 impl Dispatch<ZwpVirtualKeyboardV1, ()> for EmulationState {
344 fn event(
345 _state: &mut Self,
346 _proxy: &ZwpVirtualKeyboardV1,
347 _event: <ZwpVirtualKeyboardV1 as wayland_client::Proxy>::Event,
348 _data: &(),
349 _conn: &Connection,
350 _qh: &QueueHandle<Self>,
351 ) {
352 }
353 }
354
355 impl Dispatch<wl_seat::WlSeat, ()> for EmulationState {
356 fn event(
357 _state: &mut Self,
358 _proxy: &wl_seat::WlSeat,
359 _event: wl_seat::Event,
360 _data: &(),
361 _conn: &Connection,
362 _qh: &QueueHandle<Self>,
363 ) {
364 }
365 }
366
367 /// Input emulator combining virtual pointer and keyboard
368 pub struct InputEmulator {
369 pub pointer: VirtualPointer,
370 pub keyboard: VirtualKeyboard,
371 connection: Arc<Connection>,
372 event_queue: EventQueue<EmulationState>,
373 }
374
375 impl InputEmulator {
376 /// Create a new input emulator
377 pub fn new() -> Result<Self, EmulationError> {
378 let conn = Connection::connect_to_env()
379 .map_err(|e| EmulationError::Connection(e.to_string()))?;
380 let conn = Arc::new(conn);
381
382 let (globals, mut event_queue) = registry_queue_init(&conn)
383 .map_err(|e| EmulationError::Registry(e.to_string()))?;
384
385 let qh = event_queue.handle();
386
387 let mut state = EmulationState {
388 pointer_manager: None,
389 keyboard_manager: None,
390 seat: None,
391 };
392
393 // Bind to required globals
394 for global in globals.contents().clone_list() {
395 match global.interface.as_str() {
396 "zwlr_virtual_pointer_manager_v1" => {
397 state.pointer_manager = Some(
398 globals
399 .registry()
400 .bind(global.name, global.version.min(2), &qh, ()),
401 );
402 }
403 "zwp_virtual_keyboard_manager_v1" => {
404 state.keyboard_manager = Some(
405 globals
406 .registry()
407 .bind(global.name, global.version.min(1), &qh, ()),
408 );
409 }
410 "wl_seat" => {
411 state.seat = Some(
412 globals
413 .registry()
414 .bind(global.name, global.version.min(7), &qh, ()),
415 );
416 }
417 _ => {}
418 }
419 }
420
421 // Roundtrip to ensure we have all globals
422 event_queue
423 .roundtrip(&mut state)
424 .map_err(|e| EmulationError::Dispatch(e.to_string()))?;
425
426 // Check we have required protocols
427 let pointer_manager = state.pointer_manager.clone().ok_or_else(|| {
428 EmulationError::Protocol("zwlr_virtual_pointer_manager_v1 not available".to_string())
429 })?;
430
431 let keyboard_manager = state.keyboard_manager.clone().ok_or_else(|| {
432 EmulationError::Protocol("zwp_virtual_keyboard_manager_v1 not available".to_string())
433 })?;
434
435 let seat = state.seat.clone().ok_or_else(|| {
436 EmulationError::Protocol("wl_seat not available".to_string())
437 })?;
438
439 // Create virtual pointer
440 let pointer = pointer_manager.create_virtual_pointer(Some(&seat), &qh, ());
441
442 // Create virtual keyboard
443 let keyboard = keyboard_manager.create_virtual_keyboard(&seat, &qh, ());
444
445 // Set up a basic keymap for the keyboard
446 // This is a minimal xkb keymap
447 let keymap_string = include_str!("minimal_keymap.xkb");
448 let keymap_size = keymap_string.len() as u32;
449
450 // Create a memfd for the keymap
451 let keymap_fd = create_keymap_fd(keymap_string)
452 .map_err(|e| EmulationError::Keymap(e.to_string()))?;
453
454 keyboard.keymap(
455 1, // WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1
456 keymap_fd.as_fd(),
457 keymap_size,
458 );
459
460 // Roundtrip to ensure everything is set up
461 event_queue
462 .roundtrip(&mut state)
463 .map_err(|e| EmulationError::Dispatch(e.to_string()))?;
464
465 Ok(Self {
466 pointer: VirtualPointer {
467 pointer,
468 connection: conn.clone(),
469 },
470 keyboard: VirtualKeyboard {
471 keyboard,
472 connection: conn.clone(),
473 keymap_set: true,
474 modifier_state: ModifierTracker::default(),
475 pressed_keys: std::collections::HashSet::new(),
476 },
477 connection: conn,
478 event_queue,
479 })
480 }
481
482 /// Dispatch any pending events
483 pub fn dispatch(&mut self) -> Result<(), EmulationError> {
484 let mut state = EmulationState {
485 pointer_manager: None,
486 keyboard_manager: None,
487 seat: None,
488 };
489 self.event_queue
490 .dispatch_pending(&mut state)
491 .map_err(|e| EmulationError::Dispatch(e.to_string()))?;
492 self.connection
493 .flush()
494 .map_err(|e| EmulationError::Dispatch(e.to_string()))?;
495 Ok(())
496 }
497 }
498
499 /// Create a memfd containing the keymap string
500 fn create_keymap_fd(keymap: &str) -> std::io::Result<OwnedFd> {
501 use std::io::Write;
502
503 // Create memfd
504 let fd = rustix::fs::memfd_create(
505 "hyprkvm-keymap",
506 rustix::fs::MemfdFlags::CLOEXEC | rustix::fs::MemfdFlags::ALLOW_SEALING,
507 )?;
508
509 // Write keymap
510 let mut file = std::fs::File::from(fd);
511 file.write_all(keymap.as_bytes())?;
512 file.write_all(&[0])?; // Null terminator
513
514 // Seal the file
515 let fd = OwnedFd::from(file);
516
517 Ok(fd)
518 }
519
520 #[derive(Debug, thiserror::Error)]
521 pub enum EmulationError {
522 #[error("Failed to connect to Wayland: {0}")]
523 Connection(String),
524
525 #[error("Registry error: {0}")]
526 Registry(String),
527
528 #[error("Protocol not available: {0}")]
529 Protocol(String),
530
531 #[error("Dispatch error: {0}")]
532 Dispatch(String),
533
534 #[error("Keymap error: {0}")]
535 Keymap(String),
536 }
537
538 // Button codes (from linux/input-event-codes.h)
539 pub mod button_codes {
540 pub const BTN_LEFT: u32 = 0x110;
541 pub const BTN_RIGHT: u32 = 0x111;
542 pub const BTN_MIDDLE: u32 = 0x112;
543 pub const BTN_SIDE: u32 = 0x113;
544 pub const BTN_EXTRA: u32 = 0x114;
545 pub const BTN_FORWARD: u32 = 0x115;
546 pub const BTN_BACK: u32 = 0x116;
547 }
548
549 /// Convert keycode to human-readable name for logging
550 fn keycode_name(keycode: u32) -> &'static str {
551 match keycode {
552 1 => "ESC",
553 14 => "BACKSPACE",
554 15 => "TAB",
555 28 => "ENTER",
556 29 => "LEFTCTRL",
557 42 => "LEFTSHIFT",
558 54 => "RIGHTSHIFT",
559 56 => "LEFTALT",
560 57 => "SPACE",
561 58 => "CAPSLOCK",
562 97 => "RIGHTCTRL",
563 100 => "RIGHTALT",
564 103 => "UP",
565 105 => "LEFT",
566 106 => "RIGHT",
567 108 => "DOWN",
568 125 => "LEFTMETA",
569 126 => "RIGHTMETA",
570 _ => "OTHER",
571 }
572 }
573