2121# constants
2222TIME_STEP = 5 # seconds
2323CONFIG_RELOAD_EVERY = 6 # iterations -> ~30 s
24+ REVERT_TIMEOUT = 15 # seconds before auto-reverting a display change
2425
2526PROJECT_NAME = "SRR"
2627PROJECT_EXECUTABLE = PROJECT_NAME + ".exe"
@@ -165,6 +166,40 @@ def _state_label(state: Optional[bool]) -> str:
165166 return "AC (performance)" if state else "Battery (powersave)"
166167
167168
169+ def _snapshot_current_settings (
170+ display_map : Dict [str , bytes ],
171+ config : Dict [str , Tuple [ScreenSettings , ScreenSettings ]],
172+ ) -> Dict [str , ScreenSettings ]:
173+ snapshot : Dict [str , ScreenSettings ] = {}
174+ for monitor_id in config :
175+ adapter = display_map .get (monitor_id )
176+ if adapter is None :
177+ continue
178+ try :
179+ w , h , f = reschanger .get_display_settings (
180+ adapter , reschanger .ENUM_CURRENT_SETTINGS
181+ )
182+ snapshot [monitor_id ] = ScreenSettings (w , h , f )
183+ except Exception as e :
184+ logging .warning (f"snapshot failed for { monitor_id !r} : { e } " )
185+ return snapshot
186+
187+
188+ async def _revert_after_timeout (
189+ prev : Dict [str , ScreenSettings ],
190+ display_map : Dict [str , bytes ],
191+ ) -> None :
192+ await asyncio .sleep (REVERT_TIMEOUT )
193+ logging .warning ("revert timer fired — restoring previous display settings" )
194+ for monitor_id , ss in prev .items ():
195+ adapter = display_map .get (monitor_id )
196+ if adapter is None :
197+ continue
198+ await change_screen_settings (ss , adapter )
199+ if _tray is not None :
200+ _tray .notify ("Display settings reverted — screen may have gone dark." )
201+
202+
168203async def srr_loop () -> None :
169204 assert _shutdown_event is not None
170205 assert _reload_event is not None
@@ -176,6 +211,7 @@ async def srr_loop() -> None:
176211 if _tray is not None :
177212 _tray .set_state_text (_state_label (last_state ))
178213 counter = 0
214+ pending_revert : Optional [asyncio .Task ] = None
179215
180216 while not _shutdown_event .is_set ():
181217 try :
@@ -184,12 +220,21 @@ async def srr_loop() -> None:
184220 except asyncio .TimeoutError :
185221 pass
186222
223+ # One full TIME_STEP passed without issues — confirm the previous switch
224+ if pending_revert is not None and not pending_revert .done ():
225+ pending_revert .cancel ()
226+ pending_revert = None
227+
187228 if _reload_event .is_set ():
188229 _reload_event .clear ()
189230 current_config = await load_config (force = True )
190231 display_map = build_display_map ()
191232 if current_config is not None :
233+ prev = _snapshot_current_settings (display_map , current_config )
192234 await switch_rate (cur_power_state (), current_config , display_map )
235+ pending_revert = asyncio .create_task (
236+ _revert_after_timeout (prev , display_map )
237+ )
193238
194239 if _tray is not None and _tray .paused :
195240 continue
@@ -204,11 +249,19 @@ async def srr_loop() -> None:
204249 current_config = new_config
205250 if _tray is not None :
206251 _tray .notify ("Config reloaded." )
252+ prev = _snapshot_current_settings (display_map , current_config )
207253 await switch_rate (current_state , current_config , display_map )
254+ pending_revert = asyncio .create_task (
255+ _revert_after_timeout (prev , display_map )
256+ )
208257 counter += 1
209258
210259 if current_state != last_state and current_config is not None :
260+ prev = _snapshot_current_settings (display_map , current_config )
211261 await switch_rate (current_state , current_config , display_map )
262+ pending_revert = asyncio .create_task (
263+ _revert_after_timeout (prev , display_map )
264+ )
212265 if _tray is not None :
213266 _tray .set_state_text (_state_label (current_state ))
214267
0 commit comments