use windows::Win32::UI::Input::KeyboardAndMouse::{
SendInput, INPUT, INPUT_0, INPUT_KEYBOARD, KEYBDINPUT, KEYBD_EVENT_FLAGS,
KEYEVENTF_EXTENDEDKEY, KEYEVENTF_KEYUP, VIRTUAL_KEY,
};
use crate::utils::key_code;
use crate::utils::sleep_ms;
#[inline]
pub fn execute_inputs(inputs: &[INPUT]) -> Result<(), SendInputError> {
if inputs.is_empty() {
return Ok(());
}
let result = unsafe { SendInput(inputs, std::mem::size_of::<INPUT>() as i32) };
if result == 0 {
Err(SendInputError::SendInputFailed)
} else {
Ok(())
}
}
#[inline]
pub fn execute_single_input(input: &INPUT) -> Result<(), SendInputError> {
execute_inputs(&[input.clone()])
}
#[inline]
pub fn build_keybd_input(vk_code: u8, extended: bool, keyup: bool) -> INPUT {
let mut flags = KEYBD_EVENT_FLAGS(0);
if extended {
flags |= KEYEVENTF_EXTENDEDKEY;
}
if keyup {
flags |= KEYEVENTF_KEYUP;
}
INPUT {
r#type: INPUT_KEYBOARD,
Anonymous: INPUT_0 {
ki: KEYBDINPUT {
wVk: VIRTUAL_KEY(vk_code as u16),
wScan: 0,
dwFlags: flags,
time: 0,
dwExtraInfo: 0,
},
},
}
}
#[inline]
pub fn build_key_click_inputs(vk_code: u8, extended: bool) -> [INPUT; 2] {
[
build_keybd_input(vk_code, extended, false), build_keybd_input(vk_code, extended, true), ]
}
#[inline]
pub fn build_key_down_input(vk_code: u8, extended: bool) -> INPUT {
build_keybd_input(vk_code, extended, false)
}
#[inline]
pub fn build_key_up_input(vk_code: u8, extended: bool) -> INPUT {
build_keybd_input(vk_code, extended, true)
}
#[derive(Debug, Clone)]
pub enum SendInputError {
SendInputFailed,
}
impl std::fmt::Display for SendInputError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SendInputError::SendInputFailed => write!(f, "SendInput API call failed"),
}
}
}
impl std::error::Error for SendInputError {}
pub struct SendInputKeyboard {
delay_ms: u64,
}
impl SendInputKeyboard {
pub fn new() -> Self {
Self { delay_ms: 10 }
}
pub fn set_delay(&mut self, delay_ms: u64) {
self.delay_ms = delay_ms;
}
pub fn get_delay(&self) -> u64 {
self.delay_ms
}
pub fn click(&self, key: &str) -> Result<(), SendInputError> {
let vk_code = key_code(key);
if vk_code == 0 {
return Err(SendInputError::SendInputFailed);
}
let extended = crate::utils::key_code::is_extended_key(vk_code as u8);
let inputs = build_key_click_inputs(vk_code as u8, extended);
execute_inputs(&inputs)?;
if self.delay_ms > 0 {
sleep_ms(self.delay_ms as u32);
}
Ok(())
}
pub fn press_with_vk(&self, vk_code: u8, extended: bool) -> Result<(), SendInputError> {
let input = build_key_down_input(vk_code, extended);
execute_single_input(&input)
}
pub fn release_with_vk(&self, vk_code: u8, extended: bool) -> Result<(), SendInputError> {
let input = build_key_up_input(vk_code, extended);
execute_single_input(&input)
}
pub fn click_with_vk(&self, vk_code: u8, extended: bool) -> Result<(), SendInputError> {
let inputs = build_key_click_inputs(vk_code, extended);
execute_inputs(&inputs)
}
}
impl Default for SendInputKeyboard {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_build_and_execute_inputs() {
let inputs = build_key_click_inputs(0x41, false);
assert_eq!(inputs.len(), 2);
assert_eq!(inputs[0].r#type, INPUT_KEYBOARD);
assert_eq!(inputs[1].r#type, INPUT_KEYBOARD);
}
#[test]
fn test_send_input_creation() {
let kb = SendInputKeyboard::new();
assert_eq!(kb.get_delay(), 10);
}
#[test]
fn test_set_delay() {
let mut kb = SendInputKeyboard::new();
kb.set_delay(50);
assert_eq!(kb.get_delay(), 50);
}
#[test]
fn test_atomic_functions_compile() {
let inputs = build_key_click_inputs(0x41, false);
let _result = execute_inputs(&inputs);
assert!(true); }
}