use std::{fmt::Debug, hash::Hash};
pub use config::Config;
use errors::{Error, Result};
pub use handler::EventHandler;
use hook::{ThreadedInner, UnthreadedInner, WinEventHookInner};
use tracing::trace;
use windows::Win32::UI::Accessibility::HWINEVENTHOOK;
pub mod config;
pub mod errors;
mod event_loop;
pub mod events;
pub mod flags;
pub mod handler;
mod hook;
pub type OsHandle = HWINEVENTHOOK;
pub struct WinEventHook {
inner: Box<dyn WinEventHookInner>,
}
impl WinEventHook {
pub fn os_handle(&self) -> &Option<OsHandle> {
self.inner.handle()
}
pub fn installed(&self) -> bool {
self.inner.installed()
}
pub fn install<F: EventHandler + 'static>(config: Config, handler: F) -> Result<Self> {
trace!(?config, "validating config");
if !config.is_valid() {
return Err(Error::InvalidConfig(config));
}
trace!("config valid, attempting to install hook");
Ok(Self {
inner: match config.dedicated_thread_name.is_none() {
true => Box::new(UnthreadedInner::new(config, Box::new(handler))?),
false => Box::new(ThreadedInner::new(config, Box::new(handler))?),
},
})
}
pub fn uninstall(&mut self) -> Result<()> {
self.inner.uninstall()
}
}
impl Debug for WinEventHook {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("WinEventHook")
.field("os_handle", self.os_handle())
.finish()
}
}
impl PartialEq for WinEventHook {
fn eq(&self, other: &Self) -> bool {
self.os_handle().eq(other.os_handle())
}
}
impl Eq for WinEventHook {}
impl Hash for WinEventHook {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
let maybe_os_handle = self.os_handle().and_then(|handle| Some(handle.0));
maybe_os_handle.hash(state)
}
}
#[cfg(test)]
mod tests {
use tracing::info;
use tracing_subscriber::{EnvFilter, FmtSubscriber};
use super::{
events::{Event, NamedEvent},
Config, WinEventHook,
};
#[test]
fn can_install_threaded() {
let subscriber = FmtSubscriber::builder()
.with_env_filter(EnvFilter::from_default_env())
.finish();
tracing::subscriber::set_global_default(subscriber).unwrap();
let cfg = Config::builder()
.with_event(Event::Named(NamedEvent::ObjectShow))
.with_event(Event::Named(NamedEvent::ObjectNameChange))
.with_event(Event::Named(NamedEvent::ObjectHide))
.with_dedicated_thread()
.finish();
let mut hook =
WinEventHook::install(cfg, |ev, _, _, _, _, _| info!(?ev, "got event")).unwrap();
hook.uninstall().unwrap();
}
}