1- //! Cross-platform sleep inhibition guard (maximized safety) .
1+ //! Cross-platform sleep inhibition guard.
22//
33//! This module exposes a small RAII guard that prevents the system (or just
44//! the idle subsystem) from going to sleep while it is alive.
1313//!
1414//! Drop the guard to release the inhibition.
1515
16- /// What to keep awake.
17- #[ derive( Clone , Copy , Debug ) ]
18- pub enum Scope {
19- /// Block system sleep (idle suspend). On Linux this maps to `"sleep"`;
20- /// on macOS this uses a system/idle assertion; on Windows it sets
21- /// `ES_SYSTEM_REQUIRED`.
22- System ,
23- /// Block *idle* actions only (no suspend; mostly screen blank, idle sleep).
24- /// On Linux this maps to `"idle"`.
25- IdleOnly ,
16+ #[ derive( Debug , thiserror:: Error ) ]
17+ pub enum SleepInhibitError {
18+ #[ error( "D-Bus connection failed: {0}" ) ]
19+ DBusConnection ( #[ from] dbus:: Error ) ,
20+ #[ error( "Power management API failed: {0}" ) ]
21+ PowerManagement ( String ) ,
22+ #[ error( "Sleep inhibition not supported on this platform" ) ]
23+ UnsupportedPlatform ,
2624}
2725
2826/// RAII guard that holds a platform-specific sleep inhibition.
@@ -37,21 +35,21 @@ impl SleepGuard {
3735 /// `app` is the application name presented to the OS, and `why` is a human
3836 /// readable reason.
3937 #[ inline]
40- pub fn acquire ( scope : Scope , app : & str , why : & str ) -> anyhow:: Result < Self > {
38+ pub fn acquire ( app : & str , why : & str ) -> anyhow:: Result < Self > {
4139 Ok ( Self {
42- _guard : PlatformGuard :: acquire ( scope , app, why) ?,
40+ _guard : PlatformGuard :: acquire ( app, why) ?,
4341 } )
4442 }
4543
4644 /// Acquire using a default app name (the current executable name) and a
4745 /// generic reason.
4846 #[ inline]
49- pub fn acquire_default ( scope : Scope ) -> anyhow:: Result < Self > {
47+ pub fn acquire_default ( ) -> anyhow:: Result < Self > {
5048 let app = std:: env:: current_exe ( )
5149 . ok ( )
5250 . and_then ( |p| p. file_name ( ) . map ( |s| s. to_string_lossy ( ) . into_owned ( ) ) )
5351 . unwrap_or_else ( || "app" . into ( ) ) ;
54- Self :: acquire ( scope , & app, "prevent system sleep" )
52+ Self :: acquire ( & app, "prevent system sleep" )
5553 }
5654}
5755
@@ -67,20 +65,18 @@ enum PlatformGuard {
6765
6866impl PlatformGuard {
6967 #[ inline]
70- fn acquire ( scope : Scope , app : & str , why : & str ) -> anyhow:: Result < Self > {
68+ fn acquire ( app : & str , why : & str ) -> anyhow:: Result < Self > {
7169 #[ cfg( target_os = "linux" ) ]
7270 {
73- return Ok ( Self :: Linux ( linux_impl:: LinuxGuard :: new ( scope , app, why) ?) ) ;
71+ return Ok ( Self :: Linux ( linux_impl:: LinuxGuard :: new ( app, why) ?) ) ;
7472 }
7573 #[ cfg( target_os = "windows" ) ]
7674 {
77- return Ok ( Self :: Windows ( windows_impl:: WindowsGuard :: new (
78- scope, app, why,
79- ) ?) ) ;
75+ return Ok ( Self :: Windows ( windows_impl:: WindowsGuard :: new ( app, why) ?) ) ;
8076 }
8177 #[ cfg( target_os = "macos" ) ]
8278 {
83- return Ok ( Self :: Mac ( mac_impl:: MacGuard :: new ( scope , app, why) ?) ) ;
79+ return Ok ( Self :: Mac ( mac_impl:: MacGuard :: new ( app, why) ?) ) ;
8480 }
8581
8682 #[ allow( unreachable_code) ]
@@ -101,25 +97,22 @@ mod linux_impl {
10197 }
10298
10399 impl LinuxGuard {
104- pub fn new ( scope : Scope , app_name : & str , reason : & str ) -> anyhow:: Result < Self > {
105- let conn = Connection :: new_system ( ) ?;
100+ pub fn new ( app_name : & str , reason : & str ) -> Result < Self , SleepInhibitError > {
101+ let conn = Connection :: new_system ( ) . map_err ( SleepInhibitError :: DBusConnection ) ?;
102+
106103 let proxy = conn. with_proxy (
107104 "org.freedesktop.login1" ,
108105 "/org/freedesktop/login1" ,
109106 std:: time:: Duration :: from_secs ( 5 ) ,
110107 ) ;
111108
112- let what = match scope {
113- Scope :: System => "sleep" ,
114- Scope :: IdleOnly => "idle" ,
115- } ;
116-
117- // Call Inhibit(what, who, why, mode) -> unix fd (OwnedFd closes on drop).
118- let ( fd, ) : ( OwnedFd , ) = proxy. method_call (
119- "org.freedesktop.login1.Manager" ,
120- "Inhibit" ,
121- ( what, app_name, reason, "block" ) ,
122- ) ?;
109+ let ( fd, ) : ( OwnedFd , ) = proxy
110+ . method_call (
111+ "org.freedesktop.login1.Manager" ,
112+ "Inhibit" ,
113+ ( "sleep" , app_name, reason, "block" ) ,
114+ )
115+ . map_err ( SleepInhibitError :: DBusConnection ) ?;
123116
124117 Ok ( Self {
125118 _fd : fd
@@ -135,27 +128,20 @@ mod windows_impl {
135128 pub struct WindowsGuard ;
136129
137130 impl WindowsGuard {
138- pub fn new ( scope : Scope , _app : & str , _reason : & str ) -> anyhow :: Result < Self > {
131+ pub fn new ( _app : & str , _reason : & str ) -> Result < Self , SleepInhibitError > {
139132 // Map scope to execution state flags.
140133 // ES_CONTINUOUS is always set to make the request sticky for this call.
141134 const ES_CONTINUOUS : u32 = 0x80000000 ;
142135 const ES_SYSTEM_REQUIRED : u32 = 0x00000001 ;
143- const ES_DISPLAY_REQUIRED : u32 = 0x00000002 ;
144-
145- let mut flags: u32 = ES_CONTINUOUS ;
146- match scope {
147- Scope :: System => {
148- flags |= ES_SYSTEM_REQUIRED ;
149- } ,
150- Scope :: IdleOnly => {
151- flags |= ES_DISPLAY_REQUIRED ;
152- } ,
153- }
136+
137+ let flags: u32 = ES_CONTINUOUS | ES_SYSTEM_REQUIRED ;
154138
155139 // SAFETY: Calling documented Windows API with constant flags.
156140 let prev = unsafe { windows_sys:: Win32 :: System :: Power :: SetThreadExecutionState ( flags) } ;
157141 if prev == 0 {
158- return Err ( anyhow:: anyhow!( "SetThreadExecutionState failed" ) ) ;
142+ return Err ( SleepInhibitError :: PowerManagement (
143+ "SetThreadExecutionState failed" . into ( ) ,
144+ ) ) ;
159145 }
160146 Ok ( Self )
161147 }
@@ -206,12 +192,8 @@ mod mac_impl {
206192 }
207193
208194 impl MacGuard {
209- pub fn new ( scope : Scope , _app : & str , why : & str ) -> anyhow:: Result < Self > {
210- // Map scope to IOPM assertion type.
211- let assertion_type = match scope {
212- Scope :: System => "NoIdleSleepAssertion" ,
213- Scope :: IdleOnly => "NoDisplaySleepAssertion" ,
214- } ;
195+ pub fn new ( _app : & str , why : & str ) -> anyhow:: Result < Self > {
196+ let assertion_type = "NoIdleSleepAssertion" ;
215197
216198 let mut id: IOPMAssertionID = 0 ;
217199 // SAFETY: FFI call with well-formed CFStrings that live across the call.
0 commit comments