diff --git a/src/main.rs b/src/main.rs index 75604c9..7347d7c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -27,6 +27,9 @@ use ferretro_components::provided::{ sdl2::{Sdl2SurfaceComponent, SimpleSdl2AudioComponent}, stdlib::{SleepFramerateLimitComponent, PathBufComponent}, }; +use sdl2::mouse::{Cursor, MouseUtil}; +use sdl2::rect::Rect; +use itertools::Itertools; type Result = std::result::Result>; @@ -60,7 +63,11 @@ impl GuiState { struct Zretro { emu: Pin>, canvas: WindowCanvas, - font: Surface<'static>, + font_surf: Surface<'static>, + mouse_util: MouseUtil, + mouse_cursor: Cursor, + mouse_shadow_surf: Surface<'static>, + mouse_shadow_pos: Rect, rng: Xoshiro128Plus, ui_bg: Option>, snowflakes: Vec, @@ -68,7 +75,21 @@ struct Zretro { event_pump: EventPump, } +const BG_COLOR: Color = Color { r: 65, g: 44, b: 130, a: 255 }; const FONT_PNG: &'static [u8] = include_bytes!("lifont.png"); +const MOUSE_PNG: &'static [u8] = include_bytes!("zmouse.png"); +const MOUSE_SHADOW_PNG: &'static [u8] = include_bytes!("zmouse-shadow.png"); + +fn surface_asset(asset: &'static [u8]) -> Result> { + let rwops = sdl2::rwops::RWops::from_bytes(asset)?; + let surf_shortlived = rwops.load_png()?; + // there's a nonsemantic inheritance of lifetime from ImageRWops::load_png. TODO: pr + unsafe { + let ptr = surf_shortlived.raw(); + std::mem::forget(surf_shortlived); + Ok(Surface::from_ll(ptr)) + } +} impl Zretro { fn new(opt: Opt) -> Result { @@ -111,14 +132,14 @@ impl Zretro { emu.init().unwrap(); emu.load_game(&opt.rom).unwrap(); // TODO: optional via CLI positional arg, menu-browsable - let rwops = sdl2::rwops::RWops::from_bytes(FONT_PNG)?; - let font_shortlived = rwops.load_png()?; - // there's a nonsemantic inheritance of lifetime from ImageRWops::load_png. TODO: pr - let font = unsafe { - let ptr = font_shortlived.raw(); - std::mem::forget(font_shortlived); - Surface::from_ll(ptr) - }; + let font_surf = surface_asset(FONT_PNG)?; + let mouse_surf = surface_asset(MOUSE_PNG)?; + let mouse_shadow_surf = surface_asset(MOUSE_SHADOW_PNG)?; + + let mouse_util = sdl_context.mouse(); + let mouse_cursor = Cursor::from_surface(&mouse_surf, 0, 0)?; + mouse_cursor.set(); + let mouse_shadow_pos = mouse_shadow_surf.rect(); let mut rng = rand_xoshiro::Xoshiro128Plus::from_entropy(); let mut ui_bg = Surface::new(geometry.base_width, geometry.base_height, PixelFormatEnum::ABGR8888)?; @@ -133,7 +154,11 @@ impl Zretro { Ok(Zretro { emu, canvas, - font, + font_surf, + mouse_util, + mouse_cursor, + mouse_shadow_surf, + mouse_shadow_pos, rng, ui_bg: Some(ui_bg), snowflakes, @@ -144,7 +169,9 @@ impl Zretro { fn update_snow(&mut self) -> Result<()> { let mut ui_bg = self.ui_bg.take().ok_or("no ui_bg?")?; - ui_bg.fill_rect(None, Color::RGBA(0, 0, 128, 128))?; + let mut bg_color = BG_COLOR; + bg_color.a = 128; + ui_bg.fill_rect(None, bg_color)?; let w = ui_bg.width() as i32; let h = ui_bg.height() as i32; let halflen = self.snowflakes.len() / 2; @@ -167,25 +194,35 @@ impl Zretro { Ok(()) } + fn toggle_mode(&mut self) -> Result<()> { + self.mode.toggle(); + let audio: &SimpleSdl2AudioComponent = self.emu.component_ref()?; + match self.mode { + GuiState::Menus => { + audio.silence_buffer(); + self.mouse_cursor.set(); + self.mouse_util.show_cursor(true); + } + GuiState::Game => { + self.mouse_util.show_cursor(false); + } + } + Ok(()) + } + fn run(&mut self) -> Result<()> { let tc = self.canvas.texture_creator(); - let font_tx = tc.create_texture_from_surface(&self.font)?; - let mut font_rect = self.font.rect(); + let font_tx = tc.create_texture_from_surface(&self.font_surf)?; + let mut font_rect = self.font_surf.rect(); font_rect.set_height(64); 'outer: loop { - for event in self.event_pump.poll_iter() { + let events = self.event_pump.poll_iter().collect_vec(); + for event in events { match event { Event::Quit { .. } => break 'outer, Event::KeyDown { keycode: Some(keycode), .. } => { match keycode { - Keycode::Escape => { - self.mode.toggle(); - let audio: &SimpleSdl2AudioComponent = self.emu.component_ref()?; - match self.mode { - GuiState::Menus => audio.silence_buffer(), - GuiState::Game => {} //audio.resume(), - } - }, + Keycode::Escape => self.toggle_mode()?, _ => {} } } @@ -194,6 +231,10 @@ impl Zretro { self.emu.libretro_core().unload_game(); self.emu.load_game(filename)?; } + Event::MouseMotion { x, y, .. } => { + self.mouse_shadow_pos.set_x(x + 5); + self.mouse_shadow_pos.set_y(y + 7); + }, _ => {} } } @@ -215,8 +256,10 @@ impl Zretro { if let GuiState::Menus = self.mode { self.update_snow()?; - let ui_bg_surf_ref = self.ui_bg.as_ref().ok_or("no bg?")?; - let mut ui_tx = tc.create_texture_from_surface(ui_bg_surf_ref)?; + let ui_bg_mut = self.ui_bg.as_mut().ok_or("no bg?")?; + + let _ = self.mouse_shadow_surf.blit(None, ui_bg_mut, self.mouse_shadow_pos); + let mut ui_tx = tc.create_texture_from_surface(ui_bg_mut)?; ui_tx.set_blend_mode(BlendMode::Blend); self.canvas.copy(&ui_tx, None, None)?; self.canvas.copy(&font_tx, font_rect, font_rect)?; diff --git a/src/zmouse-shadow.png b/src/zmouse-shadow.png new file mode 100644 index 0000000..267fe08 Binary files /dev/null and b/src/zmouse-shadow.png differ diff --git a/src/zmouse.png b/src/zmouse.png new file mode 100644 index 0000000..fa37fd7 Binary files /dev/null and b/src/zmouse.png differ