Skip to main content

whatawhat_lib/
lib.rs

1#[cfg(feature = "gnome")]
2pub mod gnome;
3#[cfg(feature = "kde")]
4pub mod kde;
5#[cfg(feature = "wayland")]
6pub mod wayland_idle;
7#[cfg(feature = "wayland")]
8pub mod wayland_wlr;
9#[cfg(feature = "win")]
10pub mod win;
11#[cfg(feature = "wayland")]
12pub mod wl_connection;
13#[cfg(feature = "x11")]
14pub mod x11;
15
16pub mod idle;
17pub mod utils;
18
19#[cfg(feature = "win")]
20extern crate windows;
21
22// #[cfg(feature = "x11")]
23// extern crate xcb;
24
25use std::{sync::Arc, time::Duration};
26
27use anyhow::Result;
28use async_trait::async_trait;
29use tracing::info;
30
31#[derive(Debug, Clone)]
32pub struct ActiveWindowData {
33    /// Name of the window. For example 'bash in hello' or 'Document 1' or 'Vibing in YouTube -
34    /// Chrome'
35    pub window_title: Arc<str>,
36    /// Represents an identifier of the application.
37    /// On windows it is a process name. For example `C:\Windows\System32\cmd.exe`
38    /// On x11 it is a process name. For example `/home/etc/nvim``
39    /// On wayland, gnome, and kde it's a resource class. For example `org.kde.kate`
40    pub app_identifier: Arc<str>,
41}
42
43/// Intended to serve as a contract windows and linux systems must implement.
44#[cfg_attr(test, mockall::automock)]
45#[async_trait]
46pub trait WindowManager {
47    async fn get_active_window_data(&mut self) -> Result<ActiveWindowData>;
48
49    /// Retrieve amount of time user has been inactive in milliseconds
50    async fn is_idle(&mut self) -> Result<bool>;
51}
52
53/// Serves as a cross-compatible WindowManager implementation.
54pub struct GenericWindowManager {
55    inner: Box<dyn WindowManager + Send + Sync>,
56}
57
58impl GenericWindowManager {
59    pub async fn new(idle_timeout: Duration) -> Result<Self> {
60        #[cfg(feature = "win")]
61        {
62            use win::WindowsWindowManager;
63            return Ok(Self {
64                inner: Box::new(WindowsWindowManager::new(idle_timeout)),
65            });
66        }
67        // TODO: Should try to select not select outright
68        #[cfg(feature = "gnome")]
69        {
70            use gnome::GnomeWindowWatcher;
71            let watcher = GnomeWindowWatcher::new(idle_timeout.into()).await;
72            match watcher {
73                Ok(watcher) => {
74                    let result = Ok(Self {
75                        inner: Box::new(watcher),
76                    });
77                    info!("Loaded Gnome Wayland watcher");
78                    return result;
79                }
80                Err(e) => {
81                    use tracing::warn;
82                    warn!("Failed to load Gnome Wayland watcher: {e}");
83                }
84            }
85        }
86        #[cfg(feature = "kde")]
87        {
88            use kde::KdeWindowManager;
89            let watcher = KdeWindowManager::new(idle_timeout).await;
90            match watcher {
91                Ok(watcher) => {
92                    let result = Ok(Self {
93                        inner: Box::new(watcher),
94                    });
95                    info!("Loaded Kde wayland watcher");
96                    return result;
97                }
98                Err(e) => {
99                    use tracing::warn;
100                    warn!("Failed to load Gnome Wayland watcher: {e}");
101                }
102            }
103        }
104        #[cfg(feature = "wayland")]
105        {
106            use wayland_wlr::WaylandWindowWatcher;
107            let watcher = WaylandWindowWatcher::new(idle_timeout).await;
108            match watcher {
109                Ok(watcher) => {
110                    let result = Ok(Self {
111                        inner: Box::new(watcher),
112                    });
113                    info!("Loaded Wayland window watcher");
114                    return result;
115                }
116                Err(e) => {
117                    use tracing::warn;
118                    warn!("Failed to load Wayland window watcher: {e}");
119                }
120            }
121        }
122        #[cfg(feature = "x11")]
123        {
124            use x11::LinuxWindowManager;
125            let watcher = LinuxWindowManager::new(idle_timeout);
126            match watcher {
127                Ok(watcher) => {
128                    let result = Ok(Self {
129                        inner: Box::new(watcher),
130                    });
131                    info!("Loaded X11 window manager");
132                    return result;
133                }
134                Err(e) => {
135                    use tracing::warn;
136                    warn!("Failed to load X11 window manager: {e}");
137                }
138            }
139        }
140        #[allow(unreachable_code)]
141        {
142            Err(anyhow::anyhow!("No window manager was selected"))
143        }
144    }
145}
146
147#[async_trait]
148impl WindowManager for GenericWindowManager {
149    async fn get_active_window_data(&mut self) -> Result<ActiveWindowData> {
150        self.inner.get_active_window_data().await
151    }
152
153    async fn is_idle(&mut self) -> Result<bool> {
154        self.inner.is_idle().await
155    }
156}