tvOS only does that with a Back press it gets to see, so the view controller
lets controller events through to UIKit while the top screen is top level
(the new TOP_LEVEL_CHANGED notification), and passes presses that aren't
keyboard keys on to super. Elsewhere Back stays ours.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
It only replaced the top screen, but most callers switch from a dialog:
Play on the game screen, the store, the installers, the framedump
launcher. The screens under it stayed alive, so launching a game from the
game screen and exiting it left [MainScreen, MainScreen], one more each
time.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
That set dialogFinished_ to the caller, which processFinishDialog() then
deleted while it was still in the stack, leaking the popup. Take the
pointer out first and loop. Nothing does this now (MemStickScreen and
DevMenuScreen work around it), but it's easy to do by accident.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Popups are transparent, so the screen under one still renders, and
render() ran DoRecreateViews(). A RecreateViews() on that screen (a
resize or rotation on DisplayLayoutScreen or TouchControlLayoutScreen,
MainScreen on RECENT_FILES_CHANGED from the recent-files thread, the
achievements login finishing) then deleted the choice that opened the
popup. Closing the popup wrote through it and called its handlers.
Leave the old views in place until the screen is back on top, where
update() recreates them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
NativeIsAtTopLevel() made a virtual call on the top screen from the Java
UI thread, while the render thread could be deleting it. Two quick Back
presses could hit that. Cache the flag in ScreenManager::Update()
instead, like passInputToMapper_ (now atomic too).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>