sdl2 input support
This commit is contained in:
		
							parent
							
								
									c3cea12713
								
							
						
					
					
						commit
						4e22289ef0
					
				
					 2 changed files with 77 additions and 3 deletions
				
			
		| 
						 | 
				
			
			@ -4,6 +4,7 @@ extern crate sdl2;
 | 
			
		|||
 | 
			
		||||
use rustro::retro;
 | 
			
		||||
use rustro::retro::ffi::{GameGeometry, SystemAvInfo};
 | 
			
		||||
use rustro::retro::constants::{Input, DeviceIndex, JoypadButton, AnalogAxis};
 | 
			
		||||
 | 
			
		||||
use std::ffi::CStr;
 | 
			
		||||
use std::io::Read;
 | 
			
		||||
| 
						 | 
				
			
			@ -14,6 +15,7 @@ use std::time::{Duration, Instant};
 | 
			
		|||
use structopt::StructOpt;
 | 
			
		||||
 | 
			
		||||
use sdl2::audio::{AudioCallback, AudioFormat, AudioSpec, AudioSpecDesired, AudioDevice};
 | 
			
		||||
use sdl2::controller::{GameController, Button, Axis};
 | 
			
		||||
use sdl2::event::Event;
 | 
			
		||||
use sdl2::keyboard::Keycode;
 | 
			
		||||
use sdl2::rect::Rect;
 | 
			
		||||
| 
						 | 
				
			
			@ -36,6 +38,10 @@ struct MyEmulator {
 | 
			
		|||
    audio_spec: AudioSpec,
 | 
			
		||||
    audio_device: AudioDevice<MySdlAudio>,
 | 
			
		||||
    audio_sender: crossbeam_channel::Sender<Vec<i16>>,
 | 
			
		||||
 | 
			
		||||
    // input bits
 | 
			
		||||
    gamepad_subsys: sdl2::GameControllerSubsystem,
 | 
			
		||||
    gamepads: Vec<GameController>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl MyEmulator {
 | 
			
		||||
| 
						 | 
				
			
			@ -76,7 +82,7 @@ impl MyEmulator {
 | 
			
		|||
            samples: None,
 | 
			
		||||
        };
 | 
			
		||||
        if let Some(0) = desired_spec.freq {
 | 
			
		||||
            desired_spec.freq = Some(32000); // old default, fix for cores that don't report it
 | 
			
		||||
            desired_spec.freq = Some(32040); // old default, fix for cores that don't report it
 | 
			
		||||
        }
 | 
			
		||||
        let mut audio_spec = None;
 | 
			
		||||
        let audio_device = audio
 | 
			
		||||
| 
						 | 
				
			
			@ -90,6 +96,12 @@ impl MyEmulator {
 | 
			
		|||
            })
 | 
			
		||||
            .unwrap();
 | 
			
		||||
 | 
			
		||||
        let gamepad_subsys = sdl_context.game_controller().unwrap();
 | 
			
		||||
        let mut gamepads = Vec::new();
 | 
			
		||||
        for i in 0..gamepad_subsys.num_joysticks().unwrap() {
 | 
			
		||||
            gamepads.extend(gamepad_subsys.open(i).into_iter());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let emu = MyEmulator {
 | 
			
		||||
            retro,
 | 
			
		||||
            av_info,
 | 
			
		||||
| 
						 | 
				
			
			@ -101,9 +113,11 @@ impl MyEmulator {
 | 
			
		|||
            audio_spec: audio_spec.unwrap(),
 | 
			
		||||
            audio_device,
 | 
			
		||||
            audio_sender,
 | 
			
		||||
            gamepad_subsys,
 | 
			
		||||
            gamepads,
 | 
			
		||||
        };
 | 
			
		||||
        let mut pin_emu = Box::pin(emu);
 | 
			
		||||
        retro::wrapper::register_handler(pin_emu.as_mut());
 | 
			
		||||
        retro::wrapper::set_handler(pin_emu.as_mut());
 | 
			
		||||
        pin_emu.retro.as_ref().init();
 | 
			
		||||
        pin_emu
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -184,6 +198,32 @@ impl retro::wrapper::Handler for MyEmulator {
 | 
			
		|||
        stereo_pcm.len()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn input_poll(&mut self) {
 | 
			
		||||
        self.gamepad_subsys.update();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn input_state(&mut self, port: u32, device: Input, index: DeviceIndex) -> i16 {
 | 
			
		||||
        match self.gamepads.get(port as usize) {
 | 
			
		||||
            Some(gamepad) => {
 | 
			
		||||
                match device {
 | 
			
		||||
                    Input::Joypad(button) => {
 | 
			
		||||
                        match button_map(&button) {
 | 
			
		||||
                            Some(x) => gamepad.button(x) as i16,
 | 
			
		||||
                            None => match button {
 | 
			
		||||
                                JoypadButton::L2 => gamepad.axis(Axis::TriggerLeft),
 | 
			
		||||
                                JoypadButton::R2 => gamepad.axis(Axis::TriggerRight),
 | 
			
		||||
                                _ => 0,
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    Input::Analog(axis) => gamepad.axis(axis_map(index, axis)),
 | 
			
		||||
                    _ => 0,
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            None => 0,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn set_pixel_format(&mut self, pix_fmt: retro::ffi::PixelFormat) -> bool {
 | 
			
		||||
        self.pixel_format = match pix_fmt {
 | 
			
		||||
            retro::ffi::PixelFormat::ARGB1555 => sdl2::pixels::PixelFormatEnum::RGB555,
 | 
			
		||||
| 
						 | 
				
			
			@ -204,6 +244,7 @@ impl retro::wrapper::Handler for MyEmulator {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
struct MySdlAudio {
 | 
			
		||||
    audio_spec: AudioSpec,
 | 
			
		||||
    audio_receiver: crossbeam_channel::Receiver<Vec<i16>>,
 | 
			
		||||
| 
						 | 
				
			
			@ -243,3 +284,33 @@ struct Opt {
 | 
			
		|||
    rom: PathBuf,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn button_map(retro_button: &JoypadButton) -> Option<Button> {
 | 
			
		||||
    match retro_button {
 | 
			
		||||
        JoypadButton::B => Some(Button::A),
 | 
			
		||||
        JoypadButton::Y => Some(Button::X),
 | 
			
		||||
        JoypadButton::Select => Some(Button::Back),
 | 
			
		||||
        JoypadButton::Start => Some(Button::Start),
 | 
			
		||||
        JoypadButton::Up => Some(Button::DPadUp),
 | 
			
		||||
        JoypadButton::Down => Some(Button::DPadDown),
 | 
			
		||||
        JoypadButton::Left => Some(Button::DPadLeft),
 | 
			
		||||
        JoypadButton::Right => Some(Button::DPadRight),
 | 
			
		||||
        JoypadButton::A => Some(Button::B),
 | 
			
		||||
        JoypadButton::X => Some(Button::Y),
 | 
			
		||||
        JoypadButton::L => Some(Button::LeftShoulder),
 | 
			
		||||
        JoypadButton::R => Some(Button::RightShoulder),
 | 
			
		||||
         // SDL2 controller API doesn't have L2/R2 as buttons, they're considered axes
 | 
			
		||||
        JoypadButton::L2 => None,
 | 
			
		||||
        JoypadButton::R2 => None,
 | 
			
		||||
        JoypadButton::L3 => Some(Button::LeftStick),
 | 
			
		||||
        JoypadButton::R3 => Some(Button::RightStick),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn axis_map(index: DeviceIndex, axis: AnalogAxis) -> Axis {
 | 
			
		||||
    match (index, axis) {
 | 
			
		||||
        (DeviceIndex::Left, AnalogAxis::X) => Axis::LeftX,
 | 
			
		||||
        (DeviceIndex::Left, AnalogAxis::Y) => Axis::LeftY,
 | 
			
		||||
        (DeviceIndex::Right, AnalogAxis::X) => Axis::RightX,
 | 
			
		||||
        (DeviceIndex::Right, AnalogAxis::Y) => Axis::RightY,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,6 +19,9 @@ static mut CB_SINGLETON: StaticCallbacks = StaticCallbacks {
 | 
			
		|||
 | 
			
		||||
#[allow(unused)]
 | 
			
		||||
pub trait Handler: Unpin + 'static {
 | 
			
		||||
    fn _delegate_handler(&self) -> Option<&mut dyn Handler> { None }
 | 
			
		||||
 | 
			
		||||
    // -- main callbacks --
 | 
			
		||||
    fn video_refresh(&mut self, data: &[u8], width: c_uint, height: c_uint, pitch: c_uint) {}
 | 
			
		||||
    fn audio_sample(&mut self, left: i16, right: i16) {}
 | 
			
		||||
    fn audio_sample_batch(&mut self, stereo_pcm: &[i16]) -> usize { stereo_pcm.len() }
 | 
			
		||||
| 
						 | 
				
			
			@ -225,7 +228,7 @@ impl Drop for LibretroWrapper {
 | 
			
		|||
 | 
			
		||||
// a note on lifetimes: we explicitly lie about them here because as long as they live as long as
 | 
			
		||||
//  the library wrapper itself we're good (we wipe our 'static references on drop() too)
 | 
			
		||||
pub fn register_handler(handler: Pin<&'_ mut (dyn Handler + '_)>) {
 | 
			
		||||
pub fn set_handler(handler: Pin<&'_ mut (dyn Handler + '_)>) {
 | 
			
		||||
    unsafe {
 | 
			
		||||
        let ptr = handler.get_unchecked_mut() as *mut dyn Handler;
 | 
			
		||||
        CB_SINGLETON.handler.replace(Pin::new_unchecked(ptr.as_mut().unwrap()));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue