implement ENVIRONMENT_SET_SUBSYSTEM_INFO
This commit is contained in:
parent
7b50e6b975
commit
49aa491904
|
@ -5,7 +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, VariableDescriptor, ControllerTypeDescriptor};
|
||||
use rustro::retro::wrapper::{LibretroWrapper, VariableDescriptor, ControllerDescription2, SubsystemInfo2};
|
||||
|
||||
use std::ffi::CStr;
|
||||
use std::io::Read;
|
||||
|
@ -275,6 +275,10 @@ impl retro::wrapper::Handler for MyEmulator {
|
|||
};
|
||||
true
|
||||
}
|
||||
fn set_subsystem_info(&mut self, subsystem_info: Vec<SubsystemInfo2>) -> bool {
|
||||
println!("subsystem info: {:?}", subsystem_info);
|
||||
true
|
||||
}
|
||||
|
||||
fn get_variable(&mut self, key: &str) -> Option<String> {
|
||||
match key {
|
||||
|
@ -311,7 +315,7 @@ impl retro::wrapper::Handler for MyEmulator {
|
|||
true
|
||||
}
|
||||
|
||||
fn set_controller_info(&mut self, controller_info: Vec<ControllerTypeDescriptor>) -> bool {
|
||||
fn set_controller_info(&mut self, controller_info: Vec<ControllerDescription2>) -> bool {
|
||||
for ci in controller_info {
|
||||
// so we can have analog support in beetle/mednafen saturn
|
||||
if ci.name.as_str() == "3D Control Pad" {
|
||||
|
|
|
@ -3,9 +3,9 @@ use core::ffi::c_void;
|
|||
use core::slice::from_raw_parts;
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::ffi::{CString, CStr};
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::ops::Deref;
|
||||
use std::os::raw::{c_uint, c_char};
|
||||
use std::os::raw::{c_char, c_uint};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::pin::Pin;
|
||||
use std::time::Duration;
|
||||
|
@ -16,19 +16,18 @@ use super::constants::*;
|
|||
use super::ffi::*;
|
||||
use super::loading::*;
|
||||
|
||||
static mut CB_SINGLETON: StaticCallbacks = StaticCallbacks {
|
||||
handler: None,
|
||||
};
|
||||
static mut CB_SINGLETON: StaticCallbacks = StaticCallbacks { 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);
|
||||
extern "C" {
|
||||
extern "C" {
|
||||
fn c_ext_handle_get_log_interface(cb: *mut LogCallback) -> bool;
|
||||
fn c_ext_set_log_print_cb(cb: WrappedLogPrintFn);
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
#[allow(unused)]
|
||||
pub trait Handler: Unpin + 'static {
|
||||
fn libretro_core(&mut self) -> &mut LibretroWrapper;
|
||||
|
@ -67,8 +66,8 @@ pub trait Handler: Unpin + 'static {
|
|||
fn get_save_directory(&mut self) -> Option<PathBuf> { None }
|
||||
fn set_system_av_info(&mut self, system_av_info: SystemAvInfo) -> bool { false }
|
||||
fn set_proc_address_callback(&mut self, cb: GetProcAddressInterface) -> bool { false }
|
||||
fn set_subsystem_info(&mut self, subsystem_info: SubsystemInfo) -> bool { false }
|
||||
fn set_controller_info(&mut self, controller_info: Vec<ControllerTypeDescriptor>) -> bool { false }
|
||||
fn set_subsystem_info(&mut self, subsystem_info: Vec<SubsystemInfo2>) -> bool { false }
|
||||
fn set_controller_info(&mut self, controller_info: Vec<ControllerDescription2>) -> bool { false }
|
||||
fn set_memory_maps(&mut self, memory_map: MemoryMap) -> bool { false }
|
||||
fn set_geometry(&mut self, game_geometry: GameGeometry) -> bool { false }
|
||||
fn get_username(&mut self) -> Option<String> { None }
|
||||
|
@ -87,14 +86,38 @@ pub struct VariableDescriptor {
|
|||
pub options: Vec<String>,
|
||||
}
|
||||
|
||||
impl From<&Variable> for VariableDescriptor {
|
||||
fn from(var: &Variable) -> Self {
|
||||
let key = unsafe { CStr::from_ptr(var.key) }
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
let value = unsafe { CStr::from_ptr(var.value) }
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
let split: Vec<&str> = value.splitn(2, "; ").collect();
|
||||
let description = split.get(0).unwrap_or(&"").to_string();
|
||||
let options = split
|
||||
.get(1)
|
||||
.unwrap_or(&"")
|
||||
.split('|')
|
||||
.map(String::from)
|
||||
.collect();
|
||||
VariableDescriptor {
|
||||
key,
|
||||
description,
|
||||
options,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ControllerTypeDescriptor {
|
||||
pub struct ControllerDescription2 {
|
||||
pub name: String,
|
||||
pub base_type: DeviceType,
|
||||
pub subclass: Option<c_uint>,
|
||||
}
|
||||
|
||||
impl ControllerTypeDescriptor {
|
||||
impl ControllerDescription2 {
|
||||
pub fn device_id(&self) -> c_uint {
|
||||
match self.subclass {
|
||||
None => c_uint::from(self.base_type),
|
||||
|
@ -103,6 +126,97 @@ impl ControllerTypeDescriptor {
|
|||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&ControllerDescription> for ControllerDescription2 {
|
||||
type Error = ();
|
||||
fn try_from(t: &ControllerDescription) -> Result<Self, Self::Error> {
|
||||
let base_type = DeviceType::try_from(t.id & DEVICE_MASK).map_err(|_| ())?;
|
||||
let subclass = match t.id >> DEVICE_TYPE_SHIFT {
|
||||
0 => None,
|
||||
n => Some(n - 1),
|
||||
};
|
||||
let name = unsafe { CStr::from_ptr(t.desc) }
|
||||
.to_str()
|
||||
.map_err(|_| ())?
|
||||
.to_string();
|
||||
Ok(ControllerDescription2 {
|
||||
base_type,
|
||||
subclass,
|
||||
name,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SubsystemInfo2 {
|
||||
pub description: String,
|
||||
pub identifier: String,
|
||||
pub roms: Vec<SubsystemRomInfo2>,
|
||||
pub id: c_uint,
|
||||
}
|
||||
|
||||
impl From<&SubsystemInfo> for SubsystemInfo2 {
|
||||
fn from(si: &SubsystemInfo) -> Self {
|
||||
let rom_slice = unsafe { from_raw_parts(si.roms, si.num_roms as usize) };
|
||||
SubsystemInfo2 {
|
||||
description: unsafe { CStr::from_ptr(si.desc) }
|
||||
.to_string_lossy()
|
||||
.to_string(),
|
||||
identifier: unsafe { CStr::from_ptr(si.ident) }
|
||||
.to_string_lossy()
|
||||
.to_string(),
|
||||
roms: rom_slice.iter().map(Into::into).collect(),
|
||||
id: si.id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SubsystemRomInfo2 {
|
||||
pub description: String,
|
||||
pub valid_extensions: Vec<String>,
|
||||
pub need_fullpath: bool,
|
||||
pub block_extract: bool,
|
||||
pub required: bool,
|
||||
pub memory: Vec<SubsystemMemoryInfo2>,
|
||||
}
|
||||
|
||||
impl From<&SubsystemRomInfo> for SubsystemRomInfo2 {
|
||||
fn from(sri: &SubsystemRomInfo) -> Self {
|
||||
let mem_slice = unsafe { from_raw_parts(sri.memory, sri.num_memory as usize) };
|
||||
SubsystemRomInfo2 {
|
||||
description: unsafe { CStr::from_ptr(sri.desc) }
|
||||
.to_string_lossy()
|
||||
.to_string(),
|
||||
valid_extensions: unsafe { CStr::from_ptr(sri.valid_extensions) }
|
||||
.to_string_lossy()
|
||||
.split('|')
|
||||
.map(str::to_string)
|
||||
.collect(),
|
||||
need_fullpath: sri.need_fullpath,
|
||||
block_extract: sri.block_extract,
|
||||
required: sri.required,
|
||||
memory: mem_slice.iter().map(Into::into).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SubsystemMemoryInfo2 {
|
||||
pub extension: String,
|
||||
pub kind: c_uint,
|
||||
}
|
||||
|
||||
impl From<&SubsystemMemoryInfo> for SubsystemMemoryInfo2 {
|
||||
fn from(smi: &SubsystemMemoryInfo) -> Self {
|
||||
SubsystemMemoryInfo2 {
|
||||
extension: unsafe { CStr::from_ptr(smi.extension) }
|
||||
.to_string_lossy()
|
||||
.to_string(),
|
||||
kind: smi.kind,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct StaticCallbacks {
|
||||
handler: Option<Pin<&'static mut dyn Handler>>,
|
||||
|
@ -117,7 +231,8 @@ impl StaticCallbacks {
|
|||
Some(true)
|
||||
}
|
||||
fn string_into_void(data: *mut c_void, source: impl AsRef<str>) -> Option<bool> {
|
||||
*unsafe { (data as *mut *const c_char).as_mut()? } = CString::new(source.as_ref()).ok()?.into_raw();
|
||||
*unsafe { (data as *mut *const c_char).as_mut()? } =
|
||||
CString::new(source.as_ref()).ok()?.into_raw();
|
||||
Some(true)
|
||||
}
|
||||
fn path_into_void(data: *mut c_void, source: impl AsRef<Path>) -> Option<bool> {
|
||||
|
@ -127,7 +242,9 @@ impl StaticCallbacks {
|
|||
unsafe { (data as *mut T).as_mut() }
|
||||
}
|
||||
fn enum_from_void<T>(data: *mut c_void) -> Option<T>
|
||||
where T: TryFromPrimitive, <T as TryFromPrimitive>::Primitive: 'static
|
||||
where
|
||||
T: TryFromPrimitive,
|
||||
<T as TryFromPrimitive>::Primitive: 'static,
|
||||
{
|
||||
let number = Self::from_void(data).cloned()?;
|
||||
T::try_from_primitive(number).ok()
|
||||
|
@ -148,7 +265,11 @@ impl StaticCallbacks {
|
|||
if cfg!(debug) && parsed_cmd.is_none() {
|
||||
eprintln!(
|
||||
"Unknown{} env cmd: {}",
|
||||
if cmd >= ENVIRONMENT_EXPERIMENTAL { ", experimental" } else { "" },
|
||||
if cmd >= ENVIRONMENT_EXPERIMENTAL {
|
||||
", experimental"
|
||||
} else {
|
||||
""
|
||||
},
|
||||
cmd % ENVIRONMENT_EXPERIMENTAL
|
||||
);
|
||||
}
|
||||
|
@ -159,14 +280,21 @@ impl StaticCallbacks {
|
|||
EnvCmd::SetMessage => handler.set_message(Self::from_void::<Message>(data)?.clone()),
|
||||
EnvCmd::Shutdown => handler.shutdown(),
|
||||
EnvCmd::SetPerformanceLevel => handler.set_performance_level(*Self::from_void(data)?),
|
||||
EnvCmd::GetSystemDirectory => Self::path_into_void(data, handler.get_system_directory()?)?,
|
||||
EnvCmd::SetPixelFormat => handler.set_pixel_format(PixelFormat::from_uint(*Self::from_void(data)?)?),
|
||||
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 => {},
|
||||
EnvCmd::SetKeyboardCallback => {
|
||||
let kc: &mut KeyboardCallback = Self::from_void(data)?;
|
||||
handler.libretro_core().keyboard_event_cb.replace(kc.callback);
|
||||
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();
|
||||
|
@ -175,18 +303,25 @@ impl StaticCallbacks {
|
|||
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_replace_image_index_cb
|
||||
.replace(dcc.replace_image_index);
|
||||
core.disk_add_image_index_cb.replace(dcc.add_image_index);
|
||||
true
|
||||
},
|
||||
}
|
||||
EnvCmd::SetHwRender => {
|
||||
let hwr = Self::from_void::<HwRenderCallback>(data)?;
|
||||
handler.libretro_core().hw_context_reset_cb.replace(hwr.context_reset);
|
||||
handler.libretro_core().hw_context_destroy_cb.replace(hwr.context_destroy);
|
||||
handler
|
||||
.libretro_core()
|
||||
.hw_context_reset_cb
|
||||
.replace(hwr.context_reset);
|
||||
handler
|
||||
.libretro_core()
|
||||
.hw_context_destroy_cb
|
||||
.replace(hwr.context_destroy);
|
||||
hwr.get_current_framebuffer = Self::hw_get_current_framebuffer_fn;
|
||||
hwr.get_proc_address = Self::hw_get_proc_address_fn;
|
||||
false // TODO: finish
|
||||
},
|
||||
}
|
||||
EnvCmd::GetVariable => {
|
||||
let mut var = Self::from_void::<Variable>(data)?;
|
||||
let key = unsafe { CStr::from_ptr(var.key) }.to_str().ok()?;
|
||||
|
@ -194,44 +329,50 @@ impl StaticCallbacks {
|
|||
// leaks memory.
|
||||
var.value = CString::new(value).ok()?.into_raw();
|
||||
true
|
||||
},
|
||||
}
|
||||
EnvCmd::SetVariables => {
|
||||
let mut var = data as *const Variable;
|
||||
let mut descriptors = Vec::new();
|
||||
while !unsafe { var.as_ref() }?.key.is_null() {
|
||||
let key = unsafe { CStr::from_ptr({ var.as_ref() }?.key) }.to_str().ok()?.to_string();
|
||||
let value = unsafe { CStr::from_ptr({ var.as_ref() }?.value) }.to_str().ok()?;
|
||||
let split: Vec<&str> = value.splitn(2, "; ").collect();
|
||||
let description = split.get(0)?.to_string();
|
||||
let options = split.get(1)?.split('|').map(String::from).collect();
|
||||
descriptors.push(VariableDescriptor { key, description, options });
|
||||
descriptors.push(unsafe { var.as_ref() }?.into());
|
||||
var = var.wrapping_add(1);
|
||||
}
|
||||
handler.set_variables(descriptors)
|
||||
},
|
||||
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::GetLibretroPath => Self::path_into_void(data, handler.get_libretro_path()?)?,
|
||||
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);
|
||||
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 };
|
||||
let ri = RumbleInterface {
|
||||
set_rumble_state: StaticCallbacks::set_rumble_state_cb,
|
||||
};
|
||||
Self::clone_into_void(data, &ri)?
|
||||
},
|
||||
}
|
||||
// TODO: wrap this in something nicer than a raw u64 bit field
|
||||
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::GetCameraInterface => {},
|
||||
EnvCmd::GetLogInterface => unsafe { c_ext_handle_get_log_interface(data as *mut LogCallback) },
|
||||
EnvCmd::GetLogInterface => unsafe {
|
||||
c_ext_handle_get_log_interface(data as *mut LogCallback)
|
||||
},
|
||||
EnvCmd::GetPerfInterface => {
|
||||
let pc = PerfCallback {
|
||||
get_time_usec: Self::perf_get_time_usec_cb,
|
||||
|
@ -243,36 +384,47 @@ impl StaticCallbacks {
|
|||
perf_log: Self::perf_log_cb,
|
||||
};
|
||||
Self::clone_into_void(data, &pc)?
|
||||
},
|
||||
}
|
||||
// TODO EnvCmd::GetLocationInterface => {},
|
||||
EnvCmd::GetCoreAssetsDirectory => Self::path_into_void(data, handler.get_core_assets_directory()?)?,
|
||||
EnvCmd::GetCoreAssetsDirectory => {
|
||||
Self::path_into_void(data, handler.get_core_assets_directory()?)?
|
||||
}
|
||||
EnvCmd::GetSaveDirectory => Self::path_into_void(data, handler.get_save_directory()?)?,
|
||||
EnvCmd::SetSystemAvInfo => handler.set_system_av_info(Self::from_void::<SystemAvInfo>(data)?.clone()),
|
||||
EnvCmd::SetSystemAvInfo => {
|
||||
handler.set_system_av_info(Self::from_void::<SystemAvInfo>(data)?.clone())
|
||||
}
|
||||
EnvCmd::SetProcAddressCallback => {
|
||||
let gpa = Self::from_void::<GetProcAddressInterface>(data)?;
|
||||
handler.libretro_core().get_proc_address_cb.replace(gpa.get_proc_address);
|
||||
handler
|
||||
.libretro_core()
|
||||
.get_proc_address_cb
|
||||
.replace(gpa.get_proc_address);
|
||||
true
|
||||
},
|
||||
// TODO EnvCmd::SetSubsystemInfo => {},
|
||||
}
|
||||
EnvCmd::SetSubsystemInfo => {
|
||||
let mut info = data as *const SubsystemInfo;
|
||||
let mut descriptors = Vec::new();
|
||||
while !unsafe { info.as_ref() }?.desc.is_null() {
|
||||
descriptors.push(unsafe { info.as_ref() }?.into());
|
||||
info = info.wrapping_add(1);
|
||||
}
|
||||
handler.set_subsystem_info(descriptors)
|
||||
}
|
||||
EnvCmd::SetControllerInfo => {
|
||||
let info = unsafe { (data as *const ControllerInfo).as_ref() }?;
|
||||
let mut controller_info = Vec::new();
|
||||
// FIXME: beetle/mednafen saturn crashes without this -1... add conditional on name?
|
||||
let len = info.num_types as usize - 1;
|
||||
let types = unsafe { from_raw_parts(info.types, len) };
|
||||
for t in types {
|
||||
let base_type = DeviceType::try_from(t.id & DEVICE_MASK).ok()?;
|
||||
let subclass = match t.id >> DEVICE_TYPE_SHIFT {
|
||||
0 => None,
|
||||
n => Some(n - 1),
|
||||
};
|
||||
let name = unsafe { CStr::from_ptr(t.desc) }.to_str().ok()?.to_string();
|
||||
controller_info.push(ControllerTypeDescriptor { base_type, subclass, name });
|
||||
}
|
||||
let controller_info = unsafe { from_raw_parts(info.types, len) }
|
||||
.iter()
|
||||
.map(TryInto::try_into)
|
||||
.filter_map(|x| x.ok())
|
||||
.collect();
|
||||
handler.set_controller_info(controller_info)
|
||||
},
|
||||
}
|
||||
// TODO EnvCmd::SetMemoryMaps => {},
|
||||
EnvCmd::SetGeometry => handler.set_geometry(Self::from_void::<GameGeometry>(data)?.clone()),
|
||||
EnvCmd::SetGeometry => {
|
||||
handler.set_geometry(Self::from_void::<GameGeometry>(data)?.clone())
|
||||
}
|
||||
EnvCmd::GetUsername => Self::string_into_void(data, handler.get_username()?)?,
|
||||
EnvCmd::GetLanguage => Self::clone_into_void(data, &handler.get_language()?)?,
|
||||
// EnvCmd::SetSerializationQuirks => handler.set_serialization_quirks(Self::from_void(data)?),
|
||||
|
@ -281,8 +433,9 @@ impl StaticCallbacks {
|
|||
eprintln!("Known but unsupported env cmd: {:?}", x);
|
||||
}
|
||||
false
|
||||
},
|
||||
}.into()
|
||||
}
|
||||
}
|
||||
.into()
|
||||
}
|
||||
extern "C" fn environment_cb(cmd: u32, data: *mut c_void) -> bool {
|
||||
Self::environment_cb_inner(cmd, data).unwrap_or(false)
|
||||
|
@ -317,7 +470,7 @@ impl StaticCallbacks {
|
|||
let len = frames * 2; // stereo
|
||||
let result = cb.audio_sample_batch(from_raw_parts(data, len));
|
||||
result / 2
|
||||
},
|
||||
}
|
||||
},
|
||||
None => 0,
|
||||
}
|
||||
|
@ -350,9 +503,15 @@ impl StaticCallbacks {
|
|||
}
|
||||
}
|
||||
// TODO: trait methods
|
||||
extern "C" fn perf_get_time_usec_cb() -> Time { 0 }
|
||||
extern "C" fn perf_get_counter_cb() -> PerfTick { 0 }
|
||||
extern "C" fn perf_get_cpu_features_cb() -> u64 { 0 }
|
||||
extern "C" fn perf_get_time_usec_cb() -> Time {
|
||||
0
|
||||
}
|
||||
extern "C" fn perf_get_counter_cb() -> PerfTick {
|
||||
0
|
||||
}
|
||||
extern "C" fn perf_get_cpu_features_cb() -> u64 {
|
||||
0
|
||||
}
|
||||
extern "C" fn perf_log_cb() {}
|
||||
extern "C" fn perf_register_cb(_counter: *mut PerfCounter) {}
|
||||
extern "C" fn perf_start_cb(_counter: *mut PerfCounter) {}
|
||||
|
@ -363,7 +522,9 @@ impl StaticCallbacks {
|
|||
Self::hw_dummy_fn // FIXME: obvious hack
|
||||
}
|
||||
// note: libretro.h claims this is obsolete
|
||||
extern "C" fn hw_get_current_framebuffer_fn() -> usize { 0 }
|
||||
extern "C" fn hw_get_current_framebuffer_fn() -> usize {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LibretroWrapper {
|
||||
|
@ -408,11 +569,19 @@ impl From<LibretroApi> for LibretroWrapper {
|
|||
|
||||
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) -> Option<()> {
|
||||
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) -> Option<()> {
|
||||
self.frame_time_cb.map(|f| unsafe { f(time.as_micros() as Usec) })
|
||||
self.frame_time_cb
|
||||
.map(|f| unsafe { f(time.as_micros() as Usec) })
|
||||
}
|
||||
pub fn audio_ready(&self) -> Option<()> {
|
||||
self.audio_ready_cb.map(|f| unsafe { f() })
|
||||
|
@ -473,13 +642,17 @@ impl Deref for LibretroWrapper {
|
|||
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()));
|
||||
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();
|
||||
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);
|
||||
|
|
Loading…
Reference in New Issue