use windows::Win32::UI::Input::KeyboardAndMouse::{
SendInput, INPUT, INPUT_0, INPUT_MOUSE, MOUSEEVENTF_ABSOLUTE, MOUSEEVENTF_LEFTDOWN,
MOUSEEVENTF_LEFTUP, MOUSEEVENTF_MIDDLEDOWN, MOUSEEVENTF_MIDDLEUP, MOUSEEVENTF_MOVE,
MOUSEEVENTF_RIGHTDOWN, MOUSEEVENTF_RIGHTUP, MOUSEEVENTF_WHEEL, MOUSEINPUT, MOUSE_EVENT_FLAGS,
};
use windows::Win32::UI::WindowsAndMessaging::SetCursorPos;
#[inline]
pub fn set_cursor_pos(x: i32, y: i32) -> Result<(), SendMouseInputError> {
unsafe {
SetCursorPos(x, y).map_err(|_| SendMouseInputError::SetCursorPosFailed)?;
Ok(())
}
}
#[inline]
pub fn execute_inputs(inputs: &[INPUT]) -> Result<(), SendMouseInputError> {
if inputs.is_empty() {
return Ok(());
}
let result = unsafe { SendInput(inputs, std::mem::size_of::<INPUT>() as i32) };
if result == 0 {
Err(SendMouseInputError::SendInputFailed)
} else {
Ok(())
}
}
#[inline]
pub fn execute_single_input(input: &INPUT) -> Result<(), SendMouseInputError> {
execute_inputs(&[input.clone()])
}
#[inline]
pub fn normalize_coords(x: i32, y: i32) -> (i32, i32) {
let screen_width = unsafe {
windows::Win32::UI::WindowsAndMessaging::GetSystemMetrics(
windows::Win32::UI::WindowsAndMessaging::SM_CXSCREEN,
)
};
let screen_height = unsafe {
windows::Win32::UI::WindowsAndMessaging::GetSystemMetrics(
windows::Win32::UI::WindowsAndMessaging::SM_CYSCREEN,
)
};
let nx = ((x as i64) * 65535 + (screen_width as i64) - 1) / (screen_width as i64);
let ny = ((y as i64) * 65535 + (screen_height as i64) - 1) / (screen_height as i64);
(nx as i32, ny as i32)
}
#[inline]
pub fn build_mouse_input(flags: MOUSE_EVENT_FLAGS, dx: i32, dy: i32, data: u32) -> INPUT {
INPUT {
r#type: INPUT_MOUSE,
Anonymous: INPUT_0 {
mi: MOUSEINPUT {
dx,
dy,
mouseData: data,
dwFlags: flags,
time: 0,
dwExtraInfo: 0,
},
},
}
}
#[inline]
pub fn build_click_left() -> [INPUT; 2] {
[
build_mouse_input(MOUSEEVENTF_LEFTDOWN, 0, 0, 0),
build_mouse_input(MOUSEEVENTF_LEFTUP, 0, 0, 0),
]
}
#[inline]
pub fn build_click_left_at(x: i32, y: i32) -> Vec<INPUT> {
let (nx, ny) = normalize_coords(x, y);
vec![
build_mouse_input(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, nx, ny, 0),
build_mouse_input(MOUSEEVENTF_LEFTDOWN, 0, 0, 0),
build_mouse_input(MOUSEEVENTF_LEFTUP, 0, 0, 0),
]
}
#[inline]
pub fn build_click_right() -> [INPUT; 2] {
[
build_mouse_input(MOUSEEVENTF_RIGHTDOWN, 0, 0, 0),
build_mouse_input(MOUSEEVENTF_RIGHTUP, 0, 0, 0),
]
}
#[inline]
pub fn build_click_right_at(x: i32, y: i32) -> Vec<INPUT> {
let (nx, ny) = normalize_coords(x, y);
vec![
build_mouse_input(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, nx, ny, 0),
build_mouse_input(MOUSEEVENTF_RIGHTDOWN, 0, 0, 0),
build_mouse_input(MOUSEEVENTF_RIGHTUP, 0, 0, 0),
]
}
#[inline]
pub fn build_click_middle() -> [INPUT; 2] {
[
build_mouse_input(MOUSEEVENTF_MIDDLEDOWN, 0, 0, 0),
build_mouse_input(MOUSEEVENTF_MIDDLEUP, 0, 0, 0),
]
}
#[inline]
pub fn build_click_middle_at(x: i32, y: i32) -> Vec<INPUT> {
let (nx, ny) = normalize_coords(x, y);
vec![
build_mouse_input(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, nx, ny, 0),
build_mouse_input(MOUSEEVENTF_MIDDLEDOWN, 0, 0, 0),
build_mouse_input(MOUSEEVENTF_MIDDLEUP, 0, 0, 0),
]
}
#[inline]
pub fn build_double_click_left() -> Vec<INPUT> {
vec![
build_mouse_input(MOUSEEVENTF_LEFTDOWN, 0, 0, 0),
build_mouse_input(MOUSEEVENTF_LEFTUP, 0, 0, 0),
build_mouse_input(MOUSEEVENTF_LEFTDOWN, 0, 0, 0),
build_mouse_input(MOUSEEVENTF_LEFTUP, 0, 0, 0),
]
}
#[inline]
pub fn build_double_click_left_at(x: i32, y: i32) -> Vec<INPUT> {
let (nx, ny) = normalize_coords(x, y);
vec![
build_mouse_input(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, nx, ny, 0),
build_mouse_input(MOUSEEVENTF_LEFTDOWN, 0, 0, 0),
build_mouse_input(MOUSEEVENTF_LEFTUP, 0, 0, 0),
build_mouse_input(MOUSEEVENTF_LEFTDOWN, 0, 0, 0),
build_mouse_input(MOUSEEVENTF_LEFTUP, 0, 0, 0),
]
}
#[inline]
pub fn build_press_left() -> INPUT {
build_mouse_input(MOUSEEVENTF_LEFTDOWN, 0, 0, 0)
}
#[inline]
pub fn build_press_left_at(x: i32, y: i32) -> Vec<INPUT> {
let (nx, ny) = normalize_coords(x, y);
vec![
build_mouse_input(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, nx, ny, 0),
build_mouse_input(MOUSEEVENTF_LEFTDOWN, 0, 0, 0),
]
}
#[inline]
pub fn build_release_left() -> INPUT {
build_mouse_input(MOUSEEVENTF_LEFTUP, 0, 0, 0)
}
#[inline]
pub fn build_release_left_at(x: i32, y: i32) -> Vec<INPUT> {
let (nx, ny) = normalize_coords(x, y);
vec![
build_mouse_input(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, nx, ny, 0),
build_mouse_input(MOUSEEVENTF_LEFTUP, 0, 0, 0),
]
}
#[inline]
pub fn build_move(x: i32, y: i32) -> INPUT {
let (nx, ny) = normalize_coords(x, y);
build_mouse_input(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, nx, ny, 0)
}
#[inline]
pub fn build_move_relative(dx: i32, dy: i32) -> INPUT {
build_mouse_input(MOUSEEVENTF_MOVE, dx, dy, 0)
}
#[inline]
pub fn build_scroll_up(delta: i32) -> INPUT {
build_mouse_input(MOUSEEVENTF_WHEEL, 0, 0, delta as u32)
}
#[inline]
pub fn build_scroll_up_at(x: i32, y: i32, delta: i32) -> Vec<INPUT> {
let (nx, ny) = normalize_coords(x, y);
vec![
build_mouse_input(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, nx, ny, 0),
build_mouse_input(MOUSEEVENTF_WHEEL, 0, 0, delta as u32),
]
}
#[inline]
pub fn build_scroll_down(delta: i32) -> INPUT {
build_mouse_input(MOUSEEVENTF_WHEEL, 0, 0, (-delta) as u32)
}
#[inline]
pub fn build_scroll_down_at(x: i32, y: i32, delta: i32) -> Vec<INPUT> {
let (nx, ny) = normalize_coords(x, y);
vec![
build_mouse_input(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, nx, ny, 0),
build_mouse_input(MOUSEEVENTF_WHEEL, 0, 0, (-delta) as u32),
]
}
#[derive(Debug, Clone)]
pub enum SendMouseInputError {
SendInputFailed,
SetCursorPosFailed,
}
impl std::fmt::Display for SendMouseInputError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SendMouseInputError::SendInputFailed => write!(f, "SendInput API call failed"),
SendMouseInputError::SetCursorPosFailed => write!(f, "SetCursorPos API call failed"),
}
}
}
impl std::error::Error for SendMouseInputError {}
pub struct SendInputMouse;
impl SendInputMouse {
pub fn new() -> Self {
Self
}
pub fn click_left(&self) -> Result<(), SendMouseInputError> {
let inputs = build_click_left();
execute_inputs(&inputs)
}
pub fn click_left_at(&self, x: i32, y: i32) -> Result<(), SendMouseInputError> {
let inputs = build_click_left_at(x, y);
execute_inputs(&inputs)
}
pub fn click_right(&self) -> Result<(), SendMouseInputError> {
let inputs = build_click_right();
execute_inputs(&inputs)
}
pub fn click_right_at(&self, x: i32, y: i32) -> Result<(), SendMouseInputError> {
let inputs = build_click_right_at(x, y);
execute_inputs(&inputs)
}
pub fn click_middle(&self) -> Result<(), SendMouseInputError> {
let inputs = build_click_middle();
execute_inputs(&inputs)
}
pub fn click_middle_at(&self, x: i32, y: i32) -> Result<(), SendMouseInputError> {
let inputs = build_click_middle_at(x, y);
execute_inputs(&inputs)
}
pub fn double_click_left(&self) -> Result<(), SendMouseInputError> {
let inputs = build_double_click_left();
execute_inputs(&inputs)
}
pub fn double_click_left_at(&self, x: i32, y: i32) -> Result<(), SendMouseInputError> {
let inputs = build_double_click_left_at(x, y);
execute_inputs(&inputs)
}
pub fn press_left(&self) -> Result<(), SendMouseInputError> {
let input = build_press_left();
execute_single_input(&input)
}
pub fn release_left(&self) -> Result<(), SendMouseInputError> {
let input = build_release_left();
execute_single_input(&input)
}
pub fn move_to(&self, x: i32, y: i32) -> Result<(), SendMouseInputError> {
let input = build_move(x, y);
execute_single_input(&input)
}
pub fn move_relative(&self, dx: i32, dy: i32) -> Result<(), SendMouseInputError> {
let input = build_move_relative(dx, dy);
execute_single_input(&input)
}
pub fn scroll_up(&self, delta: i32) -> Result<(), SendMouseInputError> {
let input = build_scroll_up(delta);
execute_single_input(&input)
}
pub fn scroll_down(&self, delta: i32) -> Result<(), SendMouseInputError> {
let input = build_scroll_down(delta);
execute_single_input(&input)
}
}
impl Default for SendInputMouse {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_build_and_execute_inputs() {
let inputs = build_click_left();
assert_eq!(inputs.len(), 2);
assert_eq!(inputs[0].r#type, INPUT_MOUSE);
assert_eq!(inputs[1].r#type, INPUT_MOUSE);
}
#[test]
fn test_build_click_at_inputs() {
let inputs = build_click_left_at(100, 200);
assert_eq!(inputs.len(), 3);
}
#[test]
fn test_atomic_functions_compile() {
let inputs = build_click_left();
let _result = execute_inputs(&inputs);
assert!(true); }
}