diff --git a/ferretro_base/examples/sdl2_emulator.rs b/ferretro_base/examples/sdl2_emulator.rs index c4844c9..906c075 100644 --- a/ferretro_base/examples/sdl2_emulator.rs +++ b/ferretro_base/examples/sdl2_emulator.rs @@ -5,10 +5,11 @@ extern crate sdl2; use ferretro_base::retro; use ferretro_base::prelude::*; +use core::pin::Pin; + use std::ffi::CStr; use std::io::Read; use std::path::{Path, PathBuf}; -use std::pin::Pin; use std::time::{Duration, Instant}; use structopt::StructOpt; @@ -310,6 +311,7 @@ impl retro::wrapper::RetroCallbacks for MyEmulator { }; Some(true) } + fn get_variable(&mut self, key: &str) -> Option { match key { "beetle_saturn_analog_stick_deadzone" => Some("15%".to_string()), @@ -319,13 +321,6 @@ impl retro::wrapper::RetroCallbacks for MyEmulator { } } - fn set_variables(&mut self, variables: &Vec) -> Option { - for v in variables { - eprintln!("{:?}", v); - } - Some(true) - } - fn get_libretro_path(&mut self) -> Option { Some(self.core_path.clone()) } @@ -345,12 +340,7 @@ impl retro::wrapper::RetroCallbacks for MyEmulator { Some(true) } - fn set_subsystem_info(&mut self, subsystem_info: &Vec) -> Option { - println!("subsystem info: {:?}", subsystem_info); - Some(true) - } - - fn set_controller_info(&mut self, controller_info: &Vec) -> Option { + fn set_controller_info(&mut self, controller_info: &[ControllerDescription2]) -> Option { for ci in controller_info { // so we can have analog support in beetle/mednafen saturn if ci.name.as_str() == "3D Control Pad" { @@ -361,13 +351,6 @@ impl retro::wrapper::RetroCallbacks for MyEmulator { Some(true) } - fn set_input_descriptors(&mut self, descriptors: &Vec) -> Option { - for id in descriptors { - println!("{:?}", id); - } - Some(true) - } - fn set_geometry(&mut self, geom: &GameGeometry) -> Option { let _ = self.canvas.window_mut().set_size(geom.base_width, geom.base_height); let _ = self.canvas.set_logical_size(geom.base_width, geom.base_height); diff --git a/ferretro_base/src/lib.rs b/ferretro_base/src/lib.rs index c280b88..27a2f8f 100644 --- a/ferretro_base/src/lib.rs +++ b/ferretro_base/src/lib.rs @@ -3,6 +3,7 @@ extern crate libloading; pub mod retro; pub mod prelude { + //! Re-exports for types likely to be used by consumers of the crate. pub use crate::retro::constants::*; pub use crate::retro::wrapped_types::*; pub use crate::retro::wrapper::{RetroCallbacks, LibretroWrapper, LibretroWrapperAccess}; diff --git a/ferretro_base/src/retro/loading.rs b/ferretro_base/src/retro/loading.rs index 77a0a2f..ff29856 100644 --- a/ferretro_base/src/retro/loading.rs +++ b/ferretro_base/src/retro/loading.rs @@ -1,4 +1,5 @@ use std::ffi::CString; + use std::os::raw::{c_char, c_void}; use std::path::Path; @@ -131,8 +132,7 @@ impl LibretroApi { /// Serializes internal state. pub fn serialize(&mut self) -> Result> { let size: usize = unsafe { (&self.core_api.retro_serialize_size)() }; - let mut vec = Vec::with_capacity(size); - vec.resize(size, 0); + let mut vec = vec![0; size]; // FIXME: libretro-sys incorrectly says retro_serialize is a void(), not a bool() if unsafe { (&self.core_api.retro_serialize)(vec.as_mut_ptr() as *mut c_void, size); true } { Ok(vec) @@ -140,7 +140,8 @@ impl LibretroApi { Err("Serialize failed".into()) } } - pub fn unserialize(&mut self, data: &[u8]) -> Result<()> { + pub fn unserialize(&mut self, data: impl AsRef<[u8]>) -> Result<()> { + let data = data.as_ref(); // validate size of the data let size: usize = unsafe { (&self.core_api.retro_serialize_size)() }; if data.len() != size { @@ -201,8 +202,16 @@ impl LibretroApi { pub fn get_region(&self) -> Region { unsafe { std::mem::transmute((&self.core_api.retro_get_region)()) } } - /// Gets (read/write access to) a region of memory - pub fn get_memory(&self, id: u32) -> &mut [u8] { + /// Gets read access to a region of memory + pub fn get_memory(&self, id: u32) -> &[u8] { + unsafe { + let data = (&self.core_api.retro_get_memory_data)(id); + let size = (&self.core_api.retro_get_memory_size)(id); + std::slice::from_raw_parts(data as *const u8, size) + } + } + /// Gets write access to a region of memory + pub fn mut_memory(&mut self, id: u32) -> &mut [u8] { unsafe { let data = (&self.core_api.retro_get_memory_data)(id); let size = (&self.core_api.retro_get_memory_size)(id); diff --git a/ferretro_base/src/retro/wrapped_types.rs b/ferretro_base/src/retro/wrapped_types.rs index bc5a7d2..dcdc267 100644 --- a/ferretro_base/src/retro/wrapped_types.rs +++ b/ferretro_base/src/retro/wrapped_types.rs @@ -1,11 +1,11 @@ -use std::convert::{TryFrom, TryInto}; -use std::ffi::{CStr}; +use core::convert::{TryFrom, TryInto}; +use core::mem::size_of; +use std::ffi::CStr; use std::os::raw::{c_uint}; use std::slice::from_raw_parts; use super::constants::*; use super::ffi::*; -use std::mem::size_of; #[derive(Clone, Copy, Debug)] pub enum VideoFrame<'a> { diff --git a/ferretro_base/src/retro/wrapper.rs b/ferretro_base/src/retro/wrapper.rs index cffe4c3..330a40b 100644 --- a/ferretro_base/src/retro/wrapper.rs +++ b/ferretro_base/src/retro/wrapper.rs @@ -1,14 +1,16 @@ -use core::convert::TryInto; -use core::ffi::c_void; -use core::slice::from_raw_parts; +#![allow(clippy::option_map_unit_fn)] // sorry clippy, we really need the conciseness + +use core::convert::{TryFrom, TryInto}; +use core::ffi::c_void; +use core::mem::size_of; +use core::ops::{Deref, DerefMut}; +use core::pin::Pin; +use core::slice::from_raw_parts; +use core::time::Duration; -use std::convert::TryFrom; use std::ffi::{CStr, CString}; -use std::ops::{Deref, DerefMut}; use std::os::raw::{c_char, c_uint}; use std::path::{Path, PathBuf}; -use std::pin::Pin; -use std::time::Duration; use num_enum::TryFromPrimitive; @@ -20,7 +22,6 @@ use super::wrapped_types::*; // #[cfg(doc)] <- broken as of (at least) 1.56 #[allow(unused_imports)] use libretro_sys::{self, CoreAPI}; -use std::mem::size_of; static mut CB_SINGLETON: StaticCallbacks = StaticCallbacks { handler: None, @@ -42,6 +43,7 @@ extern "C" { /// NOTE: Most of the method docs provided here are adapted to Rust from the ones written in /// libretro.h, and many of them are descriptions of the API contract written with an intended /// audience of backend/core authors. +//noinspection RsSelfConvention #[rustfmt::skip] #[allow(unused_variables)] pub trait RetroCallbacks: Unpin + 'static { @@ -82,6 +84,7 @@ pub trait RetroCallbacks: Unpin + 'static { fn get_overscan(&mut self) -> Option { None } /// Sets a message to be displayed in implementation-specific manner /// for a certain amount of 'frames'. + /// /// Should not be used for trivial messages, which should simply be /// logged via `retro_get_log_interface` (or as a fallback, stderr). fn set_message(&mut self, message: &Message) -> Option { None } @@ -105,6 +108,7 @@ pub trait RetroCallbacks: Unpin + 'static { /// If called, it should be called in [CoreAPI::retro_load_game]. fn set_performance_level(&mut self, level: c_uint) -> Option { None } /// Returns the "system" directory of the frontend. + /// /// This directory can be used to store system specific /// content such as BIOSes, configuration data, etc. /// The returned value can be `None`. @@ -118,6 +122,7 @@ pub trait RetroCallbacks: Unpin + 'static { fn get_system_directory(&mut self) -> Option { None } /// Sets the internal pixel format used by the implementation. /// The default pixel format is [libretro_sys::PixelFormat::ARGB1555]. + /// /// This pixel format however, is deprecated (see enum retro_pixel_format). /// If the call returns false, the frontend does not support this pixel /// format. @@ -129,7 +134,7 @@ pub trait RetroCallbacks: Unpin + 'static { /// It is up to the frontend to present this in a usable way. /// This function can be called at any time, but it is recommended /// for the core to call it as early as possible. - fn set_input_descriptors(&mut self, input_descriptors: &Vec) -> Option { None } + fn set_input_descriptors(&mut self, input_descriptors: &[InputDescriptor2]) -> Option { None } /// Sets an interface to let a libretro core render with /// hardware acceleration. /// The core should call this in [CoreAPI::retro_load_game]. @@ -170,7 +175,7 @@ pub trait RetroCallbacks: Unpin + 'static { /// /// Only strings are operated on. The possible values will /// generally be displayed and stored as-is by the frontend. - fn set_variables(&mut self, variables: &Vec) -> Option { None } + fn set_variables(&mut self, variables: &[Variable2]) -> Option { None } /// Result is set to true if some variables are updated by /// frontend since last call to [Self::get_variable]. /// Variables should be queried with [Self::get_variable]. @@ -261,7 +266,7 @@ pub trait RetroCallbacks: Unpin + 'static { /// /// If a core wants to expose this interface, [Self::set_subsystem_info] /// **MUST** be called from within [CoreAPI::retro_set_environment]. - fn set_subsystem_info(&mut self, subsystem_info: &Vec) -> Option { None } + fn set_subsystem_info(&mut self, subsystem_info: &[SubsystemInfo2]) -> Option { None } /// This environment call lets a libretro core tell the frontend /// which controller subclasses are recognized in calls to /// [CoreAPI::retro_set_controller_port_device]. @@ -295,7 +300,7 @@ pub trait RetroCallbacks: Unpin + 'static { /// /// NOTE: Even if special device types are set in the libretro core, /// libretro should only poll input based on the base input device types. - fn set_controller_info(&mut self, controller_info: &Vec) -> Option { None } + fn set_controller_info(&mut self, controller_info: &[ControllerDescription2]) -> Option { None } /// This environment call lets a libretro core tell the frontend /// about the memory maps this core emulates. /// This can be used to implement, for example, cheats in a core-agnostic way. @@ -607,7 +612,8 @@ impl StaticCallbacks { let info = unsafe { (data as *const ControllerInfo).as_ref() }?; // FIXME: beetle/mednafen saturn crashes without this -1... add conditional on name? let len = info.num_types as usize - 1; - let controller_info = unsafe { from_raw_parts(info.types, len) } + let slice = unsafe { from_raw_parts(info.types, len) }; + let controller_info: Vec = slice .iter() .map(TryInto::try_into) .filter_map(|x| x.ok()) @@ -744,31 +750,28 @@ impl StaticCallbacks { } extern "C" fn perf_register_cb(counter: *mut PerfCounter) { unsafe { - match (CB_SINGLETON.handler.as_mut(), counter.as_mut()) { - (Some(cb), Some(counter)) => { + if let Some(cb) = CB_SINGLETON.handler.as_mut() { + if let Some(counter) = counter.as_mut() { cb.perf_register(counter); } - _ => {} } } } extern "C" fn perf_start_cb(counter: *mut PerfCounter) { unsafe { - match (CB_SINGLETON.handler.as_mut(), counter.as_mut()) { - (Some(cb), Some(counter)) => { + if let Some(cb) = CB_SINGLETON.handler.as_mut() { + if let Some(counter) = counter.as_mut() { cb.perf_start(counter); } - _ => {} } } } extern "C" fn perf_stop_cb(counter: *mut PerfCounter) { unsafe { - match (CB_SINGLETON.handler.as_mut(), counter.as_mut()) { - (Some(cb), Some(counter)) => { + if let Some(cb) = CB_SINGLETON.handler.as_mut() { + if let Some(counter) = counter.as_mut() { cb.perf_stop(counter); } - _ => {} } } } diff --git a/ferretro_components/src/base/mod.rs b/ferretro_components/src/base/mod.rs index d951c1e..9d9d769 100644 --- a/ferretro_components/src/base/mod.rs +++ b/ferretro_components/src/base/mod.rs @@ -4,10 +4,12 @@ use crate::prelude::*; use ferretro_base::retro::ffi::*; -use std::any::{Any, TypeId}; + +use core::any::{Any, TypeId}; +use core::pin::Pin; + use std::os::raw::c_uint; use std::path::{PathBuf, Path}; -use std::pin::Pin; use std::io::Read; use std::collections::HashMap; @@ -320,7 +322,7 @@ impl RetroCallbacks for RetroComponentBase { .into() } - fn set_input_descriptors(&mut self, input_descriptors: &Vec) -> Option { + fn set_input_descriptors(&mut self, input_descriptors: &[InputDescriptor2]) -> Option { self.cached_input_descriptors = Some(input_descriptors.to_vec()); self.components.iter_mut() .map(|comp| comp.set_input_descriptors(input_descriptors)) @@ -345,7 +347,7 @@ impl RetroCallbacks for RetroComponentBase { .next() } - fn set_variables(&mut self, variables: &Vec) -> Option { + fn set_variables(&mut self, variables: &[Variable2]) -> Option { self.cached_variables = Some(variables.to_vec()); self.components.iter_mut() .map(|comp| comp.set_variables(variables)) @@ -411,7 +413,7 @@ impl RetroCallbacks for RetroComponentBase { .into() } - fn set_subsystem_info(&mut self, subsystem_info: &Vec) -> Option { + fn set_subsystem_info(&mut self, subsystem_info: &[SubsystemInfo2]) -> Option { self.cached_subsystem_info = Some(subsystem_info.to_vec()); self.components.iter_mut() .map(|comp| comp.set_subsystem_info(subsystem_info)) @@ -420,7 +422,7 @@ impl RetroCallbacks for RetroComponentBase { .into() } - fn set_controller_info(&mut self, controller_info: &Vec) -> Option { + fn set_controller_info(&mut self, controller_info: &[ControllerDescription2]) -> Option { self.cached_controller_info = Some(controller_info.to_vec()); self.components.iter_mut() .map(|comp| comp.set_controller_info(controller_info)) diff --git a/ferretro_components/src/provided/sdl2/audio.rs b/ferretro_components/src/provided/sdl2/audio.rs index c7ab904..bda1fe7 100644 --- a/ferretro_components/src/provided/sdl2/audio.rs +++ b/ferretro_components/src/provided/sdl2/audio.rs @@ -2,7 +2,7 @@ use crate::prelude::*; use std::error::Error; use std::path::Path; -use std::time::Duration; +use core::time::Duration; use sdl2::Sdl; use sdl2::audio::{AudioCallback, AudioDevice, AudioFormat, AudioSpec, AudioSpecDesired}; diff --git a/ferretro_components/src/provided/sdl2/fps.rs b/ferretro_components/src/provided/sdl2/fps.rs new file mode 100644 index 0000000..bc1fd84 --- /dev/null +++ b/ferretro_components/src/provided/sdl2/fps.rs @@ -0,0 +1,36 @@ +use crate::base::ControlFlow; +use crate::prelude::*; + +use std::os::raw::c_uint; + +use sdl2::gfx::framerate::FPSManager; + +pub struct SimpleSdl2FramerateLimitComponent { + did_sleep: bool, + started_video: bool, + fps_manager: FPSManager, +} + +impl RetroComponent for SimpleSdl2FramerateLimitComponent { + fn pre_run(&mut self, _retro: &mut LibretroWrapper) -> ControlFlow { + self.did_sleep = false; + ControlFlow::Continue + } + fn post_run(&mut self, _retro: &mut LibretroWrapper) -> ControlFlow { + if self.started_video && !self.did_sleep { + self.fps_manager.delay(); + } + ControlFlow::Continue + } +} + +impl RetroCallbacks for SimpleSdl2FramerateLimitComponent { + fn video_refresh(&mut self, _frame: &VideoFrame) { + self.started_video = true; + self.fps_manager.delay(); + self.did_sleep = true; + } + fn set_system_av_info(&mut self, system_av_info: &SystemAvInfo) -> Option { + self.fps_manager.set_framerate(system_av_info.timing.fps.round() as u32).ok().map(|_| true) + } +} diff --git a/ferretro_components/src/provided/sdl2/gamepad.rs b/ferretro_components/src/provided/sdl2/gamepad.rs index 8d9c87e..2361de5 100644 --- a/ferretro_components/src/provided/sdl2/gamepad.rs +++ b/ferretro_components/src/provided/sdl2/gamepad.rs @@ -5,7 +5,7 @@ use std::error::Error; use std::path::Path; use sdl2::Sdl; -use sdl2::controller::{Axis, Button, GameController}; +use sdl2::controller::{Axis, GameController}; use sdl2::event::Event; use sdl2::keyboard::Keycode; @@ -69,7 +69,7 @@ impl RetroCallbacks for SimpleSdl2GamepadComponent { Some(bits as u64) } - fn set_controller_info(&mut self, controller_info: &Vec) -> Option { + fn set_controller_info(&mut self, controller_info: &[ControllerDescription2]) -> Option { for ci in controller_info { // so we can have analog support in beetle/mednafen saturn if ci.name.as_str() == "3D Control Pad" { @@ -127,7 +127,8 @@ impl SimpleSdl2GamepadComponent { } } -fn button_map(retro_button: &JoypadButton) -> Option