window_observer/lib.rs
1#![doc = include_str!("../README.md")]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3
4pub use window_getter;
5
6pub mod platform_impl;
7pub mod window;
8
9pub use ::tokio;
10pub use window::{Position, Size, Window};
11
12use crate::platform_impl::PlatformWindowObserver;
13
14/// Represents errors that can occur in the library.
15#[non_exhaustive]
16#[derive(Debug, thiserror::Error)]
17pub enum Error {
18 /// The process ID is invalid for observing windows.
19 ///
20 /// # Platform-specific
21 /// - **Windows:** This occurs when the process ID is zero.
22 /// - **macOS:** This does not occur on macOS.
23 #[error("The process ID is invalid: {0}")]
24 InvalidProcessId(u32),
25 /// This occurs when the application is not ready yet.
26 /// This also occurs when the application that has given PID is not found.
27 ///
28 /// # Platform-specific
29 /// - **Windows:** This does not occur on windows.
30 #[error("Something went wrong")]
31 SomethingWentWrong,
32 /// The application does not support observing window events.
33 ///
34 /// # Platform-specific
35 /// - **Windows:** This does not occur on windows.
36 #[error("The application does not support observing window")]
37 NotSupported,
38 /// Permission denied error. This error only occurs on macOS.
39 #[error("Permission denied.")]
40 PermissionDenied,
41 /// A platform-specific error occurred.
42 #[error("A platform-specific error occurred: {0:?}")]
43 PlatformSpecificError(#[from] platform_impl::PlatformError),
44}
45
46/// Represents a filter for window events.
47#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
48pub struct EventFilter {
49 /// Whether to observe [`Event::Foregrounded`] events.
50 pub foregrounded: bool,
51 /// Whether to observe [`Event::Backgrounded`] events.
52 pub backgrounded: bool,
53 /// Whether to observe [`Event::Focused`] events.
54 pub focused: bool,
55 /// Whether to observe [`Event::Unfocused`] events.
56 pub unfocused: bool,
57 /// Whether to observe [`Event::Created`] events.
58 pub created: bool,
59 /// Whether to observe [`Event::Resized`] events.
60 pub resized: bool,
61 /// Whether to observe [`Event::Moved`] events.
62 pub moved: bool,
63 /// Whether to observe [`Event::Hidden`] events.
64 pub hidden: bool,
65 /// Whether to observe [`Event::Showed`] events.
66 pub showed: bool,
67 /// Whether to observe [`Event::Closed`] events.
68 pub closed: bool,
69}
70
71impl EventFilter {
72 /// Creates a new `EventFilter` with all events enabled.
73 pub fn all() -> Self {
74 Self {
75 foregrounded: true,
76 backgrounded: true,
77 focused: true,
78 unfocused: true,
79 created: true,
80 resized: true,
81 moved: true,
82 hidden: true,
83 showed: true,
84 closed: true,
85 }
86 }
87
88 /// Creates a new `EventFilter` with no events enabled.
89 pub fn empty() -> Self {
90 Default::default()
91 }
92
93 pub(crate) fn should_dispatch(&self, event: &Event) -> bool {
94 matches!(event, Event::Foregrounded) && self.foregrounded
95 || matches!(event, Event::Backgrounded) && self.backgrounded
96 || matches!(event, Event::Focused) && self.focused
97 || matches!(event, Event::Unfocused) && self.unfocused
98 || matches!(event, Event::Created) && self.created
99 || matches!(event, Event::Resized) && self.resized
100 || matches!(event, Event::Moved) && self.moved
101 || matches!(event, Event::Hidden) && self.hidden
102 || matches!(event, Event::Showed) && self.showed
103 || matches!(event, Event::Closed { .. }) && self.closed
104 }
105}
106
107/// Represents events that can be observed on a window.
108#[non_exhaustive]
109#[derive(Debug, Clone, PartialEq)]
110pub enum Event {
111 /// The window was created.
112 Created,
113 /// The window was resized.
114 Resized,
115 /// The window was moved.
116 Moved,
117 /// The window was brought to the foreground.
118 /// This event does not mean the window has gained input focus.
119 Foregrounded,
120 /// The window was backgrounded. It is opposite of [`Event::Foregrounded`].
121 Backgrounded,
122 /// The window was focused.
123 ///
124 /// # Platform-specific
125 /// - **Windows:** This event is same as [`Event::Foregrounded`].
126 /// So this event and `Foregrounded` event are always dispatched together.
127 /// - **macOS:** On macOS, a window does not lose focus even when miniaturized.
128 /// Therefore, this event will not be dispatched when the window is deminiaturized.
129 Focused,
130 /// The window was unfocused.
131 ///
132 /// # Platform-specific
133 /// - **Windows:** This event is same as [`Event::Backgrounded`].
134 /// So this event and `Backgrounded` event are always dispatched together.
135 /// - **macOS:** On macOS, a window does not lose focus even when miniaturized.
136 /// Therefore, this event will not be dispatched when the window is miniaturized
137 Unfocused,
138 /// The window was hidden.
139 Hidden,
140 /// The window was showed.
141 ///
142 /// # Platform-specific
143 /// - **Windows:** This event may not occur until after the observer has started
144 /// and the first Hidden event has been triggered.
145 /// Example: When monitoring begins while the window is minimized to the taskbar,
146 /// and then the window is opened from the taskbar.
147 Showed,
148 /// The window was closed.
149 Closed { window_id: window_getter::WindowId },
150}
151
152/// Represents a window that may or may not be available.
153#[derive(Debug, Clone, PartialEq)]
154pub enum MaybeWindowAvailable {
155 /// The window is available.
156 Available { window: Window, event: Event },
157 /// The window is not available.
158 /// This can happen when the window is closed.
159 NotAvailable { event: Event },
160}
161
162/// A type alias for the result of an event.
163/// `Err` means that the event could not be processed, and `Ok` contains the event.
164pub type EventResult = Result<MaybeWindowAvailable, platform_impl::PlatformError>;
165/// A type alias for the window event transmission channel.
166pub type EventTx = tokio::sync::mpsc::UnboundedSender<EventResult>;
167/// A type alias for the window event reception channel.
168pub type EventRx = tokio::sync::mpsc::UnboundedReceiver<EventResult>;
169
170/// Observes window events.
171pub struct WindowObserver(PlatformWindowObserver);
172
173impl WindowObserver {
174 /// Creates a new [`WindowObserver`] for a given process ID and event channel
175 /// and start the observer.
176 pub async fn start(
177 pid: u32,
178 event_tx: EventTx,
179 event_filter: EventFilter,
180 ) -> Result<Self, Error> {
181 #[cfg(target_os = "macos")]
182 let pid = pid as i32;
183
184 Ok(Self(
185 PlatformWindowObserver::start(pid, event_tx, event_filter).await?,
186 ))
187 }
188
189 /// Stops the observer and cleans up resources.
190 ///
191 /// # Notes
192 /// If you don't call this method, the observer will continue to run until droped.
193 ///
194 /// # Platform-specific
195 /// - **macOS:** It will always return [`Ok`].
196 pub async fn stop(self) -> Result<(), Error> {
197 #[cfg(target_os = "macos")]
198 self.0.stop().await;
199 #[cfg(target_os = "windows")]
200 self.0.stop().await?;
201
202 Ok(())
203 }
204
205 /// Returns underlying platform-specific observer.
206 pub fn inner(&self) -> &PlatformWindowObserver {
207 &self.0
208 }
209}