Summary
On Windows 11, the pet disappears whenever any app enters fullscreen (browser video, games) on its monitor. We chased this across several layers on a live machine; two real bugs were found and fixed (#87, and the follow-ups in #89), but the core ask — pet stays visible over fullscreen content — turns out to be impossible for an unsigned desktop app. This issue documents the full diagnosis so nobody has to re-derive it.
Repro
- Default pet visible anywhere on screen.
- Fullscreen a YouTube video in any Chromium browser (or launch a fullscreen game) for ~10s.
- Exit fullscreen.
Stock 3.3.0: pet never reappears above other windows until the user manually toggles tray → Hide/Show pet. App logs show zero hide events — the window is "visible" the whole time, just buried.
What we found, layer by layer
Layer 1 — Electron's cached always-on-top state (fixed in #87)
The shell strips WS_EX_TOPMOST from other windows when an app goes fullscreen, and never restores it. No Electron event fires. Worse: Electron's isAlwaysOnTop() still returns true (it reports its own cached flag, not the OS state), so every setAlwaysOnTop(true) — including the pre-existing show/restore re-assertions — short-circuits without reaching the OS. Verified live with a Win32 probe: the OS flag stayed off through minutes of re-asserts while the cache said on. Only a real hide()/show() cycle restored it, which is why the tray toggle "worked". Fix: drop the cached flag first (setAlwaysOnTop(false) → setAlwaysOnTop(true)) on a periodic win32 timer.
Layer 2 — Chromium occlusion tracker blanks the pet (fixed in #89)
Chromium's native window occlusion tracker marks every window on a display occluded while a fullscreen app is active there and stops painting it. A transparent pet window goes blank even when its z-order is intact. Fix: disable CalculateNativeWinOcclusion on win32.
Layer 3 — the shell's fullscreen band (NOT fixable)
With both fixes in place we measured the shell's actual behavior (500ms Win32 flag sampling, forced SetWindowPos(HWND_TOPMOST) at controlled times):
- Immediately after fullscreen starts there is a ~2–3s grace period where a forced topmost assert holds (flag verified True at t+2013ms, stripped by t+4012ms).
- Once the shell's demotion engages, every re-assertion is reverted instantly — with the app re-asserting every 1s, 40/40 samples over 20s of fullscreen showed the flag off.
- Fullscreen-optimization compat flags do not help.
DISABLEDXMAXIMIZEDWINDOWEDMODE on the browser exe, browser fully restarted: identical behavior. The demotion appears to be geometry-based (any foreground window covering the monitor), independent of FSO/DXGI mode.
Conclusion: while a fullscreen app is foreground, the topmost band is effectively reserved for signed UIAccess system overlays (Game Bar, Magnifier). The mainstream "game overlay" apps get around it by injecting into the target process's renderer — not an option for a desktop pet. Making OpenPets a UIAccess app would require Authenticode signing + uiAccess=true + Program Files install, with real security-review weight; almost certainly not worth it.
Where this lands
🤖 Generated with Claude Code
Summary
On Windows 11, the pet disappears whenever any app enters fullscreen (browser video, games) on its monitor. We chased this across several layers on a live machine; two real bugs were found and fixed (#87, and the follow-ups in #89), but the core ask — pet stays visible over fullscreen content — turns out to be impossible for an unsigned desktop app. This issue documents the full diagnosis so nobody has to re-derive it.
Repro
Stock 3.3.0: pet never reappears above other windows until the user manually toggles tray → Hide/Show pet. App logs show zero
hideevents — the window is "visible" the whole time, just buried.What we found, layer by layer
Layer 1 — Electron's cached always-on-top state (fixed in #87)
The shell strips
WS_EX_TOPMOSTfrom other windows when an app goes fullscreen, and never restores it. No Electron event fires. Worse: Electron'sisAlwaysOnTop()still returnstrue(it reports its own cached flag, not the OS state), so everysetAlwaysOnTop(true)— including the pre-existingshow/restorere-assertions — short-circuits without reaching the OS. Verified live with a Win32 probe: the OS flag stayed off through minutes of re-asserts while the cache said on. Only a realhide()/show()cycle restored it, which is why the tray toggle "worked". Fix: drop the cached flag first (setAlwaysOnTop(false)→setAlwaysOnTop(true)) on a periodic win32 timer.Layer 2 — Chromium occlusion tracker blanks the pet (fixed in #89)
Chromium's native window occlusion tracker marks every window on a display occluded while a fullscreen app is active there and stops painting it. A transparent pet window goes blank even when its z-order is intact. Fix: disable
CalculateNativeWinOcclusionon win32.Layer 3 — the shell's fullscreen band (NOT fixable)
With both fixes in place we measured the shell's actual behavior (500ms Win32 flag sampling, forced
SetWindowPos(HWND_TOPMOST)at controlled times):DISABLEDXMAXIMIZEDWINDOWEDMODEon the browser exe, browser fully restarted: identical behavior. The demotion appears to be geometry-based (any foreground window covering the monitor), independent of FSO/DXGI mode.Conclusion: while a fullscreen app is foreground, the topmost band is effectively reserved for signed UIAccess system overlays (Game Bar, Magnifier). The mainstream "game overlay" apps get around it by injecting into the target process's renderer — not an option for a desktop pet. Making OpenPets a UIAccess app would require Authenticode signing +
uiAccess=true+ Program Files install, with real security-review weight; almost certainly not worth it.Where this lands
SetWindowPosvariants, FSO compat flags, occlusion switches) has been tried and measured on a live machine.🤖 Generated with Claude Code