From 50b16493151ca9372104295018762a5143dac790 Mon Sep 17 00:00:00 2001 From: lif Date: Thu, 14 Nov 2019 21:21:58 -0800 Subject: [PATCH] WIP nonfunctional SDL 2 thing --- Cargo.toml | 8 +++ src/bin/example.rs | 116 +++++++++++++++++++++++++++++++++++-------- src/retro/convert.rs | 2 +- src/retro/loading.rs | 67 ++++++++++++------------- src/retro/wrapper.rs | 24 ++++----- 5 files changed, 148 insertions(+), 69 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3ca3b16..41adee3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,4 +10,12 @@ failure = "^0.1" libloading = "^0.5" num_enum = "^0.4" structopt = "^0.3" +sdl2 = { version = "*", optional = true } +[features] +with-sdl2 = ["sdl2"] + +[[bin]] +name = "example" +path = "src/bin/example.rs" +required-features = ["with-sdl2"] diff --git a/src/bin/example.rs b/src/bin/example.rs index 2e410a6..15ab609 100644 --- a/src/bin/example.rs +++ b/src/bin/example.rs @@ -1,19 +1,108 @@ extern crate rustro; +extern crate sdl2; + +use rustro::retro; +use rustro::retro::ffi::GameGeometry; use std::ffi::CStr; -use std::path::PathBuf; +use std::path::{PathBuf, Path}; +use std::time::Duration; use structopt::StructOpt; -use rustro::retro; +use sdl2::render::{WindowCanvas, Texture}; +use sdl2::rect::Rect; -struct Foo; -impl retro::convert::Handler for Foo { - fn video_refresh(&mut self, data: &[u8], width: u32, height: u32, pitch: u32) { - println!("video_refresh {}x{}", width, height); +struct MyEmulator { + retro: retro::wrapper::LibretroWrapper, + + sdl_context: sdl2::Sdl, + video_subsystem: sdl2::VideoSubsystem, + canvas: WindowCanvas, + pixel_format: sdl2::pixels::PixelFormatEnum, +} + +impl MyEmulator { + pub fn with_core(core: impl AsRef) -> Self { + let lib = libloading::Library::new(core.as_ref()).unwrap(); + let raw_retro = retro::loading::LibretroApi::from_library(lib).unwrap(); + let retro = retro::wrapper::LibretroWrapper::from(raw_retro); + println!("api version: {}", retro.as_ref().api_version()); + println!("name: {}", unsafe { CStr::from_ptr(retro.as_ref().get_system_info().library_name) }.to_string_lossy()); + + let av = retro.as_ref().get_system_av_info(); + + let sdl_context = sdl2::init().unwrap(); + let video_subsystem = sdl_context.video().unwrap(); + + let window = video_subsystem.window( + "rust libretro", + av.geometry.base_width, + av.geometry.base_height + ).position_centered().build().unwrap(); + + let mut canvas = window.into_canvas().build().unwrap(); + + let pixel_format = sdl2::pixels::PixelFormatEnum::ABGR1555; + + let mut result = MyEmulator { retro, sdl_context, video_subsystem, canvas, pixel_format }; + retro::wrapper::register_handler(&mut result); + result.retro.as_ref().init(); + result + } + + pub fn run(&self) { + println!("run?"); + self.retro.as_ref().run(); + println!("run."); + } + + pub fn load_game(&self, rom: impl AsRef) { + self.retro.as_ref().load_game(Some(rom.as_ref()), None, None); } } +impl retro::convert::Handler for MyEmulator { + fn video_refresh(&mut self, data: &[u8], width: u32, height: u32, pitch: u32) { + println!("video_refresh {}x{}", width, height); + let rect = Rect::new(0, 0, width, height); + println!("0"); + let tex_creator = self.canvas.texture_creator(); + println!("a"); + let mut tex: Texture = tex_creator.create_texture_streaming( + self.pixel_format, + width, + height, + ).unwrap(); + println!("b"); + tex.update(None, data, pitch as usize); + println!("c"); + self.canvas.copy(&tex, rect, rect); + println!("d"); + self.canvas.present(); + println!("e"); + } + + fn set_pixel_format(&mut self, pix_fmt: retro::ffi::PixelFormat) -> bool { + self.pixel_format = match pix_fmt { + retro::ffi::PixelFormat::ARGB1555 => sdl2::pixels::PixelFormatEnum::RGB555, + retro::ffi::PixelFormat::ARGB8888 => sdl2::pixels::PixelFormatEnum::ARGB8888, + retro::ffi::PixelFormat::RGB565 => sdl2::pixels::PixelFormatEnum::RGB565, + }; + true + } +} + +pub fn main() { + let opt: Opt = Opt::from_args(); + let mut emu = MyEmulator::with_core(&opt.core); + emu.load_game(&opt.rom); + + loop { + emu.run(); + ::std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 60)); + } +} #[derive(StructOpt)] struct Opt { @@ -24,18 +113,3 @@ struct Opt { #[structopt(short, long, parse(from_os_str))] rom: PathBuf, } - -fn main() -> failure::Fallible<()> { - let opt: Opt = Opt::from_args(); - let lib = libloading::Library::new(&opt.core)?; - let retro = retro::loading::LibretroApi::from_library(&lib)?; - let wrapper = retro::wrapper::LibretroWrapper::from(retro); - let mut foo = Foo{}; - wrapper.register_handler(&mut foo); - println!("api version: {}", wrapper.as_ref().api_version()); - println!("name: {}", unsafe { CStr::from_ptr(wrapper.as_ref().get_system_info().library_name) }.to_string_lossy()); - wrapper.as_ref().init(); - wrapper.as_ref().load_game(Some(&opt.rom), None, None); - wrapper.as_ref().run(); - Ok(()) -} diff --git a/src/retro/convert.rs b/src/retro/convert.rs index 13144f0..ca0b64d 100644 --- a/src/retro/convert.rs +++ b/src/retro/convert.rs @@ -189,7 +189,7 @@ pub enum EnvCmd { // SetSerializationQuirks = ENVIRONMENT_SET_SERIALIZATION_QUIRKS, } -pub trait Handler: Send + Sync + 'static { +pub trait Handler: 'static { fn video_refresh(&mut self, data: &[u8], width: c_uint, height: c_uint, pitch: c_uint) {} fn audio_sample(&mut self, left: i16, right: i16) {} fn audio_sample_batch(&mut self, stereo_pcm: &[i16]) -> usize { stereo_pcm.len() } diff --git a/src/retro/loading.rs b/src/retro/loading.rs index f75f60c..11161bf 100644 --- a/src/retro/loading.rs +++ b/src/retro/loading.rs @@ -1,6 +1,7 @@ use std::convert::TryInto; use std::os::raw::{c_char, c_void}; use std::path::Path; +use std::marker::PhantomData; use failure::Fallible; use libloading; @@ -8,44 +9,42 @@ use libloading; use super::ffi::*; use super::convert::*; -pub struct LibretroApi<'a> { - lib: &'a libloading::Library, // for tying our lifetime to its own +pub struct LibretroApi { + lib: libloading::Library, // for tying our lifetime to its own pub core_api: CoreAPI, } -impl<'a> LibretroApi<'a> { - pub fn from_library(lib: &'a libloading::Library) -> Fallible { +impl LibretroApi { + pub fn from_library(lib: libloading::Library) -> Fallible { unsafe { - Ok(LibretroApi { - lib, - core_api: CoreAPI { - retro_set_environment: *lib.get(b"retro_set_environment")?, - retro_set_video_refresh: *lib.get(b"retro_set_video_refresh")?, - retro_set_audio_sample: *lib.get(b"retro_set_audio_sample")?, - retro_set_audio_sample_batch: *lib.get(b"retro_set_audio_sample_batch")?, - retro_set_input_poll: *lib.get(b"retro_set_input_poll")?, - retro_set_input_state: *lib.get(b"retro_set_input_state")?, - retro_init: *lib.get(b"retro_init")?, - retro_deinit: *lib.get(b"retro_deinit")?, - retro_api_version: *lib.get(b"retro_api_version")?, - retro_get_system_info: *lib.get(b"retro_get_system_info")?, - retro_get_system_av_info: *lib.get(b"retro_get_system_av_info")?, - retro_set_controller_port_device: *lib.get(b"retro_set_controller_port_device")?, - retro_reset: *lib.get(b"retro_reset")?, - retro_run: *lib.get(b"retro_run")?, - retro_serialize_size: *lib.get(b"retro_serialize_size")?, - retro_serialize: *lib.get(b"retro_serialize")?, - retro_unserialize: *lib.get(b"retro_unserialize")?, - retro_cheat_reset: *lib.get(b"retro_cheat_reset")?, - retro_cheat_set: *lib.get(b"retro_cheat_set")?, - retro_load_game: *lib.get(b"retro_load_game")?, - retro_load_game_special: *lib.get(b"retro_load_game_special")?, - retro_unload_game: *lib.get(b"retro_unload_game")?, - retro_get_region: *lib.get(b"retro_get_region")?, - retro_get_memory_data: *lib.get(b"retro_get_memory_data")?, - retro_get_memory_size: *lib.get(b"retro_get_memory_size")?, - } - }) + let core_api = CoreAPI { + retro_set_environment: *lib.get(b"retro_set_environment")?, + retro_set_video_refresh: *lib.get(b"retro_set_video_refresh")?, + retro_set_audio_sample: *lib.get(b"retro_set_audio_sample")?, + retro_set_audio_sample_batch: *lib.get(b"retro_set_audio_sample_batch")?, + retro_set_input_poll: *lib.get(b"retro_set_input_poll")?, + retro_set_input_state: *lib.get(b"retro_set_input_state")?, + retro_init: *lib.get(b"retro_init")?, + retro_deinit: *lib.get(b"retro_deinit")?, + retro_api_version: *lib.get(b"retro_api_version")?, + retro_get_system_info: *lib.get(b"retro_get_system_info")?, + retro_get_system_av_info: *lib.get(b"retro_get_system_av_info")?, + retro_set_controller_port_device: *lib.get(b"retro_set_controller_port_device")?, + retro_reset: *lib.get(b"retro_reset")?, + retro_run: *lib.get(b"retro_run")?, + retro_serialize_size: *lib.get(b"retro_serialize_size")?, + retro_serialize: *lib.get(b"retro_serialize")?, + retro_unserialize: *lib.get(b"retro_unserialize")?, + retro_cheat_reset: *lib.get(b"retro_cheat_reset")?, + retro_cheat_set: *lib.get(b"retro_cheat_set")?, + retro_load_game: *lib.get(b"retro_load_game")?, + retro_load_game_special: *lib.get(b"retro_load_game_special")?, + retro_unload_game: *lib.get(b"retro_unload_game")?, + retro_get_region: *lib.get(b"retro_get_region")?, + retro_get_memory_data: *lib.get(b"retro_get_memory_data")?, + retro_get_memory_size: *lib.get(b"retro_get_memory_size")?, + }; + Ok(LibretroApi { lib, core_api }) } } /// set_environment() must be called before init(). diff --git a/src/retro/wrapper.rs b/src/retro/wrapper.rs index 5afec64..3e50c5e 100644 --- a/src/retro/wrapper.rs +++ b/src/retro/wrapper.rs @@ -135,7 +135,7 @@ impl StaticCallbacks { None => 0, } } - pub(crate) fn set_environment(cb: &'static mut dyn Handler) { + pub(crate) fn register(cb: &'static mut dyn Handler) { unsafe { CB_SINGLETON.handler.replace(cb); } @@ -147,12 +147,12 @@ impl StaticCallbacks { } } -pub struct LibretroWrapper<'a> { - api: LibretroApi<'a>, +pub struct LibretroWrapper { + api: LibretroApi, } -impl<'a> From> for LibretroWrapper<'a> { - fn from(api: LibretroApi<'a>) -> Self { +impl From for LibretroWrapper { + fn from(api: LibretroApi) -> Self { api.set_environment(StaticCallbacks::environment_cb); api.set_video_refresh(StaticCallbacks::video_refresh_cb); api.set_audio_sample(StaticCallbacks::audio_sample_cb); @@ -163,13 +163,13 @@ impl<'a> From> for LibretroWrapper<'a> { } } -impl<'a> AsRef> for LibretroWrapper<'a> { - fn as_ref(&self) -> &LibretroApi<'a> { +impl AsRef for LibretroWrapper { + fn as_ref(&self) -> &LibretroApi { &self.api } } -impl<'a> Drop for LibretroWrapper<'a> { +impl Drop for LibretroWrapper { fn drop(&mut self) { StaticCallbacks::reset(); } @@ -177,9 +177,7 @@ impl<'a> Drop for LibretroWrapper<'a> { // a note on lifetimes: we explicitly lie about them here because as long as they live as long as // the library wrapper itself we're good (we wipe our 'static references on drop() too) -impl<'a> LibretroWrapper<'a> { - pub fn register_handler(&self, handler: &'a mut dyn Handler) { - let ptr: *mut dyn Handler = handler; - StaticCallbacks::set_environment(unsafe { ptr.as_mut() }.unwrap()); - } +pub fn register_handler(handler: &mut dyn Handler) { + let ptr: *mut dyn Handler = handler; + StaticCallbacks::register(unsafe { ptr.as_mut() }.unwrap()); }