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}; #[derive(Default)] pub struct PathBufComponent { pub sys_path: Option, pub libretro_path: Option, pub core_assets_path: Option, pub save_path: Option, } impl RetroComponent for PathBufComponent {} impl RetroCallbacks for PathBufComponent { fn get_system_directory(&mut self) -> Option { self.sys_path.clone() } fn get_libretro_path(&mut self) -> Option { self.libretro_path.clone() } fn get_core_assets_directory(&mut self) -> Option { self.core_assets_path.clone() } fn get_save_directory(&mut self) -> Option { self.save_path.clone() } } #[derive(Default)] pub struct StderrLogComponent { 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); } } #[derive(Default)] pub struct StderrSysInfoLogComponent { pub prefix: String, } impl RetroComponent for StderrSysInfoLogComponent {} impl RetroCallbacks for StderrSysInfoLogComponent { fn set_input_descriptors(&mut self, descriptors: &Vec) -> Option { for id in descriptors { eprintln!("{}{:?}", self.prefix, id); } Some(true) } fn set_variables(&mut self, variables: &Vec) -> Option { for v in variables { eprintln!("{}{:?}", self.prefix, v); } Some(true) } fn set_subsystem_info(&mut self, subsystem_info: &Vec) -> Option { for s in subsystem_info { eprintln!("{}{:?}", self.prefix, s); } Some(true) } } #[derive(Default)] pub struct StderrCallTraceComponent { pub prefix: String, } impl RetroComponent for StderrCallTraceComponent {} impl RetroCallbacks for StderrCallTraceComponent { fn video_refresh(&mut self, data: &[u8], width: c_uint, height: c_uint, pitch: c_uint) { eprintln!("{}video_refresh([u8; {}], {}, {}, {})", self.prefix, data.len(), width, height, pitch); } fn video_refresh_dupe(&mut self, width: c_uint, height: c_uint, pitch: c_uint) { eprintln!("{}video_refresh_dupe({}, {}, {})", self.prefix, width, height, pitch); } fn video_refresh_hw(&mut self, width: c_uint, height: c_uint) { eprintln!("{}video_refresh_hw({}, {})", self.prefix, width, height); } fn audio_sample(&mut self, left: i16, right: i16) { eprintln!("{}audio_sample({}, {})", self.prefix, left, right); } fn audio_sample_batch(&mut self, stereo_pcm: &[i16]) -> usize { eprintln!("{}audio_sample_batch([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 { eprintln!("{}set_rotation({:?})", self.prefix, rotation); None } fn get_overscan(&mut self) -> Option { eprintln!("{}get_overscan()", self.prefix); None } fn set_message(&mut self, message: &Message) -> Option { eprintln!("{}set_message({:?})", self.prefix, message); None } fn shutdown(&mut self) -> Option { eprintln!("{}shutdown()", self.prefix); None } fn set_performance_level(&mut self, level: c_uint) -> Option { eprintln!("{}set_performance_level({})", self.prefix, level); None } fn get_system_directory(&mut self) -> Option { eprintln!("{}get_system_directory()", self.prefix); None } fn set_pixel_format(&mut self, format: PixelFormat) -> Option { eprintln!("{}set_pixel_format({:?})", self.prefix, format); None } fn set_input_descriptors(&mut self, input_descriptors: &Vec) -> Option { eprintln!("{}set_input_descriptors(vec![InputDescriptor2; {}])", self.prefix, input_descriptors.len()); None } fn set_hw_render(&mut self, hw_render_callback: &HwRenderCallback) -> Option { eprintln!("{}set_hw_render({:?})", self.prefix, hw_render_callback); None } fn get_variable(&mut self, key: &str) -> Option { eprintln!("{}get_variable({:?})", self.prefix, key); None } fn set_variables(&mut self, variables: &Vec) -> Option { eprintln!("{}set_variables(vec![Variable2; {}])", self.prefix, variables.len()); None } fn get_variable_update(&mut self) -> Option { eprintln!("{}get_variable_update()", self.prefix); None } fn set_support_no_game(&mut self, supports_no_game: bool) -> Option { eprintln!("{}set_support_no_game({})", self.prefix, supports_no_game); None } fn get_libretro_path(&mut self) -> Option { eprintln!("{}get_libretro_path()", self.prefix); None } fn get_input_device_capabilities(&mut self) -> Option { eprintln!("{}get_input_device_capabilities()", self.prefix); None } fn get_core_assets_directory(&mut self) -> Option { eprintln!("{}get_core_assets_directory()", self.prefix); None } fn get_save_directory(&mut self) -> Option { eprintln!("{}get_save_directory()", self.prefix); None } fn set_system_av_info(&mut self, system_av_info: &SystemAvInfo) -> Option { eprintln!("{}set_system_av_info({:?})", self.prefix, system_av_info); None } fn set_subsystem_info(&mut self, subsystem_info: &Vec) -> Option { eprintln!("{}set_subsystem_info(vec![SubsystemInfo2; {}])", self.prefix, subsystem_info.len()); None } fn set_controller_info(&mut self, controller_info: &Vec) -> Option { eprintln!("{}set_controller_info(vec![ControllerDescription2; {}])", self.prefix, controller_info.len()); None } fn set_memory_maps(&mut self, memory_map: &MemoryMap) -> Option { eprintln!("{}set_memory_maps({:?})", self.prefix, memory_map); None } fn set_geometry(&mut self, game_geometry: &GameGeometry) -> Option { eprintln!("{}set_geometry({:?})", self.prefix, game_geometry); None } fn get_username(&mut self) -> Option { eprintln!("{}get_username()", self.prefix); None } fn get_language(&mut self) -> Option { eprintln!("{}get_language()", self.prefix); None } fn hw_get_current_framebuffer(&mut self) -> Option { 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... } 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, _data: &[u8], _width: c_uint, _height: c_uint, _pitch: c_uint) { self.do_sleep(); } fn video_refresh_dupe(&mut self, _width: c_uint, _height: c_uint, _pitch: c_uint) { self.do_sleep(); } fn video_refresh_hw(&mut self, _width: c_uint, _height: c_uint) { self.do_sleep(); } fn set_system_av_info(&mut self, system_av_info: &SystemAvInfo) -> Option { 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(), } } 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(); } }