Skip to content

fix: Playnite slow start no longer flips back to the PC - #83

Open
lippdev wants to merge 1 commit into
mainfrom
fix/playnite-window-swap
Open

lippdev wants to merge 1 commit into
mainfrom
fix/playnite-window-swap

Conversation

@lippdev

@lippdev lippdev commented Sep 25, 2026

Copy link
Copy Markdown
Owner

Fixes #81, reported on Reddit: when Playnite fullscreen takes a while to load, Console Mode restores the desk after ~5 s and Playnite has to be launched again.

Cause (most likely): IsExitSignaled treated the watched Playnite window disappearing (exit watch or IsWindowStillVisible) as "Playnite closed". On slow starts Playnite can show a big loading window first and then replace it with the main window.

Fix: in Playnite mode, after the window has appeared:

  • when the watched window goes away, look for another big Playnite.FullscreenApp window and re-arm the exit watch on it
  • end the session only when Playnite.FullscreenApp is no longer running, or has stayed without a big window for 20 s (PlayniteExitPolicy.WindowGrace)

Big Picture and Xbox paths are unchanged.

  • New PlayniteExitPolicy (pure) + PlayniteExitPolicyTests
  • LaunchService.IsPlayniteRunning()
  • Changelog entry under Unreleased (EN + pt-BR)

Not verified on Windows yet. I couldn't build or run it locally (macOS). To test: Playnite mode with a slow start (e.g. a big library / theme), confirm it stays on the TV, then close Playnite fullscreen and confirm the desk comes back.

🤖 Generated with Claude Code

Playnite can swap its first big window (a loading screen on slow starts) for the main
one. The exit check read the watched window disappearing as "Playnite closed" and
restored the desk after a few seconds. In Playnite mode the loop now follows the new
window (re-arming the exit watch) and ends the session only when Playnite.FullscreenApp
exits, or stays without a big window for 20 s. The decision lives in PlayniteExitPolicy,
with unit tests.

Fixes #81

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@lippdev

lippdev commented Sep 25, 2026

Copy link
Copy Markdown
Owner Author

Build-only run on this branch (workflow_dispatch, nothing published): tests 137/137 passed, portable + installer built. Artifacts for testing on Windows: https://github.com/lippdev/consolemode/actions/runs/36176223057

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Important

This PR can keep console mode active after the watched Playnite window disappears, because the replacement lookup can accept an invalid window or a different Playnite process. Please address the two ranked findings below before merging.

Reviewed changes

This review covers the Playnite window-swap exit detection, process-running check, runtime-state reset, and the new pure policy tests.

  • Window-swap handling — clears the cached HWND, searches for a replacement, re-arms the destruction watch, and applies a 20-second windowless grace period.
  • Process detection — adds a Playnite.FullscreenApp process check and resets PlayniteWindowMissingSince when a session starts.
  • Test coverage — adds four cross-platform tests for the pure exit policy. Those tests pass, but the Windows HWND integration is not exercised.

⚠️ The window-swap integration is not covered by tests

The four added tests cover only PlayniteExitPolicy.IsExit; none drives ConsoleEngine.IsPlayniteExitSignaled through the old HWND disappearing, a replacement appearing later, or a replacement being invalid. The core slow-start behavior can therefore regress while the new tests still pass.

Technical details
# Window-swap integration coverage

## Affected sites
- `src/ConsoleMode/Services/ConsoleEngine.cs:434-445` — the new stateful replacement and process/window decision path is untested.
- `tests/ConsoleMode.Tests/PlayniteExitPolicyTests.cs:10-24` — the tests exercise only the pure policy inputs, not HWND selection or watch re-arming.

## Required outcome
- Add deterministic coverage for the window replacement transition, delayed replacement, invalid or hidden candidate, and process/window mismatch cases, or introduce test seams that make those cases verifiable without running the app.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using GPT Luna | 𝕏

private bool IsPlayniteExitSignaled(bool watchedGone)
{
if (watchedGone) State.CachedBigPictureHandle = 0;
var showing = Launch.IsPlayniteActive(State);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] A newly discovered candidate is accepted as active based only on GetWindowArea; unlike the cached-handle branch in IsPlayniteActive, this path does not require IsWindowStillVisible. During splash teardown or with a hidden/stale large main window, showing can remain true while no fullscreen window is present, clearing PlayniteWindowMissingSince at line 443 and preventing the 20-second fallback, so the desktop is never restored.

Technical details
# Invalid replacement HWND prevents restoration

## Affected sites
- `src/ConsoleMode/Services/ConsoleEngine.cs:435,443-445` — treats the result of `IsPlayniteActive` as proof that a visible Playnite fullscreen window exists.
- `src/ConsoleMode/Services/LaunchService.cs:127-131` — accepts a fresh `MainWindowHandle` when its rectangle exceeds 200,000 pixels, without the visibility check used for the cached handle.

## Required outcome
- Only clear `PlayniteWindowMissingSince` and re-arm the watch for a currently valid, visible, sufficiently large Playnite fullscreen window.


if (showing) State.PlayniteWindowMissingSince = null;
else State.PlayniteWindowMissingSince ??= DateTime.Now;
var exit = PlayniteExitPolicy.IsExit(Launch.IsPlayniteRunning(), showing, State.PlayniteWindowMissingSince, DateTime.Now);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] IsPlayniteRunning() is global to the executable name and is not tied to the HWND selected by IsPlayniteActive. If an old, restarting, or concurrent Playnite.FullscreenApp process retains a large window after the launched session closes, the replacement lookup can adopt that other window and this process check keeps the policy from exiting, so the desktop is not restored.

Technical details
# Playnite session is not identified

## Affected sites
- `src/ConsoleMode/Services/LaunchService.cs:104-110` — enumerates any `Playnite.FullscreenApp` process rather than identifying the launched process.
- `src/ConsoleMode/Services/ConsoleEngine.cs:435,443-445` — accepts any matching process window as the active session and uses any same-named process to suppress exit.

## Required outcome
- Keep the tracked Playnite process/window identity associated with the console-mode launch, and use that identity consistently when evaluating replacement windows and process exit.

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.

Playnite: slow startup flips back to the PC monitor after a few seconds

1 participant