use windows::Win32::Foundation::{HWND, LPARAM, WPARAM};
use windows::Win32::UI::WindowsAndMessaging::{PostMessageW, WM_KEYDOWN, WM_KEYUP};
use crate::get_scan_code;
use crate::utils::key_code;
#[inline]
pub fn make_lparam(scan_code: u16, is_keyup: bool) -> LPARAM {
let mut lparam = ((scan_code as u32) << 16) as i32;
if is_keyup {
lparam |= 0xC0000000u32 as i32; } else {
lparam |= 0x00000001; }
LPARAM(lparam as isize)
}
#[inline]
pub fn post_key_down_atomic(hwnd: HWND, vk_code: u8, scan_code: u16) {
let lparam = make_lparam(scan_code, false);
unsafe {
let _ = PostMessageW(Some(hwnd), WM_KEYDOWN, WPARAM(vk_code as usize), lparam);
}
}
#[inline]
pub fn post_key_up_atomic(hwnd: HWND, vk_code: u8, scan_code: u16) {
let lparam = make_lparam(scan_code, true);
unsafe {
let _ = PostMessageW(Some(hwnd), WM_KEYUP, WPARAM(vk_code as usize), lparam);
}
}
#[inline]
pub fn post_key_click_atomic(hwnd: HWND, vk_code: u8, scan_code: u16) {
post_key_down_atomic(hwnd, vk_code, scan_code);
post_key_up_atomic(hwnd, vk_code, scan_code);
}
#[derive(Debug)]
pub enum PostMessageError {
InvalidWindow,
SendMessageFailed,
}
impl std::fmt::Display for PostMessageError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
PostMessageError::InvalidWindow => write!(f, "Invalid window handle"),
PostMessageError::SendMessageFailed => write!(f, "Failed to send message"),
}
}
}
impl std::error::Error for PostMessageError {}
pub struct PostMessageKeyboard {
hwnd: HWND,
}
impl PostMessageKeyboard {
pub fn new(hwnd: HWND) -> Self {
Self { hwnd }
}
pub fn hwnd(&self) -> HWND {
self.hwnd
}
pub fn press(&self, key: &str) -> Result<(), PostMessageError> {
let vk_code = key_code(key);
if vk_code == 0 {
return Err(PostMessageError::InvalidWindow);
}
let scan_code = get_scan_code(vk_code);
post_key_down_atomic(self.hwnd, vk_code, scan_code);
Ok(())
}
pub fn release(&self, key: &str) -> Result<(), PostMessageError> {
let vk_code = key_code(key);
if vk_code == 0 {
return Err(PostMessageError::InvalidWindow);
}
let scan_code = get_scan_code(vk_code);
post_key_up_atomic(self.hwnd, vk_code, scan_code);
Ok(())
}
pub fn click(&self, key: &str) -> Result<(), PostMessageError> {
let vk_code = key_code(key);
if vk_code == 0 {
return Err(PostMessageError::InvalidWindow);
}
let scan_code = get_scan_code(vk_code);
post_key_click_atomic(self.hwnd, vk_code, scan_code);
Ok(())
}
pub fn press_with_codes(&self, vk_code: u8, scan_code: u16) -> Result<(), PostMessageError> {
post_key_down_atomic(self.hwnd, vk_code, scan_code);
Ok(())
}
pub fn release_with_codes(&self, vk_code: u8, scan_code: u16) -> Result<(), PostMessageError> {
post_key_up_atomic(self.hwnd, vk_code, scan_code);
Ok(())
}
pub fn click_with_codes(&self, vk_code: u8, scan_code: u16) -> Result<(), PostMessageError> {
post_key_click_atomic(self.hwnd, vk_code, scan_code);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_post_message_creation() {
let hwnd = HWND(std::ptr::null_mut());
let kb = PostMessageKeyboard::new(hwnd);
assert_eq!(kb.hwnd().0, std::ptr::null_mut());
}
#[test]
fn test_make_lparam_format() {
let lparam_down = make_lparam(0x1E, false);
let lparam_up = make_lparam(0x1E, true);
assert_eq!((lparam_down.0 as u32) & 0xFFFF, 1);
assert_eq!(((lparam_up.0 as u32) >> 31) & 1, 1);
assert_eq!(((lparam_down.0 as u32) >> 31) & 1, 0);
assert_eq!(((lparam_down.0 as u32) >> 16) & 0xFF, 0x1E);
}
#[test]
fn test_atomic_functions_compile() {
let hwnd = HWND(std::ptr::null_mut());
post_key_down_atomic(hwnd, 0x41, 0x1E);
post_key_up_atomic(hwnd, 0x41, 0x1E);
post_key_click_atomic(hwnd, 0x41, 0x1E);
assert!(true); }
}