diff --git a/crates/console/prometeu-firmware/src/firmware/firmware.rs b/crates/console/prometeu-firmware/src/firmware/firmware.rs index 31287217..f0ff3ece 100644 --- a/crates/console/prometeu-firmware/src/firmware/firmware.rs +++ b/crates/console/prometeu-firmware/src/firmware/firmware.rs @@ -96,6 +96,15 @@ impl Firmware { signals: &InputSignals, platform: &mut dyn RuntimePlatform, ) { + let new_state = match new_state { + FirmwareState::ResumeResidentGame => { + let Some(resume_state) = self.resident_game_resume_state() else { + return; + }; + resume_state + } + state => state, + }; let entering_game_from_home = matches!(self.state, FirmwareState::HubHome(_)) && matches!(new_state, FirmwareState::GameRunning(_)); self.on_exit(signals, platform); @@ -126,6 +135,7 @@ impl Firmware { FirmwareState::LaunchHub(s) => s.on_enter(&mut req), FirmwareState::HubHome(s) => s.on_enter(&mut req), FirmwareState::LoadCartridge(s) => s.on_enter(&mut req), + FirmwareState::ResumeResidentGame => {} FirmwareState::GameRunning(s) => s.on_enter(&mut req), FirmwareState::ShellRunning(s) => s.on_enter(&mut req), FirmwareState::AppCrashes(s) => s.on_enter(&mut req), @@ -153,6 +163,7 @@ impl Firmware { FirmwareState::LaunchHub(s) => s.on_update(&mut req), FirmwareState::HubHome(s) => s.on_update(&mut req), FirmwareState::LoadCartridge(s) => s.on_update(&mut req), + FirmwareState::ResumeResidentGame => None, FirmwareState::GameRunning(s) => s.on_update(&mut req), FirmwareState::ShellRunning(s) => s.on_update(&mut req), FirmwareState::AppCrashes(s) => s.on_update(&mut req), @@ -175,6 +186,7 @@ impl Firmware { FirmwareState::LaunchHub(s) => s.on_exit(&mut req), FirmwareState::HubHome(s) => s.on_exit(&mut req), FirmwareState::LoadCartridge(s) => s.on_exit(&mut req), + FirmwareState::ResumeResidentGame => {} FirmwareState::GameRunning(s) => s.on_exit(&mut req), FirmwareState::ShellRunning(s) => s.on_exit(&mut req), FirmwareState::AppCrashes(s) => s.on_exit(&mut req), @@ -201,20 +213,27 @@ impl Firmware { } pub fn resume_resident_game_from_home(&mut self) -> bool { - let Some(task_id) = self.os.lifecycle().resident_game_task() else { + let Some(state) = self.resident_game_resume_state() else { return false; }; - if self.os.lifecycle().resume_task(task_id).is_err() { - return false; + self.state = state; + self.state_initialized = false; + true + } + + fn resident_game_resume_state(&mut self) -> Option { + let task_id = self.os.lifecycle().resident_game_task()?; + + if let Err(error) = self.os.lifecycle().resume_task(task_id) { + self.os.warn(LogSource::Pos, format!("Resident Game resume failed: {error:?}")); + return None; } let app_id = self.os.vm().current_app_id(); self.os.vm().transition_render_owner(AppMode::Game, app_id); - self.state = FirmwareState::GameRunning(GameRunningStep::new(task_id)); - self.state_initialized = false; - self.game_input_barrier_frames = 1; - true + self.game_input_barrier_frames = self.game_input_barrier_frames.max(1); + Some(FirmwareState::GameRunning(GameRunningStep::new(task_id))) } pub fn game_lifecycle_audio_paused(&mut self) -> bool { diff --git a/crates/console/prometeu-firmware/src/firmware/firmware_state.rs b/crates/console/prometeu-firmware/src/firmware/firmware_state.rs index 744ba05a..130d8812 100644 --- a/crates/console/prometeu-firmware/src/firmware/firmware_state.rs +++ b/crates/console/prometeu-firmware/src/firmware/firmware_state.rs @@ -14,6 +14,7 @@ pub enum FirmwareState { LaunchHub(LaunchHubStep), HubHome(HubHomeStep), LoadCartridge(LoadCartridgeStep), + ResumeResidentGame, GameRunning(GameRunningStep), ShellRunning(ShellRunningStep), AppCrashes(AppCrashesStep), diff --git a/crates/console/prometeu-firmware/src/firmware/firmware_step_hub_home.rs b/crates/console/prometeu-firmware/src/firmware/firmware_step_hub_home.rs index e42deff6..cede2f36 100644 --- a/crates/console/prometeu-firmware/src/firmware/firmware_step_hub_home.rs +++ b/crates/console/prometeu-firmware/src/firmware/firmware_step_hub_home.rs @@ -1,8 +1,7 @@ use crate::firmware::firmware_state::{ - AppCrashesStep, FirmwareState, GameRunningStep, LoadCartridgeStep, ShellRunningStep, + AppCrashesStep, FirmwareState, LoadCartridgeStep, ShellRunningStep, }; use crate::firmware::prometeu_context::PrometeuContext; -use prometeu_hal::InputSignals; use prometeu_hal::app_mode::AppMode; use prometeu_hal::cartridge_loader::CartridgeLoader; use prometeu_hal::log::{LogLevel, LogSource}; @@ -41,39 +40,10 @@ impl HubHomeStep { Some(SystemProfileAction::LaunchGame { path }) => match CartridgeLoader::load(&path) { Ok(cartridge) => { if cartridge.app_mode == AppMode::Game - && let Some(task_id) = ctx.os.lifecycle().resident_game_task() + && ctx.os.lifecycle().resident_game_task().is_some() { if ctx.os.vm().current_app_id() == cartridge.app_id { - match ctx.os.lifecycle().resume_task(task_id) { - Ok(()) => { - ctx.os - .vm() - .transition_render_owner(AppMode::Game, cartridge.app_id); - ctx.platform - .input_mut() - .pad_mut() - .begin_frame(&InputSignals::default()); - ctx.platform - .input_mut() - .touch_mut() - .begin_frame(&InputSignals::default()); - return Some(FirmwareState::GameRunning(GameRunningStep::new( - task_id, - ))); - } - Err(error) => { - ctx.os.log( - LogLevel::Warn, - LogSource::Hub, - 0, - format!( - "Failed to resume resident Home game {}: {error:?}", - cartridge.title - ), - ); - return None; - } - } + return Some(FirmwareState::ResumeResidentGame); } let resident_app_id = ctx.os.vm().current_app_id(); diff --git a/discussion/index.ndjson b/discussion/index.ndjson index 1f61aee2..54a349fc 100644 --- a/discussion/index.ndjson +++ b/discussion/index.ndjson @@ -2,7 +2,7 @@ {"type":"discussion","id":"DSC-0043","status":"open","ticket":"system-os-cartridge-switch-orchestrator","title":"SystemOS Cartridge Switch Orchestrator","created_at":"2026-07-03","updated_at":"2026-07-03","tags":["runtime","os","lifecycle","game","cartridge","architecture"],"agendas":[{"id":"AGD-0044","file":"AGD-0044-systemos-cartridge-switch-orchestrator.md","status":"open","created_at":"2026-07-03","updated_at":"2026-07-03"}],"decisions":[],"plans":[],"lessons":[]} {"type":"discussion","id":"DSC-0039","status":"abandoned","ticket":"render-pipeline-family-and-future-3d","title":"Render Pipeline Family and Future 3D","created_at":"2026-06-04","updated_at":"2026-06-04","tags":["gfx","renderer","runtime","architecture","pipeline"],"agendas":[{"id":"AGD-0039","file":"AGD-0039-render-pipeline-family-and-future-3d.md","status":"abandoned","created_at":"2026-06-04","updated_at":"2026-06-04","_override_reason":"User explicitly chose to close this agenda without a new decision because DSC-0038 already established enough architecture for future extension, and 3D is intentionally deferred."}],"decisions":[],"plans":[],"lessons":[],"_override_reason":"User explicitly chose to close this agenda without a new decision because DSC-0038 already established enough architecture for future extension, and 3D is intentionally deferred."} {"type":"discussion","id":"DSC-0040","status":"done","ticket":"vm-render-parallel-execution-boundary","title":"VM and Render Parallel Execution Boundary","created_at":"2026-06-04","updated_at":"2026-06-06","tags":["runtime","renderer","vm","concurrency","architecture","perf"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0048","file":"discussion/lessons/DSC-0040-vm-render-parallel-execution-boundary/LSN-0048-render-workers-need-a-closed-packet-contract.md","status":"done","created_at":"2026-06-06","updated_at":"2026-06-06"}]} -{"type":"discussion","id":"DSC-0041","status":"in_progress","ticket":"foreground-stack-game-pause-shell-vm-backed","title":"Foreground Stack, Game Pause, and VM-Backed Shell Coexistence","created_at":"2026-06-05","updated_at":"2026-07-04","tags":["runtime","os","lifecycle","shell","game","vm","foreground","architecture"],"agendas":[{"id":"AGD-0041","file":"AGD-0041-foreground-stack-game-pause-shell-vm-backed.md","status":"accepted","created_at":"2026-06-05","updated_at":"2026-07-03"}],"decisions":[{"id":"DEC-0037","file":"DEC-0037-foreground-stack-and-game-pause-contract.md","status":"accepted","created_at":"2026-07-03","updated_at":"2026-07-03","ref_agenda":"AGD-0041"}],"plans":[{"id":"PLN-0136","file":"PLN-0136-route-desktop-home-key-outside-guest-input.md","status":"done","created_at":"2026-07-03","updated_at":"2026-07-03","ref_decisions":["DEC-0037"]},{"id":"PLN-0137","file":"PLN-0137-implement-systemos-foreground-stack-state.md","status":"done","created_at":"2026-07-03","updated_at":"2026-07-03","ref_decisions":["DEC-0037"]},{"id":"PLN-0138","file":"PLN-0138-specify-foreground-pause-and-home-contract.md","status":"done","created_at":"2026-07-03","updated_at":"2026-07-03","ref_decisions":["DEC-0037"]},{"id":"PLN-0139","file":"PLN-0139-validate-game-home-shell-game-end-to-end.md","status":"done","created_at":"2026-07-03","updated_at":"2026-07-03","ref_decisions":["DEC-0037"]},{"id":"PLN-0140","file":"PLN-0140-deliver-game-pause-resume-and-suspension.md","status":"done","created_at":"2026-07-03","updated_at":"2026-07-03","ref_decisions":["DEC-0037"]},{"id":"PLN-0141","file":"PLN-0141-integrate-render-audio-and-input-pause-boundaries.md","status":"done","created_at":"2026-07-03","updated_at":"2026-07-03","ref_decisions":["DEC-0037"]},{"id":"PLN-0142","file":"PLN-0142-deliver-game-lifecycle-events-to-vm-runtime.md","status":"done","created_at":"2026-07-04","updated_at":"2026-07-04","ref_decisions":["DEC-0037"]},{"id":"PLN-0143","file":"PLN-0143-guarantee-cooperative-pause-budget-tick-before-suspension.md","status":"done","created_at":"2026-07-04","updated_at":"2026-07-04","ref_decisions":["DEC-0037"]},{"id":"PLN-0144","file":"PLN-0144-suspend-resident-game-when-shell-takes-foreground.md","status":"done","created_at":"2026-07-04","updated_at":"2026-07-04","ref_decisions":["DEC-0037"]},{"id":"PLN-0145","file":"PLN-0145-centralize-resident-game-resume-transition.md","status":"open","created_at":"2026-07-04","updated_at":"2026-07-04","ref_decisions":["DEC-0037"]},{"id":"PLN-0146","file":"PLN-0146-consolidate-lifecycle-process-lookup-helpers.md","status":"open","created_at":"2026-07-04","updated_at":"2026-07-04","ref_decisions":["DEC-0037"]},{"id":"PLN-0147","file":"PLN-0147-move-native-shell-profile-update-into-hub.md","status":"open","created_at":"2026-07-04","updated_at":"2026-07-04","ref_decisions":["DEC-0037"]}],"lessons":[]} +{"type":"discussion","id":"DSC-0041","status":"in_progress","ticket":"foreground-stack-game-pause-shell-vm-backed","title":"Foreground Stack, Game Pause, and VM-Backed Shell Coexistence","created_at":"2026-06-05","updated_at":"2026-07-04","tags":["runtime","os","lifecycle","shell","game","vm","foreground","architecture"],"agendas":[{"id":"AGD-0041","file":"AGD-0041-foreground-stack-game-pause-shell-vm-backed.md","status":"accepted","created_at":"2026-06-05","updated_at":"2026-07-03"}],"decisions":[{"id":"DEC-0037","file":"DEC-0037-foreground-stack-and-game-pause-contract.md","status":"accepted","created_at":"2026-07-03","updated_at":"2026-07-03","ref_agenda":"AGD-0041"}],"plans":[{"id":"PLN-0136","file":"PLN-0136-route-desktop-home-key-outside-guest-input.md","status":"done","created_at":"2026-07-03","updated_at":"2026-07-03","ref_decisions":["DEC-0037"]},{"id":"PLN-0137","file":"PLN-0137-implement-systemos-foreground-stack-state.md","status":"done","created_at":"2026-07-03","updated_at":"2026-07-03","ref_decisions":["DEC-0037"]},{"id":"PLN-0138","file":"PLN-0138-specify-foreground-pause-and-home-contract.md","status":"done","created_at":"2026-07-03","updated_at":"2026-07-03","ref_decisions":["DEC-0037"]},{"id":"PLN-0139","file":"PLN-0139-validate-game-home-shell-game-end-to-end.md","status":"done","created_at":"2026-07-03","updated_at":"2026-07-03","ref_decisions":["DEC-0037"]},{"id":"PLN-0140","file":"PLN-0140-deliver-game-pause-resume-and-suspension.md","status":"done","created_at":"2026-07-03","updated_at":"2026-07-03","ref_decisions":["DEC-0037"]},{"id":"PLN-0141","file":"PLN-0141-integrate-render-audio-and-input-pause-boundaries.md","status":"done","created_at":"2026-07-03","updated_at":"2026-07-03","ref_decisions":["DEC-0037"]},{"id":"PLN-0142","file":"PLN-0142-deliver-game-lifecycle-events-to-vm-runtime.md","status":"done","created_at":"2026-07-04","updated_at":"2026-07-04","ref_decisions":["DEC-0037"]},{"id":"PLN-0143","file":"PLN-0143-guarantee-cooperative-pause-budget-tick-before-suspension.md","status":"done","created_at":"2026-07-04","updated_at":"2026-07-04","ref_decisions":["DEC-0037"]},{"id":"PLN-0144","file":"PLN-0144-suspend-resident-game-when-shell-takes-foreground.md","status":"done","created_at":"2026-07-04","updated_at":"2026-07-04","ref_decisions":["DEC-0037"]},{"id":"PLN-0145","file":"PLN-0145-centralize-resident-game-resume-transition.md","status":"done","created_at":"2026-07-04","updated_at":"2026-07-04","ref_decisions":["DEC-0037"]},{"id":"PLN-0146","file":"PLN-0146-consolidate-lifecycle-process-lookup-helpers.md","status":"open","created_at":"2026-07-04","updated_at":"2026-07-04","ref_decisions":["DEC-0037"]},{"id":"PLN-0147","file":"PLN-0147-move-native-shell-profile-update-into-hub.md","status":"open","created_at":"2026-07-04","updated_at":"2026-07-04","ref_decisions":["DEC-0037"]}],"lessons":[]} {"type":"discussion","id":"DSC-0042","status":"done","ticket":"real-render-worker-establishment","title":"Real Render Worker Establishment","created_at":"2026-06-06","updated_at":"2026-06-20","tags":["runtime","renderer","worker","concurrency","host","hal","architecture"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0049","file":"discussion/lessons/DSC-0042-real-render-worker-establishment/LSN-0049-render-worker-publication-boundary.md","status":"done","created_at":"2026-06-20","updated_at":"2026-06-20"}]} {"type":"discussion","id":"DSC-0038","status":"done","ticket":"render-frame-packet-boundary","title":"Logical Render Pipelines and Command Packets","created_at":"2026-05-25","updated_at":"2026-06-04","tags":["gfx","renderer","runtime","frame-composer","architecture","ui","pipeline"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0047","file":"discussion/lessons/DSC-0038-render-frame-packet-boundary/LSN-0047-typed-render-submissions-preserve-domain-boundaries.md","status":"done","created_at":"2026-06-04","updated_at":"2026-06-04"}]} {"type":"discussion","id":"DSC-0035","status":"done","ticket":"task-owned-shell-windows","title":"Agenda - Task-Owned Shell Windows","created_at":"2026-05-15","updated_at":"2026-05-15","tags":["runtime","os","task","window-manager","shell","lifecycle"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0044","file":"discussion/lessons/DSC-0035-task-owned-shell-windows/LSN-0044-task-window-liveness-belongs-to-the-task.md","status":"done","created_at":"2026-05-15","updated_at":"2026-05-15"}]} diff --git a/discussion/workflow/plans/PLN-0145-centralize-resident-game-resume-transition.md b/discussion/workflow/plans/PLN-0145-centralize-resident-game-resume-transition.md index f3982d76..e73b3070 100644 --- a/discussion/workflow/plans/PLN-0145-centralize-resident-game-resume-transition.md +++ b/discussion/workflow/plans/PLN-0145-centralize-resident-game-resume-transition.md @@ -2,7 +2,7 @@ id: PLN-0145 ticket: foreground-stack-game-pause-shell-vm-backed title: Centralize Resident Game Resume Transition -status: open +status: done created: 2026-07-04 ref_decisions: [DEC-0037] tags: [runtime, os, lifecycle, shell, game, vm, foreground, architecture]