Start making nicer types for interop with Rust

This commit is contained in:
lif 2019-11-07 18:43:14 -08:00
parent e34b9daa38
commit 0d0dda356c
4 changed files with 163 additions and 15 deletions

View File

@ -7,6 +7,7 @@ edition = "2018"
[dependencies]
failure = "^0.1"
libloading = "^0.5"
num_enum = "^0.4"
[build-dependencies]
bindgen = "^0.51" # keep an eye on https://github.com/rust-lang/rust-bindgen/issues/1541

138
src/libretro_convert.rs Normal file
View 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

View File

@ -1,11 +1,19 @@
use std::convert::TryInto;
use std::os::raw::{c_char, c_uint, c_void};
use std::path::Path;
use failure::Fallible;
use libloading;
use crate::libretro_types::*;
use libloading;
use std::ffi::OsStr;
use crate::libretro_convert::*;
struct StaticCallbacks {
}
impl StaticCallbacks {
}
pub struct LibretroApi<'a> {
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) {
unsafe { (&self.retro_set_environment)(cb) }
}
/// video_refresh() must be called before run().
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) {
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) {
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) {
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) {
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) {
// TODO assert!(called retro_set_environment);
unsafe { (&self.retro_init)() }
@ -130,9 +139,8 @@ impl<'a> LibretroApi<'a> {
}
}
/** Gets information about system audio/video timings and geometry.
* Can be called only after retro_load_game() has successfully completed.
* NOTE: The implementation of this function might not initialize every
* variable if needed.
* Can be called only after load_game() has successfully completed.
* NOTE: The implementation of this function might not initialize every variable if needed.
* E.g. geom.aspect_ratio might not be initialized if core doesn't
* desire a particular aspect ratio. */
pub fn get_system_av_info(&self) -> retro_system_av_info {
@ -232,10 +240,10 @@ impl<'a> LibretroApi<'a> {
pub fn unload_game(&self) {
unsafe { (&self.retro_unload_game)() }
}
pub fn get_region(&self) -> u32 {
unsafe { (&self.retro_get_region)() as u32 }
pub fn get_region(&self) -> Region {
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] {
unsafe {
let data = (&self.retro_get_memory_data)(id);

View File

@ -6,6 +6,7 @@ use std::ffi::CStr;
#[allow(non_camel_case_types, non_upper_case_globals, non_snake_case, dead_code)]
mod libretro_types;
mod libretro_loading;
mod libretro_convert;
fn main() -> failure::Fallible<()> {
let lib = libloading::Library::new("/home/lifning/.config/retroarch/cores/gambatte_libretro.so")?;