1use dbus::blocking::{BlockingSender, Connection};
9use nosleep_types::NoSleepType;
10use snafu::{prelude::*, Backtrace};
11
12#[derive(Debug, Snafu)]
13pub enum Error {
14 #[snafu(display("General D-Bus Error"))]
15 DBus {
16 source: dbus::Error,
17 backtrace: Backtrace,
18 },
19
20 #[snafu(display("Invalid response from D-Bus"))]
21 InvalidResponse { backtrace: Backtrace },
22}
23
24pub type Result<T, E = Error> = std::result::Result<T, E>;
25
26#[derive(Debug, Copy, Clone)]
27enum DBusAPI {
28 GnomeApi, FreeDesktopPowerApi, FreeDesktopScreenSaverAPI, }
32
33enum GnomeAPIInhibitFlags {
35 InhibitSuspendSession = 4,
36 InhibitMarkSessionIdle = 8,
37}
38
39struct NoSleepHandleCookie {
40 handle: u32,
42 api: DBusAPI,
44}
45
46struct NoSleepHandle {
49 cookies: Vec<NoSleepHandleCookie>,
51}
52
53impl NoSleepHandle {
54 pub fn stop(&self, d_bus: &Connection) -> Result<()> {
56 for cookie in &self.cookies {
57 let msg = uninhibit_msg(&cookie.api, cookie.handle);
58 d_bus
59 .send_with_reply_and_block(msg, std::time::Duration::from_millis(5000))
60 .context(DBusSnafu)?;
61 }
62 Ok(())
63 }
64}
65
66pub struct NoSleep {
67 d_bus: Connection,
69
70 no_sleep_handle: Option<NoSleepHandle>,
72}
73
74impl NoSleep {
75 pub fn new() -> Result<NoSleep> {
78 Ok(NoSleep {
79 d_bus: Connection::new_session().context(DBusSnafu)?,
80 no_sleep_handle: None,
81 })
82 }
83
84 pub fn start(&mut self, nosleep_type: NoSleepType) -> Result<()> {
89 self.stop()?;
91
92 let response = self.inhibit(&DBusAPI::GnomeApi, &nosleep_type);
93 if let Ok(cookie) = response {
94 self.no_sleep_handle = Some(NoSleepHandle {
95 cookies: vec![cookie],
96 });
97 return Ok(());
98 }
99 let mut cookies: Vec<NoSleepHandleCookie> = vec![];
101 if nosleep_type == NoSleepType::PreventUserIdleDisplaySleep {
102 let cookie = self.inhibit(&DBusAPI::FreeDesktopScreenSaverAPI, &nosleep_type)?;
103 cookies.push(cookie);
104 }
105 let cookie = self.inhibit(&DBusAPI::FreeDesktopPowerApi, &nosleep_type)?;
107 cookies.push(cookie);
108 self.no_sleep_handle = Some(NoSleepHandle { cookies });
109 Ok(())
110 }
111
112 pub fn stop(&self) -> Result<()> {
114 if let Some(handle) = &self.no_sleep_handle {
115 return handle.stop(&self.d_bus);
116 }
117 Ok(())
118 }
119
120 fn inhibit(&self, api: &DBusAPI, nosleep_type: &NoSleepType) -> Result<NoSleepHandleCookie> {
121 let msg = inhibit_msg(api, nosleep_type);
122 let response = self
123 .d_bus
124 .send_with_reply_and_block(msg, std::time::Duration::from_millis(5000))
125 .context(DBusSnafu)?;
126 let handle = response.get1().context(InvalidResponseSnafu)?;
127 Ok(NoSleepHandleCookie { handle, api: *api })
128 }
129}
130
131fn inhibit_msg(api: &DBusAPI, nosleep_type: &NoSleepType) -> dbus::Message {
132 match api {
133 DBusAPI::GnomeApi => {
134 let flags = match nosleep_type {
140 NoSleepType::PreventUserIdleDisplaySleep => {
141 GnomeAPIInhibitFlags::InhibitMarkSessionIdle as u32
142 | GnomeAPIInhibitFlags::InhibitSuspendSession as u32
143 }
144 NoSleepType::PreventUserIdleSystemSleep => {
145 GnomeAPIInhibitFlags::InhibitSuspendSession as u32
146 }
147 };
148 dbus::Message::call_with_args(
149 "org.gnome.SessionManager",
150 "/org/gnome/SessionManager",
151 "org.gnome.SessionManager",
152 "Inhibit",
153 (
154 "org.powersaveblocker.app",
155 0u32,
156 "Power Save Blocker",
157 flags,
158 ),
159 )
160 }
161 DBusAPI::FreeDesktopPowerApi => dbus::Message::call_with_args(
165 "org.freedesktop.PowerManagement",
166 "/org/freedesktop/PowerManagement/Inhibit",
167 "org.freedesktop.PowerManagement.Inhibit",
168 "Inhibit",
169 ("org.powersaveblocker.app", "Power Save Blocker"),
170 ),
171 DBusAPI::FreeDesktopScreenSaverAPI => dbus::Message::call_with_args(
172 "org.freedesktop.ScreenSaver",
173 "/org/freedesktop/ScreenSaver",
174 "org.freedesktop.ScreenSaver",
175 "Inhibit",
176 ("org.powersaveblocker.app", "Power Save Blocker"),
177 ),
178 }
179}
180
181fn uninhibit_msg(api: &DBusAPI, handle: u32) -> dbus::Message {
182 match api {
183 DBusAPI::GnomeApi => {
184 dbus::Message::call_with_args(
187 "org.gnome.SessionManager",
188 "/org/gnome/SessionManager",
189 "org.gnome.SessionManager",
190 "Uninhibit",
191 (handle,),
192 )
193 }
194 DBusAPI::FreeDesktopPowerApi => dbus::Message::call_with_args(
195 "org.freedesktop.PowerManagement",
196 "/org/freedesktop/PowerManagement/Inhibit",
197 "org.freedesktop.PowerManagement.Inhibit",
198 "UnInhibit",
199 (handle,),
200 ),
201 DBusAPI::FreeDesktopScreenSaverAPI => dbus::Message::call_with_args(
202 "org.freedesktop.ScreenSaver",
203 "/org/freedesktop/ScreenSaver",
204 "org.freedesktop.ScreenSaver",
205 "UnInhibit",
206 (handle,),
207 ),
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use crate::*;
214
215 #[test]
216 fn test_inhibit_gnome_api_message_prevent_display_sleep() {
217 let msg = inhibit_msg(
218 &DBusAPI::GnomeApi,
219 &NoSleepType::PreventUserIdleDisplaySleep,
220 );
221 assert_eq!("/org/gnome/SessionManager", &*msg.path().unwrap());
222 assert_eq!("org.gnome.SessionManager", &*msg.interface().unwrap());
223 assert_eq!("Inhibit", &*msg.member().unwrap());
224 assert_eq!("org.gnome.SessionManager", &*msg.destination().unwrap());
225 assert_eq!(12, msg.get_items().last().unwrap().inner::<u32>().unwrap());
226 }
227
228 #[test]
229 fn test_inhibit_gnome_api_message_prevent_system_sleep() {
230 let msg = inhibit_msg(&DBusAPI::GnomeApi, &NoSleepType::PreventUserIdleSystemSleep);
231 assert_eq!("/org/gnome/SessionManager", &*msg.path().unwrap());
232 assert_eq!("org.gnome.SessionManager", &*msg.interface().unwrap());
233 assert_eq!("Inhibit", &*msg.member().unwrap());
234 assert_eq!("org.gnome.SessionManager", &*msg.destination().unwrap());
235 assert_eq!(4, msg.get_items().last().unwrap().inner::<u32>().unwrap());
236 }
237
238 #[test]
239 fn test_uninhibit_gnome_api() {
240 let msg = uninhibit_msg(&DBusAPI::GnomeApi, 0);
241 assert_eq!("/org/gnome/SessionManager", &*msg.path().unwrap());
242 assert_eq!("org.gnome.SessionManager", &*msg.interface().unwrap());
243 assert_eq!("Uninhibit", &*msg.member().unwrap());
244 assert_eq!("org.gnome.SessionManager", &*msg.destination().unwrap());
245 assert_eq!(0, msg.get_items().last().unwrap().inner::<u32>().unwrap());
246 }
247
248 #[test]
249 fn test_inhibit_freedesktop_screen_saver_api() {
250 let msg = inhibit_msg(
251 &DBusAPI::FreeDesktopScreenSaverAPI,
252 &NoSleepType::PreventUserIdleDisplaySleep,
253 );
254 assert_eq!("/org/freedesktop/ScreenSaver", &*msg.path().unwrap());
255 assert_eq!("org.freedesktop.ScreenSaver", &*msg.interface().unwrap());
256 assert_eq!("Inhibit", &*msg.member().unwrap());
257 assert_eq!("org.freedesktop.ScreenSaver", &*msg.destination().unwrap());
258 }
259
260 #[test]
261 fn test_uninhibit_freedesktop_screen_saver_api() {
262 let msg = uninhibit_msg(&DBusAPI::FreeDesktopScreenSaverAPI, 0);
263 assert_eq!("/org/freedesktop/ScreenSaver", &*msg.path().unwrap());
264 assert_eq!("org.freedesktop.ScreenSaver", &*msg.interface().unwrap());
265 assert_eq!("UnInhibit", &*msg.member().unwrap());
266 assert_eq!("org.freedesktop.ScreenSaver", &*msg.destination().unwrap());
267 assert_eq!(0, msg.get_items().last().unwrap().inner::<u32>().unwrap());
268 }
269
270 #[test]
271 fn test_freedesktop_power_api() {
272 let msg = inhibit_msg(
273 &DBusAPI::FreeDesktopPowerApi,
274 &NoSleepType::PreventUserIdleSystemSleep,
275 );
276 assert_eq!(
277 "/org/freedesktop/PowerManagement/Inhibit",
278 &*msg.path().unwrap()
279 );
280 assert_eq!(
281 "org.freedesktop.PowerManagement.Inhibit",
282 &*msg.interface().unwrap()
283 );
284 assert_eq!("Inhibit", &*msg.member().unwrap());
285 assert_eq!(
286 "org.freedesktop.PowerManagement",
287 &*msg.destination().unwrap()
288 );
289 }
290
291 #[test]
293 #[ignore]
294 fn test_start() {
295 let mut nosleep = NoSleep::new().unwrap();
296 nosleep
297 .start(NoSleepType::PreventUserIdleSystemSleep)
298 .unwrap();
299 std::thread::sleep(std::time::Duration::from_millis(2000));
300 nosleep.stop().unwrap();
301 std::thread::sleep(std::time::Duration::from_millis(2000));
302 }
303}