reorganize wrapped types & pass through sensor and perf counter methods
This commit is contained in:
parent
ea3aa70936
commit
f36518d2e6
|
@ -1,6 +1,3 @@
|
|||
use std::convert::{TryFrom, TryInto};
|
||||
use std::os::raw::c_uint;
|
||||
|
||||
use num_enum::{TryFromPrimitive, IntoPrimitive};
|
||||
|
||||
use super::ffi::*;
|
||||
|
@ -20,6 +17,7 @@ pub enum DeviceType {
|
|||
}
|
||||
|
||||
impl DeviceType {
|
||||
// would be const fn, but const u32::from isn't stable
|
||||
pub fn device_subclass(&self, id: u32) -> u32 {
|
||||
((id + 1) << DEVICE_TYPE_SHIFT) | u32::from(*self)
|
||||
}
|
||||
|
@ -105,37 +103,6 @@ pub enum PointerStat {
|
|||
Pressed = DEVICE_ID_POINTER_PRESSED,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum InputDeviceId {
|
||||
None(c_uint),
|
||||
Joypad(JoypadButton),
|
||||
Mouse(MouseButton),
|
||||
Keyboard(c_uint),
|
||||
LightGun(LightGunButton),
|
||||
Analog(AnalogAxis),
|
||||
Pointer(PointerStat),
|
||||
}
|
||||
|
||||
impl<D> TryFrom<(D, c_uint)> for InputDeviceId
|
||||
where D: TryInto<DeviceType>,
|
||||
<D as std::convert::TryInto<DeviceType>>::Error: std::error::Error + Send + Sync + 'static,
|
||||
{
|
||||
type Error = failure::Error;
|
||||
|
||||
fn try_from(pair: (D, c_uint)) -> failure::Fallible<Self> {
|
||||
let (device, id) = pair;
|
||||
Ok(match device.try_into()? {
|
||||
DeviceType::None => InputDeviceId::None(id),
|
||||
DeviceType::Joypad => InputDeviceId::Joypad(JoypadButton::try_from(id)?),
|
||||
DeviceType::Mouse => InputDeviceId::Mouse(MouseButton::try_from(id)?),
|
||||
DeviceType::Keyboard => InputDeviceId::Keyboard(id),
|
||||
DeviceType::LightGun => InputDeviceId::LightGun(LightGunButton::try_from(id)?),
|
||||
DeviceType::Analog => InputDeviceId::Analog(AnalogAxis::try_from(id)?),
|
||||
DeviceType::Pointer => InputDeviceId::Pointer(PointerStat::try_from(id)?),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(TryFromPrimitive, IntoPrimitive)]
|
||||
#[repr(u32)]
|
||||
pub enum EnvRotation {
|
||||
|
|
|
@ -2,4 +2,5 @@ pub mod constants;
|
|||
#[allow(non_camel_case_types, non_upper_case_globals, non_snake_case, dead_code)]
|
||||
pub mod ffi;
|
||||
pub mod loading;
|
||||
pub mod wrapped_types;
|
||||
pub mod wrapper;
|
||||
|
|
|
@ -0,0 +1,203 @@
|
|||
use std::convert::{TryFrom, TryInto};
|
||||
use std::ffi::{CStr};
|
||||
use std::os::raw::{c_uint};
|
||||
use std::slice::from_raw_parts;
|
||||
|
||||
use super::constants::*;
|
||||
use super::ffi::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum InputDeviceId {
|
||||
None(c_uint),
|
||||
Joypad(JoypadButton),
|
||||
Mouse(MouseButton),
|
||||
Keyboard(c_uint),
|
||||
LightGun(LightGunButton),
|
||||
Analog(AnalogAxis),
|
||||
Pointer(PointerStat),
|
||||
}
|
||||
|
||||
impl<D> TryFrom<(D, c_uint)> for InputDeviceId
|
||||
where D: TryInto<DeviceType>,
|
||||
<D as std::convert::TryInto<DeviceType>>::Error: std::error::Error + Send + Sync + 'static,
|
||||
{
|
||||
type Error = failure::Error;
|
||||
|
||||
fn try_from(pair: (D, c_uint)) -> failure::Fallible<Self> {
|
||||
let (device, id) = pair;
|
||||
Ok(match device.try_into()? {
|
||||
DeviceType::None => InputDeviceId::None(id),
|
||||
DeviceType::Joypad => InputDeviceId::Joypad(JoypadButton::try_from(id)?),
|
||||
DeviceType::Mouse => InputDeviceId::Mouse(MouseButton::try_from(id)?),
|
||||
DeviceType::Keyboard => InputDeviceId::Keyboard(id),
|
||||
DeviceType::LightGun => InputDeviceId::LightGun(LightGunButton::try_from(id)?),
|
||||
DeviceType::Analog => InputDeviceId::Analog(AnalogAxis::try_from(id)?),
|
||||
DeviceType::Pointer => InputDeviceId::Pointer(PointerStat::try_from(id)?),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Variable2 {
|
||||
pub key: String,
|
||||
pub description: String,
|
||||
pub options: Vec<String>,
|
||||
}
|
||||
|
||||
impl From<&Variable> for Variable2 {
|
||||
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();
|
||||
Variable2 {
|
||||
key,
|
||||
description,
|
||||
options,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ControllerDescription2 {
|
||||
pub name: String,
|
||||
pub base_type: DeviceType,
|
||||
pub subclass: Option<c_uint>,
|
||||
}
|
||||
|
||||
impl ControllerDescription2 {
|
||||
pub fn device_id(&self) -> c_uint {
|
||||
match self.subclass {
|
||||
None => c_uint::from(self.base_type),
|
||||
Some(sc) => self.base_type.device_subclass(sc),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 InputDescriptor2 {
|
||||
pub port: c_uint,
|
||||
pub device_id: InputDeviceId,
|
||||
pub index: InputIndex,
|
||||
pub description: String,
|
||||
}
|
||||
|
||||
impl TryFrom<&InputDescriptor> for InputDescriptor2 {
|
||||
type Error = ();
|
||||
fn try_from(t: &InputDescriptor) -> Result<Self, Self::Error> {
|
||||
if t.id >> DEVICE_TYPE_SHIFT != 0 {
|
||||
eprintln!("Device subclass encountered in retro_input_descriptor, ignoring");
|
||||
}
|
||||
let description = unsafe { CStr::from_ptr(t.description) }
|
||||
.to_str()
|
||||
.map_err(|_| ())?
|
||||
.to_string();
|
||||
Ok(InputDescriptor2 {
|
||||
port: t.port,
|
||||
device_id: InputDeviceId::try_from((t.device & DEVICE_MASK, t.id)).map_err(|_| ())?,
|
||||
index: InputIndex::try_from(t.index).map_err(|_| ())?,
|
||||
description,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[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,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,6 +15,7 @@ use num_enum::TryFromPrimitive;
|
|||
use super::constants::*;
|
||||
use super::ffi::*;
|
||||
use super::loading::*;
|
||||
use super::wrapped_types::*;
|
||||
|
||||
static mut CB_SINGLETON: StaticCallbacks = StaticCallbacks { handler: None };
|
||||
|
||||
|
@ -77,171 +78,15 @@ pub trait Handler: Unpin + 'static {
|
|||
// -- environment-set callbacks (API extensions) --
|
||||
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(Debug)]
|
||||
pub struct Variable2 {
|
||||
pub key: String,
|
||||
pub description: String,
|
||||
pub options: Vec<String>,
|
||||
}
|
||||
|
||||
impl From<&Variable> for Variable2 {
|
||||
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();
|
||||
Variable2 {
|
||||
key,
|
||||
description,
|
||||
options,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ControllerDescription2 {
|
||||
pub name: String,
|
||||
pub base_type: DeviceType,
|
||||
pub subclass: Option<c_uint>,
|
||||
}
|
||||
|
||||
impl ControllerDescription2 {
|
||||
pub fn device_id(&self) -> c_uint {
|
||||
match self.subclass {
|
||||
None => c_uint::from(self.base_type),
|
||||
Some(sc) => self.base_type.device_subclass(sc),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 InputDescriptor2 {
|
||||
pub port: c_uint,
|
||||
pub device_id: InputDeviceId,
|
||||
pub index: InputIndex,
|
||||
pub description: String,
|
||||
}
|
||||
|
||||
impl TryFrom<&InputDescriptor> for InputDescriptor2 {
|
||||
type Error = ();
|
||||
fn try_from(t: &InputDescriptor) -> Result<Self, Self::Error> {
|
||||
if t.id >> DEVICE_TYPE_SHIFT != 0 {
|
||||
eprintln!("Device subclass encountered in retro_input_descriptor, ignoring");
|
||||
}
|
||||
let description = unsafe { CStr::from_ptr(t.description) }
|
||||
.to_str()
|
||||
.map_err(|_| ())?
|
||||
.to_string();
|
||||
Ok(InputDescriptor2 {
|
||||
port: t.port,
|
||||
device_id: InputDeviceId::try_from((t.device & DEVICE_MASK, t.id)).map_err(|_| ())?,
|
||||
index: InputIndex::try_from(t.index).map_err(|_| ())?,
|
||||
description,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[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,
|
||||
}
|
||||
}
|
||||
fn perf_get_time_usec_cb(&mut self) -> Time { 0 }
|
||||
fn perf_get_counter_cb(&mut self) -> PerfTick { 0 }
|
||||
fn perf_get_cpu_features_cb(&mut self) -> u64 { 0 }
|
||||
fn perf_log_cb(&mut self) {}
|
||||
fn perf_register_cb(&mut self, counter: &mut PerfCounter) {}
|
||||
fn perf_start_cb(&mut self, counter: &mut PerfCounter) {}
|
||||
fn perf_stop_cb(&mut self, counter: &mut PerfCounter) {}
|
||||
fn set_sensor_state(&mut self, port: c_uint, action: SensorAction, rate: c_uint) -> bool { false }
|
||||
fn get_sensor_input(&mut self, port: c_uint, id: c_uint) -> f32 { 0.0 }
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -321,7 +166,7 @@ impl StaticCallbacks {
|
|||
input_desc = input_desc.wrapping_add(1);
|
||||
}
|
||||
handler.set_input_descriptors(descriptors)
|
||||
},
|
||||
}
|
||||
EnvCmd::SetKeyboardCallback => {
|
||||
let kc: &mut KeyboardCallback = Self::from_void(data)?;
|
||||
handler
|
||||
|
@ -403,11 +248,17 @@ impl StaticCallbacks {
|
|||
EnvCmd::GetInputDeviceCapabilities => {
|
||||
Self::clone_into_void(data, &handler.get_input_device_capabilities()?)?
|
||||
}
|
||||
// TODO EnvCmd::GetSensorInterface => {},
|
||||
EnvCmd::GetSensorInterface => {
|
||||
let si = SensorInterface {
|
||||
set_sensor_state: Self::set_sensor_state,
|
||||
get_sensor_input: Self::get_sensor_input,
|
||||
};
|
||||
Self::clone_into_void(data, &si)?
|
||||
}
|
||||
// TODO EnvCmd::GetCameraInterface => {},
|
||||
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,
|
||||
|
@ -537,21 +388,67 @@ impl StaticCallbacks {
|
|||
None => false,
|
||||
}
|
||||
}
|
||||
// TODO: trait methods
|
||||
extern "C" fn perf_get_time_usec_cb() -> Time {
|
||||
0
|
||||
unsafe {
|
||||
CB_SINGLETON.handler.as_mut().map(|cb| cb.perf_get_time_usec_cb())
|
||||
}.unwrap_or_default()
|
||||
}
|
||||
extern "C" fn perf_get_counter_cb() -> PerfTick {
|
||||
0
|
||||
unsafe {
|
||||
CB_SINGLETON.handler.as_mut().map(|cb| cb.perf_get_counter_cb())
|
||||
}.unwrap_or_default()
|
||||
}
|
||||
extern "C" fn perf_get_cpu_features_cb() -> u64 {
|
||||
0
|
||||
unsafe {
|
||||
CB_SINGLETON.handler.as_mut().map(|cb| cb.perf_get_cpu_features_cb())
|
||||
}.unwrap_or_default()
|
||||
}
|
||||
extern "C" fn perf_log_cb() {
|
||||
unsafe { CB_SINGLETON.handler.as_mut().map(|cb| cb.perf_log_cb()); }
|
||||
}
|
||||
extern "C" fn perf_register_cb(counter: *mut PerfCounter) {
|
||||
unsafe {
|
||||
match (CB_SINGLETON.handler.as_mut(), counter.as_mut()) {
|
||||
(Some(cb), Some(counter)) => {
|
||||
cb.perf_register_cb(counter);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
extern "C" fn perf_start_cb(counter: *mut PerfCounter) {
|
||||
unsafe {
|
||||
match (CB_SINGLETON.handler.as_mut(), counter.as_mut()) {
|
||||
(Some(cb), Some(counter)) => {
|
||||
cb.perf_start_cb(counter);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
extern "C" fn perf_stop_cb(counter: *mut PerfCounter) {
|
||||
unsafe {
|
||||
match (CB_SINGLETON.handler.as_mut(), counter.as_mut()) {
|
||||
(Some(cb), Some(counter)) => {
|
||||
cb.perf_stop_cb(counter);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
extern "C" fn perf_log_cb() {}
|
||||
extern "C" fn perf_register_cb(_counter: *mut PerfCounter) {}
|
||||
extern "C" fn perf_start_cb(_counter: *mut PerfCounter) {}
|
||||
extern "C" fn perf_stop_cb(_counter: *mut PerfCounter) {}
|
||||
|
||||
extern "C" fn set_sensor_state(port: c_uint, action: SensorAction, rate: c_uint) -> bool {
|
||||
unsafe {
|
||||
CB_SINGLETON.handler.as_mut().map(|cb| cb.set_sensor_state(port, action, rate))
|
||||
}.unwrap_or_default()
|
||||
}
|
||||
extern "C" fn get_sensor_input(port: c_uint, id: c_uint) -> f32 {
|
||||
unsafe {
|
||||
CB_SINGLETON.handler.as_mut().map(|cb| cb.get_sensor_input(port, id))
|
||||
}.unwrap_or_default()
|
||||
}
|
||||
|
||||
// TODO: trait methods, etc.
|
||||
extern "C" fn hw_dummy_fn() {}
|
||||
extern "C" fn hw_get_proc_address_fn(_sym: *const c_char) -> ProcAddressFn {
|
||||
Self::hw_dummy_fn // FIXME: obvious hack
|
||||
|
|
Loading…
Reference in New Issue