Skip to content

Awake: Add "Keep awake when lid closed" option.#46408

Open
andrewleech wants to merge 1 commit intomicrosoft:mainfrom
andrewleech:feature/awake-lid-close-override
Open

Awake: Add "Keep awake when lid closed" option.#46408
andrewleech wants to merge 1 commit intomicrosoft:mainfrom
andrewleech:feature/awake-lid-close-override

Conversation

@andrewleech
Copy link
Copy Markdown

@andrewleech andrewleech commented Mar 23, 2026

Summary

Laptops running Awake in INDEFINITE or TIMED mode still go to sleep when the lid is closed, because Windows applies the power plan's lid close action regardless of SetThreadExecutionState. This adds a toggle that overrides the lid close action to "Do nothing" in the active power scheme while Awake is in a non-passive mode. The toggle defaults to off, so existing behavior is unchanged for users who want sleep-on-close.

The core of the change is LidOverrideManager, a new static class that wraps the Win32 PowerWriteACValueIndex/PowerWriteDCValueIndex APIs to temporarily set the lid close action to "Do nothing" (for both AC and DC power), then restore the original values when the override is removed. It persists a JSON state file alongside the normal settings so that if Awake crashes or gets killed, the next launch detects the orphaned override and restores the original lid settings before doing anything else.

stateDiagram-v2
    [*] --> Passive
    Passive --> Active: Mode set to INDEFINITE/TIMED/EXPIRABLE<br/>+ keepAwakeOnLidClose=true
    Active --> Passive: Mode set to PASSIVE<br/>or timer expires
    Active --> Active: Power event (resume)<br/>→ reapply override
    Active --> CrashRecovery: Process killed
    CrashRecovery --> Passive: Next launch reads state file<br/>→ restores original values

    state Active {
        [*] --> OverrideApplied: WriteLidCloseAction(DoNothing)
        OverrideApplied --> OverrideRemoved: RestoreLidSettings()
        OverrideRemoved --> [*]
    }
Loading

Key design decisions the diff won't immediately convey:

  • Mode parameter passed explicitly to ApplyLidOverrideIfNeeded rather than reading CurrentOperatingMode — the mode setters save settings and return before updating the static property, so the FileSystemWatcher can re-trigger ProcessSettings with a stale value.
  • TIMED mode short-circuit in SetLidOverride mirrors the existing SetDisplay pattern — toggling the lid setting directly without saving first avoids restarting the countdown timer.
  • State file uses a direct path rather than SettingsUtils because crash-recovery state has a delete-on-restore lifecycle that doesn't fit the settings framework.
  • The Settings UI detects lid presence via its own minimal SystemPowerCapabilities P/Invoke since it runs in a separate process from the Awake module.

Relates to #34479 (and the issues consolidated there: #16422, #41095, #35051, #13785, #13622, #34903).

Testing

  • Manual: toggled the setting on/off across all four modes (PASSIVE, INDEFINITE, TIMED, EXPIRABLE) on a laptop with a lid. Verified via powercfg /q that the lid close action changes to 0 when enabled and restores to 1 (Sleep) when disabled.
  • Crash recovery: applied override, killed Awake via Task Manager, relaunched — confirmed original settings restored and state file cleaned up.
  • No-lid device: verified on a desktop that the toggle is hidden and LidOverrideManager.Initialize returns early.
  • Unit tests added for AwakeSettings.Clone() to guard against future property omissions (reflection-based property list assertion).
  • Not tested: power scheme switching while override is active (the code handles it by detecting the active scheme at restore time).

Trade-offs and Alternatives

The override modifies the system-wide power plan, not just the Awake process. This is unavoidable — there is no per-process lid close action API. The risk is mitigated by crash recovery and by restoring settings on every exit path.

An alternative would be registering for WM_POWERBROADCAST / PBT_APMSUSPEND and calling SetSuspendState(FALSE) to cancel the suspend, but this is fragile (the cancel can be overridden by the kernel) and doesn't work for hibernate or shutdown lid actions.

@github-actions

This comment has been minimized.

@andrewleech andrewleech force-pushed the feature/awake-lid-close-override branch from 9da6c61 to 2728b11 Compare March 23, 2026 01:27
@github-actions

This comment has been minimized.

Add a new toggle that prevents system sleep when the laptop lid is closed
while Awake is active. Uses Windows PowerWriteACValueIndex/PowerWriteDCValueIndex
APIs to temporarily override the lid close action to "Do nothing".

- Add LidOverrideManager for thread-safe lid override with crash recovery
- Add P/Invoke declarations for power management APIs
- Add tray menu item (visible only on devices with lids)
- Add Settings UI toggle with proper enable/disable states
- Restore original lid settings when Awake is disabled or exits

Signed-off-by: Andrew Leech <andrew@alelec.net>
@andrewleech andrewleech force-pushed the feature/awake-lid-close-override branch from 2728b11 to 1e2524b Compare March 23, 2026 09:03
@andrewleech
Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant