WIP nonfunctional SDL 2 thing
This commit is contained in:
parent
6668c9c11f
commit
50b1649315
|
@ -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"]
|
||||
|
|
|
@ -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<Path>) -> 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<Path>) {
|
||||
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(())
|
||||
}
|
||||
|
|
|
@ -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() }
|
||||
|
|
|
@ -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<Self> {
|
||||
impl LibretroApi {
|
||||
pub fn from_library(lib: libloading::Library) -> Fallible<Self> {
|
||||
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().
|
||||
|
|
|
@ -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<LibretroApi<'a>> for LibretroWrapper<'a> {
|
||||
fn from(api: LibretroApi<'a>) -> Self {
|
||||
impl From<LibretroApi> 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<LibretroApi<'a>> for LibretroWrapper<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> AsRef<LibretroApi<'a>> for LibretroWrapper<'a> {
|
||||
fn as_ref(&self) -> &LibretroApi<'a> {
|
||||
impl AsRef<LibretroApi> 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());
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue