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