use core::ffi::c_void;
use asdf_overlay_hook::DetourHook;
use once_cell::sync::OnceCell;
use tracing::debug;
use windows::{
Win32::{
Foundation::{HWND, POINT, RECT},
UI::{
Input::{
HRAWINPUT, KeyboardAndMouse::GetActiveWindow, RAW_INPUT_DATA_COMMAND_FLAGS,
RAWINPUT, RAWINPUTHEADER, RID_HEADER, RID_INPUT,
},
WindowsAndMessaging::GetForegroundWindow,
},
},
core::BOOL,
};
use crate::{
backend::{Backends, window::InputBlockData},
hook::proc::message_reading,
};
windows::core::link!("user32.dll" "system" fn ClipCursor(lprect: *const RECT) -> BOOL);
windows::core::link!("user32.dll" "system" fn SetCursorPos(x: i32, y: i32) -> BOOL);
windows::core::link!("user32.dll" "system" fn GetClipCursor(lprect: *mut RECT) -> BOOL);
windows::core::link!("user32.dll" "system" fn GetCursorPos(lppoint: *mut POINT) -> BOOL);
windows::core::link!("user32.dll" "system" fn GetPhysicalCursorPos(lppoint: *mut POINT) -> BOOL);
windows::core::link!("user32.dll" "system" fn GetKeyboardState(buf: *mut u8) -> BOOL);
windows::core::link!("user32.dll" "system" fn GetKeyState(vkey: i32) -> i16);
windows::core::link!("user32.dll" "system" fn GetAsyncKeyState(vkey: i32) -> i16);
windows::core::link!(
"user32.dll" "system"
fn GetRawInputData(
hrawinput: HRAWINPUT,
uicommand: RAW_INPUT_DATA_COMMAND_FLAGS,
pdata: *mut c_void,
pcbsize: *mut u32,
cbsizeheader: u32,
) -> u32
);
windows::core::link!("user32.dll" "system" fn GetRawInputBuffer(pdata: *mut RAWINPUT, pcbsize: *mut u32, cbsizeheader: u32) -> u32);
struct Hook {
clip_cursor: DetourHook<ClipCursorFn>,
set_cursor_pos: DetourHook<SetCursorFn>,
get_clip_cursor: DetourHook<GetClipCursorFn>,
get_cursor_pos: DetourHook<GetCursorPos>,
get_physical_cursor_pos: DetourHook<GetPhysicalCursorPos>,
get_async_key_state: DetourHook<GetAsyncKeyStateFn>,
get_key_state: DetourHook<GetKeyStateFn>,
get_keyboard_state: DetourHook<GetKeyboardStateFn>,
get_raw_input_data: DetourHook<GetRawInputDataFn>,
get_raw_input_buffer: DetourHook<GetRawInputBufferFn>,
}
static HOOK: OnceCell<Hook> = OnceCell::new();
type ClipCursorFn = unsafe extern "system" fn(*const RECT) -> BOOL;
type SetCursorFn = unsafe extern "system" fn(i32, i32) -> BOOL;
type GetClipCursorFn = unsafe extern "system" fn(*mut RECT) -> BOOL;
type GetCursorPos = unsafe extern "system" fn(*mut POINT) -> BOOL;
type GetPhysicalCursorPos = unsafe extern "system" fn(*mut POINT) -> BOOL;
type GetAsyncKeyStateFn = unsafe extern "system" fn(i32) -> i16;
type GetKeyStateFn = unsafe extern "system" fn(i32) -> i16;
type GetKeyboardStateFn = unsafe extern "system" fn(*mut u8) -> BOOL;
type GetRawInputDataFn = unsafe extern "system" fn(
HRAWINPUT,
RAW_INPUT_DATA_COMMAND_FLAGS,
*mut c_void,
*mut u32,
u32,
) -> u32;
type GetRawInputBufferFn = unsafe extern "system" fn(*mut RAWINPUT, *mut u32, u32) -> u32;
pub fn hook() -> anyhow::Result<()> {
HOOK.get_or_try_init(|| unsafe {
debug!("hooking ClipCursor");
let clip_cursor = DetourHook::attach(ClipCursor as _, hooked_clip_cursor as _)?;
debug!("hooking SetCursorPos");
let set_cursor_pos = DetourHook::attach(SetCursorPos as _, hooked_set_cursor_pos as _)?;
debug!("hooking GetClipCursor");
let get_clip_cursor = DetourHook::attach(GetClipCursor as _, hooked_get_clip_cursor as _)?;
debug!("hooking GetCursorPos");
let get_cursor_pos = DetourHook::attach(GetCursorPos as _, hooked_get_cursor_pos as _)?;
debug!("hooking GetPhysicalCursorPos");
let get_physical_cursor_pos = DetourHook::attach(
GetPhysicalCursorPos as _,
hooked_get_physical_cursor_pos as _,
)?;
debug!("hooking GetAsyncKeyState");
let get_async_key_state =
DetourHook::attach(GetAsyncKeyState as _, hooked_get_async_key_state as _)?;
debug!("hooking GetKeyState");
let get_key_state = DetourHook::attach(GetKeyState as _, hooked_get_key_state as _)?;
debug!("hooking GetKeyboardState");
let get_keyboard_state =
DetourHook::attach(GetKeyboardState as _, hooked_get_keyboard_state as _)?;
debug!("hooking GetRawInputData");
let get_raw_input_data =
DetourHook::attach(GetRawInputData as _, hooked_get_raw_input_data as _)?;
debug!("hooking GetRawInputBuffer");
let get_raw_input_buffer =
DetourHook::attach(GetRawInputBuffer as _, hooked_get_raw_input_buffer as _)?;
Ok::<_, anyhow::Error>(Hook {
clip_cursor,
set_cursor_pos,
get_clip_cursor,
get_cursor_pos,
get_physical_cursor_pos,
get_async_key_state,
get_key_state,
get_keyboard_state,
get_raw_input_data,
get_raw_input_buffer,
})
})?;
Ok(())
}
#[inline]
fn active_hwnd_can_block() -> Option<HWND> {
let hwnd = unsafe { GetActiveWindow() };
if !hwnd.is_invalid() && !message_reading() {
Some(hwnd)
} else {
None
}
}
#[inline]
fn active_hwnd_with<R>(f: impl FnOnce(&mut InputBlockData) -> R) -> Option<R> {
Backends::with_backend(active_hwnd_can_block()?.0 as _, |backend| {
let mut proc = backend.proc.lock();
Some(f(proc.blocking_state.as_mut()?))
})
.flatten()
}
#[inline]
fn foreground_hwnd_input_blocked() -> bool {
let hwnd = unsafe { GetForegroundWindow() };
!hwnd.is_invalid()
&& Backends::with_backend(hwnd.0 as _, |backend| backend.proc.lock().input_blocking())
.unwrap_or(false)
}
#[inline]
fn active_hwnd_input_blocked() -> bool {
active_hwnd_with(|_| ()).is_some()
}
#[tracing::instrument]
extern "system" fn hooked_clip_cursor(lprect: *const RECT) -> BOOL {
if active_hwnd_with(|data| {
data.clip_cursor = unsafe { lprect.as_ref() }.copied();
})
.is_some()
{
return BOOL(1);
}
unsafe { HOOK.wait().clip_cursor.original_fn()(lprect) }
}
#[tracing::instrument]
extern "system" fn hooked_set_cursor_pos(x: i32, y: i32) -> BOOL {
if foreground_hwnd_input_blocked() {
return BOOL(1);
}
unsafe { HOOK.wait().set_cursor_pos.original_fn()(x, y) }
}
#[tracing::instrument]
extern "system" fn hooked_get_clip_cursor(lprect: *mut RECT) -> BOOL {
match active_hwnd_with(|data| data.clip_cursor).flatten() {
Some(rect) => {
unsafe { lprect.write(rect) };
BOOL(1)
}
None => unsafe { HOOK.wait().get_clip_cursor.original_fn()(lprect) },
}
}
#[tracing::instrument]
extern "system" fn hooked_get_cursor_pos(lppoint: *mut POINT) -> BOOL {
if foreground_hwnd_input_blocked() {
if !lppoint.is_null() {
unsafe {
lppoint.write(POINT { x: 0, y: 0 });
}
}
return BOOL(1);
}
unsafe { HOOK.wait().get_cursor_pos.original_fn()(lppoint) }
}
#[tracing::instrument]
extern "system" fn hooked_get_physical_cursor_pos(lppoint: *mut POINT) -> BOOL {
if foreground_hwnd_input_blocked() {
if !lppoint.is_null() {
unsafe {
lppoint.write(POINT { x: 0, y: 0 });
}
}
return BOOL(1);
}
unsafe { HOOK.wait().get_physical_cursor_pos.original_fn()(lppoint) }
}
#[tracing::instrument]
extern "system" fn hooked_get_async_key_state(vkey: i32) -> i16 {
if foreground_hwnd_input_blocked() {
return 0;
}
unsafe { HOOK.wait().get_async_key_state.original_fn()(vkey) }
}
#[tracing::instrument]
extern "system" fn hooked_get_key_state(vkey: i32) -> i16 {
if active_hwnd_input_blocked() {
return 0;
}
unsafe { HOOK.wait().get_key_state.original_fn()(vkey) }
}
#[tracing::instrument]
extern "system" fn hooked_get_keyboard_state(buf: *mut u8) -> BOOL {
if active_hwnd_input_blocked() {
unsafe {
buf.write_bytes(0u8, 256);
};
return BOOL(1);
}
unsafe { HOOK.wait().get_keyboard_state.original_fn()(buf) }
}
#[tracing::instrument]
extern "system" fn hooked_get_raw_input_data(
hrawinput: HRAWINPUT,
uicommand: RAW_INPUT_DATA_COMMAND_FLAGS,
pdata: *mut c_void,
pcbsize: *mut u32,
cbsizeheader: u32,
) -> u32 {
if foreground_hwnd_input_blocked() {
let data_size: u32 = match uicommand {
RID_HEADER => core::mem::size_of::<RAWINPUTHEADER>() as u32,
RID_INPUT => core::mem::size_of::<RAWINPUT>() as u32,
_ => 0,
};
if !pcbsize.is_null() {
unsafe { pcbsize.write(data_size) };
}
if pdata.is_null() {
return 0;
}
let hid_header = RAWINPUTHEADER {
dwType: 2, dwSize: data_size,
..Default::default()
};
match uicommand {
RID_HEADER => unsafe {
pdata.cast::<RAWINPUTHEADER>().write(hid_header);
},
RID_INPUT => unsafe {
pdata.cast::<RAWINPUT>().write(RAWINPUT {
header: hid_header,
..Default::default()
});
},
_ => {}
}
return data_size;
}
unsafe {
HOOK.wait().get_raw_input_data.original_fn()(
hrawinput,
uicommand,
pdata,
pcbsize,
cbsizeheader,
)
}
}
#[tracing::instrument]
extern "system" fn hooked_get_raw_input_buffer(
pdata: *mut RAWINPUT,
pcbsize: *mut u32,
cbsizeheader: u32,
) -> u32 {
if foreground_hwnd_input_blocked() {
unsafe { *pcbsize = 0 };
return 0;
}
unsafe { HOOK.wait().get_raw_input_buffer.original_fn()(pdata, pcbsize, cbsizeheader) }
}