refactor stdlib components into submods

This commit is contained in:
lifning 2021-11-02 17:49:49 -07:00
parent c94400f1b6
commit 11e014b9ed
14 changed files with 217 additions and 158 deletions

View File

@ -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<String> {
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<Variable2>) -> Option<bool> {
for v in variables {
eprintln!("{:?}", v);
}
Some(true)
}
fn get_libretro_path(&mut self) -> Option<PathBuf> {
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<SubsystemInfo2>) -> Option<bool> {
println!("subsystem info: {:?}", subsystem_info);
Some(true)
}
fn set_controller_info(&mut self, controller_info: &Vec<ControllerDescription2>) -> Option<bool> {
fn set_controller_info(&mut self, controller_info: &[ControllerDescription2]) -> Option<bool> {
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<InputDescriptor2>) -> Option<bool> {
for id in descriptors {
println!("{:?}", id);
}
Some(true)
}
fn set_geometry(&mut self, geom: &GameGeometry) -> Option<bool> {
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);

View File

@ -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};

View File

@ -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<Vec<u8>> {
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);

View File

@ -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> {

View File

@ -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<bool> { 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<bool> { 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<bool> { 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<PathBuf> { 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<InputDescriptor2>) -> Option<bool> { None }
fn set_input_descriptors(&mut self, input_descriptors: &[InputDescriptor2]) -> Option<bool> { 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<Variable2>) -> Option<bool> { None }
fn set_variables(&mut self, variables: &[Variable2]) -> Option<bool> { 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<SubsystemInfo2>) -> Option<bool> { None }
fn set_subsystem_info(&mut self, subsystem_info: &[SubsystemInfo2]) -> Option<bool> { 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<ControllerDescription2>) -> Option<bool> { None }
fn set_controller_info(&mut self, controller_info: &[ControllerDescription2]) -> Option<bool> { 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<ControllerDescription2> = 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);
}
_ => {}
}
}
}

View File

@ -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<InputDescriptor2>) -> Option<bool> {
fn set_input_descriptors(&mut self, input_descriptors: &[InputDescriptor2]) -> Option<bool> {
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<Variable2>) -> Option<bool> {
fn set_variables(&mut self, variables: &[Variable2]) -> Option<bool> {
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<SubsystemInfo2>) -> Option<bool> {
fn set_subsystem_info(&mut self, subsystem_info: &[SubsystemInfo2]) -> Option<bool> {
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<ControllerDescription2>) -> Option<bool> {
fn set_controller_info(&mut self, controller_info: &[ControllerDescription2]) -> Option<bool> {
self.cached_controller_info = Some(controller_info.to_vec());
self.components.iter_mut()
.map(|comp| comp.set_controller_info(controller_info))

View File

@ -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};

View File

@ -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<bool> {
self.fps_manager.set_framerate(system_av_info.timing.fps.round() as u32).ok().map(|_| true)
}
}

View File

@ -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<ControllerDescription2>) -> Option<bool> {
fn set_controller_info(&mut self, controller_info: &[ControllerDescription2]) -> Option<bool> {
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<Button> {
fn button_map(retro_button: &JoypadButton) -> Option<sdl2::controller::Button> {
use sdl2::controller::Button;
match retro_button {
JoypadButton::B => Some(Button::A),
JoypadButton::Y => Some(Button::X),

View File

@ -1,14 +1,16 @@
//! [RetroComponent](crate::base::RetroComponent)s implementing interactive gameplay functionality
//! with SDL2.
mod canvas;
mod audio;
mod canvas;
mod fps;
mod gamepad;
mod opengl;
mod surface;
pub use canvas::SimpleSdl2CanvasComponent;
pub use opengl::SimpleSdl2OpenglComponent;
pub use audio::SimpleSdl2AudioComponent;
pub use canvas::SimpleSdl2CanvasComponent;
pub use fps::SimpleSdl2FramerateLimitComponent;
pub use gamepad::SimpleSdl2GamepadComponent;
pub use opengl::SimpleSdl2OpenglComponent;
pub use surface::Sdl2SurfaceComponent;

View File

@ -0,0 +1,63 @@
use crate::base::ControlFlow;
use crate::prelude::*;
use std::time::{Duration, Instant};
/// Uses [std::thread::sleep] to maintain the target FPS specified by the core.
///
/// The sleep occurs during either `video_refresh` or at the end of `run()`.
pub struct SleepFramerateLimitComponent {
did_sleep: bool,
started_video: bool,
fps: f64,
frame_begin: Instant,
}
impl RetroComponent for SleepFramerateLimitComponent {
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.do_sleep();
}
ControlFlow::Continue
}
}
impl RetroCallbacks for SleepFramerateLimitComponent {
fn video_refresh(&mut self, _frame: &VideoFrame) {
self.started_video = true;
self.do_sleep();
}
fn set_system_av_info(&mut self, system_av_info: &SystemAvInfo) -> Option<bool> {
self.fps = system_av_info.timing.fps;
Some(true)
}
}
impl SleepFramerateLimitComponent {
pub fn new(retro: &mut LibretroWrapper) -> Self {
SleepFramerateLimitComponent {
did_sleep: false,
started_video: false,
fps: retro.get_system_av_info().timing.fps,
frame_begin: Instant::now(),
}
}
pub fn do_sleep(&mut self) {
// similar hack to the sample rate, make sure we don't divide by zero.
let mut spf = 1.0 / self.fps;
if spf.is_nan() || spf.is_infinite() {
spf = 1.0 / 60.0;
}
Duration::from_secs_f64(spf)
.checked_sub(self.frame_begin.elapsed())
.map(std::thread::sleep);
self.did_sleep = true;
self.frame_begin = Instant::now();
}
}

View File

@ -1,38 +1,9 @@
//! Generally-useful [RetroComponent](crate::base::RetroComponent)s that have no dependencies
//! outside of [std].
use crate::prelude::*;
use ferretro_base::retro::ffi::{Message, Language};
use std::os::raw::c_uint;
use std::path::PathBuf;
use std::time::{Duration, Instant};
use crate::base::ControlFlow;
use crate::prelude::*;
use ferretro_base::retro::ffi::{Message, Language};
/// Provides paths to the BIOS ROMs, core library, assets, and saves to cores that need them.
#[derive(Default)]
pub struct PathBufComponent {
pub sys_path: Option<PathBuf>,
pub libretro_path: Option<PathBuf>,
pub core_assets_path: Option<PathBuf>,
pub save_path: Option<PathBuf>,
}
impl RetroComponent for PathBufComponent {}
impl RetroCallbacks for PathBufComponent {
fn get_system_directory(&mut self) -> Option<PathBuf> {
self.sys_path.clone()
}
fn get_libretro_path(&mut self) -> Option<PathBuf> {
self.libretro_path.clone()
}
fn get_core_assets_directory(&mut self) -> Option<PathBuf> {
self.core_assets_path.clone()
}
fn get_save_directory(&mut self) -> Option<PathBuf> {
self.save_path.clone()
}
}
/// Write's the core's own log statements to stderr.
#[derive(Default)]
@ -42,6 +13,7 @@ pub struct StderrLogComponent {
}
impl RetroComponent for StderrLogComponent {}
impl RetroCallbacks for StderrLogComponent {
fn log_print(&mut self, level: ferretro_base::retro::ffi::LogLevel, msg: &str) {
eprint!("{}[{:?}] {}", self.prefix, level, msg);
@ -57,22 +29,23 @@ pub struct StderrSysInfoLogComponent {
}
impl RetroComponent for StderrSysInfoLogComponent {}
impl RetroCallbacks for StderrSysInfoLogComponent {
fn set_input_descriptors(&mut self, descriptors: &Vec<InputDescriptor2>) -> Option<bool> {
fn set_input_descriptors(&mut self, descriptors: &[InputDescriptor2]) -> Option<bool> {
for id in descriptors {
eprintln!("{}{:?}", self.prefix, id);
}
Some(true)
}
fn set_variables(&mut self, variables: &Vec<Variable2>) -> Option<bool> {
fn set_variables(&mut self, variables: &[Variable2]) -> Option<bool> {
for v in variables {
eprintln!("{}{:?}", self.prefix, v);
}
Some(true)
}
fn set_subsystem_info(&mut self, subsystem_info: &Vec<SubsystemInfo2>) -> Option<bool> {
fn set_subsystem_info(&mut self, subsystem_info: &[SubsystemInfo2]) -> Option<bool> {
for s in subsystem_info {
eprintln!("{}{:?}", self.prefix, s);
}
@ -88,6 +61,7 @@ pub struct StderrCallTraceComponent {
}
impl RetroComponent for StderrCallTraceComponent {}
impl RetroCallbacks for StderrCallTraceComponent {
fn video_refresh(&mut self, frame: &VideoFrame) {
eprintln!("{}video_refresh({:?})", self.prefix, frame);
@ -131,7 +105,7 @@ impl RetroCallbacks for StderrCallTraceComponent {
eprintln!("{}set_pixel_format({:?})", self.prefix, format);
None
}
fn set_input_descriptors(&mut self, input_descriptors: &Vec<InputDescriptor2>) -> Option<bool> {
fn set_input_descriptors(&mut self, input_descriptors: &[InputDescriptor2]) -> Option<bool> {
eprintln!("{}set_input_descriptors(vec![InputDescriptor2; {}])", self.prefix, input_descriptors.len());
None
}
@ -143,7 +117,7 @@ impl RetroCallbacks for StderrCallTraceComponent {
eprintln!("{}get_variable({:?})", self.prefix, key);
None
}
fn set_variables(&mut self, variables: &Vec<Variable2>) -> Option<bool> {
fn set_variables(&mut self, variables: &[Variable2]) -> Option<bool> {
eprintln!("{}set_variables(vec![Variable2; {}])", self.prefix, variables.len());
None
}
@ -175,11 +149,11 @@ impl RetroCallbacks for StderrCallTraceComponent {
eprintln!("{}set_system_av_info({:?})", self.prefix, system_av_info);
None
}
fn set_subsystem_info(&mut self, subsystem_info: &Vec<SubsystemInfo2>) -> Option<bool> {
fn set_subsystem_info(&mut self, subsystem_info: &[SubsystemInfo2]) -> Option<bool> {
eprintln!("{}set_subsystem_info(vec![SubsystemInfo2; {}])", self.prefix, subsystem_info.len());
None
}
fn set_controller_info(&mut self, controller_info: &Vec<ControllerDescription2>) -> Option<bool> {
fn set_controller_info(&mut self, controller_info: &[ControllerDescription2]) -> Option<bool> {
eprintln!("{}set_controller_info(vec![ControllerDescription2; {}])", self.prefix, controller_info.len());
None
}
@ -209,58 +183,3 @@ impl RetroCallbacks for StderrCallTraceComponent {
}
// TODO: etc...
}
/// Uses [std::thread::sleep] to maintain the target FPS specified by the core.
///
/// The sleep occurs during either `video_refresh` or at the end of `run()`.
pub struct SleepFramerateLimitComponent {
did_sleep: bool,
fps: f64,
frame_begin: Instant,
}
impl RetroComponent for SleepFramerateLimitComponent {
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.did_sleep {
self.do_sleep();
}
ControlFlow::Continue
}
}
impl RetroCallbacks for SleepFramerateLimitComponent {
fn video_refresh(&mut self, _frame: &VideoFrame) {
self.do_sleep();
}
fn set_system_av_info(&mut self, system_av_info: &SystemAvInfo) -> Option<bool> {
self.fps = system_av_info.timing.fps;
Some(true)
}
}
impl SleepFramerateLimitComponent {
pub fn new(retro: &mut LibretroWrapper) -> Self {
SleepFramerateLimitComponent {
did_sleep: false,
fps: retro.get_system_av_info().timing.fps,
frame_begin: Instant::now(),
}
}
pub fn do_sleep(&mut self) {
// similar hack to the sample rate, make sure we don't divide by zero.
let mut spf = 1.0 / self.fps;
if spf.is_nan() || spf.is_infinite() {
spf = 1.0 / 60.0;
}
Duration::from_secs_f64(spf)
.checked_sub(self.frame_begin.elapsed())
.map(std::thread::sleep);
self.did_sleep = true;
self.frame_begin = Instant::now();
}
}

View File

@ -0,0 +1,12 @@
//! Generally-useful [RetroComponent](crate::base::RetroComponent)s that have no dependencies
//! outside of [std].
mod fps;
mod logs;
mod paths;
pub use fps::SleepFramerateLimitComponent;
pub use logs::StderrCallTraceComponent;
pub use logs::StderrLogComponent;
pub use logs::StderrSysInfoLogComponent;
pub use paths::PathBufComponent;

View File

@ -0,0 +1,28 @@
use std::path::PathBuf;
use crate::prelude::*;
/// Provides paths to the BIOS ROMs, core library, assets, and saves to cores that need them.
#[derive(Default)]
pub struct PathBufComponent {
pub sys_path: Option<PathBuf>,
pub libretro_path: Option<PathBuf>,
pub core_assets_path: Option<PathBuf>,
pub save_path: Option<PathBuf>,
}
impl RetroComponent for PathBufComponent {}
impl RetroCallbacks for PathBufComponent {
fn get_system_directory(&mut self) -> Option<PathBuf> {
self.sys_path.clone()
}
fn get_libretro_path(&mut self) -> Option<PathBuf> {
self.libretro_path.clone()
}
fn get_core_assets_directory(&mut self) -> Option<PathBuf> {
self.core_assets_path.clone()
}
fn get_save_directory(&mut self) -> Option<PathBuf> {
self.save_path.clone()
}
}