working sdl2 example, albeit messy
This commit is contained in:
parent
d3f83c48c1
commit
e183627018
|
@ -6,8 +6,12 @@ use rustro::retro::ffi::GameGeometry;
|
|||
use rustro::retro::ffi::SystemAvInfo;
|
||||
|
||||
use std::ffi::CStr;
|
||||
use std::io::Write;
|
||||
use std::ops::Deref;
|
||||
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;
|
||||
|
||||
|
@ -16,7 +20,6 @@ use sdl2::rect::Rect;
|
|||
use sdl2::event::Event;
|
||||
use sdl2::keyboard::Keycode;
|
||||
use sdl2::video::WindowContext;
|
||||
use std::io::Write;
|
||||
|
||||
struct MyEmulator {
|
||||
retro: retro::wrapper::LibretroWrapper,
|
||||
|
@ -26,7 +29,7 @@ struct 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 raw_retro = retro::loading::LibretroApi::from_library(lib).unwrap();
|
||||
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 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);
|
||||
result.retro.as_ref().init();
|
||||
result
|
||||
retro::wrapper::register_handler(pin_emu.as_mut());
|
||||
pin_emu.retro.as_ref().init();
|
||||
pin_emu
|
||||
}
|
||||
|
||||
pub fn run(&self) {
|
||||
|
@ -58,20 +62,9 @@ impl MyEmulator {
|
|||
|
||||
impl retro::convert::Handler for MyEmulator {
|
||||
fn video_refresh(&mut self, data: &[u8], width: u32, height: u32, pitch: u32) {
|
||||
println!("self ref: {:?}", self as *mut Self);
|
||||
//println!("video_refresh {}x{}", width, height);
|
||||
//let rect = Rect::new(0, 0, width, height);
|
||||
let rect = Rect::new(0, 0, width, height);
|
||||
if let Some(tex) = self.texture.as_mut() {
|
||||
tex.with_lock(None, |tex_buffer: &mut [u8], tex_pitch: usize| {
|
||||
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();
|
||||
tex.update(rect, 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<()> {
|
||||
let opt: Opt = Opt::from_args();
|
||||
let mut emu = MyEmulator::with_core(&opt.core);
|
||||
|
@ -102,11 +103,12 @@ pub fn main() -> failure::Fallible<()> {
|
|||
let texture_creator = canvas.texture_creator();
|
||||
emu.texture = texture_creator.create_texture_streaming(emu.pixel_format, width, height).ok();
|
||||
|
||||
let mut x: u8 = 1;
|
||||
println!("emu ref: {:?}", &emu as *const MyEmulator);
|
||||
let target_frame_time = Duration::from_secs_f64(1.0 / emu.av_info.timing.fps);
|
||||
|
||||
let mut event_pump = sdl_context.event_pump().map_err(failure::err_msg)?;
|
||||
'running: loop {
|
||||
let frame_begin = Instant::now();
|
||||
|
||||
for event in event_pump.poll_iter() {
|
||||
match event {
|
||||
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...
|
||||
emu.run();
|
||||
canvas.clear();
|
||||
canvas.copy(emu.texture.as_ref().unwrap(), None, Some(Rect::new(0, 0, width, height))).map_err(failure::err_msg)?;
|
||||
canvas.present();
|
||||
::std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 60));
|
||||
|
||||
target_frame_time.checked_sub(frame_begin.elapsed()).map(sleep);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -189,7 +189,7 @@ pub enum EnvCmd {
|
|||
// 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 audio_sample(&mut self, left: i16, right: i16) {}
|
||||
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 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::path::Path;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use failure::Fallible;
|
||||
use libloading;
|
||||
|
||||
use super::ffi::*;
|
||||
use super::convert::*;
|
||||
|
||||
pub struct LibretroApi {
|
||||
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::os::raw::{c_uint, c_char};
|
||||
use std::ops::DerefMut;
|
||||
use std::path::Path;
|
||||
use std::pin::Pin;
|
||||
|
||||
use libretro_sys::{Message, PixelFormat, SystemAvInfo, GameGeometry};
|
||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||
use num_enum::TryFromPrimitive;
|
||||
|
||||
use super::convert::*;
|
||||
use super::loading::*;
|
||||
|
@ -18,7 +20,7 @@ static mut CB_SINGLETON: StaticCallbacks = StaticCallbacks {
|
|||
|
||||
#[derive(Default)]
|
||||
struct StaticCallbacks {
|
||||
handler: Option<&'static mut dyn Handler>,
|
||||
handler: Option<Pin<&'static mut dyn Handler>>,
|
||||
}
|
||||
|
||||
unsafe impl Sync for StaticCallbacks {}
|
||||
|
@ -135,11 +137,6 @@ impl StaticCallbacks {
|
|||
None => 0,
|
||||
}
|
||||
}
|
||||
pub(crate) fn register(cb: &'static mut dyn Handler) {
|
||||
unsafe {
|
||||
CB_SINGLETON.handler.replace(cb);
|
||||
}
|
||||
}
|
||||
pub(crate) fn reset() {
|
||||
unsafe {
|
||||
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
|
||||
// the library wrapper itself we're good (we wipe our 'static references on drop() too)
|
||||
pub fn register_handler(handler: &mut dyn Handler) {
|
||||
let ptr: *mut dyn Handler = handler;
|
||||
StaticCallbacks::register(unsafe { ptr.as_mut() }.unwrap());
|
||||
pub fn register_handler(handler: Pin<&'_ mut (dyn Handler + '_)>) {
|
||||
unsafe {
|
||||
let mut ptr = handler.get_unchecked_mut() as *mut dyn Handler;
|
||||
CB_SINGLETON.handler.replace(Pin::new_unchecked(ptr.as_mut().unwrap()));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue