automata_windows/
mouse_hook.rs1#[cfg(not(target_os = "windows"))]
4use crate::Result;
5
6#[cfg(not(target_os = "windows"))]
7pub struct MouseHook;
8
9#[cfg(not(target_os = "windows"))]
10impl MouseHook {
11 pub fn start() -> Result<Self> {
12 anyhow::bail!("Windows only")
13 }
14 pub fn receiver(&self) -> &std::sync::mpsc::Receiver<(i32, i32)> {
15 unimplemented!()
16 }
17}
18
19#[cfg(target_os = "windows")]
22pub use win::MouseHook;
23
24#[cfg(target_os = "windows")]
25mod win {
26 use std::cell::RefCell;
27 use std::sync::mpsc;
28 use std::thread;
29
30 use windows::Win32::Foundation::{LPARAM, LRESULT, WPARAM};
31 use windows::Win32::UI::WindowsAndMessaging::{
32 CallNextHookEx, DispatchMessageW, GetCursorPos, GetMessageW, HC_ACTION, HHOOK, MSG,
33 SetWindowsHookExW, UnhookWindowsHookEx, WH_MOUSE_LL, WM_MOUSEMOVE,
34 };
35
36 use crate::Result;
37
38 thread_local! {
39 static HOOK_SENDER: RefCell<Option<mpsc::SyncSender<(i32, i32)>>> =
40 RefCell::new(None);
41 }
42
43 unsafe extern "system" fn hook_proc(code: i32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
44 if code == HC_ACTION as i32 && wparam.0 as u32 == WM_MOUSEMOVE {
45 let mut pt = windows::Win32::Foundation::POINT::default();
46 if unsafe { GetCursorPos(&mut pt) }.is_ok() {
47 HOOK_SENDER.with(|cell| {
48 if let Some(tx) = cell.borrow().as_ref() {
49 let _ = tx.try_send((pt.x, pt.y));
50 }
51 });
52 }
53 }
54 unsafe { CallNextHookEx(Some(HHOOK::default()), code, wparam, lparam) }
55 }
56
57 pub struct MouseHook {
61 receiver: mpsc::Receiver<(i32, i32)>,
62 _thread: thread::JoinHandle<()>,
63 }
64
65 impl MouseHook {
66 pub fn start() -> Result<Self> {
67 let (tx, rx) = mpsc::sync_channel::<(i32, i32)>(1);
68
69 let handle = thread::spawn(move || unsafe {
70 HOOK_SENDER.with(|cell| *cell.borrow_mut() = Some(tx));
71
72 let hook = SetWindowsHookExW(WH_MOUSE_LL, Some(hook_proc), None, 0)
73 .expect("SetWindowsHookExW");
74
75 let mut msg = MSG::default();
76 while GetMessageW(&mut msg, None, 0, 0).as_bool() {
77 let _ = DispatchMessageW(&msg);
78 }
79 UnhookWindowsHookEx(hook).ok();
80 });
81
82 Ok(Self {
83 receiver: rx,
84 _thread: handle,
85 })
86 }
87
88 pub fn receiver(&self) -> &mpsc::Receiver<(i32, i32)> {
90 &self.receiver
91 }
92 }
93}