use super::error::WindowsEmulationCreationError; use input_event::{ scancode, Event, KeyboardEvent, PointerEvent, BTN_BACK, BTN_FORWARD, BTN_LEFT, BTN_MIDDLE, BTN_RIGHT, }; use async_trait::async_trait; use std::ops::BitOrAssign; use std::time::Duration; use tokio::task::AbortHandle; use windows::Win32::UI::Input::KeyboardAndMouse::{ SendInput, INPUT_0, KEYEVENTF_EXTENDEDKEY, MOUSEEVENTF_XDOWN, MOUSEEVENTF_XUP, }; use windows::Win32::UI::Input::KeyboardAndMouse::{ INPUT, INPUT_KEYBOARD, INPUT_MOUSE, KEYBDINPUT, KEYEVENTF_KEYUP, KEYEVENTF_SCANCODE, MOUSEEVENTF_HWHEEL, MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_LEFTUP, MOUSEEVENTF_MIDDLEDOWN, MOUSEEVENTF_MIDDLEUP, MOUSEEVENTF_MOVE, MOUSEEVENTF_RIGHTDOWN, MOUSEEVENTF_RIGHTUP, MOUSEEVENTF_WHEEL, MOUSEINPUT, }; use windows::Win32::UI::WindowsAndMessaging::{XBUTTON1, XBUTTON2}; use super::{EmulationHandle, InputEmulation}; const DEFAULT_REPEAT_DELAY: Duration = Duration::from_millis(500); const DEFAULT_REPEAT_INTERVAL: Duration = Duration::from_millis(32); pub struct WindowsEmulation { repeat_task: Option, } impl WindowsEmulation { pub fn new() -> Result { Ok(Self { repeat_task: None }) } } #[async_trait] impl InputEmulation for WindowsEmulation { async fn consume(&mut self, event: Event, _: EmulationHandle) { match event { Event::Pointer(pointer_event) => match pointer_event { PointerEvent::Motion { time: _, relative_x, relative_y, } => { rel_mouse(relative_x as i32, relative_y as i32); } PointerEvent::Button { time: _, button, state, } => mouse_button(button, state), PointerEvent::Axis { time: _, axis, value, } => scroll(axis, value as i32), PointerEvent::AxisDiscrete120 { axis, value } => scroll(axis, value), PointerEvent::Frame {} => {} }, Event::Keyboard(keyboard_event) => match keyboard_event { KeyboardEvent::Key { time: _, key, state, } => { match state { // pressed 0 => self.kill_repeat_task(), 1 => self.spawn_repeat_task(key).await, _ => {} } key_event(key, state) } KeyboardEvent::Modifiers { .. } => {} }, _ => {} } } async fn create(&mut self, _handle: EmulationHandle) {} async fn destroy(&mut self, _handle: EmulationHandle) {} } impl WindowsEmulation { async fn spawn_repeat_task(&mut self, key: u32) { // there can only be one repeating key and it's // always the last to be pressed self.kill_repeat_task(); let repeat_task = tokio::task::spawn_local(async move { tokio::time::sleep(DEFAULT_REPEAT_DELAY).await; loop { key_event(key, 1); tokio::time::sleep(DEFAULT_REPEAT_INTERVAL).await; } }); self.repeat_task = Some(repeat_task.abort_handle()); } fn kill_repeat_task(&mut self) { if let Some(task) = self.repeat_task.take() { task.abort(); } } } fn send_input_safe(input: INPUT) { unsafe { loop { /* retval = number of successfully submitted events */ if SendInput(&[input], std::mem::size_of::() as i32) > 0 { break; } } } } fn send_mouse_input(mi: MOUSEINPUT) { send_input_safe(INPUT { r#type: INPUT_MOUSE, Anonymous: INPUT_0 { mi }, }); } fn send_keyboard_input(ki: KEYBDINPUT) { send_input_safe(INPUT { r#type: INPUT_KEYBOARD, Anonymous: INPUT_0 { ki }, }); } fn rel_mouse(dx: i32, dy: i32) { let mi = MOUSEINPUT { dx, dy, mouseData: 0, dwFlags: MOUSEEVENTF_MOVE, time: 0, dwExtraInfo: 0, }; send_mouse_input(mi); } fn mouse_button(button: u32, state: u32) { let dw_flags = match state { 0 => match button { BTN_LEFT => MOUSEEVENTF_LEFTUP, BTN_RIGHT => MOUSEEVENTF_RIGHTUP, BTN_MIDDLE => MOUSEEVENTF_MIDDLEUP, BTN_BACK => MOUSEEVENTF_XUP, BTN_FORWARD => MOUSEEVENTF_XUP, _ => return, }, 1 => match button { BTN_LEFT => MOUSEEVENTF_LEFTDOWN, BTN_RIGHT => MOUSEEVENTF_RIGHTDOWN, BTN_MIDDLE => MOUSEEVENTF_MIDDLEDOWN, BTN_BACK => MOUSEEVENTF_XDOWN, BTN_FORWARD => MOUSEEVENTF_XDOWN, _ => return, }, _ => return, }; let mouse_data = match button { BTN_BACK => XBUTTON1 as u32, BTN_FORWARD => XBUTTON2 as u32, _ => 0, }; let mi = MOUSEINPUT { dx: 0, dy: 0, // no movement mouseData: mouse_data, dwFlags: dw_flags, time: 0, dwExtraInfo: 0, }; send_mouse_input(mi); } fn scroll(axis: u8, value: i32) { let event_type = match axis { 0 => MOUSEEVENTF_WHEEL, 1 => MOUSEEVENTF_HWHEEL, _ => return, }; let mi = MOUSEINPUT { dx: 0, dy: 0, mouseData: -value as u32, dwFlags: event_type, time: 0, dwExtraInfo: 0, }; send_mouse_input(mi); } fn key_event(key: u32, state: u8) { let scancode = match linux_keycode_to_windows_scancode(key) { Some(code) => code, None => return, }; let extended = scancode > 0xff; let scancode = scancode & 0xff; let mut flags = KEYEVENTF_SCANCODE; if extended { flags.bitor_assign(KEYEVENTF_EXTENDEDKEY); } if state == 0 { flags.bitor_assign(KEYEVENTF_KEYUP); } let ki = KEYBDINPUT { wVk: Default::default(), wScan: scancode, dwFlags: flags, time: 0, dwExtraInfo: 0, }; send_keyboard_input(ki); } fn linux_keycode_to_windows_scancode(linux_keycode: u32) -> Option { let linux_scancode = match scancode::Linux::try_from(linux_keycode) { Ok(s) => s, Err(_) => { log::warn!("unknown keycode: {linux_keycode}"); return None; } }; log::trace!("linux code: {linux_scancode:?}"); let windows_scancode = match scancode::Windows::try_from(linux_scancode) { Ok(s) => s, Err(_) => { log::warn!("failed to translate linux code into windows scancode: {linux_scancode:?}"); return None; } }; log::trace!("windows code: {windows_scancode:?}"); Some(windows_scancode as u16) }