diff --git a/crates/prometeu-core/src/firmware/firmware.rs b/crates/prometeu-core/src/firmware/firmware.rs index 5d1bb38d..fcb1716a 100644 --- a/crates/prometeu-core/src/firmware/firmware.rs +++ b/crates/prometeu-core/src/firmware/firmware.rs @@ -1,4 +1,4 @@ -use crate::firmware::firmware_state::{FirmwareState, LoadGameStep, ResetStep}; +use crate::firmware::firmware_state::{FirmwareState, LoadCartridgeStep, ResetStep}; use crate::firmware::prometeu_context::PrometeuContext; use crate::hardware::{HardwareBridge, InputSignals}; use crate::model::Cartridge; @@ -26,6 +26,10 @@ impl Firmware { } pub fn step_frame(&mut self, signals: &InputSignals, hw: &mut dyn HardwareBridge) { + // Atualiza input uma vez por frame de host + hw.pad_mut().begin_frame(signals); + hw.touch_mut().begin_frame(signals); + if !self.state_initialized { self.on_enter(signals, hw); self.state_initialized = true; @@ -59,7 +63,7 @@ impl Firmware { FirmwareState::SplashScreen(s) => s.on_enter(&mut req), FirmwareState::LaunchHub(s) => s.on_enter(&mut req), FirmwareState::HubHome(s) => s.on_enter(&mut req), - FirmwareState::LoadGame(s) => s.on_enter(&mut req), + FirmwareState::LoadCartridge(s) => s.on_enter(&mut req), FirmwareState::GameRunning(s) => s.on_enter(&mut req), FirmwareState::AppCrashes(s) => s.on_enter(&mut req), } @@ -78,7 +82,7 @@ impl Firmware { FirmwareState::SplashScreen(s) => s.on_update(&mut req), FirmwareState::LaunchHub(s) => s.on_update(&mut req), FirmwareState::HubHome(s) => s.on_update(&mut req), - FirmwareState::LoadGame(s) => s.on_update(&mut req), + FirmwareState::LoadCartridge(s) => s.on_update(&mut req), FirmwareState::GameRunning(s) => s.on_update(&mut req), FirmwareState::AppCrashes(s) => s.on_update(&mut req), } @@ -97,14 +101,14 @@ impl Firmware { FirmwareState::SplashScreen(s) => s.on_exit(&mut req), FirmwareState::LaunchHub(s) => s.on_exit(&mut req), FirmwareState::HubHome(s) => s.on_exit(&mut req), - FirmwareState::LoadGame(s) => s.on_exit(&mut req), + FirmwareState::LoadCartridge(s) => s.on_exit(&mut req), FirmwareState::GameRunning(s) => s.on_exit(&mut req), FirmwareState::AppCrashes(s) => s.on_exit(&mut req), } } pub fn load_cartridge(&mut self, cartridge: Cartridge) { - self.state = FirmwareState::LoadGame(LoadGameStep { cartridge }); + self.state = FirmwareState::LoadCartridge(LoadCartridgeStep { cartridge }); self.state_initialized = false; } } \ No newline at end of file diff --git a/crates/prometeu-core/src/firmware/firmware_state.rs b/crates/prometeu-core/src/firmware/firmware_state.rs index c2e36478..da2d1228 100644 --- a/crates/prometeu-core/src/firmware/firmware_state.rs +++ b/crates/prometeu-core/src/firmware/firmware_state.rs @@ -2,7 +2,7 @@ pub use crate::firmware::firmware_step_reset::ResetStep; pub use crate::firmware::firmware_step_splash_screen::SplashScreenStep; pub use crate::firmware::firmware_step_launch_hub::LaunchHubStep; pub use crate::firmware::firmware_step_hub_home::HubHomeStep; -pub use crate::firmware::firmware_step_load_game::LoadGameStep; +pub use crate::firmware::firmware_step_load_cartridge::LoadCartridgeStep; pub use crate::firmware::firmware_step_game_running::GameRunningStep; pub use crate::firmware::firmware_step_crash_screen::AppCrashesStep; @@ -12,7 +12,7 @@ pub enum FirmwareState { SplashScreen(SplashScreenStep), LaunchHub(LaunchHubStep), HubHome(HubHomeStep), - LoadGame(LoadGameStep), + LoadCartridge(LoadCartridgeStep), GameRunning(GameRunningStep), AppCrashes(AppCrashesStep), } \ No newline at end of file diff --git a/crates/prometeu-core/src/firmware/firmware_step_game_running.rs b/crates/prometeu-core/src/firmware/firmware_step_game_running.rs index ad995295..38302461 100644 --- a/crates/prometeu-core/src/firmware/firmware_step_game_running.rs +++ b/crates/prometeu-core/src/firmware/firmware_step_game_running.rs @@ -8,7 +8,13 @@ impl GameRunningStep { pub fn on_enter(&mut self, _ctx: &mut PrometeuContext) {} pub fn on_update(&mut self, ctx: &mut PrometeuContext) -> Option { - ctx.os.step_frame(ctx.vm, ctx.signals, ctx.hw).map(|err| FirmwareState::AppCrashes(AppCrashesStep { error: err })) + let result = ctx.os.step_frame(ctx.vm, ctx.signals, ctx.hw); + + if !ctx.os.logical_frame_active { + ctx.hw.gfx_mut().present(); + } + + result.map(|err| FirmwareState::AppCrashes(AppCrashesStep { error: err })) } pub fn on_exit(&mut self, _ctx: &mut PrometeuContext) {} diff --git a/crates/prometeu-core/src/firmware/firmware_step_hub_home.rs b/crates/prometeu-core/src/firmware/firmware_step_hub_home.rs index 97c4151f..ff2ab7b7 100644 --- a/crates/prometeu-core/src/firmware/firmware_step_hub_home.rs +++ b/crates/prometeu-core/src/firmware/firmware_step_hub_home.rs @@ -1,4 +1,4 @@ -use crate::firmware::firmware_state::FirmwareState; +use crate::firmware::firmware_state::{AppCrashesStep, FirmwareState}; use crate::firmware::prometeu_context::PrometeuContext; #[derive(Debug, Clone)] @@ -8,11 +8,29 @@ impl HubHomeStep { pub fn on_enter(&mut self, _ctx: &mut PrometeuContext) {} pub fn on_update(&mut self, ctx: &mut PrometeuContext) -> Option { - // req.hw.gfx_mut().clear(Color::BLACK); - ctx.hw.pad_mut().begin_frame(ctx.signals); + let mut error = None; + + // Sempre atualiza a GUI do Hub (limpa a tela e processa input se não houver foco) 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 roda aqui, desenhando sobre o fundo do Hub + error = ctx.os.step_frame(ctx.vm, ctx.signals, ctx.hw); + } + } + + // Renderiza as bordas das janelas do System App + ctx.hub.render(ctx.os, ctx.hw); + ctx.hw.gfx_mut().present(); + if let Some(err) = error { + return Some(FirmwareState::AppCrashes(AppCrashesStep { error: err })); + } + None } diff --git a/crates/prometeu-core/src/firmware/firmware_step_load_cartridge.rs b/crates/prometeu-core/src/firmware/firmware_step_load_cartridge.rs new file mode 100644 index 00000000..5d67967d --- /dev/null +++ b/crates/prometeu-core/src/firmware/firmware_step_load_cartridge.rs @@ -0,0 +1,30 @@ +use crate::firmware::firmware_state::{FirmwareState, GameRunningStep, HubHomeStep}; +use crate::firmware::prometeu_context::PrometeuContext; +use crate::model::{AppMode, Cartridge, Color, Rect}; + +#[derive(Debug, Clone)] +pub struct LoadCartridgeStep { + pub cartridge: Cartridge, +} + +impl LoadCartridgeStep { + pub fn on_enter(&mut self, ctx: &mut PrometeuContext) { + ctx.os.initialize_vm(ctx.vm, &self.cartridge); + } + + pub fn on_update(&mut self, ctx: &mut PrometeuContext) -> Option { + if self.cartridge.header.mode == AppMode::System { + let id = ctx.hub.window_manager.add_window( + self.cartridge.header.title.clone(), + Rect { x: 40, y: 20, w: 240, h: 140 }, + Color::WHITE + ); + ctx.hub.window_manager.set_focus(id); + return Some(FirmwareState::HubHome(HubHomeStep)); + } + + Some(FirmwareState::GameRunning(GameRunningStep)) + } + + pub fn on_exit(&mut self, _ctx: &mut PrometeuContext) {} +} \ No newline at end of file diff --git a/crates/prometeu-core/src/firmware/firmware_step_load_game.rs b/crates/prometeu-core/src/firmware/firmware_step_load_game.rs deleted file mode 100644 index 58b4a918..00000000 --- a/crates/prometeu-core/src/firmware/firmware_step_load_game.rs +++ /dev/null @@ -1,20 +0,0 @@ -use crate::firmware::firmware_state::{FirmwareState, GameRunningStep}; -use crate::firmware::prometeu_context::PrometeuContext; -use crate::model::Cartridge; - -#[derive(Debug, Clone)] -pub struct LoadGameStep { - pub cartridge: Cartridge, -} - -impl LoadGameStep { - pub fn on_enter(&mut self, ctx: &mut PrometeuContext) { - ctx.os.initialize_vm(ctx.vm, &self.cartridge); - } - - pub fn on_update(&mut self, _ctx: &mut PrometeuContext) -> Option { - Some(FirmwareState::GameRunning(GameRunningStep)) - } - - pub fn on_exit(&mut self, _ctx: &mut PrometeuContext) {} -} \ No newline at end of file diff --git a/crates/prometeu-core/src/firmware/mod.rs b/crates/prometeu-core/src/firmware/mod.rs index 62c8f988..c4a223ca 100644 --- a/crates/prometeu-core/src/firmware/mod.rs +++ b/crates/prometeu-core/src/firmware/mod.rs @@ -5,7 +5,7 @@ pub(crate) mod firmware_step_reset; pub(crate) mod firmware_step_splash_screen; pub(crate) mod firmware_step_launch_hub; pub(crate) mod firmware_step_hub_home; -pub(crate) mod firmware_step_load_game; +pub(crate) mod firmware_step_load_cartridge; pub(crate) mod firmware_step_game_running; pub(crate) mod firmware_step_crash_screen; mod prometeu_context; diff --git a/crates/prometeu-core/src/model/mod.rs b/crates/prometeu-core/src/model/mod.rs index b1b43dc4..d84643e4 100644 --- a/crates/prometeu-core/src/model/mod.rs +++ b/crates/prometeu-core/src/model/mod.rs @@ -6,6 +6,7 @@ mod tile_bank; mod sprite; mod sample; mod cartridge; +mod window; pub use button::Button; pub use cartridge::{AppHeader, AppMode, Cartridge}; @@ -15,3 +16,4 @@ pub use sprite::Sprite; pub use tile::Tile; pub use tile_bank::{TileBank, TileSize}; pub use tile_layer::{HudTileLayer, ScrollableTileLayer, TileMap}; +pub use window::{Rect, Window, WindowId}; diff --git a/crates/prometeu-core/src/model/window.rs b/crates/prometeu-core/src/model/window.rs new file mode 100644 index 00000000..8b4123c2 --- /dev/null +++ b/crates/prometeu-core/src/model/window.rs @@ -0,0 +1,21 @@ +use crate::model::Color; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Rect { + pub x: i32, + pub y: i32, + pub w: i32, + pub h: i32, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct WindowId(pub u32); + +#[derive(Debug, Clone)] +pub struct Window { + pub id: WindowId, + pub viewport: Rect, + pub has_focus: bool, + pub title: String, + pub color: Color, +} diff --git a/crates/prometeu-core/src/prometeu_hub/prometeu_hub.rs b/crates/prometeu-core/src/prometeu_hub/prometeu_hub.rs index 7bf69b0b..5189a7c9 100644 --- a/crates/prometeu-core/src/prometeu_hub/prometeu_hub.rs +++ b/crates/prometeu-core/src/prometeu_hub/prometeu_hub.rs @@ -1,32 +1,63 @@ use crate::hardware::HardwareBridge; -use crate::model::Color; +use crate::model::{Color, Rect, Window, WindowId}; use crate::prometeu_os::PrometeuOS; -// TODO: Mover para arquivo próprio se crescer. -/// Sistema de janelas do PROMETEU. -pub struct WindowSystem { - pub theme_color: Color, +/// Gerenciador de janelas do PROMETEU. +pub struct WindowManager { + pub windows: Vec, + pub focused: Option, } -impl WindowSystem { +impl WindowManager { pub fn new() -> Self { Self { - theme_color: Color::INDIGO, + windows: Vec::new(), + focused: None, + } + } + + pub fn add_window(&mut self, title: String, viewport: Rect, color: Color) -> WindowId { + let id = WindowId(self.windows.len() as u32); + let window = Window { + id, + viewport, + has_focus: false, + title, + color, + }; + self.windows.push(window); + id + } + + pub fn remove_window(&mut self, id: WindowId) { + self.windows.retain(|w| w.id != id); + if self.focused == Some(id) { + self.focused = None; + } + } + + pub fn remove_all_windows(&mut self) { + self.windows.clear(); + self.focused = None; + } + + pub fn set_focus(&mut self, id: WindowId) { + self.focused = Some(id); + for window in &mut self.windows { + window.has_focus = window.id == id; } } } /// PrometeuHub: Launcher e ambiente de UI do sistema. pub struct PrometeuHub { - pub window_system: WindowSystem, - pub color: Color, + pub window_manager: WindowManager, } impl PrometeuHub { pub fn new() -> Self { Self { - window_system: WindowSystem::new(), - color: Color::BLACK, + window_manager: WindowManager::new(), } } @@ -35,22 +66,74 @@ impl PrometeuHub { } pub fn gui_update(&mut self, _os: &mut PrometeuOS, hw: &mut dyn HardwareBridge) { - if hw.pad().a.down { - self.color = Color::GREEN; - } - if hw.pad().b.down { - self.color = Color::INDIGO; - } - if hw.pad().x.down { - self.color = Color::YELLOW; - } - if hw.pad().y.down { - self.color = Color::RED; + hw.gfx_mut().clear(Color::BLACK); + + let mut next_window = None; + + if hw.pad().a.pressed { + next_window = Some(("Green Window".to_string(), Rect { x: 0, y: 0, w: 160, h: 90 }, Color::GREEN)); + } else if hw.pad().b.pressed { + next_window = Some(("Indigo Window".to_string(), Rect { x: 160, y: 0, w: 160, h: 90 }, Color::INDIGO)); + } else if hw.pad().x.pressed { + next_window = Some(("Yellow Window".to_string(), Rect { x: 0, y: 90, w: 160, h: 90 }, Color::YELLOW)); + } else if hw.pad().y.pressed { + next_window = Some(("Red Window".to_string(), Rect { x: 160, y: 90, w: 160, h: 90 }, Color::RED)); } - hw.gfx_mut().clear(self.color); + if let Some((title, rect, color)) = next_window { + self.window_manager.remove_all_windows(); + let id = self.window_manager.add_window(title, rect, color); + self.window_manager.set_focus(id); + } } - pub fn render(&mut self, _os: &mut PrometeuOS, _hw: &mut dyn HardwareBridge) { + pub fn render(&mut self, _os: &mut PrometeuOS, hw: &mut dyn HardwareBridge) { + for window in &self.window_manager.windows { + hw.gfx_mut().fill_rect( + window.viewport.x, + window.viewport.y, + window.viewport.w, + window.viewport.h, + window.color, + ); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::model::Rect; + + #[test] + fn test_window_manager_focus() { + let mut wm = WindowManager::new(); + let id1 = wm.add_window("Window 1".to_string(), Rect { x: 0, y: 0, w: 10, h: 10 }, Color::WHITE); + let id2 = wm.add_window("Window 2".to_string(), Rect { x: 10, y: 10, w: 10, h: 10 }, Color::WHITE); + + assert_eq!(wm.windows.len(), 2); + assert_eq!(wm.focused, None); + + wm.set_focus(id1); + assert_eq!(wm.focused, Some(id1)); + assert!(wm.windows[0].has_focus); + assert!(!wm.windows[1].has_focus); + + wm.set_focus(id2); + assert_eq!(wm.focused, Some(id2)); + assert!(!wm.windows[0].has_focus); + assert!(wm.windows[1].has_focus); + } + + #[test] + fn test_window_manager_remove_window() { + let mut wm = WindowManager::new(); + let id = wm.add_window("Window".to_string(), Rect { x: 0, y: 0, w: 10, h: 10 }, Color::WHITE); + wm.set_focus(id); + assert_eq!(wm.focused, Some(id)); + + wm.remove_window(id); + assert_eq!(wm.windows.len(), 0); + assert_eq!(wm.focused, None); } } \ No newline at end of file diff --git a/crates/prometeu-core/src/prometeu_os/prometeu_os.rs b/crates/prometeu-core/src/prometeu_os/prometeu_os.rs index 1f8e425a..c562bf7f 100644 --- a/crates/prometeu-core/src/prometeu_os/prometeu_os.rs +++ b/crates/prometeu-core/src/prometeu_os/prometeu_os.rs @@ -79,7 +79,6 @@ impl PrometeuOS { if run.reason == crate::virtual_machine::LogicalFrameEndingReason::FrameSync { hw.gfx_mut().render_all(); - self.end_logical_frame(hw); self.logical_frame_index += 1; self.logical_frame_active = false; self.logical_frame_remaining_cycles = 0; @@ -95,15 +94,10 @@ impl PrometeuOS { None } - fn begin_logical_frame(&mut self, signals: &InputSignals, hw: &mut dyn HardwareBridge) { + fn begin_logical_frame(&mut self, _signals: &InputSignals, hw: &mut dyn HardwareBridge) { hw.audio_mut().clear_commands(); - hw.touch_mut().begin_frame(signals); - hw.pad_mut().begin_frame(signals); } - fn end_logical_frame(&mut self, hw: &mut dyn HardwareBridge) { - hw.gfx_mut().present(); - } // Helper para syscalls pub fn get_color(&self, index: usize, hw: &mut dyn HardwareBridge) -> Color {