267 lines
9.0 KiB
Rust
267 lines
9.0 KiB
Rust
//! Generally-useful [RetroComponent](crate::base::RetroComponent)s that have no dependencies
|
|
//! outside of [std].
|
|
|
|
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)]
|
|
pub struct StderrLogComponent {
|
|
/// May be set to add a prefix to each logged line.
|
|
pub prefix: String,
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
/// Writes all the input descriptors, variables, and subsystem information to stderr as they are
|
|
/// provided by the core.
|
|
#[derive(Default)]
|
|
pub struct StderrSysInfoLogComponent {
|
|
/// May be set to add a prefix to each logged line.
|
|
pub prefix: String,
|
|
}
|
|
|
|
impl RetroComponent for StderrSysInfoLogComponent {}
|
|
impl RetroCallbacks for StderrSysInfoLogComponent {
|
|
fn set_input_descriptors(&mut self, descriptors: &Vec<InputDescriptor2>) -> Option<bool> {
|
|
for id in descriptors {
|
|
eprintln!("{}{:?}", self.prefix, id);
|
|
}
|
|
Some(true)
|
|
}
|
|
|
|
fn set_variables(&mut self, variables: &Vec<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> {
|
|
for s in subsystem_info {
|
|
eprintln!("{}{:?}", self.prefix, s);
|
|
}
|
|
Some(true)
|
|
}
|
|
}
|
|
|
|
/// Trace-logs every callback call made and their arguments to stderr.
|
|
#[derive(Default)]
|
|
pub struct StderrCallTraceComponent {
|
|
/// May be set to add a prefix to each logged line.
|
|
pub prefix: String,
|
|
}
|
|
|
|
impl RetroComponent for StderrCallTraceComponent {}
|
|
impl RetroCallbacks for StderrCallTraceComponent {
|
|
fn video_refresh(&mut self, frame: &VideoFrame) {
|
|
eprintln!("{}video_refresh({:?})", self.prefix, frame);
|
|
}
|
|
fn audio_samples(&mut self, stereo_pcm: &[i16]) -> usize {
|
|
eprintln!("{}audio_samples([i16; {}])", self.prefix, stereo_pcm.len());
|
|
0
|
|
}
|
|
fn input_poll(&mut self) {
|
|
eprintln!("{}input_poll()", self.prefix);
|
|
}
|
|
fn input_state(&mut self, port: u32, device: InputDeviceId, index: InputIndex) -> i16 {
|
|
eprintln!("{}input_state({}, {:?}, {:?})", self.prefix, port, device, index);
|
|
0
|
|
}
|
|
fn set_rotation(&mut self, rotation: EnvRotation) -> Option<bool> {
|
|
eprintln!("{}set_rotation({:?})", self.prefix, rotation);
|
|
None
|
|
}
|
|
fn get_overscan(&mut self) -> Option<bool> {
|
|
eprintln!("{}get_overscan()", self.prefix);
|
|
None
|
|
}
|
|
fn set_message(&mut self, message: &Message) -> Option<bool> {
|
|
eprintln!("{}set_message({:?})", self.prefix, message);
|
|
None
|
|
}
|
|
fn shutdown(&mut self) -> Option<bool> {
|
|
eprintln!("{}shutdown()", self.prefix);
|
|
None
|
|
}
|
|
fn set_performance_level(&mut self, level: c_uint) -> Option<bool> {
|
|
eprintln!("{}set_performance_level({})", self.prefix, level);
|
|
None
|
|
}
|
|
fn get_system_directory(&mut self) -> Option<PathBuf> {
|
|
eprintln!("{}get_system_directory()", self.prefix);
|
|
None
|
|
}
|
|
fn set_pixel_format(&mut self, format: PixelFormat) -> Option<bool> {
|
|
eprintln!("{}set_pixel_format({:?})", self.prefix, format);
|
|
None
|
|
}
|
|
fn set_input_descriptors(&mut self, input_descriptors: &Vec<InputDescriptor2>) -> Option<bool> {
|
|
eprintln!("{}set_input_descriptors(vec![InputDescriptor2; {}])", self.prefix, input_descriptors.len());
|
|
None
|
|
}
|
|
fn set_hw_render(&mut self, hw_render_callback: &HwRenderCallback) -> Option<bool> {
|
|
eprintln!("{}set_hw_render({:?})", self.prefix, hw_render_callback);
|
|
None
|
|
}
|
|
fn get_variable(&mut self, key: &str) -> Option<String> {
|
|
eprintln!("{}get_variable({:?})", self.prefix, key);
|
|
None
|
|
}
|
|
fn set_variables(&mut self, variables: &Vec<Variable2>) -> Option<bool> {
|
|
eprintln!("{}set_variables(vec![Variable2; {}])", self.prefix, variables.len());
|
|
None
|
|
}
|
|
fn get_variable_update(&mut self) -> Option<bool> {
|
|
eprintln!("{}get_variable_update()", self.prefix);
|
|
None
|
|
}
|
|
fn set_support_no_game(&mut self, supports_no_game: bool) -> Option<bool> {
|
|
eprintln!("{}set_support_no_game({})", self.prefix, supports_no_game);
|
|
None
|
|
}
|
|
fn get_libretro_path(&mut self) -> Option<PathBuf> {
|
|
eprintln!("{}get_libretro_path()", self.prefix);
|
|
None
|
|
}
|
|
fn get_input_device_capabilities(&mut self) -> Option<u64> {
|
|
eprintln!("{}get_input_device_capabilities()", self.prefix);
|
|
None
|
|
}
|
|
fn get_core_assets_directory(&mut self) -> Option<PathBuf> {
|
|
eprintln!("{}get_core_assets_directory()", self.prefix);
|
|
None
|
|
}
|
|
fn get_save_directory(&mut self) -> Option<PathBuf> {
|
|
eprintln!("{}get_save_directory()", self.prefix);
|
|
None
|
|
}
|
|
fn set_system_av_info(&mut self, system_av_info: &SystemAvInfo) -> Option<bool> {
|
|
eprintln!("{}set_system_av_info({:?})", self.prefix, system_av_info);
|
|
None
|
|
}
|
|
fn set_subsystem_info(&mut self, subsystem_info: &Vec<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> {
|
|
eprintln!("{}set_controller_info(vec![ControllerDescription2; {}])", self.prefix, controller_info.len());
|
|
None
|
|
}
|
|
fn set_memory_maps(&mut self, memory_map: &MemoryMap) -> Option<bool> {
|
|
eprintln!("{}set_memory_maps({:?})", self.prefix, memory_map);
|
|
None
|
|
}
|
|
fn set_geometry(&mut self, game_geometry: &GameGeometry) -> Option<bool> {
|
|
eprintln!("{}set_geometry({:?})", self.prefix, game_geometry);
|
|
None
|
|
}
|
|
fn get_username(&mut self) -> Option<String> {
|
|
eprintln!("{}get_username()", self.prefix);
|
|
None
|
|
}
|
|
fn get_language(&mut self) -> Option<Language> {
|
|
eprintln!("{}get_language()", self.prefix);
|
|
None
|
|
}
|
|
fn hw_get_current_framebuffer(&mut self) -> Option<usize> {
|
|
eprintln!("{}hw_get_current_framebuffer()", self.prefix);
|
|
None
|
|
}
|
|
fn hw_get_proc_address(&mut self, sym: &str) -> Option<*const ()> {
|
|
eprintln!("{}hw_get_proc_address({:?})", self.prefix, sym);
|
|
None
|
|
}
|
|
// 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();
|
|
}
|
|
}
|