Skip to main content

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}