iOS: make quick menu stop actually leave the game

Stop shut the VM down and then left you looking at live gameplay until the
shutdown notification came back, which is the whole of MTGS teardown, the memory
card close and the NVRAM write. The Now Running card in the library stayed up the
whole time too, because nothing cleared runningGameName.

Worse, if a Reset ROM or a disc restart was still in flight, AppState had a
pendingBootAction queued and the shutdown observer takes that branch in preference
to going back to the menu, so Stop rebooted the game instead of quitting it. That
was private with no way to clear it, hence cancelPendingBoot.

It leaves for the library up front now instead of waiting on the notification,
which is the order the library's own Stop already uses. That also drops
GameScreenView out of the hierarchy, which kills the onChange that was scheduling
an unpause on the way out. Draining the cpu thread tasks happens before the stop
check, so that unpause was landing first and resuming a frame after you had
already pressed Stop.

Back to Menu claimed to quit the game in its accessibility hint. It pauses.
This commit is contained in:
J1coding
2026-08-02 18:58:24 +02:00
committed by Jeen
parent 759889df1a
commit 09c1ebb73c
3 changed files with 11 additions and 1 deletions
@@ -202,6 +202,11 @@ final class AppState: @unchecked Sendable {
return true
}
/// Drops a queued reboot, so stopping mid Reset ROM quits instead of booting the game again.
func cancelPendingBoot() {
pendingBootAction = nil
}
func returnToMenu() {
if ARMSX2Bridge.isVMRunning() {
ARMSX2Bridge.setVMPaused(true)
@@ -316,6 +316,11 @@ struct GameScreenView: View {
onStop: {
if settings.hapticFeedback { HapticManager.medium.impactOccurred() }
overlayRoute = .hidden
// Leave now rather than waiting on the shutdown notification, so nobody
// watches live gameplay through the card and NVRAM flush.
appState.cancelPendingBoot()
appState.returnToMenu()
appState.runningGameName = nil
ARMSX2Bridge.requestVMStop()
},
onResume: {
@@ -266,7 +266,7 @@ struct QuickMenuView: View {
OverlayActionRow(label: settings.localized("Clear Current Game Cache"), systemImage: "trash.slash", action: onClearCache)
}
OverlayActionRow(label: settings.localized("Back to Menu"), systemImage: "list.bullet", action: onBackToMenu)
.accessibilityHint(settings.localized("Quits this game and returns to the library"))
.accessibilityHint(settings.localized("Leaves the game paused and returns to the library"))
}
/// Hosts an injected SwiftUI `Menu` (controller skin / change disc) as a row matching the