Files
lan-mouse/input-emulation/src/windows.rs
NeoTheFox 3f13714d8a Add rustfmt.toml for explicit styling (#348)
* Propose an explicit .rustfnt.toml
Use 2024 style, 4 spaces for tabs and epand the default width a tad

* Auto-format the existing code with new rules
2025-11-02 11:52:01 +01:00

238 lines
6.9 KiB
Rust

use super::error::{EmulationError, WindowsEmulationCreationError};
use input_event::{
BTN_BACK, BTN_FORWARD, BTN_LEFT, BTN_MIDDLE, BTN_RIGHT, Event, KeyboardEvent, PointerEvent,
scancode,
};
use async_trait::async_trait;
use std::ops::BitOrAssign;
use std::time::Duration;
use tokio::task::AbortHandle;
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::Input::KeyboardAndMouse::{
INPUT_0, KEYEVENTF_EXTENDEDKEY, MOUSEEVENTF_XDOWN, MOUSEEVENTF_XUP, SendInput,
};
use windows::Win32::UI::WindowsAndMessaging::{XBUTTON1, XBUTTON2};
use super::{Emulation, EmulationHandle};
const DEFAULT_REPEAT_DELAY: Duration = Duration::from_millis(500);
const DEFAULT_REPEAT_INTERVAL: Duration = Duration::from_millis(32);
pub(crate) struct WindowsEmulation {
repeat_task: Option<AbortHandle>,
}
impl WindowsEmulation {
pub(crate) fn new() -> Result<Self, WindowsEmulationCreationError> {
Ok(Self { repeat_task: None })
}
}
#[async_trait]
impl Emulation for WindowsEmulation {
async fn consume(&mut self, event: Event, _: EmulationHandle) -> Result<(), EmulationError> {
match event {
Event::Pointer(pointer_event) => match pointer_event {
PointerEvent::Motion { time: _, dx, dy } => {
rel_mouse(dx as i32, dy 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),
},
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 { .. } => {}
},
}
// FIXME
Ok(())
}
async fn create(&mut self, _handle: EmulationHandle) {}
async fn destroy(&mut self, _handle: EmulationHandle) {}
async fn terminate(&mut self) {}
}
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::<INPUT>() 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<u16> {
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)
}