155 lines
5.1 KiB
Rust
155 lines
5.1 KiB
Rust
use crate::prelude::*;
|
|
|
|
use std::ffi::CStr;
|
|
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);
|
|
}
|
|
|
|
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<sdl2::surface::Surface<'a>> {
|
|
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::ARGB8888);
|
|
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<Self> {
|
|
// 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",
|
|
unsafe { CStr::from_ptr(sys_info.library_name) }.to_string_lossy(),
|
|
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<bool> {
|
|
Some(true)
|
|
}
|
|
|
|
fn get_variable(&mut self, key: &str) -> Option<String> {
|
|
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<bool> {
|
|
self.set_geometry(&av_info.geometry);
|
|
Some(true)
|
|
}
|
|
|
|
fn set_geometry(&mut self, geom: &GameGeometry) -> Option<bool> {
|
|
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)
|
|
}
|
|
}
|