use crate::*;
use lazy_static::lazy_static;
use std::collections::HashMap;
use std::mem;
use winapi::um::winuser::{self, INPUT};
use winapi::um::{errhandlingapi, winbase};
#[inline]
fn key_to_virtual_keycode(key: Key) -> Result<i32> {
assert!(
!key.is_mouse_button(),
"This should have been handled already"
);
lazy_static! {
static ref MAP: HashMap<Key, i32> = {
let mut m = HashMap::new();
m.insert(Key::Backspace, winuser::VK_BACK);
m.insert(Key::Tab, winuser::VK_TAB);
m.insert(Key::Clear, winuser::VK_CLEAR);
m.insert(Key::Enter, winuser::VK_RETURN);
m.insert(Key::Shift, winuser::VK_SHIFT);
m.insert(Key::LeftShift, winuser::VK_LSHIFT);
m.insert(Key::RightShift, winuser::VK_RSHIFT);
m.insert(Key::Control, winuser::VK_CONTROL);
m.insert(Key::LeftControl, winuser::VK_LCONTROL);
m.insert(Key::RightControl, winuser::VK_RCONTROL);
m.insert(Key::Menu, winuser::VK_MENU);
m.insert(Key::LeftMenu, winuser::VK_LMENU);
m.insert(Key::RightMenu, winuser::VK_RMENU);
m.insert(Key::Home, winuser::VK_HOME);
m.insert(Key::LeftWindows, winuser::VK_LWIN);
m.insert(Key::RightWindows, winuser::VK_RWIN);
m.insert(Key::Pause, winuser::VK_PAUSE);
m.insert(Key::CapsLock, winuser::VK_CAPITAL);
m.insert(Key::Escape, winuser::VK_ESCAPE);
m.insert(Key::Space, winuser::VK_SPACE);
m.insert(Key::PageUp, winuser::VK_PRIOR);
m.insert(Key::PageDown, winuser::VK_NEXT);
m.insert(Key::End, winuser::VK_END);
m.insert(Key::Select, winuser::VK_SELECT);
m.insert(Key::Print, winuser::VK_PRINT);
m.insert(Key::Execute, winuser::VK_EXECUTE);
m.insert(Key::PrintScreen, winuser::VK_SNAPSHOT);
m.insert(Key::Insert, winuser::VK_INSERT);
m.insert(Key::Delete, winuser::VK_DELETE);
m.insert(Key::Help, winuser::VK_HELP);
m.insert(Key::Apps, winuser::VK_APPS);
m.insert(Key::Sleep, winuser::VK_SLEEP);
m.insert(Key::BrowserBack, winuser::VK_BROWSER_BACK);
m.insert(Key::BrowserForward, winuser::VK_BROWSER_FORWARD);
m.insert(Key::BrowserRefresh, winuser::VK_BROWSER_REFRESH);
m.insert(Key::BrowserStop, winuser::VK_BROWSER_STOP);
m.insert(Key::BrowserSearch, winuser::VK_BROWSER_SEARCH);
m.insert(Key::BrowserFavorites, winuser::VK_BROWSER_FAVORITES);
m.insert(Key::BrowserHome, winuser::VK_BROWSER_HOME);
m.insert(Key::VolumeMute, winuser::VK_VOLUME_MUTE);
m.insert(Key::VolumeDown, winuser::VK_VOLUME_DOWN);
m.insert(Key::VolumeUp, winuser::VK_VOLUME_UP);
m.insert(Key::MediaNextTrack, winuser::VK_MEDIA_NEXT_TRACK);
m.insert(Key::MediaPreviousTrack, winuser::VK_MEDIA_PREV_TRACK);
m.insert(Key::MediaStop, winuser::VK_MEDIA_STOP);
m.insert(Key::MediaPlayPause, winuser::VK_MEDIA_PLAY_PAUSE);
m.insert(Key::LaunchMail, winuser::VK_LAUNCH_MAIL);
m.insert(Key::SelectMedia, winuser::VK_SELECT);
m.insert(Key::LaunchApp1, winuser::VK_LAUNCH_APP1);
m.insert(Key::LaunchApp2, winuser::VK_LAUNCH_APP2);
m.insert(Key::Attn, winuser::VK_ATTN);
m.insert(Key::Crsel, winuser::VK_CRSEL);
m.insert(Key::Exsel, winuser::VK_EXSEL);
m.insert(Key::Ereof, winuser::VK_EREOF);
m.insert(Key::Play, winuser::VK_PLAY);
m.insert(Key::Zoom, winuser::VK_ZOOM);
m.insert(Key::NoName, winuser::VK_NONAME);
m.insert(Key::Left, winuser::VK_LEFT);
m.insert(Key::Up, winuser::VK_UP);
m.insert(Key::Right, winuser::VK_RIGHT);
m.insert(Key::Down, winuser::VK_DOWN);
m.insert(Key::Kana, winuser::VK_KANA);
m.insert(Key::Hangul, winuser::VK_HANGUL);
m.insert(Key::Junja, winuser::VK_JUNJA);
m.insert(Key::Final, winuser::VK_FINAL);
m.insert(Key::Kanji, winuser::VK_KANJI);
m.insert(Key::Convert, winuser::VK_CONVERT);
m.insert(Key::Nonconvert, winuser::VK_NONCONVERT);
m.insert(Key::Accept, winuser::VK_ACCEPT);
m.insert(Key::ModeChange, winuser::VK_MODECHANGE);
m.insert(Key::ProcessKey, winuser::VK_PROCESSKEY);
m.insert(Key::ImeOn, 0x16);
m.insert(Key::ImeOff, 0x1a);
m.insert(Key::Oem1, winuser::VK_OEM_1);
m.insert(Key::OemPlus, winuser::VK_OEM_PLUS);
m.insert(Key::OemComma, winuser::VK_OEM_COMMA);
m.insert(Key::OemMinus, winuser::VK_OEM_MINUS);
m.insert(Key::OemPeriod, winuser::VK_OEM_PERIOD);
m.insert(Key::Oem2, winuser::VK_OEM_2);
m.insert(Key::Oem3, winuser::VK_OEM_3);
m.insert(Key::Oem4, winuser::VK_OEM_4);
m.insert(Key::Oem5, winuser::VK_OEM_5);
m.insert(Key::Oem6, winuser::VK_OEM_6);
m.insert(Key::Oem7, winuser::VK_OEM_7);
m.insert(Key::Oem8, winuser::VK_OEM_8);
m.insert(Key::Oem102, winuser::VK_OEM_102);
m.insert(Key::OemClear, winuser::VK_OEM_CLEAR);
m.insert(Key::_0, '0' as _);
m.insert(Key::_1, '1' as _);
m.insert(Key::_2, '2' as _);
m.insert(Key::_3, '3' as _);
m.insert(Key::_4, '4' as _);
m.insert(Key::_5, '5' as _);
m.insert(Key::_6, '6' as _);
m.insert(Key::_7, '7' as _);
m.insert(Key::_8, '8' as _);
m.insert(Key::_9, '9' as _);
m.insert(Key::Numpad0, winuser::VK_NUMPAD0);
m.insert(Key::Numpad1, winuser::VK_NUMPAD1);
m.insert(Key::Numpad2, winuser::VK_NUMPAD2);
m.insert(Key::Numpad3, winuser::VK_NUMPAD3);
m.insert(Key::Numpad4, winuser::VK_NUMPAD4);
m.insert(Key::Numpad5, winuser::VK_NUMPAD5);
m.insert(Key::Numpad6, winuser::VK_NUMPAD6);
m.insert(Key::Numpad7, winuser::VK_NUMPAD7);
m.insert(Key::Numpad8, winuser::VK_NUMPAD8);
m.insert(Key::Numpad9, winuser::VK_NUMPAD9);
m.insert(Key::Multiply, winuser::VK_MULTIPLY);
m.insert(Key::Add, winuser::VK_ADD);
m.insert(Key::Subtract, winuser::VK_SUBTRACT);
m.insert(Key::Decimal, winuser::VK_DECIMAL);
m.insert(Key::Divide, winuser::VK_DIVIDE);
m.insert(Key::Separator, winuser::VK_SEPARATOR);
m.insert(Key::NumLock, winuser::VK_NUMLOCK);
m.insert(Key::ScrollLock, winuser::VK_SCROLL);
m.insert(Key::A, 'A' as _);
m.insert(Key::B, 'B' as _);
m.insert(Key::C, 'C' as _);
m.insert(Key::D, 'D' as _);
m.insert(Key::E, 'E' as _);
m.insert(Key::F, 'F' as _);
m.insert(Key::G, 'G' as _);
m.insert(Key::H, 'H' as _);
m.insert(Key::I, 'I' as _);
m.insert(Key::J, 'J' as _);
m.insert(Key::K, 'K' as _);
m.insert(Key::L, 'L' as _);
m.insert(Key::M, 'M' as _);
m.insert(Key::N, 'N' as _);
m.insert(Key::O, 'O' as _);
m.insert(Key::P, 'P' as _);
m.insert(Key::Q, 'Q' as _);
m.insert(Key::R, 'R' as _);
m.insert(Key::S, 'S' as _);
m.insert(Key::T, 'T' as _);
m.insert(Key::U, 'U' as _);
m.insert(Key::V, 'V' as _);
m.insert(Key::W, 'W' as _);
m.insert(Key::X, 'X' as _);
m.insert(Key::Y, 'Y' as _);
m.insert(Key::Z, 'Z' as _);
m.insert(Key::F1, winuser::VK_F1);
m.insert(Key::F2, winuser::VK_F2);
m.insert(Key::F3, winuser::VK_F3);
m.insert(Key::F4, winuser::VK_F4);
m.insert(Key::F5, winuser::VK_F5);
m.insert(Key::F6, winuser::VK_F6);
m.insert(Key::F7, winuser::VK_F7);
m.insert(Key::F8, winuser::VK_F8);
m.insert(Key::F9, winuser::VK_F9);
m.insert(Key::F10, winuser::VK_F10);
m.insert(Key::F11, winuser::VK_F11);
m.insert(Key::F12, winuser::VK_F12);
m.insert(Key::F13, winuser::VK_F13);
m.insert(Key::F14, winuser::VK_F14);
m.insert(Key::F15, winuser::VK_F15);
m.insert(Key::F16, winuser::VK_F16);
m.insert(Key::F17, winuser::VK_F17);
m.insert(Key::F18, winuser::VK_F18);
m.insert(Key::F19, winuser::VK_F19);
m.insert(Key::F20, winuser::VK_F20);
m.insert(Key::F21, winuser::VK_F21);
m.insert(Key::F22, winuser::VK_F22);
m.insert(Key::F23, winuser::VK_F23);
m.insert(Key::F24, winuser::VK_F24);
m
};
}
MAP.get(&key).copied().ok_or(Error::UnsupportedKey(key))
}
fn keyboard_input_key(key: Key, action: Action) -> Result<INPUT> {
unsafe {
let mut result: INPUT = mem::zeroed();
if key.is_mouse_button() {
lazy_static! {
static ref FLAGS: HashMap<(Key, Action), u32> = {
let mut m = HashMap::new();
m.insert(
(Key::MouseLeft, Action::Press),
winuser::MOUSEEVENTF_LEFTDOWN,
);
m.insert(
(Key::MouseLeft, Action::Release),
winuser::MOUSEEVENTF_LEFTUP,
);
m.insert(
(Key::MouseRight, Action::Press),
winuser::MOUSEEVENTF_RIGHTDOWN,
);
m.insert(
(Key::MouseRight, Action::Release),
winuser::MOUSEEVENTF_RIGHTUP,
);
m.insert(
(Key::MouseMiddle, Action::Press),
winuser::MOUSEEVENTF_MIDDLEDOWN,
);
m.insert(
(Key::MouseMiddle, Action::Release),
winuser::MOUSEEVENTF_MIDDLEUP,
);
m
};
}
result.type_ = winuser::INPUT_MOUSE;
let mi = result.u.mi_mut();
if let Key::MouseX(n) = key {
mi.mouseData = n as _;
mi.dwFlags = match action {
Action::Press => winuser::MOUSEEVENTF_XDOWN,
Action::Release => winuser::MOUSEEVENTF_XUP,
};
} else {
mi.dwFlags = FLAGS.get(&(key, action)).copied().unwrap();
};
} else {
result.type_ = winuser::INPUT_KEYBOARD;
let ki = result.u.ki_mut();
ki.wVk = key_to_virtual_keycode(key)? as _;
ki.dwFlags = match action {
Action::Press => 0,
Action::Release => winuser::KEYEVENTF_KEYUP,
};
}
Ok(result)
}
}
fn keyboard_input_char(c: char, action: Action) -> Result<INPUT> {
if c as u32 > 0xff {
return Err(Error::UnsupportedChar(c));
}
unsafe {
let mut result: INPUT = mem::zeroed();
result.type_ = winuser::INPUT_KEYBOARD;
let ki = result.u.ki_mut();
ki.wScan = c as _;
ki.dwFlags = match action {
Action::Press => winuser::KEYEVENTF_UNICODE,
Action::Release => winuser::KEYEVENTF_KEYUP | winuser::KEYEVENTF_UNICODE,
};
Ok(result)
}
}
fn move_mouse_relative(dx: i32, dy: i32) -> Result<INPUT> {
unsafe {
let mut result: INPUT = mem::zeroed();
result.type_ = winuser::INPUT_MOUSE;
let mi = result.u.mi_mut();
mi.dx = dx;
mi.dy = dy;
mi.dwFlags = winuser::MOUSEEVENTF_MOVE;
Ok(result)
}
}
fn move_mouse_absolute(x: f32, y: f32, map: bool) -> Result<INPUT> {
unsafe {
let mut result: INPUT = mem::zeroed();
result.type_ = winuser::INPUT_MOUSE;
let mi = result.u.mi_mut();
mi.dx = (x * 65535f32) as i32;
mi.dy = (y * 65535f32) as i32;
mi.dwFlags = if map {
winuser::MOUSEEVENTF_ABSOLUTE
| winuser::MOUSEEVENTF_VIRTUALDESK
| winuser::MOUSEEVENTF_MOVE
} else {
winuser::MOUSEEVENTF_ABSOLUTE | winuser::MOUSEEVENTF_MOVE
};
Ok(result)
}
}
fn scroll_event(delta: f32, direction: ScrollDirection) -> Result<INPUT> {
unsafe {
let mut result: INPUT = mem::zeroed();
result.type_ = winuser::INPUT_MOUSE;
let mi = result.u.mi_mut();
mi.mouseData = (delta * winuser::WHEEL_DELTA as f32) as _;
mi.dwFlags = match direction {
ScrollDirection::Horizontal => winuser::MOUSEEVENTF_HWHEEL,
ScrollDirection::Vertical => winuser::MOUSEEVENTF_WHEEL,
};
Ok(result)
}
}
fn event_to_input(e: Event) -> Result<INPUT> {
match e {
Event::Key { key, action } => keyboard_input_key(key, action),
Event::Char { character, action } => keyboard_input_char(character, action),
Event::MoveMouseRelative { dx, dy } => move_mouse_relative(dx, dy),
Event::MoveMouseAbsolute {
x,
y,
map_to_virtual_desktop,
} => move_mouse_absolute(x, y, map_to_virtual_desktop),
Event::Scroll { delta, direction } => scroll_event(delta, direction),
}
}
fn send_inputs(inputs: &[INPUT]) -> u32 {
unsafe {
winuser::SendInput(
inputs.len() as _,
inputs.as_ptr() as _,
std::mem::size_of::<INPUT>() as _,
)
}
}
fn get_last_error() -> Error {
let error_code = unsafe { errhandlingapi::GetLastError() };
let message = {
use std::ptr;
let mut buffer_ptr = ptr::null_mut::<u16>();
let allocated_length = unsafe {
winbase::FormatMessageW(
winbase::FORMAT_MESSAGE_ALLOCATE_BUFFER | winbase::FORMAT_MESSAGE_FROM_SYSTEM,
ptr::null(),
error_code,
0,
&mut buffer_ptr as *mut *mut u16 as _,
0,
ptr::null_mut(),
)
};
if allocated_length == 0 {
String::from("Failed to retreive the error message")
} else {
let buffer =
unsafe { std::slice::from_raw_parts_mut(buffer_ptr, allocated_length as _) };
let message = String::from_utf16_lossy(buffer);
let ptr = unsafe { winbase::LocalFree(buffer_ptr as _) };
assert!(!ptr.is_null(), "Failed to deallocate the message");
message
}
};
Error::OsError {
code: error_code,
message,
}
}
pub struct WindowsSimulate;
impl Simulate for WindowsSimulate {
fn send_events(&self, events: impl IntoIterator<Item = Event>) -> Result<()> {
let iter = events.into_iter();
let mut buffer = Vec::with_capacity(iter.size_hint().0);
for e in iter {
buffer.push(event_to_input(e)?);
}
let count = send_inputs(&buffer);
if count == 0 {
Err(get_last_error())
} else {
Ok(())
}
}
}