use crate::prelude::*; use std::path::Path; use sdl2::Sdl; use sdl2::pixels::PixelFormatEnum; use sdl2::render::WindowCanvas; use sdl2::video::FullscreenType; pub(crate) fn paint_frame_on_canvas(frame: &VideoFrame, canvas: &mut WindowCanvas) -> bool { if let Ok(surf) = frame_to_surface(frame) { let (cw, ch) = canvas.output_size().unwrap(); let should_intscale = if cw >= surf.width() && ch >= surf.height() { sdl2::sys::SDL_bool::SDL_TRUE } else { sdl2::sys::SDL_bool::SDL_FALSE }; unsafe { sdl2::sys::SDL_RenderSetIntegerScale(canvas.raw(), should_intscale); } if surf.pixel_format_enum() != PixelFormatEnum::Unknown { let format = sdl2::pixels::PixelFormat::try_from(canvas.default_pixel_format()).unwrap(); let surface = surf.convert(&format).unwrap(); if let Ok(tex) = canvas .texture_creator() .create_texture_from_surface(surface) { canvas.copy(&tex, None, None).unwrap(); } } canvas.present(); true } else { false } } pub(crate) fn sdl2_pixfmt(retro_fmt: PixelFormat) -> sdl2::pixels::PixelFormatEnum { match retro_fmt { PixelFormat::ARGB1555 => sdl2::pixels::PixelFormatEnum::ARGB1555, PixelFormat::ARGB8888 => sdl2::pixels::PixelFormatEnum::ARGB8888, PixelFormat::RGB565 => sdl2::pixels::PixelFormatEnum::RGB565, } } pub(crate) fn frame_to_surface<'a>(frame: &'a VideoFrame) -> crate::base::Result> { let (width, height) = frame.dimensions(); if let Some((bytes, pitch)) = frame.data_pitch_as_bytes() { let pixel_format = frame .pixel_format() .map(sdl2_pixfmt) .ok_or("Unknown pixel format")?; // dirty, but must be &mut for SDL API. // safe as long as we don't offer a &mut Surface in the public API. let data = unsafe { core::slice::from_raw_parts_mut(bytes.as_ptr() as *mut u8, bytes.len()) }; sdl2::surface::Surface::from_data(data, width, height, pitch as u32, pixel_format) .map_err(Into::into) } else { let pixel_format = frame .pixel_format() .map(sdl2_pixfmt) .unwrap_or(PixelFormatEnum::Unknown); sdl2::surface::Surface::new(width, height, pixel_format) .map_err(Into::into) } } /// Creates a root window in SDL2, then displays each 2D video frame provided by the core /// by converting it to a [sdl2::render::Texture] and copying it to the window's canvas. /// /// This component has no public interface and manages the SDL2 window on its own. pub struct SimpleSdl2CanvasComponent { canvas: WindowCanvas, win_size: (u32, u32), } impl SimpleSdl2CanvasComponent { pub fn new(sdl_context: &mut Sdl, ft: FullscreenType) -> crate::base::Result { // default to old libsnes 256x224 window size until load_game (prereq of get_system_av_info) let mut window = sdl_context .video()? .window("ferretro SDL", 256, 224) .resizable() .hidden() .build()?; window.set_fullscreen(ft)?; let canvas = window .into_canvas() .build()?; let win_size = canvas.output_size().unwrap(); Ok(SimpleSdl2CanvasComponent { canvas, win_size, }) } } impl RetroComponent for SimpleSdl2CanvasComponent { fn post_load_game(&mut self, retro: &mut LibretroWrapper, rom: &Path) -> crate::base::Result<()> { let sys_info = retro.get_system_info(); let title = format!( "{} - {} - ferretro SDL", sys_info.library_name, rom.file_stem().unwrap_or_default().to_string_lossy(), ); self.canvas.window_mut().set_title(&title)?; self.canvas.window_mut().show(); self.set_geometry(&retro.get_system_av_info().geometry); Ok(()) } } impl RetroCallbacks for SimpleSdl2CanvasComponent { fn video_refresh(&mut self, frame: &VideoFrame) { let output_size = self.canvas.output_size().unwrap(); if output_size != self.win_size { self.canvas.clear(); self.win_size = output_size; } paint_frame_on_canvas(frame, &mut self.canvas); } fn set_pixel_format(&mut self, _pix_fmt: PixelFormat) -> Option { Some(true) } fn get_variable(&mut self, key: &str) -> Option { match key { "parallel-n64-gfxplugin" => Some("angrylion".to_string()), "mupen64plus-rdp-plugin" => Some("angrylion".to_string()), "mupen64plus-rsp-plugin" => Some("cxd4".to_string()), _ => None, } } fn set_system_av_info(&mut self, av_info: &SystemAvInfo) -> Option { self.set_geometry(&av_info.geometry); Some(true) } fn set_geometry(&mut self, geom: &GameGeometry) -> Option { let _ = self.canvas.window_mut().set_size(geom.base_width, geom.base_height); let _ = self.canvas.set_logical_size(geom.base_width, geom.base_height); Some(true) } }