---
name: adom-screensaver-wake
description: How any Adom app (Hydrogen Desktop, adom-desktop, or any tool that needs the laptop screen visible) asks the running Adom screensaver to get out of the way AND keeps the display powered on. Use when an app needs to screenshot, drive UI automation, show an OAuth window, or otherwise SEE the user's screen while the Adom screensaver/billboard might be up. Trigger words, wake the screensaver, dismiss screensaver, adom-screensaver-wake, screensaver wake event, screensaver control port, turn off screensaver from code, screensaver IPC, screensaver is covering the screen, need the screen, ES_DISPLAY_REQUIRED, force display on, screensaver wake.flag, adom-screensaver port.txt.
---

# adom-screensaver-wake, ask the saver to get off the screen

Parent skill: **adom-screensaver**

When the Adom screensaver / Wiki billboard is up (the user went idle), it is a **topmost
fullscreen window** and, after the display-off timeout, the **panel is physically off**. Any
Adom app that needs to *see* the screen (screenshot, pup/UI automation, OAuth approval window)
must (1) dismiss the saver and (2) make sure the display is on. This skill is that contract.

## The two responsibilities (don't skip #2)

1. **Dismiss the saver**, signal it (below). It exits immediately.
2. **Hold the display ON yourself**, call `SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED)`
   in *your* (the requester's) process and keep it held while you need the screen. The saver
   pulses the display on as it exits, but **that pulse clears when the saver process dies** -
   only a live holder keeps the panel on. Release with `SetThreadExecutionState(ES_CONTINUOUS)`.

If the panel was physically off, give it **~1.5-2s** to power on after you hold ES_DISPLAY_REQUIRED
before you screenshot.

## Three ways to dismiss (all handled by the saver, pick what fits your language)

### 1. Named event `adom-screensaver-wake`, guaranteed, instant
Works even after the panel powered off (the saver listens on a blocking thread, not its paint
loop). This is the **primary** path.

PowerShell / adom-desktop relay:
```powershell
[System.Threading.EventWaitHandle]::OpenExisting('adom-screensaver-wake').Set()
# throws if no saver is running -> nothing to dismiss
```
Rust (Hydrogen Desktop), via the `windows` crate:
```rust
use windows::Win32::System::Threading::{OpenEventW, SetEvent};
use windows::Win32::System::Threading::EVENT_MODIFY_STATE;
let h = OpenEventW(EVENT_MODIFY_STATE, false, w!("adom-screensaver-wake"))?; // Err = not running
SetEvent(h)?;
```

### 2. Flag file, no event API needed (picked up within ~0.75s)
```
create:  %LOCALAPPDATA%\adom-screensaver\wake.flag    (the saver deletes it on read)
```
```powershell
New-Item -ItemType File "$env:LOCALAPPDATA\adom-screensaver\wake.flag" -Force | Out-Null
```

### 3. Localhost control port, when you also want STATUS
The saver runs a loopback HTTP server. **The port is dynamic** (Hyper-V/WSL phantom-reserve
fixed ports), so read it from `%LOCALAPPDATA%\adom-screensaver\port.txt`, never hardcode.
```powershell
$port = (Get-Content "$env:LOCALAPPDATA\adom-screensaver\port.txt" -Raw).Trim()
# is the saver up, and what is it showing / how long until display-off?
Invoke-WebRequest -UseBasicParsing "http://127.0.0.1:$port/status"
#   -> {"running":true,"slides":16,"currentSlug":"shotlog","currentTitle":"Shotlog",
#       "secondsUntilDisplayOff":47,"power":"ac","battery":98,"waking":false}
# dismiss:
Invoke-WebRequest -UseBasicParsing "http://127.0.0.1:$port/wake" -Method POST
```
`port.txt` absent / connection refused == the saver isn't running (nothing to dismiss).

## Recommended recipe (e.g. adom-desktop about to screenshot)

```powershell
# 1) hold the display on in THIS process for the whole capture
Add-Type 'using System;using System.Runtime.InteropServices;public class P{[DllImport("kernel32.dll")]public static extern uint SetThreadExecutionState(uint f);}'
[P]::SetThreadExecutionState(0x80000000 -bor 0x00000002) | Out-Null   # ES_CONTINUOUS|ES_DISPLAY_REQUIRED
# 2) dismiss the saver
try { [System.Threading.EventWaitHandle]::OpenExisting('adom-screensaver-wake').Set() } catch {}
# 3) let the panel power on if it was off
Start-Sleep -Milliseconds 1800
# 4) ... capture / automate ...
# 5) release when done
[P]::SetThreadExecutionState(0x80000000) | Out-Null                   # ES_CONTINUOUS
```

## Notes
- **Keep-awake is separate.** Dismissing the saver never affects `adom-stayawake.exe`; the
  machine stays awake regardless.
- The saver is single-instance (`adom-screensaver-singleton` mutex) and re-arms on the next idle, so
  after you're done it simply comes back when the user goes idle again, you don't restart it.
- For a headless test of the channel without the fullscreen window, run `adom-screensaver.scr /serve`
  (control port + wake listener only).
