get mednafen_saturn_libretro.so working
This commit is contained in:
parent
bdb746c861
commit
a591037d19
3 changed files with 98 additions and 47 deletions
|
@ -19,11 +19,13 @@ use sdl2::controller::{GameController, Button, Axis};
|
||||||
use sdl2::event::Event;
|
use sdl2::event::Event;
|
||||||
use sdl2::keyboard::Keycode;
|
use sdl2::keyboard::Keycode;
|
||||||
use sdl2::rect::Rect;
|
use sdl2::rect::Rect;
|
||||||
use sdl2::render::{TextureCreator, WindowCanvas};
|
use sdl2::render::WindowCanvas;
|
||||||
use sdl2::video::WindowContext;
|
|
||||||
|
|
||||||
struct MyEmulator {
|
struct MyEmulator {
|
||||||
retro: retro::wrapper::LibretroWrapper,
|
retro: retro::wrapper::LibretroWrapper,
|
||||||
|
core_path: PathBuf,
|
||||||
|
sys_path: Option<PathBuf>,
|
||||||
|
|
||||||
sys_info: SystemInfo,
|
sys_info: SystemInfo,
|
||||||
av_info: SystemAvInfo,
|
av_info: SystemAvInfo,
|
||||||
|
|
||||||
|
@ -32,7 +34,6 @@ struct MyEmulator {
|
||||||
// video bits
|
// video bits
|
||||||
canvas: WindowCanvas,
|
canvas: WindowCanvas,
|
||||||
pixel_format: sdl2::pixels::PixelFormatEnum,
|
pixel_format: sdl2::pixels::PixelFormatEnum,
|
||||||
texture_creator: TextureCreator<WindowContext>,
|
|
||||||
|
|
||||||
// audio bits
|
// audio bits
|
||||||
audio_buffer: Vec<i16>,
|
audio_buffer: Vec<i16>,
|
||||||
|
@ -46,8 +47,9 @@ struct MyEmulator {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MyEmulator {
|
impl MyEmulator {
|
||||||
pub fn with_core(core: impl AsRef<Path>) -> Pin<Box<Self>> {
|
pub fn new(core_path: impl AsRef<Path>, sys_path: &Option<impl AsRef<Path>>) -> Pin<Box<Self>> {
|
||||||
let lib = libloading::Library::new(core.as_ref()).unwrap();
|
let core_path = PathBuf::from(core_path.as_ref());
|
||||||
|
let lib = libloading::Library::new(&core_path).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);
|
||||||
|
|
||||||
|
@ -72,18 +74,15 @@ impl MyEmulator {
|
||||||
|
|
||||||
let sdl_context = sdl2::init().unwrap();
|
let sdl_context = sdl2::init().unwrap();
|
||||||
|
|
||||||
let (width, height) = (av_info.geometry.base_width, av_info.geometry.base_height);
|
|
||||||
|
|
||||||
let window = sdl_context
|
let window = sdl_context
|
||||||
.video()
|
.video()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.window(title.as_str(), width, height)
|
.window(title.as_str(), av_info.geometry.base_width, av_info.geometry.base_height)
|
||||||
.opengl()
|
.opengl()
|
||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let canvas = window.into_canvas().build().unwrap();
|
let canvas = window.into_canvas().build().unwrap();
|
||||||
let texture_creator = canvas.texture_creator();
|
|
||||||
|
|
||||||
let (audio_sender, audio_receiver) = crossbeam_channel::bounded(2);
|
let (audio_sender, audio_receiver) = crossbeam_channel::bounded(2);
|
||||||
|
|
||||||
|
@ -115,18 +114,19 @@ impl MyEmulator {
|
||||||
|
|
||||||
let emu = MyEmulator {
|
let emu = MyEmulator {
|
||||||
retro,
|
retro,
|
||||||
|
core_path,
|
||||||
av_info,
|
av_info,
|
||||||
sys_info,
|
sys_info,
|
||||||
sdl_context,
|
sdl_context,
|
||||||
canvas,
|
canvas,
|
||||||
pixel_format,
|
pixel_format,
|
||||||
texture_creator,
|
|
||||||
audio_buffer: Default::default(),
|
audio_buffer: Default::default(),
|
||||||
audio_spec: audio_spec.unwrap(),
|
audio_spec: audio_spec.unwrap(),
|
||||||
audio_device,
|
audio_device,
|
||||||
audio_sender,
|
audio_sender,
|
||||||
gamepad_subsys,
|
gamepad_subsys,
|
||||||
gamepads,
|
gamepads,
|
||||||
|
sys_path: sys_path.as_ref().map(|p| p.as_ref().to_path_buf())
|
||||||
};
|
};
|
||||||
let mut pin_emu = Box::pin(emu);
|
let mut pin_emu = Box::pin(emu);
|
||||||
retro::wrapper::set_handler(pin_emu.as_mut());
|
retro::wrapper::set_handler(pin_emu.as_mut());
|
||||||
|
@ -198,7 +198,8 @@ impl retro::wrapper::Handler for MyEmulator {
|
||||||
let rect = Rect::new(0, 0, width, height);
|
let rect = Rect::new(0, 0, width, height);
|
||||||
|
|
||||||
if let Ok(mut tex) =
|
if let Ok(mut tex) =
|
||||||
self.texture_creator
|
self.canvas
|
||||||
|
.texture_creator()
|
||||||
.create_texture_static(self.pixel_format, width, height)
|
.create_texture_static(self.pixel_format, width, height)
|
||||||
{
|
{
|
||||||
if tex.update(rect, data, pitch as usize).is_ok() {
|
if tex.update(rect, data, pitch as usize).is_ok() {
|
||||||
|
@ -248,6 +249,10 @@ impl retro::wrapper::Handler for MyEmulator {
|
||||||
|
|
||||||
fn get_can_dupe(&mut self) -> Option<bool> { Some(true) }
|
fn get_can_dupe(&mut self) -> Option<bool> { Some(true) }
|
||||||
|
|
||||||
|
fn get_system_directory(&mut self) -> Option<PathBuf> {
|
||||||
|
self.sys_path.clone()
|
||||||
|
}
|
||||||
|
|
||||||
fn set_pixel_format(&mut self, pix_fmt: retro::ffi::PixelFormat) -> bool {
|
fn set_pixel_format(&mut self, pix_fmt: retro::ffi::PixelFormat) -> bool {
|
||||||
self.pixel_format = match pix_fmt {
|
self.pixel_format = match pix_fmt {
|
||||||
retro::ffi::PixelFormat::ARGB1555 => sdl2::pixels::PixelFormatEnum::RGB555,
|
retro::ffi::PixelFormat::ARGB1555 => sdl2::pixels::PixelFormatEnum::RGB555,
|
||||||
|
@ -257,18 +262,22 @@ impl retro::wrapper::Handler for MyEmulator {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_libretro_path(&mut self) -> Option<PathBuf> {
|
||||||
|
Some(self.core_path.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_save_directory(&mut self) -> Option<PathBuf> {
|
||||||
|
Some(std::env::temp_dir())
|
||||||
|
}
|
||||||
|
|
||||||
fn set_system_av_info(&mut self, av_info: SystemAvInfo) -> bool {
|
fn set_system_av_info(&mut self, av_info: SystemAvInfo) -> bool {
|
||||||
let (width, height) = (av_info.geometry.base_width, av_info.geometry.base_height);
|
self.set_geometry(av_info.geometry.clone());
|
||||||
let _ = self.canvas.window_mut().set_size(width, height);
|
|
||||||
let _ = self.canvas.set_logical_size(width, height);
|
|
||||||
self.av_info = av_info;
|
self.av_info = av_info;
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_geometry(&mut self, geom: GameGeometry) -> bool {
|
fn set_geometry(&mut self, geom: GameGeometry) -> bool {
|
||||||
let (width, height) = (geom.base_width, geom.base_height);
|
let _ = self.canvas.window_mut().set_size(geom.base_width, geom.base_height);
|
||||||
let _ = self.canvas.window_mut().set_size(width, height);
|
let _ = self.canvas.set_logical_size(geom.base_width, geom.base_height);
|
||||||
let _ = self.canvas.set_logical_size(width, height);
|
|
||||||
self.av_info.geometry = geom;
|
self.av_info.geometry = geom;
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -278,7 +287,6 @@ impl retro::wrapper::Handler for MyEmulator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct MySdlAudio {
|
struct MySdlAudio {
|
||||||
audio_spec: AudioSpec,
|
audio_spec: AudioSpec,
|
||||||
audio_receiver: crossbeam_channel::Receiver<Vec<i16>>,
|
audio_receiver: crossbeam_channel::Receiver<Vec<i16>>,
|
||||||
|
@ -298,7 +306,7 @@ impl AudioCallback for MySdlAudio {
|
||||||
|
|
||||||
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::new(&opt.core, &opt.system);
|
||||||
emu.load_game(&opt.rom);
|
emu.load_game(&opt.rom);
|
||||||
emu.run();
|
emu.run();
|
||||||
|
|
||||||
|
@ -310,9 +318,12 @@ struct Opt {
|
||||||
/// Core module to use.
|
/// Core module to use.
|
||||||
#[structopt(short, long, parse(from_os_str))]
|
#[structopt(short, long, parse(from_os_str))]
|
||||||
core: PathBuf,
|
core: PathBuf,
|
||||||
/// Rom to load using the core.
|
/// ROM to load using the core.
|
||||||
#[structopt(short, long, parse(from_os_str))]
|
#[structopt(short, long, parse(from_os_str))]
|
||||||
rom: PathBuf,
|
rom: PathBuf,
|
||||||
|
/// System directory, often containing BIOS files
|
||||||
|
#[structopt(short, long, parse(from_os_str))]
|
||||||
|
system: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn button_map(retro_button: &JoypadButton) -> Option<Button> {
|
fn button_map(retro_button: &JoypadButton) -> Option<Button> {
|
||||||
|
|
|
@ -138,15 +138,8 @@ pub enum EnvRotation {
|
||||||
CounterClockwise270 = 3,
|
CounterClockwise270 = 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct EnvVariable {
|
|
||||||
key: &'static str,
|
|
||||||
description: &'static str,
|
|
||||||
expected_values: Vec<&'static str>,
|
|
||||||
value: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: experimental calls
|
// TODO: experimental calls
|
||||||
#[derive(TryFromPrimitive, IntoPrimitive)]
|
#[derive(TryFromPrimitive, IntoPrimitive, Debug)]
|
||||||
#[repr(u32)]
|
#[repr(u32)]
|
||||||
pub enum EnvCmd {
|
pub enum EnvCmd {
|
||||||
SetRotation = ENVIRONMENT_SET_ROTATION,
|
SetRotation = ENVIRONMENT_SET_ROTATION,
|
||||||
|
|
|
@ -13,23 +13,20 @@ use num_enum::TryFromPrimitive;
|
||||||
use super::constants::*;
|
use super::constants::*;
|
||||||
use super::ffi::*;
|
use super::ffi::*;
|
||||||
use super::loading::*;
|
use super::loading::*;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
static mut CB_SINGLETON: StaticCallbacks = StaticCallbacks {
|
static mut CB_SINGLETON: StaticCallbacks = StaticCallbacks {
|
||||||
handler: None,
|
handler: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// stable Rust doesn't have varargs, so we can't represent a callback with the signature of
|
||||||
|
// void (*retro_log_printf_t)(enum retro_log_level level, const char* fmt, ...)
|
||||||
|
// without a little help from an Actual-C wrapper.
|
||||||
pub type WrappedLogPrintFn = extern "C" fn(level: LogLevel, fmt: *const c_char);
|
pub type WrappedLogPrintFn = extern "C" fn(level: LogLevel, fmt: *const c_char);
|
||||||
extern "C" {
|
extern "C" {
|
||||||
fn c_ext_handle_get_log_interface(cb: *mut LogCallback) -> bool;
|
fn c_ext_handle_get_log_interface(cb: *mut LogCallback) -> bool;
|
||||||
fn c_ext_set_log_print_cb(cb: WrappedLogPrintFn);
|
fn c_ext_set_log_print_cb(cb: WrappedLogPrintFn);
|
||||||
}
|
}
|
||||||
extern "C" fn rust_retro_ffi_log_print(level: LogLevel, msg: *const c_char) {
|
|
||||||
unsafe {
|
|
||||||
if let Some(cb) = CB_SINGLETON.handler.as_mut() {
|
|
||||||
cb.log_print(level, CStr::from_ptr(msg).to_string_lossy().as_ref());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub trait Handler: Unpin + 'static {
|
pub trait Handler: Unpin + 'static {
|
||||||
|
@ -52,17 +49,12 @@ pub trait Handler: Unpin + 'static {
|
||||||
fn get_system_directory(&mut self) -> Option<PathBuf> { None }
|
fn get_system_directory(&mut self) -> Option<PathBuf> { None }
|
||||||
fn set_pixel_format(&mut self, format: PixelFormat) -> bool { false }
|
fn set_pixel_format(&mut self, format: PixelFormat) -> bool { false }
|
||||||
fn set_input_descriptors(&mut self, input_descriptors: &[InputDescriptor]) -> bool { false }
|
fn set_input_descriptors(&mut self, input_descriptors: &[InputDescriptor]) -> bool { false }
|
||||||
fn set_keyboard_callback(&mut self, cb: KeyboardCallback) -> bool { false }
|
|
||||||
fn set_disk_control_interface(&mut self, cb: DiskControlCallback) -> bool { false }
|
|
||||||
fn set_hw_render(&mut self, hw_render_callback: HwRenderCallback) -> bool { false }
|
fn set_hw_render(&mut self, hw_render_callback: HwRenderCallback) -> bool { false }
|
||||||
fn get_variable(&mut self) -> Option<EnvVariable> { None }
|
fn get_variable(&mut self) -> Option<EnvVariable> { None }
|
||||||
fn set_variables(&mut self, variables: &[EnvVariable]) -> bool { false }
|
fn set_variables(&mut self, variables: &[EnvVariable]) -> bool { false }
|
||||||
fn get_variable_update(&mut self) -> Option<bool> { None }
|
fn get_variable_update(&mut self) -> Option<bool> { None }
|
||||||
fn set_support_no_game(&mut self, supports_no_game: bool) -> bool { false }
|
fn set_support_no_game(&mut self, supports_no_game: bool) -> bool { false }
|
||||||
fn get_libretro_path(&mut self) -> Option<PathBuf> { None }
|
fn get_libretro_path(&mut self) -> Option<PathBuf> { None }
|
||||||
fn set_frame_time_callback(&mut self, cb: FrameTimeCallback) -> bool { false }
|
|
||||||
fn set_audio_callback(&mut self, audio_callback: AudioCallback) -> bool { false }
|
|
||||||
fn get_rumble_interface(&mut self) -> Option<RumbleInterface> { None }
|
|
||||||
fn get_input_device_capabilities(&mut self) -> Option<u64> { None }
|
fn get_input_device_capabilities(&mut self) -> Option<u64> { None }
|
||||||
fn get_sensor_interface(&mut self) -> Option<SensorInterface> { None }
|
fn get_sensor_interface(&mut self) -> Option<SensorInterface> { None }
|
||||||
fn get_camera_interface(&mut self) -> Option<CameraCallback> { None }
|
fn get_camera_interface(&mut self) -> Option<CameraCallback> { None }
|
||||||
|
@ -83,6 +75,7 @@ pub trait Handler: Unpin + 'static {
|
||||||
|
|
||||||
// -- environment-set callbacks (API extensions) --
|
// -- environment-set callbacks (API extensions) --
|
||||||
fn log_print(&mut self, level: LogLevel, msg: &str) {}
|
fn log_print(&mut self, level: LogLevel, msg: &str) {}
|
||||||
|
fn set_rumble_state(&mut self, port: c_uint, effect: RumbleEffect, strength: u16) -> bool { false }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -129,14 +122,24 @@ impl StaticCallbacks {
|
||||||
// TODO EnvCmd::SetKeyboardCallback => {},
|
// TODO EnvCmd::SetKeyboardCallback => {},
|
||||||
// TODO EnvCmd::SetDiskControlInterface => {},
|
// TODO EnvCmd::SetDiskControlInterface => {},
|
||||||
// TODO EnvCmd::SetHwRender => {},
|
// TODO EnvCmd::SetHwRender => {},
|
||||||
// TODO EnvCmd::GetVariable => {}, -- also change to mut parameter?
|
EnvCmd::GetVariable => {
|
||||||
|
// TODO: actually implement
|
||||||
|
let v = Self::from_void::<Variable>(data)?;
|
||||||
|
eprintln!(
|
||||||
|
"Unsupported env cmd: GetVariable ({})",
|
||||||
|
unsafe { CStr::from_ptr(v.key) }.to_string_lossy());
|
||||||
|
false
|
||||||
|
},
|
||||||
// TODO EnvCmd::SetVariables => {},
|
// TODO EnvCmd::SetVariables => {},
|
||||||
EnvCmd::GetVariableUpdate => Self::clone_into_void(data, &handler.get_variable_update()?)?,
|
EnvCmd::GetVariableUpdate => Self::clone_into_void(data, &handler.get_variable_update()?)?,
|
||||||
EnvCmd::SetSupportNoGame => handler.set_support_no_game(*Self::from_void(data)?),
|
EnvCmd::SetSupportNoGame => handler.set_support_no_game(*Self::from_void(data)?),
|
||||||
EnvCmd::GetLibretroPath => Self::path_into_void(data, handler.get_libretro_path()?)?,
|
EnvCmd::GetLibretroPath => Self::path_into_void(data, handler.get_libretro_path()?)?,
|
||||||
// TODO EnvCmd::SetFrameTimeCallback => {},
|
// TODO EnvCmd::SetFrameTimeCallback => {},
|
||||||
// TODO EnvCmd::SetAudioCallback => {},
|
// TODO EnvCmd::SetAudioCallback => {},
|
||||||
// TODO EnvCmd::GetRumbleInterface => {},
|
EnvCmd::GetRumbleInterface => {
|
||||||
|
let ri = RumbleInterface { set_rumble_state: StaticCallbacks::set_rumble_state_cb };
|
||||||
|
Self::clone_into_void(data, &ri)?
|
||||||
|
},
|
||||||
EnvCmd::GetInputDeviceCapabilities => Self::clone_into_void(data, &handler.get_input_device_capabilities()?)?,
|
EnvCmd::GetInputDeviceCapabilities => Self::clone_into_void(data, &handler.get_input_device_capabilities()?)?,
|
||||||
// TODO EnvCmd::GetSensorInterface => {},
|
// TODO EnvCmd::GetSensorInterface => {},
|
||||||
// TODO EnvCmd::GetCameraInterface => {},
|
// TODO EnvCmd::GetCameraInterface => {},
|
||||||
|
@ -154,7 +157,10 @@ impl StaticCallbacks {
|
||||||
EnvCmd::GetUsername => Self::string_into_void(data, handler.get_username()?)?,
|
EnvCmd::GetUsername => Self::string_into_void(data, handler.get_username()?)?,
|
||||||
EnvCmd::GetLanguage => Self::clone_into_void(data, &handler.get_language()?)?,
|
EnvCmd::GetLanguage => Self::clone_into_void(data, &handler.get_language()?)?,
|
||||||
// EnvCmd::SetSerializationQuirks => handler.set_serialization_quirks(Self::from_void(data)?),
|
// EnvCmd::SetSerializationQuirks => handler.set_serialization_quirks(Self::from_void(data)?),
|
||||||
_ => false,
|
x => {
|
||||||
|
eprintln!("Unsupported env cmd: {:?}", x);
|
||||||
|
false
|
||||||
|
},
|
||||||
}.into()
|
}.into()
|
||||||
}
|
}
|
||||||
extern "C" fn environment_cb(cmd: u32, data: *mut c_void) -> bool {
|
extern "C" fn environment_cb(cmd: u32, data: *mut c_void) -> bool {
|
||||||
|
@ -167,7 +173,7 @@ impl StaticCallbacks {
|
||||||
height: c_uint,
|
height: c_uint,
|
||||||
pitch: usize,
|
pitch: usize,
|
||||||
) {
|
) {
|
||||||
if !data.is_null() {
|
if !data.is_null() && data != HW_FRAME_BUFFER_VALID {
|
||||||
if let Some(cb) = unsafe { CB_SINGLETON.handler.as_mut() } {
|
if let Some(cb) = unsafe { CB_SINGLETON.handler.as_mut() } {
|
||||||
let data = data as *const u8;
|
let data = data as *const u8;
|
||||||
let len = pitch * (height as usize);
|
let len = pitch * (height as usize);
|
||||||
|
@ -208,10 +214,29 @@ impl StaticCallbacks {
|
||||||
None => 0,
|
None => 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" fn log_print_cb(level: LogLevel, msg: *const c_char) {
|
||||||
|
unsafe {
|
||||||
|
if let Some(cb) = CB_SINGLETON.handler.as_mut() {
|
||||||
|
cb.log_print(level, CStr::from_ptr(msg).to_string_lossy().as_ref());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn set_rumble_state_cb(port: c_uint, effect: RumbleEffect, strength: u16) -> bool {
|
||||||
|
match unsafe { CB_SINGLETON.handler.as_mut() } {
|
||||||
|
Some(cb) => cb.set_rumble_state(port, effect, strength),
|
||||||
|
None => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct LibretroWrapper {
|
pub struct LibretroWrapper {
|
||||||
api: LibretroApi,
|
api: LibretroApi,
|
||||||
|
keyboard_event_cb: Option<KeyboardEventFn>,
|
||||||
|
frame_time_cb: Option<FrameTimeCallbackFn>,
|
||||||
|
audio_ready_cb: Option<AudioCallbackFn>,
|
||||||
|
audio_set_state_cb: Option<AudioSetStateCallbackFn>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<LibretroApi> for LibretroWrapper {
|
impl From<LibretroApi> for LibretroWrapper {
|
||||||
|
@ -222,8 +247,30 @@ impl From<LibretroApi> for LibretroWrapper {
|
||||||
api.set_audio_sample_batch(StaticCallbacks::audio_sample_batch_cb);
|
api.set_audio_sample_batch(StaticCallbacks::audio_sample_batch_cb);
|
||||||
api.set_input_poll(StaticCallbacks::input_poll_cb);
|
api.set_input_poll(StaticCallbacks::input_poll_cb);
|
||||||
api.set_input_state(StaticCallbacks::input_state_cb);
|
api.set_input_state(StaticCallbacks::input_state_cb);
|
||||||
unsafe { c_ext_set_log_print_cb(rust_retro_ffi_log_print); }
|
unsafe { c_ext_set_log_print_cb(StaticCallbacks::log_print_cb); }
|
||||||
LibretroWrapper { api }
|
LibretroWrapper {
|
||||||
|
api,
|
||||||
|
keyboard_event_cb: None,
|
||||||
|
frame_time_cb: None,
|
||||||
|
audio_ready_cb: None,
|
||||||
|
audio_set_state_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 frame_time(&self, time: Duration) {
|
||||||
|
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_set_state(&self, enabled: bool) {
|
||||||
|
self.audio_set_state_cb.map(|f| unsafe { f(enabled) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue