use crate::error::*;
use crate::platform::Simulation;
use crate::platform::windows::mapping::{WindowsScancode, button_to_platform, key_to_platform};
use crate::types::*;
use smallvec::SmallVec;
use std::time::Duration;
use windows::Win32::UI::Input::KeyboardAndMouse::{
INPUT, INPUT_KEYBOARD, INPUT_MOUSE, KEYBDINPUT, KEYEVENTF_EXTENDEDKEY, KEYEVENTF_KEYUP,
KEYEVENTF_SCANCODE, MOUSEEVENTF_ABSOLUTE, MOUSEEVENTF_HWHEEL, MOUSEEVENTF_MOVE,
MOUSEEVENTF_VIRTUALDESK, MOUSEEVENTF_WHEEL, MOUSEINPUT, SendInput,
};
use windows::Win32::UI::WindowsAndMessaging::{
GetSystemMetrics, SM_CXVIRTUALSCREEN, SM_CYVIRTUALSCREEN, SM_XVIRTUALSCREEN, SM_YVIRTUALSCREEN,
};
pub struct Backend {
events: SmallVec<[INPUT; 32]>,
screen: Screen,
}
impl Backend {
pub(crate) fn new() -> Result<Self> {
Ok(Self {
events: SmallVec::new(),
screen: Screen {
width: unsafe { GetSystemMetrics(SM_CXVIRTUALSCREEN) },
height: unsafe { GetSystemMetrics(SM_CYVIRTUALSCREEN) },
left: unsafe { GetSystemMetrics(SM_XVIRTUALSCREEN) },
top: unsafe { GetSystemMetrics(SM_YVIRTUALSCREEN) },
},
})
}
fn to_absolute_x(&self, x: i32) -> i32 {
let normalized_x = x - self.screen.left;
(normalized_x * 65536) / self.screen.width
}
fn to_absolute_y(&self, y: i32) -> i32 {
let normalized_y = y - self.screen.top;
(normalized_y * 65536) / self.screen.height
}
fn send_input(&mut self) -> Result<()> {
unsafe {
let result = SendInput(&self.events, size_of::<INPUT>() as i32);
if result as usize != self.events.len() {
return Err(KeyflowError::PlatformError(format!(
"SendInput failed: expected {}, sent {}",
self.events.len(),
result
)));
}
}
self.events.clear();
Ok(())
}
fn add_key(&mut self, scancode: WindowsScancode, released: bool) {
let mut flags = KEYEVENTF_SCANCODE;
if scancode.extended {
flags |= KEYEVENTF_EXTENDEDKEY;
}
if released {
flags |= KEYEVENTF_KEYUP;
}
self.events.push(INPUT {
r#type: INPUT_KEYBOARD,
Anonymous: ::windows::Win32::UI::Input::KeyboardAndMouse::INPUT_0 {
ki: KEYBDINPUT {
wVk: windows::Win32::UI::Input::KeyboardAndMouse::VIRTUAL_KEY(0),
wScan: scancode.code,
dwFlags: flags,
time: 0,
dwExtraInfo: 0,
},
},
});
}
fn add_button(&mut self, button: Button, released: bool) {
if let Some((flags, mouse_data)) = button_to_platform(button, released) {
self.events.push(INPUT {
r#type: INPUT_MOUSE,
Anonymous: windows::Win32::UI::Input::KeyboardAndMouse::INPUT_0 {
mi: MOUSEINPUT {
dx: 0,
dy: 0,
mouseData: mouse_data,
dwFlags: flags,
time: 0,
dwExtraInfo: 0,
},
},
});
}
}
fn add_movement(&mut self, movement: Movement) {
let (dx, dy, flag) = match movement {
Movement::Relative { dx, dy } => (dx, dy, MOUSEEVENTF_MOVE),
Movement::Absolute { x, y } => {
let x = x.clamp(self.screen.left, self.screen.left + self.screen.width);
let y = y.clamp(self.screen.top, self.screen.top + self.screen.height);
(
self.to_absolute_x(x),
self.to_absolute_y(y),
MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK,
)
}
};
self.events.push(INPUT {
r#type: INPUT_MOUSE,
Anonymous: windows::Win32::UI::Input::KeyboardAndMouse::INPUT_0 {
mi: MOUSEINPUT {
dx,
dy,
mouseData: 0,
dwFlags: flag,
time: 0,
dwExtraInfo: 0,
},
},
});
}
fn add_scroll(&mut self, scroll: Scroll) {
let (delta, flag) = match scroll {
Scroll::Vertical(delta) => ((delta * 120) as u32, MOUSEEVENTF_WHEEL),
Scroll::Horizontal(delta) => ((delta * 120) as u32, MOUSEEVENTF_HWHEEL),
};
self.events.push(INPUT {
r#type: INPUT_MOUSE,
Anonymous: windows::Win32::UI::Input::KeyboardAndMouse::INPUT_0 {
mi: MOUSEINPUT {
dx: 0,
dy: 0,
mouseData: delta,
dwFlags: flag,
time: 0,
dwExtraInfo: 0,
},
},
});
}
}
impl Simulation for Backend {
fn send_key(&mut self, key: Key, action: Action) -> Result<()> {
if let Some(scancode) = key_to_platform(key) {
match action {
Action::Press => {
self.add_key(scancode, false);
}
Action::Release => {
self.add_key(scancode, true);
}
Action::Click => {
self.add_key(scancode, false);
self.send_input()?;
std::thread::sleep(Duration::from_millis(1));
self.add_key(scancode, true);
}
}
}
self.send_input()
}
fn send_button(&mut self, button: Button, action: Action) -> Result<()> {
match action {
Action::Press => {
self.add_button(button, false);
}
Action::Release => {
self.add_button(button, true);
}
Action::Click => {
self.add_button(button, false);
self.send_input()?;
std::thread::sleep(Duration::from_millis(1));
self.add_button(button, true);
}
}
self.send_input()
}
fn send_movement(&mut self, movement: Movement) -> Result<()> {
self.add_movement(movement);
self.send_input()
}
fn send_scroll(&mut self, scroll: Scroll) -> Result<()> {
self.add_scroll(scroll);
self.send_input()
}
fn send_batch(&mut self, events: Vec<InputEvent>) -> Result<()> {
for event in events {
match event {
InputEvent::KeyEvent { key, action } => {
if let Some(scancode) = key_to_platform(key) {
match action {
Action::Press => self.add_key(scancode, false),
Action::Release => self.add_key(scancode, true),
Action::Click => {
self.add_key(scancode, false);
self.send_input()?;
std::thread::sleep(Duration::from_millis(1));
self.add_key(scancode, true);
}
}
}
}
InputEvent::ButtonEvent { button, action } => match action {
Action::Press => self.add_button(button, false),
Action::Release => self.add_button(button, true),
Action::Click => {
self.add_button(button, false);
self.send_input()?;
std::thread::sleep(Duration::from_millis(1));
self.add_button(button, true);
}
},
InputEvent::MouseMove(movement) => {
self.add_movement(movement);
}
InputEvent::MouseScroll(scroll) => {
self.add_scroll(scroll);
}
InputEvent::Delay(duration) => {
if !self.events.is_empty() {
self.send_input()?;
}
std::thread::sleep(duration);
}
}
}
if !self.events.is_empty() {
self.send_input()?
}
Ok(())
}
}