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    Showed,
142    /// The window was closed.
143    Closed { window_id: window_getter::WindowId },
144}
145
146/// Represents a window that may or may not be available.
147#[derive(Debug, Clone, PartialEq)]
148pub enum MaybeWindowAvailable {
149    /// The window is available.
150    Available { window: Window, event: Event },
151    /// The window is not available.
152    /// This can happen when the window is closed.
153    NotAvailable { event: Event },
154}
155
156/// A type alias for the result of an event.
157/// `Err` means that the event could not be processed, and `Ok` contains the event.
158pub type EventResult = Result<MaybeWindowAvailable, platform_impl::PlatformError>;
159/// A type alias for the window event transmission channel.
160pub type EventTx = tokio::sync::mpsc::UnboundedSender<EventResult>;
161/// A type alias for the window event reception channel.
162pub type EventRx = tokio::sync::mpsc::UnboundedReceiver<EventResult>;
163
164/// Observes window events.
165pub struct WindowObserver(PlatformWindowObserver);
166
167impl WindowObserver {
168    /// Creates a new [`WindowObserver`] for a given process ID and event channel
169    /// and start the observer.
170    pub async fn start(
171        pid: u32,
172        event_tx: EventTx,
173        event_filter: EventFilter,
174    ) -> Result<Self, Error> {
175        #[cfg(target_os = "macos")]
176        let pid = pid as i32;
177
178        Ok(Self(
179            PlatformWindowObserver::start(pid, event_tx, event_filter).await?,
180        ))
181    }
182
183    /// Stops the observer and cleans up resources.
184    ///
185    /// # Notes
186    /// If you don't call this method, the observer will continue to run until droped.
187    ///
188    /// # Platform-specific
189    /// - **macOS:** It will always return [`Ok`].
190    pub async fn stop(self) -> Result<(), Error> {
191        #[cfg(target_os = "macos")]
192        self.0.stop().await;
193        #[cfg(target_os = "windows")]
194        self.0.stop().await?;
195
196        Ok(())
197    }
198
199    /// Returns underlying platform-specific observer.
200    pub fn inner(&self) -> &PlatformWindowObserver {
201        &self.0
202    }
203}