diff --git a/crates/console/prometeu-firmware/src/firmware/firmware.rs b/crates/console/prometeu-firmware/src/firmware/firmware.rs index ac5db226..83470ec7 100644 --- a/crates/console/prometeu-firmware/src/firmware/firmware.rs +++ b/crates/console/prometeu-firmware/src/firmware/firmware.rs @@ -343,6 +343,22 @@ mod tests { assert_eq!(firmware.hub.window_manager.windows.len(), 1); } + #[test] + fn system_running_ticks_focused_system_app_through_hub_pipeline() { + let mut firmware = Firmware::new(None); + let mut hardware = Hardware::new(); + let signals = InputSignals::default(); + + firmware.load_cartridge(valid_cartridge(AppMode::System)); + firmware.tick(&signals, &mut hardware); + + let tick_index_before_system_update = firmware.os.tick_index; + firmware.tick(&signals, &mut hardware); + + assert!(matches!(firmware.state, FirmwareState::SystemRunning(_))); + assert_eq!(firmware.os.tick_index, tick_index_before_system_update + 1); + } + #[test] fn load_cartridge_routes_game_apps_to_game_running() { let mut firmware = Firmware::new(None); 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 f3c8b348..8ad987b5 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 @@ -11,26 +11,8 @@ impl HubHomeStep { } pub fn on_update(&mut self, ctx: &mut PrometeuContext) -> Option { - let mut error = None; - - // Always updates the Hub GUI (clears screen and processes input if no focus) - ctx.hub.gui_update(ctx.os, ctx.hw); - - if let Some(focused_id) = ctx.hub.window_manager.focused { - if ctx.hw.pad().start().down { - ctx.hub.window_manager.remove_window(focused_id); - } else { - // System App runs here, drawing over the Hub background - error = ctx.os.tick(ctx.vm, ctx.signals, ctx.hw); - } - } - - // Renders the System App window borders - ctx.hub.render(ctx.os, ctx.hw); - - ctx.hw.gfx_mut().present(); - - if let Some(report) = error { + let outcome = ctx.hub.update_system_profile(ctx.os, ctx.vm, ctx.signals, ctx.hw); + if let Some(report) = outcome.crash { return Some(FirmwareState::AppCrashes(AppCrashesStep { report })); } diff --git a/crates/console/prometeu-firmware/src/firmware/firmware_step_system_running.rs b/crates/console/prometeu-firmware/src/firmware/firmware_step_system_running.rs index 10922c6f..bfc18cd9 100644 --- a/crates/console/prometeu-firmware/src/firmware/firmware_step_system_running.rs +++ b/crates/console/prometeu-firmware/src/firmware/firmware_step_system_running.rs @@ -11,23 +11,16 @@ impl SystemRunningStep { } pub fn on_update(&mut self, ctx: &mut PrometeuContext) -> Option { - let mut error = None; - - ctx.hub.gui_update(ctx.os, ctx.hw); - - if let Some(focused_id) = ctx.hub.window_manager.focused { - if ctx.hw.pad().start().down { - ctx.hub.window_manager.remove_window(focused_id); - return Some(FirmwareState::HubHome(HubHomeStep)); - } - - error = ctx.os.tick(ctx.vm, ctx.signals, ctx.hw); + let outcome = ctx.hub.update_system_profile(ctx.os, ctx.vm, ctx.signals, ctx.hw); + if let Some(report) = outcome.crash { + return Some(FirmwareState::AppCrashes(AppCrashesStep { report })); } - ctx.hub.render(ctx.os, ctx.hw); - ctx.hw.gfx_mut().present(); + if !outcome.focused_window_active { + return Some(FirmwareState::HubHome(HubHomeStep)); + } - error.map(|report| FirmwareState::AppCrashes(AppCrashesStep { report })) + None } pub fn on_exit(&mut self, _ctx: &mut PrometeuContext) {} diff --git a/crates/console/prometeu-system/src/programs/prometeu_hub/prometeu_hub.rs b/crates/console/prometeu-system/src/programs/prometeu_hub/prometeu_hub.rs index fab95f92..5519af6e 100644 --- a/crates/console/prometeu-system/src/programs/prometeu_hub/prometeu_hub.rs +++ b/crates/console/prometeu-system/src/programs/prometeu_hub/prometeu_hub.rs @@ -1,15 +1,21 @@ -use crate::VirtualMachineRuntime; use crate::programs::prometeu_hub::window_manager::WindowManager; -use prometeu_hal::HardwareBridge; +use crate::{CrashReport, VirtualMachineRuntime}; use prometeu_hal::color::Color; use prometeu_hal::log::{LogLevel, LogSource}; use prometeu_hal::window::Rect; +use prometeu_hal::{HardwareBridge, InputSignals}; +use prometeu_vm::VirtualMachine; /// PrometeuHub: Launcher and system UI environment. pub struct PrometeuHub { pub window_manager: WindowManager, } +pub struct SystemProfileUpdate { + pub crash: Option, + pub focused_window_active: bool, +} + impl Default for PrometeuHub { fn default() -> Self { Self::new() @@ -75,4 +81,29 @@ impl PrometeuHub { ); } } + + pub fn update_system_profile( + &mut self, + os: &mut VirtualMachineRuntime, + vm: &mut VirtualMachine, + signals: &InputSignals, + hw: &mut dyn HardwareBridge, + ) -> SystemProfileUpdate { + let mut crash = None; + + self.gui_update(os, hw); + + if let Some(focused_id) = self.window_manager.focused { + if hw.pad().start().down { + self.window_manager.remove_window(focused_id); + } else { + crash = os.tick(vm, signals, hw); + } + } + + self.render(os, hw); + hw.gfx_mut().present(); + + SystemProfileUpdate { crash, focused_window_active: self.window_manager.focused.is_some() } + } } diff --git a/discussion/index.ndjson b/discussion/index.ndjson index 4d3b1169..3d255de3 100644 --- a/discussion/index.ndjson +++ b/discussion/index.ndjson @@ -29,4 +29,4 @@ {"type":"discussion","id":"DSC-0018","status":"done","ticket":"asset-load-asset-id-int-contract","title":"Asset Load Asset ID Int Contract","created_at":"2026-03-27","updated_at":"2026-03-27","tags":["asset","runtime","abi"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0019","file":"discussion/lessons/DSC-0018-asset-load-asset-id-int-contract/LSN-0019-asset-load-id-abi-convergence.md","status":"done","created_at":"2026-03-27","updated_at":"2026-03-27"}]} {"type":"discussion","id":"DSC-0019","status":"done","ticket":"jenkinsfile-correction","title":"Jenkinsfile Correction and Relocation","created_at":"2026-04-07","updated_at":"2026-04-07","tags":["ci","jenkins"],"agendas":[{"id":"AGD-0017","file":"AGD-0017-jenkinsfile-correction.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"decisions":[{"id":"DEC-0002","file":"DEC-0002-jenkinsfile-strategy.md","status":"accepted","created_at":"2026-04-07","updated_at":"2026-04-07"}],"plans":[{"id":"PLN-0002","file":"PLN-0002-jenkinsfile-execution.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}],"lessons":[{"id":"LSN-0020","file":"discussion/lessons/DSC-0019-jenkins-ci-standardization/LSN-0020-jenkins-standard-relocation.md","status":"done","created_at":"2026-04-07","updated_at":"2026-04-07"}]} {"type":"discussion","id":"DSC-0030","status":"done","ticket":"internal-viewport-270p","title":"Agenda - Internal Viewport 270p (480x270)","created_at":"2026-04-27","updated_at":"2026-04-28","tags":["gfx","runtime","viewport","resolution","frame-composer","host"],"agendas":[],"decisions":[],"plans":[],"lessons":[{"id":"LSN-0039","file":"discussion/lessons/DSC-0030-internal-viewport-270p/LSN-0039-resolution-baseline-changes-are-runtime-wide-contracts.md","status":"done","created_at":"2026-04-28","updated_at":"2026-04-28"}]} -{"type":"discussion","id":"DSC-0031","status":"in_progress","ticket":"runtime-mode-separation-game-system","title":"Agenda - Runtime Mode Separation: Game and System","created_at":"2026-05-11","updated_at":"2026-05-14","tags":["runtime","firmware","hub","system-apps","game-mode","scheduler"],"agendas":[{"id":"AGD-0031","file":"AGD-0031-runtime-mode-separation-game-system.md","status":"accepted","created_at":"2026-05-11","updated_at":"2026-05-14"}],"decisions":[{"id":"DEC-0023","file":"DEC-0023-system-pipeline-separation.md","status":"accepted","created_at":"2026-05-14","updated_at":"2026-05-14"}],"plans":[{"id":"PLN-0046","file":"PLN-0046-profile-separation-specification.md","status":"done","created_at":"2026-05-14","updated_at":"2026-05-14","ref_decisions":["DEC-0023"]},{"id":"PLN-0047","file":"PLN-0047-firmware-runtime-profile-dispatch.md","status":"done","created_at":"2026-05-14","updated_at":"2026-05-14","ref_decisions":["DEC-0023"]},{"id":"PLN-0048","file":"PLN-0048-minimal-system-hub-pipeline.md","status":"open","created_at":"2026-05-14","updated_at":"2026-05-14","ref_decisions":["DEC-0023"]},{"id":"PLN-0049","file":"PLN-0049-system-profile-abi-gates.md","status":"open","created_at":"2026-05-14","updated_at":"2026-05-14","ref_decisions":["DEC-0023"]},{"id":"PLN-0050","file":"PLN-0050-system-pipeline-evidence-and-learning.md","status":"open","created_at":"2026-05-14","updated_at":"2026-05-14","ref_decisions":["DEC-0023"]}],"lessons":[]} +{"type":"discussion","id":"DSC-0031","status":"in_progress","ticket":"runtime-mode-separation-game-system","title":"Agenda - Runtime Mode Separation: Game and System","created_at":"2026-05-11","updated_at":"2026-05-14","tags":["runtime","firmware","hub","system-apps","game-mode","scheduler"],"agendas":[{"id":"AGD-0031","file":"AGD-0031-runtime-mode-separation-game-system.md","status":"accepted","created_at":"2026-05-11","updated_at":"2026-05-14"}],"decisions":[{"id":"DEC-0023","file":"DEC-0023-system-pipeline-separation.md","status":"accepted","created_at":"2026-05-14","updated_at":"2026-05-14"}],"plans":[{"id":"PLN-0046","file":"PLN-0046-profile-separation-specification.md","status":"done","created_at":"2026-05-14","updated_at":"2026-05-14","ref_decisions":["DEC-0023"]},{"id":"PLN-0047","file":"PLN-0047-firmware-runtime-profile-dispatch.md","status":"done","created_at":"2026-05-14","updated_at":"2026-05-14","ref_decisions":["DEC-0023"]},{"id":"PLN-0048","file":"PLN-0048-minimal-system-hub-pipeline.md","status":"done","created_at":"2026-05-14","updated_at":"2026-05-14","ref_decisions":["DEC-0023"]},{"id":"PLN-0049","file":"PLN-0049-system-profile-abi-gates.md","status":"open","created_at":"2026-05-14","updated_at":"2026-05-14","ref_decisions":["DEC-0023"]},{"id":"PLN-0050","file":"PLN-0050-system-pipeline-evidence-and-learning.md","status":"open","created_at":"2026-05-14","updated_at":"2026-05-14","ref_decisions":["DEC-0023"]}],"lessons":[]} diff --git a/discussion/workflow/plans/PLN-0048-minimal-system-hub-pipeline.md b/discussion/workflow/plans/PLN-0048-minimal-system-hub-pipeline.md index ca865bdf..659974be 100644 --- a/discussion/workflow/plans/PLN-0048-minimal-system-hub-pipeline.md +++ b/discussion/workflow/plans/PLN-0048-minimal-system-hub-pipeline.md @@ -2,7 +2,7 @@ id: PLN-0048 ticket: runtime-mode-separation-game-system title: Minimal System Hub Pipeline -status: open +status: done created: 2026-05-14 completed: ref_decisions: [DEC-0023]