use std::mem::size_of;
use windows::Win32::UI::Input::KeyboardAndMouse::{
INPUT, INPUT_0, INPUT_KEYBOARD, INPUT_MOUSE, KEYBD_EVENT_FLAGS, KEYBDINPUT,
KEYEVENTF_EXTENDEDKEY, KEYEVENTF_KEYUP, KEYEVENTF_SCANCODE, MOUSEEVENTF_ABSOLUTE,
MOUSEEVENTF_HWHEEL, MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_LEFTUP, MOUSEEVENTF_MIDDLEDOWN,
MOUSEEVENTF_MIDDLEUP, MOUSEEVENTF_MOVE, MOUSEEVENTF_RIGHTDOWN, MOUSEEVENTF_RIGHTUP,
MOUSEEVENTF_VIRTUALDESK, MOUSEEVENTF_WHEEL, MOUSEEVENTF_XDOWN, MOUSEEVENTF_XUP, MOUSEINPUT,
SendInput, VIRTUAL_KEY,
};
use crate::{Display, Event, Key, MouseButton, Simulate, keycodes::windows::get_win_codes};
impl Simulate {
pub fn simulate(event: Event) {
InputBuilder::new().add_event(event).send();
}
pub fn mouse_move(dx: i32, dy: i32) {
InputBuilder::new().add_mouse_move(dx, dy).send();
}
pub fn mouse_move_to(x: i32, y: i32) {
InputBuilder::new().add_mouse_move_to(x, y).send();
}
pub fn mouse_wheel(dx: f64, dy: f64) {
InputBuilder::new().add_mouse_wheel(dx, dy).send();
}
pub fn mouse_button(button: MouseButton, down: bool) {
InputBuilder::new().add_mouse_button(button, down).send();
}
pub fn keyboard(key: Key, down: bool) {
InputBuilder::new().add_keyboard(key, down).send();
}
}
struct InputBuilder {
inputs: Vec<INPUT>,
}
impl InputBuilder {
fn new() -> Self {
Self { inputs: Vec::new() }
}
fn add_event(self, event: Event) -> Self {
match event {
Event::MouseMove { delta, .. } => self.add_mouse_move(delta.x, delta.y),
Event::MouseWheel { delta, .. } => self.add_mouse_wheel(delta.x, delta.y),
Event::MouseDown { button, .. } => self.add_mouse_button(button, true),
Event::MouseUp { button, .. } => self.add_mouse_button(button, false),
Event::KeyDown { key, .. } => self.add_keyboard(key, true),
Event::KeyUp { key, .. } => self.add_keyboard(key, false),
}
}
fn add_mouse_move(mut self, dx: i32, dy: i32) -> Self {
self.push_mouse(MOUSEINPUT {
dx,
dy,
dwFlags: MOUSEEVENTF_MOVE,
..Default::default()
});
self
}
fn add_mouse_move_to(mut self, x: i32, y: i32) -> Self {
let (vx, vy, vw, vh) = Display::get_virtual_screen_boundary();
if vw <= 1 || vh <= 1 {
return self;
}
let dx = (((x - vx) as i64 * 65535) / (vw - 1) as i64) as i32;
let dy = (((y - vy) as i64 * 65535) / (vh - 1) as i64) as i32;
self.push_mouse(MOUSEINPUT {
dx,
dy,
dwFlags: MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK,
..Default::default()
});
self
}
fn add_mouse_button(mut self, button: MouseButton, down: bool) -> Self {
let (flags, data) = match (button, down) {
(MouseButton::Left, true) => (MOUSEEVENTF_LEFTDOWN, 0),
(MouseButton::Left, false) => (MOUSEEVENTF_LEFTUP, 0),
(MouseButton::Right, true) => (MOUSEEVENTF_RIGHTDOWN, 0),
(MouseButton::Right, false) => (MOUSEEVENTF_RIGHTUP, 0),
(MouseButton::Middle, true) => (MOUSEEVENTF_MIDDLEDOWN, 0),
(MouseButton::Middle, false) => (MOUSEEVENTF_MIDDLEUP, 0),
(MouseButton::Back, true) => (MOUSEEVENTF_XDOWN, 1),
(MouseButton::Back, false) => (MOUSEEVENTF_XUP, 1),
(MouseButton::Forward, true) => (MOUSEEVENTF_XDOWN, 2),
(MouseButton::Forward, false) => (MOUSEEVENTF_XUP, 2),
};
self.push_mouse(MOUSEINPUT {
mouseData: data,
dwFlags: flags,
..Default::default()
});
self
}
fn add_mouse_wheel(mut self, dx: f64, dy: f64) -> Self {
if dy.abs() > f64::EPSILON {
self.push_mouse(MOUSEINPUT {
mouseData: (dy * 120.0) as i32 as u32,
dwFlags: MOUSEEVENTF_WHEEL,
..Default::default()
});
}
if dx.abs() > f64::EPSILON {
self.push_mouse(MOUSEINPUT {
mouseData: (dx * 120.0) as i32 as u32,
dwFlags: MOUSEEVENTF_HWHEEL,
..Default::default()
});
}
self
}
fn add_keyboard(mut self, key: Key, down: bool) -> Self {
let (vk, scancode) = match get_win_codes(key) {
Some(codes) => codes,
None => return self,
};
let (w_vk, w_scan, mut flags) = if scancode != 0 {
(0u16, scancode as u16, KEYEVENTF_SCANCODE.0)
} else {
(vk as u16, 0u16, 0u32)
};
if (w_scan >> 8) == 0xE0 || (w_scan >> 8) == 0xE1 {
flags |= KEYEVENTF_EXTENDEDKEY.0;
}
if !down {
flags |= KEYEVENTF_KEYUP.0;
}
self.push_keyboard(KEYBDINPUT {
wVk: VIRTUAL_KEY(w_vk),
wScan: w_scan as u16,
dwFlags: KEYBD_EVENT_FLAGS(flags),
..Default::default()
});
self
}
fn push_mouse(&mut self, mi: MOUSEINPUT) {
self.inputs.push(INPUT {
r#type: INPUT_MOUSE,
Anonymous: INPUT_0 { mi },
});
}
fn push_keyboard(&mut self, ki: KEYBDINPUT) {
self.inputs.push(INPUT {
r#type: INPUT_KEYBOARD,
Anonymous: INPUT_0 { ki },
});
}
fn send(self) {
if self.inputs.is_empty() {
return;
}
unsafe {
SendInput(&self.inputs, size_of::<INPUT>() as i32);
}
}
}