use crate::error::{Error, Result};
use crate::event::Event;
use crate::platform;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, RwLock};
use std::thread::JoinHandle;
pub trait EventHandler: Send + Sync {
fn handle_event(&self, event: &Event);
}
impl<F> EventHandler for F
where
F: Fn(&Event) + Send + Sync,
{
fn handle_event(&self, event: &Event) {
self(event);
}
}
pub trait GrabHandler: Send + Sync {
fn handle_event(&self, event: &Event) -> Option<Event>;
}
impl<F> GrabHandler for F
where
F: Fn(&Event) -> Option<Event> + Send + Sync,
{
fn handle_event(&self, event: &Event) -> Option<Event> {
self(event)
}
}
pub struct Hook {
running: Arc<AtomicBool>,
thread_handle: RwLock<Option<JoinHandle<()>>>,
}
impl Default for Hook {
fn default() -> Self {
Self::new()
}
}
impl Hook {
pub fn new() -> Self {
Self {
running: Arc::new(AtomicBool::new(false)),
thread_handle: RwLock::new(None),
}
}
pub fn run<H: EventHandler + 'static>(&self, handler: H) -> Result<()> {
if self.running.swap(true, Ordering::SeqCst) {
return Err(Error::AlreadyRunning);
}
crate::state::reset_mask();
let result = platform::run_hook(&self.running, handler);
self.running.store(false, Ordering::SeqCst);
result
}
pub fn run_async<H: EventHandler + 'static>(&self, handler: H) -> Result<()> {
if self.running.swap(true, Ordering::SeqCst) {
return Err(Error::AlreadyRunning);
}
crate::state::reset_mask();
let running = self.running.clone();
let handle = std::thread::spawn(move || {
let _ = platform::run_hook(&running, handler);
running.store(false, Ordering::SeqCst);
});
*self.thread_handle.write().unwrap() = Some(handle);
Ok(())
}
pub fn grab<H: GrabHandler + 'static>(&self, handler: H) -> Result<()> {
if self.running.swap(true, Ordering::SeqCst) {
return Err(Error::AlreadyRunning);
}
crate::state::reset_mask();
let result = platform::run_grab_hook(&self.running, handler);
self.running.store(false, Ordering::SeqCst);
result
}
pub fn grab_async<H: GrabHandler + 'static>(&self, handler: H) -> Result<()> {
if self.running.swap(true, Ordering::SeqCst) {
return Err(Error::AlreadyRunning);
}
crate::state::reset_mask();
let running = self.running.clone();
let handle = std::thread::spawn(move || {
let _ = platform::run_grab_hook(&running, handler);
running.store(false, Ordering::SeqCst);
});
*self.thread_handle.write().unwrap() = Some(handle);
Ok(())
}
pub fn stop(&self) -> Result<()> {
if !self.running.swap(false, Ordering::SeqCst) {
return Err(Error::NotRunning);
}
platform::stop_hook()?;
if let Some(handle) = self.thread_handle.write().unwrap().take() {
handle
.join()
.map_err(|_| Error::ThreadError("failed to join hook thread".into()))?;
}
Ok(())
}
pub fn is_running(&self) -> bool {
self.running.load(Ordering::SeqCst)
}
}
impl Drop for Hook {
fn drop(&mut self) {
if self.is_running() {
let _ = self.stop();
}
}
}
pub fn listen<F>(callback: F) -> Result<()>
where
F: Fn(&Event) + Send + Sync + 'static,
{
let hook = Hook::new();
hook.run(callback)
}
pub fn grab<F>(callback: F) -> Result<()>
where
F: Fn(&Event) -> Option<Event> + Send + Sync + 'static,
{
let hook = Hook::new();
hook.grab(callback)
}