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.
@@ -34,24 +32,23 @@ pub struct SleepGuard {
3432impl SleepGuard {
3533 /// Acquire a system sleep inhibitor.
3634 ///
37- /// `app` is the application name presented to the OS, and `why` is a human
38- /// readable reason.
35+ /// `app` is the application name presented to the OS, and `why` is a human readable reason.
3936 #[ inline]
40- pub fn acquire ( scope : Scope , app : & str , why : & str ) -> anyhow:: Result < Self > {
37+ pub fn acquire ( app : & str , why : & str ) -> anyhow:: Result < Self > {
4138 Ok ( Self {
42- _guard : PlatformGuard :: acquire ( scope , app, why) ?,
39+ _guard : PlatformGuard :: acquire ( app, why) ?,
4340 } )
4441 }
4542
4643 /// Acquire using a default app name (the current executable name) and a
4744 /// generic reason.
4845 #[ inline]
49- pub fn acquire_default ( scope : Scope ) -> anyhow:: Result < Self > {
46+ pub fn acquire_default ( ) -> anyhow:: Result < Self > {
5047 let app = std:: env:: current_exe ( )
5148 . ok ( )
5249 . and_then ( |p| p. file_name ( ) . map ( |s| s. to_string_lossy ( ) . into_owned ( ) ) )
5350 . unwrap_or_else ( || "app" . into ( ) ) ;
54- Self :: acquire ( scope , & app, "prevent system sleep" )
51+ Self :: acquire ( & app, "prevent system sleep" )
5552 }
5653}
5754
@@ -67,20 +64,18 @@ enum PlatformGuard {
6764
6865impl PlatformGuard {
6966 #[ inline]
70- fn acquire ( scope : Scope , app : & str , why : & str ) -> anyhow:: Result < Self > {
67+ fn acquire ( app : & str , why : & str ) -> anyhow:: Result < Self > {
7168 #[ cfg( target_os = "linux" ) ]
7269 {
73- return Ok ( Self :: Linux ( linux_impl:: LinuxGuard :: new ( scope , app, why) ?) ) ;
70+ return Ok ( Self :: Linux ( linux_impl:: LinuxGuard :: new ( app, why) ?) ) ;
7471 }
7572 #[ cfg( target_os = "windows" ) ]
7673 {
77- return Ok ( Self :: Windows ( windows_impl:: WindowsGuard :: new (
78- scope, app, why,
79- ) ?) ) ;
74+ return Ok ( Self :: Windows ( windows_impl:: WindowsGuard :: new ( app, why) ?) ) ;
8075 }
8176 #[ cfg( target_os = "macos" ) ]
8277 {
83- return Ok ( Self :: Mac ( mac_impl:: MacGuard :: new ( scope , app, why) ?) ) ;
78+ return Ok ( Self :: Mac ( mac_impl:: MacGuard :: new ( app, why) ?) ) ;
8479 }
8580
8681 #[ allow( unreachable_code) ]
@@ -101,29 +96,23 @@ mod linux_impl {
10196 }
10297
10398 impl LinuxGuard {
104- pub fn new ( scope : Scope , app_name : & str , reason : & str ) -> anyhow:: Result < Self > {
105- let conn = Connection :: new_system ( ) ?;
99+ pub fn new ( app_name : & str , reason : & str ) -> Result < Self , SleepInhibitError > {
100+ let conn = Connection :: new_system ( )
101+ . map_err ( |e| SleepInhibitError :: DBusConnection ( e) ) ?;
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).
118109 let ( fd, ) : ( OwnedFd , ) = proxy. method_call (
119110 "org.freedesktop.login1.Manager" ,
120111 "Inhibit" ,
121- ( what , app_name, reason, "block" ) ,
122- ) ?;
112+ ( "sleep" , app_name, reason, "block" ) ,
113+ ) . map_err ( |e| SleepInhibitError :: DBusConnection ( e ) ) ?;
123114
124- Ok ( Self {
125- _fd : fd
126- } )
115+ Ok ( Self { _fd : fd } )
127116 }
128117 }
129118}
@@ -135,27 +124,21 @@ mod windows_impl {
135124 pub struct WindowsGuard ;
136125
137126 impl WindowsGuard {
138- pub fn new ( scope : Scope , _app : & str , _reason : & str ) -> anyhow:: Result < Self > {
127+ pub fn new ( _app : & str , _reason : & str ) -> Result < Self , SleepInhibitError >
128+ {
139129 // Map scope to execution state flags.
140130 // ES_CONTINUOUS is always set to make the request sticky for this call.
141131 const ES_CONTINUOUS : u32 = 0x80000000 ;
142132 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- }
133+
134+ let flags: u32 = ES_CONTINUOUS | ES_SYSTEM_REQUIRED ;
154135
155136 // SAFETY: Calling documented Windows API with constant flags.
156137 let prev = unsafe { windows_sys:: Win32 :: System :: Power :: SetThreadExecutionState ( flags) } ;
157138 if prev == 0 {
158- return Err ( anyhow:: anyhow!( "SetThreadExecutionState failed" ) ) ;
139+ return Err ( SleepInhibitError :: PowerManagement (
140+ "SetThreadExecutionState failed" . into ( )
141+ ) ) ;
159142 }
160143 Ok ( Self )
161144 }
@@ -206,12 +189,8 @@ mod mac_impl {
206189 }
207190
208191 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- } ;
192+ pub fn new ( _app : & str , why : & str ) -> anyhow:: Result < Self > {
193+ let assertion_type = "NoIdleSleepAssertion" ;
215194
216195 let mut id: IOPMAssertionID = 0 ;
217196 // SAFETY: FFI call with well-formed CFStrings that live across the call.
0 commit comments