1#![cfg(windows)]
2#![warn(
3 unsafe_op_in_unsafe_fn,
4 missing_docs,
5 missing_debug_implementations,
6 missing_copy_implementations,
7 rust_2018_idioms,
8 clippy::pedantic,
9 clippy::todo,
10 clippy::manual_assert,
11 clippy::must_use_candidate,
12 clippy::inconsistent_struct_constructor,
13 clippy::wrong_self_convention,
14 clippy::new_without_default,
15 rustdoc::broken_intra_doc_links,
16 rustdoc::private_intra_doc_links
17)]
18#![allow(
19 clippy::module_inception,
20 clippy::module_name_repetitions,
21 clippy::missing_errors_doc,
22 clippy::borrow_as_ptr,
23 clippy::cast_sign_loss,
24 clippy::missing_panics_doc,
25 clippy::doc_markdown,
26 clippy::cast_possible_wrap,
27 clippy::cast_possible_truncation
28)]
29
30pub(crate) mod message_loop;
59pub mod raw_event;
61
62mod event;
63pub use event::*;
64
65mod hook;
66pub use hook::*;
67
68#[cfg(test)]
69mod tests {
70 use std::{ptr::NonNull, time::Instant};
71
72 use windows_sys::Win32::System::Threading::GetCurrentThreadId;
73
74 use crate::{
75 AccessibleObjectId, EventFilter, ObjectWindowEvent, WindowEventHook, WindowEventType,
76 message_loop::MessageOnlyWindow, raw_event,
77 };
78
79 #[tokio::test]
80 async fn recv_object_create_on_window_create() {
81 let (event_tx, mut event_rx) = tokio::sync::mpsc::unbounded_channel();
82 let hook = WindowEventHook::hook(
83 EventFilter::default()
84 .event(raw_event::OBJECT_CREATE)
85 .skip_own_process(false)
86 .skip_own_thread(false),
87 event_tx,
88 )
89 .await
90 .unwrap();
91
92 let window = MessageOnlyWindow::new().unwrap();
93 let window_thread_id = unsafe { GetCurrentThreadId() };
94
95 while let Some(event) = event_rx.recv().await {
96 if event.event_type() == WindowEventType::Object(ObjectWindowEvent::Create)
97 && event.thread_id() == window_thread_id
98 {
99 assert_eq!(event.window_handle(), NonNull::new(window.handle()));
100 assert_eq!(event.child_id(), None);
101 assert_eq!(event.object_type(), AccessibleObjectId::Window);
102 assert!(event.timestamp() <= Instant::now());
103 break;
104 }
105 }
106
107 hook.unhook().await.unwrap();
108 }
109
110 #[tokio::test]
111 async fn can_unhook_with_unread_events() {
112 let (event_tx, _event_rx) = tokio::sync::mpsc::unbounded_channel();
113 let hook = WindowEventHook::hook(EventFilter::default(), event_tx)
114 .await
115 .unwrap();
116
117 for _ in 0..100 {
119 MessageOnlyWindow::new().unwrap().destroy().unwrap();
120 }
121
122 hook.unhook().await.unwrap();
123 }
124
125 #[tokio::test]
126 async fn can_have_multiple_hooks() {
127 let (event_tx, _event_rx) = tokio::sync::mpsc::unbounded_channel();
128 let hook1 = WindowEventHook::hook(EventFilter::default(), event_tx.clone())
129 .await
130 .unwrap();
131 let hook2 = WindowEventHook::hook(EventFilter::default(), event_tx)
132 .await
133 .unwrap();
134
135 for _ in 0..100 {
137 MessageOnlyWindow::new().unwrap().destroy().unwrap();
138 }
139
140 hook1.unhook().await.unwrap();
141 hook2.unhook().await.unwrap();
142 }
143}