pub(super) mod raw;
pub(super) mod channels;
pub(super) mod low_level;
use crate::hook::inner::{raw::RawHook, channels::HookChannels};
use crate::event::*;
use std::{
thread::JoinHandle,
sync::{Arc, Condvar, Mutex}
};
use once_cell::sync::Lazy;
use winapi::{shared::{
ntdef::NULL,
minwindef::*,
windef::*
}};
use winapi::um::{
processthreadsapi::GetCurrentThreadId,
winuser::{
HOOKPROC, LPMSG,
SetWindowsHookExA, UnhookWindowsHookEx, GetMessageA, PostThreadMessageA,
WM_QUIT,
WH_KEYBOARD_LL, WH_MOUSE_LL,
}
};
pub struct GlobalHooks {
keyboard: Option<InnerHook>,
mouse: Option<InnerHook>,
}
impl GlobalHooks {
pub fn is_any_hook_present(&self) -> bool {
self.keyboard.is_some() || self.mouse.is_some()
}
pub fn setup_mouse_hook(&mut self) {
use crate::hook::inner::low_level::mouse_procedure;
self.mouse = Some(InnerHook::new(WH_MOUSE_LL, Some(mouse_procedure)));
}
pub fn setup_keyboard_hook(&mut self) {
use crate::hook::inner::low_level::keyboard_procedure;
self.keyboard = Some(InnerHook::new(WH_KEYBOARD_LL, Some(keyboard_procedure)));
}
pub fn drop_hooks(&mut self) {
self.keyboard = None;
self.mouse = None;
}
}
pub(super) static GLOBAL_CHANNEL: Lazy<HookChannels> = Lazy::new(|| HookChannels::new());
pub(super) static GLOBAL_HOOK: Mutex<GlobalHooks> = Mutex::new(GlobalHooks{keyboard: None, mouse: None});
pub struct InnerHook {
hook_handle: Arc<Mutex<RawHook>>,
thread_handle: Arc<Mutex<Option<JoinHandle<()>>>>,
}
impl Drop for InnerHook {
fn drop(&mut self) {
let (winapi_handle, thread_id) = if let Ok(inner) = self.hook_handle.lock() {
((*inner).raw_handle, (*inner).thread_id)
} else {
return;
};
if winapi_handle == NULL as HHOOK || thread_id == NULL as DWORD {
return;
}
unsafe {
if 0 == UnhookWindowsHookEx(winapi_handle) {
return;
}
if 0 == PostThreadMessageA(thread_id, WM_QUIT, NULL as WPARAM, NULL as LPARAM) {
return;
}
}
if let Ok(mut lock) = self.thread_handle.lock() {
if let Some(jh) = lock.take() {
let _ignore_error = jh.join();
}
}
}
}
impl InnerHook {
pub fn new(hook_id: INT, handler: HOOKPROC) -> InnerHook {
let raw_hook = Arc::new(Mutex::new(RawHook::new()));
let deferred_handle = raw_hook.clone();
let is_started = Arc::new((Mutex::new(false), Condvar::new()));
let set_started = is_started.clone();
let install_hook = Arc::new(Mutex::new(Some(std::thread::spawn(move || {
let hhook;
unsafe {
hhook = SetWindowsHookExA(hook_id, handler, NULL as HINSTANCE, NULL as DWORD);
}
if hhook != NULL as HHOOK {
if let Ok(mut exclusive) = deferred_handle.lock() {
exclusive.raw_handle = hhook;
exclusive.thread_id = unsafe { GetCurrentThreadId() };
}
}
{
let (start_lock, start_cvar) = &*set_started;
let mut started = start_lock.lock().unwrap();
*started = true;
start_cvar.notify_one();
}
let mut msg = std::mem::MaybeUninit::uninit();
unsafe {
GetMessageA(
msg.as_mut_ptr() as LPMSG,
-1isize as HWND, NULL as UINT,
NULL as UINT,
);
}
}))));
{
let (start_lock, start_cvar) = &*is_started;
let mut started = start_lock.lock().unwrap();
while !*started {
started = start_cvar.wait(started).unwrap();
}
}
InnerHook {
hook_handle: raw_hook,
thread_handle: install_hook,
}
}
pub fn recv() -> Result<InputEvent, std::sync::mpsc::RecvError> {
GLOBAL_CHANNEL.recv()
}
pub fn try_recv() -> Result<InputEvent, std::sync::mpsc::TryRecvError> {
GLOBAL_CHANNEL.try_recv()
}
}