mod hook;
mod input;
mod vkcode;
use hook::HookHandler;
use input::Input;
use windows::Win32::Foundation::{LPARAM, LRESULT, WPARAM};
use windows::Win32::UI::WindowsAndMessaging::HHOOK;
use crate::button::{Button, ButtonAction};
use crate::event::{self, EventReceiver, NativeEventOperation};
use std::sync::atomic::{AtomicBool, Ordering};
use once_cell::sync::Lazy;
use windows::Win32::UI::{HiDpi, WindowsAndMessaging};
const SHOULD_BE_IGNORED_FLAG: usize = 0x1;
const INJECTED_FLAG: usize = 0x2;
#[derive(Debug)]
struct ButtonState([AtomicBool; Button::VARIANT_COUNT]);
impl ButtonState {
const fn new() -> Self {
let inner = unsafe {
std::mem::transmute([false; Button::VARIANT_COUNT])
};
ButtonState(inner)
}
#[inline]
fn press(&self, button: Button, order: Ordering) {
self.0[button as usize].store(true, order);
}
#[inline]
fn release(&self, button: Button, order: Ordering) {
self.0[button as usize].store(false, order)
}
#[inline]
fn is_pressed(&self, button: Button, order: Ordering) -> bool {
self.0[button as usize].load(order)
}
#[inline]
fn is_released(&self, button: Button, order: Ordering) -> bool {
!self.0[button as usize].load(order)
}
}
static BUTTON_STATE: ButtonState = ButtonState::new();
static INPUT: Lazy<Input> = Lazy::new(Input::new);
#[inline]
fn send_input(button: Button, action: ButtonAction, recursive: bool, assume: fn(Button)) {
let left_and_right_modifier = match button {
Button::Shift => Some((Button::LShift, Button::RShift)),
Button::Ctrl => Some((Button::LCtrl, Button::RCtrl)),
Button::Alt => Some((Button::LAlt, Button::RAlt)),
Button::Super => Some((Button::LSuper, Button::RSuper)),
_ => None,
};
if let Some((left, right)) = left_and_right_modifier {
assume(left);
assume(right);
assume(button);
INPUT.button_input(left, action, recursive);
INPUT.button_input(right, action, recursive);
} else {
assume(button);
INPUT.button_input(button, action, recursive);
}
}
impl Button {
#[inline]
pub fn press(self) {
send_input(self, ButtonAction::Press, false, Button::assume_pressed);
}
#[inline]
pub fn press_recursive(self) {
send_input(self, ButtonAction::Press, true, Button::assume_pressed);
}
#[inline]
pub fn release(self) {
send_input(self, ButtonAction::Release, false, Button::assume_released);
}
#[inline]
pub fn release_recursive(self) {
send_input(self, ButtonAction::Release, true, Button::assume_released);
}
#[inline]
pub fn click(self) {
self.press();
self.release();
}
#[inline]
pub fn click_recursive(self) {
self.press_recursive();
self.release_recursive();
}
#[inline]
pub fn is_pressed(self) -> bool {
BUTTON_STATE.is_pressed(self, Ordering::SeqCst)
}
#[inline]
pub fn is_released(self) -> bool {
BUTTON_STATE.is_released(self, Ordering::SeqCst)
}
#[inline]
fn assume_pressed(self) {
BUTTON_STATE.press(self, Ordering::SeqCst);
}
#[inline]
fn assume_released(self) {
BUTTON_STATE.release(self, Ordering::SeqCst);
}
}
pub mod mouse {
use super::INPUT;
#[inline]
pub fn get_position() -> (i32, i32) {
INPUT.cursor_position()
}
#[inline]
pub fn move_absolute(x: i32, y: i32) {
INPUT.move_absolute(x, y, false);
}
#[inline]
pub fn move_absolute_recursive(x: i32, y: i32) {
INPUT.move_absolute(x, y, true);
}
#[inline]
pub fn move_relative(dx: i32, dy: i32) {
INPUT.move_relative(dx, dy, false);
}
#[inline]
pub fn move_relative_recursive(dx: i32, dy: i32) {
INPUT.move_relative(dx, dy, true);
}
#[inline]
pub fn rotate(speed: i32) {
INPUT.rotate_wheel(speed, false);
}
#[inline]
pub fn rotate_recursive(speed: i32) {
INPUT.rotate_wheel(speed, true);
}
}
static HOOK_HANDLER: Lazy<HookHandler> = Lazy::new(HookHandler::new);
extern "system" fn keyboard_hook_proc(n_code: i32, w_param: WPARAM, l_param: LPARAM) -> LRESULT {
match hook::keyboard_hook_proc_inner(&HOOK_HANDLER, n_code, l_param) {
NativeEventOperation::Block => LRESULT(1),
NativeEventOperation::Dispatch => unsafe {
WindowsAndMessaging::CallNextHookEx(HHOOK(0), n_code, w_param, l_param)
},
}
}
extern "system" fn mouse_hook_proc(n_code: i32, w_param: WPARAM, l_param: LPARAM) -> LRESULT {
match hook::mouse_hook_proc_inner(&HOOK_HANDLER, &INPUT, n_code, w_param, l_param) {
NativeEventOperation::Block => LRESULT(1),
NativeEventOperation::Dispatch => unsafe {
WindowsAndMessaging::CallNextHookEx(HHOOK(0), n_code, w_param, l_param)
},
}
}
pub fn install_hook() -> EventReceiver {
unsafe {
HiDpi::SetProcessDpiAwarenessContext(HiDpi::DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE);
}
INPUT.update_cursor_position();
let (tx, rx) = event::channel();
HOOK_HANDLER.install(tx, keyboard_hook_proc, mouse_hook_proc);
rx
}
pub fn uninstall_hook() {
HOOK_HANDLER.uninstall();
}