Two-stage No-JIT warning and red in-game indicator.

This commit is contained in:
Brandon
2026-08-05 00:33:19 +02:00
committed by Jeen
parent 1a13086768
commit 0bfc42d2a4
2 changed files with 61 additions and 6 deletions
@@ -227,6 +227,7 @@ struct GameScreenView: View {
@State private var menuButtonHidden = false
@State private var vmMenuAvailable = false
@State private var gameMenuAvailable = false
@State private var noJITFallbackActive = false
// MARK: Overlay Route
// The pause card + every screen launched from it are driven by one FSM. Opening a child
// transitions `.paused -> .pausedPresenting(child)` without tearing the card down; the child
@@ -402,7 +403,7 @@ struct GameScreenView: View {
}
.overlay(alignment: .topTrailing) {
if !menuButtonHidden {
menuButton()
menuButtonCluster()
.padding(.top, 8)
.padding(.trailing, 4)
.gameplayLaunchChrome(visible: appState.gameplayLaunchControlsVisible)
@@ -614,7 +615,7 @@ struct GameScreenView: View {
VStack {
HStack {
Spacer()
menuButton()
menuButtonCluster()
}
.padding(.top, isLandscape ? 8 : 4)
.padding(.trailing, isLandscape ? 8 : 4)
@@ -634,6 +635,21 @@ struct GameScreenView: View {
.accessibilityHint(settings.localized("Opens the pause menu"))
}
private func menuButtonCluster() -> some View {
HStack(spacing: 6) {
if noJITFallbackActive {
Text(settings.localized("No JIT"))
.font(.caption.weight(.semibold))
.foregroundStyle(.red)
.padding(.horizontal, 9)
.padding(.vertical, 6)
.background(.black.opacity(0.40), in: Capsule())
.accessibilityLabel(settings.localized("No JIT mode"))
}
menuButton()
}
}
@MainActor
private func enterEmulationOnlyModeIfReady() {
guard settings.emulationOnlyModeEnabled,
@@ -1072,12 +1088,16 @@ struct GameScreenView: View {
private func refreshRuntimeMenuState() {
let vmRunning = ARMSX2Bridge.isVMRunning()
let gameReady = ARMSX2Bridge.hasValidSaveStateGame()
let noJITActive = ARMSX2Bridge.isNoJITFallbackActive()
if vmMenuAvailable != vmRunning {
vmMenuAvailable = vmRunning
}
if gameMenuAvailable != gameReady {
gameMenuAvailable = gameReady
}
if noJITFallbackActive != noJITActive {
noJITFallbackActive = noJITActive
}
let identity = gameReady ? runtimePadLayoutIdentityForCurrentGame() : nil
if runtimePadLayoutIdentity != identity {
runtimePadLayoutIdentity = identity
@@ -47,6 +47,7 @@ struct RootView: View {
@State private var settings = SettingsStore.shared
@State private var fileImporter = FileImportHandler.shared
@State private var showBootSplash = true
@State private var showNoJITFinalConfirmation = false
// The background layer observes renderer state directly. Keeping only the
// host reference here prevents snapshot/visibility publications from
// invalidating the complete menu hierarchy.
@@ -176,21 +177,55 @@ struct RootView: View {
.alert(
"⚠️ \(settings.localized("JIT Access Not Detected"))",
isPresented: Binding(
get: { appState.pendingJITGameBoot != nil },
set: { if !$0 { appState.cancelPendingJITGameBoot() } }
get: {
appState.pendingJITGameBoot != nil && !showNoJITFinalConfirmation
},
set: { isPresented in
if !isPresented && !showNoJITFinalConfirmation {
appState.cancelPendingJITGameBoot()
}
}
)
) {
Button(settings.localized("Cancel"), role: .cancel) {
appState.cancelPendingJITGameBoot()
}
Button(settings.localized("Continue")) {
appState.continuePendingJITGameBoot()
Button(settings.localized("Continue"), role: .destructive) {
showNoJITFinalConfirmation = true
}
} message: {
Text(settings.localized(
"JIT access is not available. Match the StikDebug script to the JIT Script setting in Emulator settings."
))
}
.alert(
"⚠️ \(settings.localized("JIT Access Not Detected"))",
isPresented: Binding(
get: {
showNoJITFinalConfirmation && appState.pendingJITGameBoot != nil
},
set: { isPresented in
guard !isPresented else { return }
showNoJITFinalConfirmation = false
if appState.pendingJITGameBoot != nil {
appState.cancelPendingJITGameBoot()
}
}
)
) {
Button(settings.localized("Cancel"), role: .cancel) {
showNoJITFinalConfirmation = false
appState.cancelPendingJITGameBoot()
}
Button(settings.localized("Continue"), role: .destructive) {
appState.continuePendingJITGameBoot()
showNoJITFinalConfirmation = false
}
} message: {
Text(settings.localized(
"Are you sure you want to continue?\nJIT access is required for 60 FPS.\n\nEXPECT TOO LOW PERFORMANCE.\nPROCEED AT YOUR OWN RISK!"
))
}
}
}