working sdl2 example, albeit messy
This commit is contained in:
parent
d3f83c48c1
commit
e183627018
4 changed files with 38 additions and 46 deletions
|
@ -6,8 +6,12 @@ use rustro::retro::ffi::GameGeometry;
|
||||||
use rustro::retro::ffi::SystemAvInfo;
|
use rustro::retro::ffi::SystemAvInfo;
|
||||||
|
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::ops::Deref;
|
||||||
use std::path::{PathBuf, Path};
|
use std::path::{PathBuf, Path};
|
||||||
use std::time::Duration;
|
use std::pin::Pin;
|
||||||
|
use std::thread::sleep;
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
|
@ -16,7 +20,6 @@ use sdl2::rect::Rect;
|
||||||
use sdl2::event::Event;
|
use sdl2::event::Event;
|
||||||
use sdl2::keyboard::Keycode;
|
use sdl2::keyboard::Keycode;
|
||||||
use sdl2::video::WindowContext;
|
use sdl2::video::WindowContext;
|
||||||
use std::io::Write;
|
|
||||||
|
|
||||||
struct MyEmulator {
|
struct MyEmulator {
|
||||||
retro: retro::wrapper::LibretroWrapper,
|
retro: retro::wrapper::LibretroWrapper,
|
||||||
|
@ -26,7 +29,7 @@ struct MyEmulator {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MyEmulator {
|
impl MyEmulator {
|
||||||
pub fn with_core(core: impl AsRef<Path>) -> Self {
|
pub fn with_core(core: impl AsRef<Path>) -> Pin<Box<Self>> {
|
||||||
let lib = libloading::Library::new(core.as_ref()).unwrap();
|
let lib = libloading::Library::new(core.as_ref()).unwrap();
|
||||||
let raw_retro = retro::loading::LibretroApi::from_library(lib).unwrap();
|
let raw_retro = retro::loading::LibretroApi::from_library(lib).unwrap();
|
||||||
let retro = retro::wrapper::LibretroWrapper::from(raw_retro);
|
let retro = retro::wrapper::LibretroWrapper::from(raw_retro);
|
||||||
|
@ -36,11 +39,12 @@ impl MyEmulator {
|
||||||
let av_info = retro.as_ref().get_system_av_info();
|
let av_info = retro.as_ref().get_system_av_info();
|
||||||
|
|
||||||
let pixel_format = sdl2::pixels::PixelFormatEnum::ABGR1555;
|
let pixel_format = sdl2::pixels::PixelFormatEnum::ABGR1555;
|
||||||
let mut result = MyEmulator { retro, av_info, pixel_format, texture: None };
|
let mut emu = MyEmulator { retro, av_info, pixel_format, texture: None };
|
||||||
|
let mut pin_emu = Box::pin(emu);
|
||||||
|
|
||||||
retro::wrapper::register_handler(&mut result);
|
retro::wrapper::register_handler(pin_emu.as_mut());
|
||||||
result.retro.as_ref().init();
|
pin_emu.retro.as_ref().init();
|
||||||
result
|
pin_emu
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&self) {
|
pub fn run(&self) {
|
||||||
|
@ -58,20 +62,9 @@ impl MyEmulator {
|
||||||
|
|
||||||
impl retro::convert::Handler for MyEmulator {
|
impl retro::convert::Handler for MyEmulator {
|
||||||
fn video_refresh(&mut self, data: &[u8], width: u32, height: u32, pitch: u32) {
|
fn video_refresh(&mut self, data: &[u8], width: u32, height: u32, pitch: u32) {
|
||||||
println!("self ref: {:?}", self as *mut Self);
|
let rect = Rect::new(0, 0, width, height);
|
||||||
//println!("video_refresh {}x{}", width, height);
|
|
||||||
//let rect = Rect::new(0, 0, width, height);
|
|
||||||
if let Some(tex) = self.texture.as_mut() {
|
if let Some(tex) = self.texture.as_mut() {
|
||||||
tex.with_lock(None, |tex_buffer: &mut [u8], tex_pitch: usize| {
|
tex.update(rect, data, pitch as usize).unwrap();
|
||||||
let src_pitch = pitch as usize;
|
|
||||||
let src_width = width as usize;
|
|
||||||
for y in 0..(height as usize) {
|
|
||||||
let src_slice = (y * src_pitch)..((y * src_pitch) + src_width);
|
|
||||||
let tex_slice = (y * tex_pitch)..((y * tex_pitch) + src_width);
|
|
||||||
tex_buffer[tex_slice].copy_from_slice(&data[src_slice]);
|
|
||||||
}
|
|
||||||
}).unwrap();
|
|
||||||
//tex.update(None, data, pitch as usize).unwrap();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,6 +78,14 @@ impl retro::convert::Handler for MyEmulator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Drop for MyEmulator {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if let Some(x) = self.texture.take() {
|
||||||
|
unsafe { x.destroy(); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn main() -> failure::Fallible<()> {
|
pub fn main() -> failure::Fallible<()> {
|
||||||
let opt: Opt = Opt::from_args();
|
let opt: Opt = Opt::from_args();
|
||||||
let mut emu = MyEmulator::with_core(&opt.core);
|
let mut emu = MyEmulator::with_core(&opt.core);
|
||||||
|
@ -102,11 +103,12 @@ pub fn main() -> failure::Fallible<()> {
|
||||||
let texture_creator = canvas.texture_creator();
|
let texture_creator = canvas.texture_creator();
|
||||||
emu.texture = texture_creator.create_texture_streaming(emu.pixel_format, width, height).ok();
|
emu.texture = texture_creator.create_texture_streaming(emu.pixel_format, width, height).ok();
|
||||||
|
|
||||||
let mut x: u8 = 1;
|
let target_frame_time = Duration::from_secs_f64(1.0 / emu.av_info.timing.fps);
|
||||||
println!("emu ref: {:?}", &emu as *const MyEmulator);
|
|
||||||
|
|
||||||
let mut event_pump = sdl_context.event_pump().map_err(failure::err_msg)?;
|
let mut event_pump = sdl_context.event_pump().map_err(failure::err_msg)?;
|
||||||
'running: loop {
|
'running: loop {
|
||||||
|
let frame_begin = Instant::now();
|
||||||
|
|
||||||
for event in event_pump.poll_iter() {
|
for event in event_pump.poll_iter() {
|
||||||
match event {
|
match event {
|
||||||
Event::Quit {..}
|
Event::Quit {..}
|
||||||
|
@ -116,22 +118,14 @@ pub fn main() -> failure::Fallible<()> {
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(tex) = emu.texture.as_mut() {
|
|
||||||
tex.with_lock(None, |tex_buffer: &mut [u8], tex_pitch: usize| {
|
|
||||||
for i in 0..tex_buffer.len() {
|
|
||||||
tex_buffer[i] = x;
|
|
||||||
x = (x + 1) % 255;
|
|
||||||
}
|
|
||||||
}).unwrap();
|
|
||||||
//tex.update(None, data, pitch as usize).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
// The rest of the game loop goes here...
|
// The rest of the game loop goes here...
|
||||||
emu.run();
|
emu.run();
|
||||||
canvas.clear();
|
canvas.clear();
|
||||||
canvas.copy(emu.texture.as_ref().unwrap(), None, Some(Rect::new(0, 0, width, height))).map_err(failure::err_msg)?;
|
canvas.copy(emu.texture.as_ref().unwrap(), None, Some(Rect::new(0, 0, width, height))).map_err(failure::err_msg)?;
|
||||||
canvas.present();
|
canvas.present();
|
||||||
::std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 60));
|
|
||||||
|
target_frame_time.checked_sub(frame_begin.elapsed()).map(sleep);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -189,7 +189,7 @@ pub enum EnvCmd {
|
||||||
// SetSerializationQuirks = ENVIRONMENT_SET_SERIALIZATION_QUIRKS,
|
// SetSerializationQuirks = ENVIRONMENT_SET_SERIALIZATION_QUIRKS,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Handler: 'static {
|
pub trait Handler: Unpin + 'static {
|
||||||
fn video_refresh(&mut self, data: &[u8], width: c_uint, height: c_uint, pitch: c_uint) {}
|
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(&mut self, left: i16, right: i16) {}
|
||||||
fn audio_sample_batch(&mut self, stereo_pcm: &[i16]) -> usize { stereo_pcm.len() }
|
fn audio_sample_batch(&mut self, stereo_pcm: &[i16]) -> usize { stereo_pcm.len() }
|
||||||
|
@ -235,3 +235,5 @@ pub trait Handler: 'static {
|
||||||
fn get_language(&mut self) -> Option<Language> { None }
|
fn get_language(&mut self) -> Option<Language> { None }
|
||||||
// fn set_serialization_quirks(&mut self, quirks: &mut u64) -> bool { false }
|
// fn set_serialization_quirks(&mut self, quirks: &mut u64) -> bool { false }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// impl<T: Handler> Unpin for T {}
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
use std::convert::TryInto;
|
|
||||||
use std::os::raw::{c_char, c_void};
|
use std::os::raw::{c_char, c_void};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
use failure::Fallible;
|
use failure::Fallible;
|
||||||
use libloading;
|
use libloading;
|
||||||
|
|
||||||
use super::ffi::*;
|
use super::ffi::*;
|
||||||
use super::convert::*;
|
|
||||||
|
|
||||||
pub struct LibretroApi {
|
pub struct LibretroApi {
|
||||||
lib: libloading::Library, // for tying our lifetime to its own
|
lib: libloading::Library, // for tying our lifetime to its own
|
||||||
|
|
|
@ -4,10 +4,12 @@ use core::slice::from_raw_parts;
|
||||||
|
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::os::raw::{c_uint, c_char};
|
use std::os::raw::{c_uint, c_char};
|
||||||
|
use std::ops::DerefMut;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use std::pin::Pin;
|
||||||
|
|
||||||
use libretro_sys::{Message, PixelFormat, SystemAvInfo, GameGeometry};
|
use libretro_sys::{Message, PixelFormat, SystemAvInfo, GameGeometry};
|
||||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
use num_enum::TryFromPrimitive;
|
||||||
|
|
||||||
use super::convert::*;
|
use super::convert::*;
|
||||||
use super::loading::*;
|
use super::loading::*;
|
||||||
|
@ -18,7 +20,7 @@ static mut CB_SINGLETON: StaticCallbacks = StaticCallbacks {
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct StaticCallbacks {
|
struct StaticCallbacks {
|
||||||
handler: Option<&'static mut dyn Handler>,
|
handler: Option<Pin<&'static mut dyn Handler>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Sync for StaticCallbacks {}
|
unsafe impl Sync for StaticCallbacks {}
|
||||||
|
@ -135,11 +137,6 @@ impl StaticCallbacks {
|
||||||
None => 0,
|
None => 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub(crate) fn register(cb: &'static mut dyn Handler) {
|
|
||||||
unsafe {
|
|
||||||
CB_SINGLETON.handler.replace(cb);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub(crate) fn reset() {
|
pub(crate) fn reset() {
|
||||||
unsafe {
|
unsafe {
|
||||||
CB_SINGLETON.handler.take();
|
CB_SINGLETON.handler.take();
|
||||||
|
@ -177,7 +174,9 @@ impl Drop for LibretroWrapper {
|
||||||
|
|
||||||
// a note on lifetimes: we explicitly lie about them here because as long as they live as long as
|
// 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)
|
// the library wrapper itself we're good (we wipe our 'static references on drop() too)
|
||||||
pub fn register_handler(handler: &mut dyn Handler) {
|
pub fn register_handler(handler: Pin<&'_ mut (dyn Handler + '_)>) {
|
||||||
let ptr: *mut dyn Handler = handler;
|
unsafe {
|
||||||
StaticCallbacks::register(unsafe { ptr.as_mut() }.unwrap());
|
let mut ptr = handler.get_unchecked_mut() as *mut dyn Handler;
|
||||||
|
CB_SINGLETON.handler.replace(Pin::new_unchecked(ptr.as_mut().unwrap()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue