From 2dd8556fe21c836d7f41ba17de35163213e637a3 Mon Sep 17 00:00:00 2001 From: lifning <> Date: Fri, 10 Dec 2021 02:33:43 -0800 Subject: [PATCH] fullscreen option, add keyboard component --- .../examples/multifunction_emulator.rs | 17 +++- .../src/provided/sdl2/canvas.rs | 30 ++++--- .../src/provided/sdl2/keyboard.rs | 88 +++++++++++++++++++ ferretro_components/src/provided/sdl2/mod.rs | 2 + .../src/provided/sdl2/opengl.rs | 26 +++--- 5 files changed, 139 insertions(+), 24 deletions(-) create mode 100644 ferretro_components/src/provided/sdl2/keyboard.rs diff --git a/ferretro_components/examples/multifunction_emulator.rs b/ferretro_components/examples/multifunction_emulator.rs index 874b225..6d32786 100644 --- a/ferretro_components/examples/multifunction_emulator.rs +++ b/ferretro_components/examples/multifunction_emulator.rs @@ -4,6 +4,7 @@ extern crate ffmpeg_next as ffmpeg; extern crate sdl2; use std::path::PathBuf; +use sdl2::video::FullscreenType; use structopt::StructOpt; use ferretro_components::prelude::*; @@ -21,7 +22,7 @@ struct Opt { #[structopt(short, long, parse(from_os_str))] core: PathBuf, /// ROM to load using the core. - #[structopt(short, long, parse(from_os_str))] + #[structopt(parse(from_os_str))] rom: PathBuf, /// Save state to load at startup. #[structopt(long, parse(from_os_str))] @@ -35,6 +36,9 @@ struct Opt { /// Disable OpenGL context creation. #[structopt(long)] no_opengl: bool, + /// Fullscreen (borderless). + #[structopt(short, long)] + fullscreen: bool, /// Print core-provided system information to stderr. #[structopt(long)] sys_info: bool, @@ -71,16 +75,22 @@ pub fn main() -> Result<(), Box> { emu.register_component(ffmpeg_comp)?; } + let ft = if opt.fullscreen { + FullscreenType::Desktop + } else { + FullscreenType::Off + }; + let sdl2_ogl_res = if opt.no_opengl { Err("OpenGL disabled".into()) } else { - SimpleSdl2OpenglComponent::new(&mut sdl_context, emu.libretro_core()) + SimpleSdl2OpenglComponent::new(&mut sdl_context, ft) }; if let Ok(sdl2_ogl) = sdl2_ogl_res { emu.register_component(sdl2_ogl)?; } else { - match SimpleSdl2CanvasComponent::new(&mut sdl_context, emu.libretro_core()) { + match SimpleSdl2CanvasComponent::new(&mut sdl_context, ft) { Ok(sdl2_canvas) => emu.register_component(sdl2_canvas)?, Err(e) => eprintln!("Couldn't initialize SDL2 video component: {:?}", e), } @@ -88,6 +98,7 @@ pub fn main() -> Result<(), Box> { emu.register_component(SimpleSdl2AudioComponent::new(&mut sdl_context)?)?; + emu.register_component(SimpleSdl2KeyboardComponent::new(&mut sdl_context)?)?; emu.register_component(SimpleSdl2GamepadComponent::new(&mut sdl_context))?; emu.register_component(SleepFramerateLimitComponent::default())?; diff --git a/ferretro_components/src/provided/sdl2/canvas.rs b/ferretro_components/src/provided/sdl2/canvas.rs index bdbf16c..3527e05 100644 --- a/ferretro_components/src/provided/sdl2/canvas.rs +++ b/ferretro_components/src/provided/sdl2/canvas.rs @@ -6,6 +6,7 @@ use std::path::Path; use sdl2::Sdl; use sdl2::rect::Rect; use sdl2::render::WindowCanvas; +use sdl2::video::FullscreenType; pub(crate) fn paint_frame_on_canvas(frame: &VideoFrame, canvas: &mut WindowCanvas) -> bool { if let Some((pixel_data, pitch)) = frame.data_pitch_as_bytes() { @@ -56,19 +57,18 @@ pub struct SimpleSdl2CanvasComponent { } impl SimpleSdl2CanvasComponent { - pub fn new(sdl_context: &mut Sdl, retro: &LibretroWrapper) -> Result> { - let sys_info = retro.get_system_info(); - let title = format!( - "{} - ferretro SDL", - unsafe { CStr::from_ptr(sys_info.library_name) }.to_string_lossy() - ); - + pub fn new(sdl_context: &mut Sdl, ft: FullscreenType) -> Result> { // default to old libsnes 256x224 window size until load_game (prereq of get_system_av_info) - let canvas = sdl_context + let mut window = sdl_context .video()? - .window(title.as_str(), 256, 224) + .window("ferretro SDL", 256, 224) .resizable() - .build()? + .hidden() + .build()?; + + window.set_fullscreen(ft)?; + + let canvas = window .into_canvas() .build()?; @@ -79,7 +79,15 @@ impl SimpleSdl2CanvasComponent { } impl RetroComponent for SimpleSdl2CanvasComponent { - fn post_load_game(&mut self, retro: &mut LibretroWrapper, _rom: &Path) -> crate::base::Result<()> { + fn post_load_game(&mut self, retro: &mut LibretroWrapper, rom: &Path) -> crate::base::Result<()> { + let sys_info = retro.get_system_info(); + let title = format!( + "{} - {} - ferretro SDL", + unsafe { CStr::from_ptr(sys_info.library_name) }.to_string_lossy(), + rom.file_stem().unwrap_or_default().to_string_lossy(), + ); + self.canvas.window_mut().set_title(&title)?; + self.canvas.window_mut().show(); self.set_geometry(&retro.get_system_av_info().geometry); Ok(()) } diff --git a/ferretro_components/src/provided/sdl2/keyboard.rs b/ferretro_components/src/provided/sdl2/keyboard.rs new file mode 100644 index 0000000..5cd45c9 --- /dev/null +++ b/ferretro_components/src/provided/sdl2/keyboard.rs @@ -0,0 +1,88 @@ +use std::collections::HashMap; +use crate::base::ControlFlow; +use crate::prelude::*; + +use sdl2::Sdl; +use sdl2::event::Event; +use sdl2::keyboard::Keycode; + +// FIXME: make it not hard require EventPump ownership, or allow user-provided function? +/// Maps the first "RetroPad" layout to the keyboard. +/// +/// NOTE: This component is intended for exceedingly simple use-cases, and *must own* the +/// [sdl2::EventPump]. +pub struct SimpleSdl2KeyboardComponent { + button_map: HashMap, + button_state: HashMap, + event_pump: sdl2::EventPump, +} + +impl RetroCallbacks for SimpleSdl2KeyboardComponent { + fn input_state(&mut self, port: u32, device: InputDeviceId, _index: InputIndex) -> i16 { + if port != 0 { + return 0; + } + self.button_state.get(&device).cloned().unwrap_or_default() + } + + fn get_input_device_capabilities(&mut self) -> Option { + let bits = 1 << (DeviceType::Joypad as u32); + Some(bits as u64) + } +} + +impl RetroComponent for SimpleSdl2KeyboardComponent { + fn pre_run(&mut self, _retro: &mut LibretroWrapper) -> ControlFlow { + for event in self.event_pump.poll_iter() { + match event { + Event::Quit { .. } + | Event::KeyDown { + keycode: Some(Keycode::Escape), + .. + } => return ControlFlow::Break, + Event::KeyDown { keycode: Some(k), .. } => { + if let Some(button) = self.button_map.get(&k) { + self.button_state.insert(button.to_owned(), 1); + } + } + Event::KeyUp { keycode: Some(k), .. } => { + if let Some(button) = self.button_map.get(&k) { + self.button_state.remove(button); + } + } + _ => {} + } + } + ControlFlow::Continue + } +} + +impl SimpleSdl2KeyboardComponent { + pub fn new(sdl_context: &mut Sdl) -> crate::base::Result { + let event_pump = sdl_context.event_pump()?; + + Ok(SimpleSdl2KeyboardComponent { + button_map: [ + (Keycode::Up, InputDeviceId::Joypad(JoypadButton::Up)), + (Keycode::Down, InputDeviceId::Joypad(JoypadButton::Down)), + (Keycode::Left, InputDeviceId::Joypad(JoypadButton::Left)), + (Keycode::Right, InputDeviceId::Joypad(JoypadButton::Right)), + (Keycode::X, InputDeviceId::Joypad(JoypadButton::A)), + (Keycode::Z, InputDeviceId::Joypad(JoypadButton::B)), + (Keycode::S, InputDeviceId::Joypad(JoypadButton::X)), + (Keycode::A, InputDeviceId::Joypad(JoypadButton::Y)), + (Keycode::Q, InputDeviceId::Joypad(JoypadButton::L)), + (Keycode::W, InputDeviceId::Joypad(JoypadButton::R)), + (Keycode::E, InputDeviceId::Joypad(JoypadButton::L2)), + (Keycode::R, InputDeviceId::Joypad(JoypadButton::R2)), + (Keycode::T, InputDeviceId::Joypad(JoypadButton::L3)), + (Keycode::Y, InputDeviceId::Joypad(JoypadButton::R3)), + (Keycode::Return, InputDeviceId::Joypad(JoypadButton::Start)), + (Keycode::Backspace, InputDeviceId::Joypad(JoypadButton::Select)), + (Keycode::Tab, InputDeviceId::Joypad(JoypadButton::Select)), + ].into_iter().collect(), + button_state: Default::default(), + event_pump, + }) + } +} diff --git a/ferretro_components/src/provided/sdl2/mod.rs b/ferretro_components/src/provided/sdl2/mod.rs index 22cf44f..d722719 100644 --- a/ferretro_components/src/provided/sdl2/mod.rs +++ b/ferretro_components/src/provided/sdl2/mod.rs @@ -6,6 +6,7 @@ mod audio_ratecontrol; mod canvas; mod fps; mod gamepad; +mod keyboard; mod opengl; mod surface; @@ -14,5 +15,6 @@ pub use audio_ratecontrol::Sdl2RateControlledAudioComponent; pub use canvas::SimpleSdl2CanvasComponent; pub use fps::SimpleSdl2FramerateLimitComponent; pub use gamepad::SimpleSdl2GamepadComponent; +pub use keyboard::SimpleSdl2KeyboardComponent; pub use opengl::SimpleSdl2OpenglComponent; pub use surface::Sdl2SurfaceComponent; diff --git a/ferretro_components/src/provided/sdl2/opengl.rs b/ferretro_components/src/provided/sdl2/opengl.rs index 3e6533e..8a16ddb 100644 --- a/ferretro_components/src/provided/sdl2/opengl.rs +++ b/ferretro_components/src/provided/sdl2/opengl.rs @@ -6,6 +6,7 @@ use std::path::Path; use sdl2::Sdl; use sdl2::render::WindowCanvas; +use sdl2::video::FullscreenType; use super::canvas::paint_frame_on_canvas; @@ -28,21 +29,18 @@ pub struct SimpleSdl2OpenglComponent { } impl SimpleSdl2OpenglComponent { - pub fn new(sdl_context: &mut Sdl, retro: &LibretroWrapper) -> Result> { - let sys_info = retro.get_system_info(); - let title = format!( - "{} - ferretro SDL GL", - unsafe { CStr::from_ptr(sys_info.library_name) }.to_string_lossy() - ); - + pub fn new(sdl_context: &mut Sdl, ft: FullscreenType) -> Result> { let video = sdl_context.video()?; // default to old libsnes 256x224 window size until load_game (prereq of get_system_av_info) - let window = video - .window(title.as_str(), 256, 224) + let mut window = video + .window("ferretro SDL GL", 256, 224) .opengl() .resizable() + .hidden() .build()?; + window.set_fullscreen(ft)?; + #[allow(non_snake_case)] let glGetIntegerv: unsafe extern "C" fn(c_uint, *mut c_uint) = unsafe { std::mem::transmute(video.gl_get_proc_address("glGetIntegerv")) @@ -90,7 +88,15 @@ impl SimpleSdl2OpenglComponent { } impl RetroComponent for SimpleSdl2OpenglComponent { - fn post_load_game(&mut self, retro: &mut LibretroWrapper, _rom: &Path) -> crate::base::Result<()> { + fn post_load_game(&mut self, retro: &mut LibretroWrapper, rom: &Path) -> crate::base::Result<()> { + let sys_info = retro.get_system_info(); + let title = format!( + "{} - {} - ferretro SDL GL", + unsafe { CStr::from_ptr(sys_info.library_name) }.to_string_lossy(), + rom.file_stem().unwrap_or_default().to_string_lossy(), + ); + self.canvas.window_mut().set_title(&title)?; + self.canvas.window_mut().show(); self.set_geometry(&retro.get_system_av_info().geometry); Ok(()) }