fullscreen option, add keyboard component
This commit is contained in:
parent
8efff459c4
commit
2dd8556fe2
|
@ -4,6 +4,7 @@ extern crate ffmpeg_next as ffmpeg;
|
||||||
extern crate sdl2;
|
extern crate sdl2;
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use sdl2::video::FullscreenType;
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
use ferretro_components::prelude::*;
|
use ferretro_components::prelude::*;
|
||||||
|
@ -21,7 +22,7 @@ struct Opt {
|
||||||
#[structopt(short, long, parse(from_os_str))]
|
#[structopt(short, long, parse(from_os_str))]
|
||||||
core: PathBuf,
|
core: PathBuf,
|
||||||
/// ROM to load using the core.
|
/// ROM to load using the core.
|
||||||
#[structopt(short, long, parse(from_os_str))]
|
#[structopt(parse(from_os_str))]
|
||||||
rom: PathBuf,
|
rom: PathBuf,
|
||||||
/// Save state to load at startup.
|
/// Save state to load at startup.
|
||||||
#[structopt(long, parse(from_os_str))]
|
#[structopt(long, parse(from_os_str))]
|
||||||
|
@ -35,6 +36,9 @@ struct Opt {
|
||||||
/// Disable OpenGL context creation.
|
/// Disable OpenGL context creation.
|
||||||
#[structopt(long)]
|
#[structopt(long)]
|
||||||
no_opengl: bool,
|
no_opengl: bool,
|
||||||
|
/// Fullscreen (borderless).
|
||||||
|
#[structopt(short, long)]
|
||||||
|
fullscreen: bool,
|
||||||
/// Print core-provided system information to stderr.
|
/// Print core-provided system information to stderr.
|
||||||
#[structopt(long)]
|
#[structopt(long)]
|
||||||
sys_info: bool,
|
sys_info: bool,
|
||||||
|
@ -71,16 +75,22 @@ pub fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
emu.register_component(ffmpeg_comp)?;
|
emu.register_component(ffmpeg_comp)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let ft = if opt.fullscreen {
|
||||||
|
FullscreenType::Desktop
|
||||||
|
} else {
|
||||||
|
FullscreenType::Off
|
||||||
|
};
|
||||||
|
|
||||||
let sdl2_ogl_res = if opt.no_opengl {
|
let sdl2_ogl_res = if opt.no_opengl {
|
||||||
Err("OpenGL disabled".into())
|
Err("OpenGL disabled".into())
|
||||||
} else {
|
} else {
|
||||||
SimpleSdl2OpenglComponent::new(&mut sdl_context, emu.libretro_core())
|
SimpleSdl2OpenglComponent::new(&mut sdl_context, ft)
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Ok(sdl2_ogl) = sdl2_ogl_res {
|
if let Ok(sdl2_ogl) = sdl2_ogl_res {
|
||||||
emu.register_component(sdl2_ogl)?;
|
emu.register_component(sdl2_ogl)?;
|
||||||
} else {
|
} 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)?,
|
Ok(sdl2_canvas) => emu.register_component(sdl2_canvas)?,
|
||||||
Err(e) => eprintln!("Couldn't initialize SDL2 video component: {:?}", e),
|
Err(e) => eprintln!("Couldn't initialize SDL2 video component: {:?}", e),
|
||||||
}
|
}
|
||||||
|
@ -88,6 +98,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
||||||
|
|
||||||
emu.register_component(SimpleSdl2AudioComponent::new(&mut sdl_context)?)?;
|
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(SimpleSdl2GamepadComponent::new(&mut sdl_context))?;
|
||||||
|
|
||||||
emu.register_component(SleepFramerateLimitComponent::default())?;
|
emu.register_component(SleepFramerateLimitComponent::default())?;
|
||||||
|
|
|
@ -6,6 +6,7 @@ use std::path::Path;
|
||||||
use sdl2::Sdl;
|
use sdl2::Sdl;
|
||||||
use sdl2::rect::Rect;
|
use sdl2::rect::Rect;
|
||||||
use sdl2::render::WindowCanvas;
|
use sdl2::render::WindowCanvas;
|
||||||
|
use sdl2::video::FullscreenType;
|
||||||
|
|
||||||
pub(crate) fn paint_frame_on_canvas(frame: &VideoFrame, canvas: &mut WindowCanvas) -> bool {
|
pub(crate) fn paint_frame_on_canvas(frame: &VideoFrame, canvas: &mut WindowCanvas) -> bool {
|
||||||
if let Some((pixel_data, pitch)) = frame.data_pitch_as_bytes() {
|
if let Some((pixel_data, pitch)) = frame.data_pitch_as_bytes() {
|
||||||
|
@ -56,19 +57,18 @@ pub struct SimpleSdl2CanvasComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SimpleSdl2CanvasComponent {
|
impl SimpleSdl2CanvasComponent {
|
||||||
pub fn new(sdl_context: &mut Sdl, retro: &LibretroWrapper) -> Result<Self, Box<dyn std::error::Error>> {
|
pub fn new(sdl_context: &mut Sdl, ft: FullscreenType) -> Result<Self, Box<dyn std::error::Error>> {
|
||||||
let sys_info = retro.get_system_info();
|
|
||||||
let title = format!(
|
|
||||||
"{} - ferretro SDL",
|
|
||||||
unsafe { CStr::from_ptr(sys_info.library_name) }.to_string_lossy()
|
|
||||||
);
|
|
||||||
|
|
||||||
// default to old libsnes 256x224 window size until load_game (prereq of get_system_av_info)
|
// 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()?
|
.video()?
|
||||||
.window(title.as_str(), 256, 224)
|
.window("ferretro SDL", 256, 224)
|
||||||
.resizable()
|
.resizable()
|
||||||
.build()?
|
.hidden()
|
||||||
|
.build()?;
|
||||||
|
|
||||||
|
window.set_fullscreen(ft)?;
|
||||||
|
|
||||||
|
let canvas = window
|
||||||
.into_canvas()
|
.into_canvas()
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
|
@ -79,7 +79,15 @@ impl SimpleSdl2CanvasComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RetroComponent for 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);
|
self.set_geometry(&retro.get_system_av_info().geometry);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<Keycode, InputDeviceId>,
|
||||||
|
button_state: HashMap<InputDeviceId, i16>,
|
||||||
|
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<u64> {
|
||||||
|
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<Self> {
|
||||||
|
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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ mod audio_ratecontrol;
|
||||||
mod canvas;
|
mod canvas;
|
||||||
mod fps;
|
mod fps;
|
||||||
mod gamepad;
|
mod gamepad;
|
||||||
|
mod keyboard;
|
||||||
mod opengl;
|
mod opengl;
|
||||||
mod surface;
|
mod surface;
|
||||||
|
|
||||||
|
@ -14,5 +15,6 @@ pub use audio_ratecontrol::Sdl2RateControlledAudioComponent;
|
||||||
pub use canvas::SimpleSdl2CanvasComponent;
|
pub use canvas::SimpleSdl2CanvasComponent;
|
||||||
pub use fps::SimpleSdl2FramerateLimitComponent;
|
pub use fps::SimpleSdl2FramerateLimitComponent;
|
||||||
pub use gamepad::SimpleSdl2GamepadComponent;
|
pub use gamepad::SimpleSdl2GamepadComponent;
|
||||||
|
pub use keyboard::SimpleSdl2KeyboardComponent;
|
||||||
pub use opengl::SimpleSdl2OpenglComponent;
|
pub use opengl::SimpleSdl2OpenglComponent;
|
||||||
pub use surface::Sdl2SurfaceComponent;
|
pub use surface::Sdl2SurfaceComponent;
|
||||||
|
|
|
@ -6,6 +6,7 @@ use std::path::Path;
|
||||||
|
|
||||||
use sdl2::Sdl;
|
use sdl2::Sdl;
|
||||||
use sdl2::render::WindowCanvas;
|
use sdl2::render::WindowCanvas;
|
||||||
|
use sdl2::video::FullscreenType;
|
||||||
|
|
||||||
use super::canvas::paint_frame_on_canvas;
|
use super::canvas::paint_frame_on_canvas;
|
||||||
|
|
||||||
|
@ -28,21 +29,18 @@ pub struct SimpleSdl2OpenglComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SimpleSdl2OpenglComponent {
|
impl SimpleSdl2OpenglComponent {
|
||||||
pub fn new(sdl_context: &mut Sdl, retro: &LibretroWrapper) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
|
pub fn new(sdl_context: &mut Sdl, ft: FullscreenType) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
|
||||||
let sys_info = retro.get_system_info();
|
|
||||||
let title = format!(
|
|
||||||
"{} - ferretro SDL GL",
|
|
||||||
unsafe { CStr::from_ptr(sys_info.library_name) }.to_string_lossy()
|
|
||||||
);
|
|
||||||
|
|
||||||
let video = sdl_context.video()?;
|
let video = sdl_context.video()?;
|
||||||
// default to old libsnes 256x224 window size until load_game (prereq of get_system_av_info)
|
// default to old libsnes 256x224 window size until load_game (prereq of get_system_av_info)
|
||||||
let window = video
|
let mut window = video
|
||||||
.window(title.as_str(), 256, 224)
|
.window("ferretro SDL GL", 256, 224)
|
||||||
.opengl()
|
.opengl()
|
||||||
.resizable()
|
.resizable()
|
||||||
|
.hidden()
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
|
window.set_fullscreen(ft)?;
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
let glGetIntegerv: unsafe extern "C" fn(c_uint, *mut c_uint) = unsafe {
|
let glGetIntegerv: unsafe extern "C" fn(c_uint, *mut c_uint) = unsafe {
|
||||||
std::mem::transmute(video.gl_get_proc_address("glGetIntegerv"))
|
std::mem::transmute(video.gl_get_proc_address("glGetIntegerv"))
|
||||||
|
@ -90,7 +88,15 @@ impl SimpleSdl2OpenglComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RetroComponent for 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);
|
self.set_geometry(&retro.get_system_av_info().geometry);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue