Start making nicer types for interop with Rust
This commit is contained in:
parent
e34b9daa38
commit
0d0dda356c
4 changed files with 163 additions and 15 deletions
|
@ -7,6 +7,7 @@ edition = "2018"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
failure = "^0.1"
|
failure = "^0.1"
|
||||||
libloading = "^0.5"
|
libloading = "^0.5"
|
||||||
|
num_enum = "^0.4"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
bindgen = "^0.51" # keep an eye on https://github.com/rust-lang/rust-bindgen/issues/1541
|
bindgen = "^0.51" # keep an eye on https://github.com/rust-lang/rust-bindgen/issues/1541
|
||||||
|
|
138
src/libretro_convert.rs
Normal file
138
src/libretro_convert.rs
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
use std::os::raw::c_uint;
|
||||||
|
use num_enum::TryFromPrimitive;
|
||||||
|
use crate::libretro_types::*;
|
||||||
|
|
||||||
|
#[derive(TryFromPrimitive)]
|
||||||
|
#[repr(u32)]
|
||||||
|
pub enum Region {
|
||||||
|
NTSC = RETRO_REGION_NTSC,
|
||||||
|
PAL = RETRO_REGION_PAL,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(TryFromPrimitive)]
|
||||||
|
#[repr(u32)]
|
||||||
|
pub enum DeviceType {
|
||||||
|
None = RETRO_DEVICE_NONE,
|
||||||
|
Joypad = RETRO_DEVICE_JOYPAD,
|
||||||
|
Mouse = RETRO_DEVICE_MOUSE,
|
||||||
|
Keyboard = RETRO_DEVICE_KEYBOARD,
|
||||||
|
LightGun = RETRO_DEVICE_LIGHTGUN,
|
||||||
|
Analog = RETRO_DEVICE_ANALOG,
|
||||||
|
Pointer = RETRO_DEVICE_POINTER,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(TryFromPrimitive)]
|
||||||
|
#[repr(u32)]
|
||||||
|
pub enum DeviceIndex {
|
||||||
|
Left = RETRO_DEVICE_INDEX_ANALOG_LEFT,
|
||||||
|
Right = RETRO_DEVICE_INDEX_ANALOG_RIGHT,
|
||||||
|
Button = RETRO_DEVICE_INDEX_ANALOG_BUTTON,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(TryFromPrimitive)]
|
||||||
|
#[repr(u32)]
|
||||||
|
pub enum JoypadButton {
|
||||||
|
B = RETRO_DEVICE_ID_JOYPAD_B,
|
||||||
|
Y = RETRO_DEVICE_ID_JOYPAD_Y,
|
||||||
|
Select = RETRO_DEVICE_ID_JOYPAD_SELECT,
|
||||||
|
Start = RETRO_DEVICE_ID_JOYPAD_START,
|
||||||
|
Up = RETRO_DEVICE_ID_JOYPAD_UP,
|
||||||
|
Down = RETRO_DEVICE_ID_JOYPAD_DOWN,
|
||||||
|
Left = RETRO_DEVICE_ID_JOYPAD_LEFT,
|
||||||
|
Right = RETRO_DEVICE_ID_JOYPAD_RIGHT,
|
||||||
|
A = RETRO_DEVICE_ID_JOYPAD_A,
|
||||||
|
X = RETRO_DEVICE_ID_JOYPAD_X,
|
||||||
|
L = RETRO_DEVICE_ID_JOYPAD_L,
|
||||||
|
R = RETRO_DEVICE_ID_JOYPAD_R,
|
||||||
|
L2 = RETRO_DEVICE_ID_JOYPAD_L2,
|
||||||
|
R2 = RETRO_DEVICE_ID_JOYPAD_R2,
|
||||||
|
L3 = RETRO_DEVICE_ID_JOYPAD_L3,
|
||||||
|
R3 = RETRO_DEVICE_ID_JOYPAD_R3,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(TryFromPrimitive)]
|
||||||
|
#[repr(u32)]
|
||||||
|
pub enum AnalogAxis {
|
||||||
|
X = RETRO_DEVICE_ID_ANALOG_X,
|
||||||
|
Y = RETRO_DEVICE_ID_ANALOG_Y,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(TryFromPrimitive)]
|
||||||
|
#[repr(u32)]
|
||||||
|
pub enum MouseButton {
|
||||||
|
X = RETRO_DEVICE_ID_MOUSE_X,
|
||||||
|
Y = RETRO_DEVICE_ID_MOUSE_Y,
|
||||||
|
Left = RETRO_DEVICE_ID_MOUSE_LEFT,
|
||||||
|
Right = RETRO_DEVICE_ID_MOUSE_RIGHT,
|
||||||
|
WheelUp = RETRO_DEVICE_ID_MOUSE_WHEELUP,
|
||||||
|
WheelDown = RETRO_DEVICE_ID_MOUSE_WHEELDOWN,
|
||||||
|
Middle = RETRO_DEVICE_ID_MOUSE_MIDDLE,
|
||||||
|
HorizWheelUp = RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELUP,
|
||||||
|
HorizWheelDown = RETRO_DEVICE_ID_MOUSE_HORIZ_WHEELDOWN,
|
||||||
|
Button4 = RETRO_DEVICE_ID_MOUSE_BUTTON_4,
|
||||||
|
Button5 = RETRO_DEVICE_ID_MOUSE_BUTTON_5,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(TryFromPrimitive)]
|
||||||
|
#[repr(u32)]
|
||||||
|
pub enum LightGunButton {
|
||||||
|
ScreenX = RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X,
|
||||||
|
ScreenY = RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y,
|
||||||
|
IsOffscreen = RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN,
|
||||||
|
Trigger = RETRO_DEVICE_ID_LIGHTGUN_TRIGGER,
|
||||||
|
Reload = RETRO_DEVICE_ID_LIGHTGUN_RELOAD,
|
||||||
|
Start = RETRO_DEVICE_ID_LIGHTGUN_START,
|
||||||
|
Select = RETRO_DEVICE_ID_LIGHTGUN_SELECT,
|
||||||
|
AuxC = RETRO_DEVICE_ID_LIGHTGUN_AUX_C,
|
||||||
|
DpadUp = RETRO_DEVICE_ID_LIGHTGUN_DPAD_UP,
|
||||||
|
DpadDown = RETRO_DEVICE_ID_LIGHTGUN_DPAD_DOWN,
|
||||||
|
DpadLeft = RETRO_DEVICE_ID_LIGHTGUN_DPAD_LEFT,
|
||||||
|
DpadRight = RETRO_DEVICE_ID_LIGHTGUN_DPAD_RIGHT,
|
||||||
|
X = RETRO_DEVICE_ID_LIGHTGUN_X,
|
||||||
|
Y = RETRO_DEVICE_ID_LIGHTGUN_Y,
|
||||||
|
Cursor = RETRO_DEVICE_ID_LIGHTGUN_CURSOR,
|
||||||
|
Turbo = RETRO_DEVICE_ID_LIGHTGUN_TURBO,
|
||||||
|
Pause = RETRO_DEVICE_ID_LIGHTGUN_PAUSE,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(TryFromPrimitive)]
|
||||||
|
#[repr(u32)]
|
||||||
|
pub enum PointerStat {
|
||||||
|
X = RETRO_DEVICE_ID_POINTER_X,
|
||||||
|
Y = RETRO_DEVICE_ID_POINTER_Y,
|
||||||
|
Pressed = RETRO_DEVICE_ID_POINTER_PRESSED,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Input {
|
||||||
|
None(c_uint),
|
||||||
|
Joypad(JoypadButton),
|
||||||
|
Mouse(MouseButton),
|
||||||
|
Keyboard(c_uint),
|
||||||
|
LightGun(LightGunButton),
|
||||||
|
Analog(AnalogAxis),
|
||||||
|
Pointer(PointerStat),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D> From<(D, c_uint)> for Input
|
||||||
|
where D: Into<DeviceType>,
|
||||||
|
{
|
||||||
|
fn from(pair: (D, c_uint)) -> Self {
|
||||||
|
let (device, id) = pair;
|
||||||
|
match device.into() {
|
||||||
|
DeviceType::None => Input::None(id),
|
||||||
|
DeviceType::Joypad => Input::Joypad(JoypadButton::try_from(id).expect("Invalid joypad button ID")),
|
||||||
|
DeviceType::Mouse => Input::Mouse(MouseButton::try_from(id).expect("Invalid mouse button ID")),
|
||||||
|
DeviceType::Keyboard => Input::Keyboard(id),
|
||||||
|
DeviceType::LightGun => Input::LightGun(LightGunButton::try_from(id).expect("Invalid lightgun button ID")),
|
||||||
|
DeviceType::Analog => Input::Analog(AnalogAxis::try_from(id).expect("Invalid analog axis ID")),
|
||||||
|
DeviceType::Pointer => Input::Pointer(PointerStat::try_from(id).expect("Invalid pointer stat ID")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type VideoRefreshCb = dyn FnMut(&[u8], c_uint, c_uint, c_uint); // buffer, width, height, pitch
|
||||||
|
type AudioSampleCb = dyn FnMut(i16, i16); // left, right
|
||||||
|
type AudioSampleBatchCb = dyn FnMut(&[i16]) -> usize; // pcm16le stereo samples
|
||||||
|
type InputPollCb = dyn FnMut();
|
||||||
|
type InputStateCb = dyn FnMut(u32, Input, DeviceIndex) -> i16; // port, device, index
|
|
@ -1,11 +1,19 @@
|
||||||
|
use std::convert::TryInto;
|
||||||
use std::os::raw::{c_char, c_uint, c_void};
|
use std::os::raw::{c_char, c_uint, c_void};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use failure::Fallible;
|
use failure::Fallible;
|
||||||
|
use libloading;
|
||||||
|
|
||||||
use crate::libretro_types::*;
|
use crate::libretro_types::*;
|
||||||
use libloading;
|
use crate::libretro_convert::*;
|
||||||
use std::ffi::OsStr;
|
|
||||||
|
struct StaticCallbacks {
|
||||||
|
|
||||||
|
}
|
||||||
|
impl StaticCallbacks {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
pub struct LibretroApi<'a> {
|
pub struct LibretroApi<'a> {
|
||||||
pub retro_set_environment:
|
pub retro_set_environment:
|
||||||
|
@ -90,26 +98,27 @@ impl<'a> LibretroApi<'a> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// retro_set_environment() must be called before retro_init().
|
/// set_environment() must be called before init().
|
||||||
pub fn set_environment(&self, cb: retro_environment_t) {
|
pub fn set_environment(&self, cb: retro_environment_t) {
|
||||||
unsafe { (&self.retro_set_environment)(cb) }
|
unsafe { (&self.retro_set_environment)(cb) }
|
||||||
}
|
}
|
||||||
|
/// video_refresh() must be called before run().
|
||||||
pub fn set_video_refresh(&self, cb: retro_video_refresh_t) {
|
pub fn set_video_refresh(&self, cb: retro_video_refresh_t) {
|
||||||
unsafe { (&self.retro_set_video_refresh)(env) }
|
unsafe { (&self.retro_set_video_refresh)(cb) }
|
||||||
}
|
}
|
||||||
pub fn set_audio_sample(&self, cb: retro_audio_sample_t) {
|
pub fn set_audio_sample(&self, cb: retro_audio_sample_t) {
|
||||||
unsafe { (&self.retro_set_audio_sample)(env) }
|
unsafe { (&self.retro_set_audio_sample)(cb) }
|
||||||
}
|
}
|
||||||
pub fn set_audio_sample_batch(&self, cb: retro_audio_sample_batch_t) {
|
pub fn set_audio_sample_batch(&self, cb: retro_audio_sample_batch_t) {
|
||||||
unsafe { (&self.retro_set_audio_sample_batch)(env) }
|
unsafe { (&self.retro_set_audio_sample_batch)(cb) }
|
||||||
}
|
}
|
||||||
pub fn set_input_poll(&self, cb: retro_input_poll_t) {
|
pub fn set_input_poll(&self, cb: retro_input_poll_t) {
|
||||||
unsafe { (&self.retro_set_input_poll)(env) }
|
unsafe { (&self.retro_set_input_poll)(cb) }
|
||||||
}
|
}
|
||||||
pub fn set_input_state(&self, cb: retro_input_state_t) {
|
pub fn set_input_state(&self, cb: retro_input_state_t) {
|
||||||
unsafe { (&self.retro_set_input_state)(env) }
|
unsafe { (&self.retro_set_input_state)(cb) }
|
||||||
}
|
}
|
||||||
/// retro_set_environment() must be called before retro_init().
|
/// set_environment() must be called before init().
|
||||||
pub fn init(&self) {
|
pub fn init(&self) {
|
||||||
// TODO assert!(called retro_set_environment);
|
// TODO assert!(called retro_set_environment);
|
||||||
unsafe { (&self.retro_init)() }
|
unsafe { (&self.retro_init)() }
|
||||||
|
@ -130,9 +139,8 @@ impl<'a> LibretroApi<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/** Gets information about system audio/video timings and geometry.
|
/** Gets information about system audio/video timings and geometry.
|
||||||
* Can be called only after retro_load_game() has successfully completed.
|
* Can be called only after load_game() has successfully completed.
|
||||||
* NOTE: The implementation of this function might not initialize every
|
* NOTE: The implementation of this function might not initialize every variable if needed.
|
||||||
* variable if needed.
|
|
||||||
* E.g. geom.aspect_ratio might not be initialized if core doesn't
|
* E.g. geom.aspect_ratio might not be initialized if core doesn't
|
||||||
* desire a particular aspect ratio. */
|
* desire a particular aspect ratio. */
|
||||||
pub fn get_system_av_info(&self) -> retro_system_av_info {
|
pub fn get_system_av_info(&self) -> retro_system_av_info {
|
||||||
|
@ -232,10 +240,10 @@ impl<'a> LibretroApi<'a> {
|
||||||
pub fn unload_game(&self) {
|
pub fn unload_game(&self) {
|
||||||
unsafe { (&self.retro_unload_game)() }
|
unsafe { (&self.retro_unload_game)() }
|
||||||
}
|
}
|
||||||
pub fn get_region(&self) -> u32 {
|
pub fn get_region(&self) -> Region {
|
||||||
unsafe { (&self.retro_get_region)() as u32 }
|
unsafe { (&self.retro_get_region)().try_into().expect("Invalid region") }
|
||||||
}
|
}
|
||||||
/// Gets region of memory
|
/// Gets (read/write access to) a region of memory
|
||||||
pub fn get_memory(&self, id: u32) -> &mut [u8] {
|
pub fn get_memory(&self, id: u32) -> &mut [u8] {
|
||||||
unsafe {
|
unsafe {
|
||||||
let data = (&self.retro_get_memory_data)(id);
|
let data = (&self.retro_get_memory_data)(id);
|
||||||
|
|
|
@ -6,6 +6,7 @@ use std::ffi::CStr;
|
||||||
#[allow(non_camel_case_types, non_upper_case_globals, non_snake_case, dead_code)]
|
#[allow(non_camel_case_types, non_upper_case_globals, non_snake_case, dead_code)]
|
||||||
mod libretro_types;
|
mod libretro_types;
|
||||||
mod libretro_loading;
|
mod libretro_loading;
|
||||||
|
mod libretro_convert;
|
||||||
|
|
||||||
fn main() -> failure::Fallible<()> {
|
fn main() -> failure::Fallible<()> {
|
||||||
let lib = libloading::Library::new("/home/lifning/.config/retroarch/cores/gambatte_libretro.so")?;
|
let lib = libloading::Library::new("/home/lifning/.config/retroarch/cores/gambatte_libretro.so")?;
|
||||||
|
|
Loading…
Add table
Reference in a new issue