ferretro/ferretro_components/src/provided/sdl2/canvas.rs

104 lines
3.3 KiB
Rust

use crate::prelude::*;
use std::ffi::CStr;
use std::path::Path;
use sdl2::Sdl;
use sdl2::rect::Rect;
use sdl2::render::WindowCanvas;
pub(crate) fn paint_frame_on_canvas(frame: &VideoFrame, canvas: &mut WindowCanvas) -> bool {
if let Some((pixel_data, pitch)) = frame.data_pitch_as_bytes() {
let (width, height) = frame.dimensions();
let pixel_format = frame.pixel_format().map(sdl2_pixfmt);
if let Ok(mut tex) = canvas
.texture_creator()
.create_texture_static(pixel_format, width, height)
{
let rect = Rect::new(0, 0, width, height);
if tex.update(rect, pixel_data, pitch).is_ok() {
canvas.clear();
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,
}
}
/// 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,
}
impl SimpleSdl2CanvasComponent {
pub fn new(sdl_context: &mut Sdl, retro: &LibretroWrapper) -> Result<Self, Box<dyn std::error::Error>> {
let sys_info = retro.get_system_info();
let title = format!(
"{} - ferretro SDL",
unsafe { CStr::from_ptr(sys_info.library_name) }.to_string_lossy()
);
// default to old libsnes 256x224 window size until load_game (prereq of get_system_av_info)
let canvas = sdl_context
.video()?
.window(title.as_str(), 256, 224)
.build()?
.into_canvas()
.build()?;
Ok(SimpleSdl2CanvasComponent {
canvas,
})
}
}
impl RetroComponent for SimpleSdl2CanvasComponent {
fn post_load_game(&mut self, retro: &mut LibretroWrapper, _rom: &Path) -> crate::base::Result<()> {
self.set_geometry(&retro.get_system_av_info().geometry);
Ok(())
}
}
impl RetroCallbacks for SimpleSdl2CanvasComponent {
fn video_refresh(&mut self, frame: &VideoFrame) {
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()),
_ => 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)
}
}