use crate::{
common::{get_class_name, Result, HMODULE, HWND},
message::message_loop,
msaa::object::AccessibleObject,
};
use std::{
fmt::{Debug, Formatter},
sync::{Arc, RwLock},
thread,
time::SystemTime,
};
pub use windows::Win32::UI::Accessibility::{HWINEVENTHOOK, WINEVENTPROC};
use windows::Win32::UI::{
Accessibility::{SetWinEventHook, UnhookWinEvent},
WindowsAndMessaging::{EVENT_MAX, EVENT_MIN, WINEVENT_OUTOFCONTEXT},
};
#[derive(Clone)]
struct WinEventHookHandle(HWINEVENTHOOK);
impl WinEventHookHandle {
pub const fn new() -> Self {
Self(HWINEVENTHOOK(std::ptr::null_mut()))
}
}
unsafe impl Sync for WinEventHookHandle {}
unsafe impl Send for WinEventHookHandle {}
impl From<HWINEVENTHOOK> for WinEventHookHandle {
fn from(value: HWINEVENTHOOK) -> Self {
Self(value)
}
}
impl std::ops::Deref for WinEventHookHandle {
type Target = HWINEVENTHOOK;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Default for WinEventHookHandle {
fn default() -> Self {
Self(HWINEVENTHOOK::default())
}
}
pub fn set_win_event_hook(
event_min: u32,
event_max: u32,
h_mod_win_event_proc: Option<HMODULE>,
fn_win_event_proc: WINEVENTPROC,
id_process: u32,
id_thread: u32,
flags: u32,
) -> HWINEVENTHOOK {
unsafe {
SetWinEventHook(
event_min,
event_max,
h_mod_win_event_proc,
fn_win_event_proc,
id_process,
id_thread,
flags,
)
}
}
pub fn unhook_win_event(h_win_event_hook: HWINEVENTHOOK) -> bool {
unsafe { UnhookWinEvent(h_win_event_hook) }.as_bool()
}
static H_WIN_EVENT: RwLock<WinEventHookHandle> = RwLock::new(WinEventHookHandle::new());
static EVENTS: RwLock<Vec<WinEventHook>> = RwLock::new(vec![]);
#[derive(Clone)]
#[allow(dead_code)]
pub struct WinEventSource {
pub h_wnd: HWND,
pub id_object: i32,
pub id_child: i32,
pub id_thread: u32,
pub ms_time: u32,
}
unsafe impl Send for WinEventSource {}
unsafe impl Sync for WinEventSource {}
impl WinEventSource {
pub fn get_object(&self) -> Result<(AccessibleObject, i32)> {
AccessibleObject::from_event(self.h_wnd, self.id_object, self.id_child)
}
pub fn get_class_name(&self) -> String {
get_class_name(self.h_wnd)
}
}
impl Debug for WinEventSource {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"WinEventSource(window:[{:?}, {}], object:[{}, {}], time:{})",
self.h_wnd.0,
self.get_class_name(),
self.id_object,
self.id_child,
self.ms_time
)
}
}
unsafe extern "system" fn hook_proc(
_: HWINEVENTHOOK,
event: u32,
h_wnd: HWND,
id_object: i32,
id_child: i32,
id_event_thread: u32,
ms_event_time: u32,
) {
let source = WinEventSource {
h_wnd,
id_object,
id_child,
id_thread: id_event_thread,
ms_time: ms_event_time,
};
let lock = EVENTS.read().unwrap();
for i in lock.iter() {
if event == i.2 {
(&*i.0)(source.clone())
}
}
}
#[derive(Clone)]
pub struct WinEventHook(Arc<dyn Fn(WinEventSource) + Send + Sync>, SystemTime, u32);
impl WinEventHook {
pub fn new(event: u32, func: impl Fn(WinEventSource) + Send + Sync + 'static) -> Self {
let h_win_event = { H_WIN_EVENT.read().unwrap().clone() };
if h_win_event.is_invalid() {
thread::spawn(|| {
let mut lock = H_WIN_EVENT.write().unwrap();
if !lock.is_invalid() {
return;
}
let handle = set_win_event_hook(
EVENT_MIN,
EVENT_MAX,
None,
Some(hook_proc),
0,
0,
WINEVENT_OUTOFCONTEXT,
);
*lock = WinEventHookHandle::from(handle);
drop(lock);
message_loop(|_| ());
unhook_win_event(handle);
*H_WIN_EVENT.write().unwrap() = WinEventHookHandle::default();
});
}
let self_ = Self(Arc::new(func), SystemTime::now(), event);
EVENTS.write().unwrap().push(self_.clone());
self_
}
pub fn unhook(&self) {
let mut lock = EVENTS.write().unwrap();
for i in 0..lock.len() {
if let Some(x) = lock.get(i) {
if x == self {
lock.remove(i);
}
}
}
if lock.is_empty() {
drop(lock);
let mut lock = H_WIN_EVENT.write().unwrap();
unhook_win_event(lock.0);
*lock = WinEventHookHandle(HWINEVENTHOOK::default());
}
}
}
impl Debug for WinEventHook {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "WinEventHook({})", self.2)
}
}
impl PartialEq for WinEventHook {
fn eq(&self, other: &Self) -> bool {
self.1 == other.1
}
}