Wait to call retro_set_environment until we have the Handler registered

This commit is contained in:
lif 2019-11-17 20:52:35 -08:00
parent e1ab7d69aa
commit 0e3ba53eec
2 changed files with 97 additions and 24 deletions

View File

@ -5,6 +5,7 @@ extern crate sdl2;
use rustro::retro;
use rustro::retro::ffi::{GameGeometry, SystemInfo, SystemAvInfo};
use rustro::retro::constants::{Input, DeviceIndex, JoypadButton, AnalogAxis, DeviceType};
use rustro::retro::wrapper::LibretroWrapper;
use std::ffi::CStr;
use std::io::Read;
@ -199,6 +200,10 @@ impl Drop for MyEmulator {
}
impl retro::wrapper::Handler for MyEmulator {
fn libretro_core(&mut self) -> &mut LibretroWrapper {
&mut self.retro
}
fn video_refresh(&mut self, data: &[u8], width: u32, height: u32, pitch: u32) {
let rect = Rect::new(0, 0, width, height);

View File

@ -15,7 +15,6 @@ use num_enum::TryFromPrimitive;
use super::constants::*;
use super::ffi::*;
use super::loading::*;
use failure::_core::convert::Infallible;
static mut CB_SINGLETON: StaticCallbacks = StaticCallbacks {
handler: None,
@ -32,7 +31,8 @@ extern "C" {
#[allow(unused)]
pub trait Handler: Unpin + 'static {
fn _delegate_handler(&self) -> Option<&mut dyn Handler> { None }
fn libretro_core(&mut self) -> &mut LibretroWrapper;
fn delegate_handler(&self) -> Option<&mut dyn Handler> { None }
// -- main callbacks --
fn video_refresh(&mut self, data: &[u8], width: c_uint, height: c_uint, pitch: c_uint) {}
@ -139,8 +139,23 @@ impl StaticCallbacks {
EnvCmd::GetSystemDirectory => Self::path_into_void(data, handler.get_system_directory()?)?,
EnvCmd::SetPixelFormat => handler.set_pixel_format(PixelFormat::from_uint(*Self::from_void(data)?)?),
// TODO EnvCmd::SetInputDescriptors => {},
// TODO EnvCmd::SetKeyboardCallback => {},
// TODO EnvCmd::SetDiskControlInterface => {},
EnvCmd::SetKeyboardCallback => {
let kc: &mut KeyboardCallback = Self::from_void(data)?;
handler.libretro_core().keyboard_event_cb.replace(kc.callback);
true
},
EnvCmd::SetDiskControlInterface => {
let dcc: &mut DiskControlCallback = Self::from_void(data)?;
let core = handler.libretro_core();
core.disk_set_eject_state_cb.replace(dcc.set_eject_state);
core.disk_get_eject_state_cb.replace(dcc.get_eject_state);
core.disk_set_image_index_cb.replace(dcc.set_image_index);
core.disk_get_image_index_cb.replace(dcc.get_image_index);
core.disk_get_num_images_cb.replace(dcc.get_num_images);
core.disk_replace_image_index_cb.replace(dcc.replace_image_index);
core.disk_add_image_index_cb.replace(dcc.add_image_index);
true
},
// TODO EnvCmd::SetHwRender => {},
EnvCmd::GetVariable => {
let mut var = Self::from_void::<Variable>(data)?;
@ -158,8 +173,17 @@ impl StaticCallbacks {
EnvCmd::GetVariableUpdate => Self::clone_into_void(data, &handler.get_variable_update()?)?,
EnvCmd::SetSupportNoGame => handler.set_support_no_game(*Self::from_void(data)?),
EnvCmd::GetLibretroPath => Self::path_into_void(data, handler.get_libretro_path()?)?,
// TODO EnvCmd::SetFrameTimeCallback => {},
// TODO EnvCmd::SetAudioCallback => {},
EnvCmd::SetFrameTimeCallback => {
let ftc: &mut FrameTimeCallback = Self::from_void(data)?;
handler.libretro_core().frame_time_cb.replace(ftc.callback);
true
},
EnvCmd::SetAudioCallback => {
let ac: &mut AudioCallback = Self::from_void(data)?;
handler.libretro_core().audio_ready_cb.replace(ac.callback);
handler.libretro_core().audio_set_state_cb.replace(ac.set_state);
true
},
EnvCmd::GetRumbleInterface => {
let ri = RumbleInterface { set_rumble_state: StaticCallbacks::set_rumble_state_cb };
Self::clone_into_void(data, &ri)?
@ -183,7 +207,7 @@ impl StaticCallbacks {
for t in types {
let orig_name = match DeviceType::try_from(t.id) {
Ok(x) => format!("{:?}", x),
Err(_) => format!("Device ID {}", t.id),
Err(_) => format!("[Device ID 0x{:x?}]", t.id),
};
let new_name = unsafe { CStr::from_ptr(t.desc) }.to_string_lossy();
eprintln!("SetControllerInfo: {} is {}", orig_name, new_name);
@ -275,41 +299,74 @@ pub struct LibretroWrapper {
frame_time_cb: Option<FrameTimeCallbackFn>,
audio_ready_cb: Option<AudioCallbackFn>,
audio_set_state_cb: Option<AudioSetStateCallbackFn>,
disk_get_eject_state_cb: Option<GetEjectStateFn>,
disk_set_eject_state_cb: Option<SetEjectStateFn>,
disk_get_image_index_cb: Option<GetImageIndexFn>,
disk_set_image_index_cb: Option<SetImageIndexFn>,
disk_get_num_images_cb: Option<GetNumImagesFn>,
disk_replace_image_index_cb: Option<ReplaceImageIndexFn>,
disk_add_image_index_cb: Option<AddImageIndexFn>,
}
impl From<LibretroApi> for LibretroWrapper {
fn from(api: LibretroApi) -> Self {
// TODO: figure out a way to register these *after* we set CB_SINGLETON?
api.set_environment(StaticCallbacks::environment_cb);
api.set_video_refresh(StaticCallbacks::video_refresh_cb);
api.set_audio_sample(StaticCallbacks::audio_sample_cb);
api.set_audio_sample_batch(StaticCallbacks::audio_sample_batch_cb);
api.set_input_poll(StaticCallbacks::input_poll_cb);
api.set_input_state(StaticCallbacks::input_state_cb);
unsafe { c_ext_set_log_print_cb(StaticCallbacks::log_print_cb); }
LibretroWrapper {
api,
keyboard_event_cb: None,
frame_time_cb: None,
audio_ready_cb: None,
audio_set_state_cb: None
audio_set_state_cb: None,
disk_set_eject_state_cb: None,
disk_get_eject_state_cb: None,
disk_get_image_index_cb: None,
disk_set_image_index_cb: None,
disk_get_num_images_cb: None,
disk_replace_image_index_cb: None,
disk_add_image_index_cb: None
}
}
}
impl LibretroWrapper {
// TODO: enum for the RETROK_* constants instead of c_uint for keycode...
pub fn keyboard_event(&self, down: bool, keycode: c_uint, character: u32, key_modifiers: u16) {
self.keyboard_event_cb.map(|f| unsafe { f(down, keycode, character, key_modifiers) });
pub fn keyboard_event(&self, down: bool, keycode: c_uint, character: u32, key_modifiers: u16) -> Option<()> {
self.keyboard_event_cb.map(|f| unsafe { f(down, keycode, character, key_modifiers) })
}
pub fn frame_time(&self, time: Duration) {
self.frame_time_cb.map(|f| unsafe { f(time.as_micros() as Usec) });
pub fn frame_time(&self, time: Duration) -> Option<()> {
self.frame_time_cb.map(|f| unsafe { f(time.as_micros() as Usec) })
}
pub fn audio_ready(&self) {
self.audio_ready_cb.map(|f| unsafe { f() });
pub fn audio_ready(&self) -> Option<()> {
self.audio_ready_cb.map(|f| unsafe { f() })
}
pub fn audio_set_state(&self, enabled: bool) {
self.audio_set_state_cb.map(|f| unsafe { f(enabled) });
pub fn audio_set_state(&self, enabled: bool) -> Option<()> {
self.audio_set_state_cb.map(|f| unsafe { f(enabled) })
}
pub fn disk_get_eject_state(&self) -> Option<bool> {
self.disk_get_eject_state_cb.map(|f| unsafe { f() })
}
pub fn disk_set_eject_state(&self) -> Option<bool> {
self.disk_set_eject_state_cb.map(|f| unsafe { f(ejected) })
}
pub fn disk_get_image_index(&self) -> Option<c_uint> {
self.disk_get_image_index_cb.map(|f| unsafe { f() })
}
pub fn disk_set_image_index(&self, index: c_uint) -> Option<bool> {
self.disk_set_image_index_cb.map(|f| unsafe { f(index) })
}
pub fn disk_get_num_images(&self) -> Option<c_uint> {
self.disk_get_num_images_cb.map(|f| unsafe { f() })
}
pub fn disk_replace_image_index(&self, index: c_uint, info: Option<GameInfo>) -> Option<bool> {
self.disk_replace_image_index_cb.map(|f| unsafe {
let info_ptr = match info {
None => ::core::ptr::null(),
Some(x) => &x,
};
f(index, info_ptr)
})
}
pub fn disk_add_image_index(&self) -> Option<bool> {
self.disk_add_image_index_cb.map(|f| unsafe { f() })
}
}
@ -326,7 +383,18 @@ pub fn set_handler(handler: Pin<&'_ mut (dyn Handler + '_)>) {
unsafe {
let ptr = handler.get_unchecked_mut() as *mut dyn Handler;
CB_SINGLETON.handler.replace(Pin::new_unchecked(ptr.as_mut().unwrap()));
c_ext_set_log_print_cb(StaticCallbacks::log_print_cb);
}
// some cores start calling the environment cb as soon as we call retro_set_environment,
// not waiting for a call to retro_init - so we'd better have CB_SINGLETON set first
let api = unsafe { CB_SINGLETON.handler.as_mut() }.unwrap().libretro_core();
api.set_environment(StaticCallbacks::environment_cb);
api.set_video_refresh(StaticCallbacks::video_refresh_cb);
api.set_audio_sample(StaticCallbacks::audio_sample_cb);
api.set_audio_sample_batch(StaticCallbacks::audio_sample_batch_cb);
api.set_input_poll(StaticCallbacks::input_poll_cb);
api.set_input_state(StaticCallbacks::input_state_cb);
}
pub fn unset_handler() {