161 lines
5.7 KiB
Rust
161 lines
5.7 KiB
Rust
use crate::base::ControlFlow;
|
|
use crate::prelude::*;
|
|
|
|
use std::error::Error;
|
|
use std::path::Path;
|
|
|
|
use sdl2::Sdl;
|
|
use sdl2::controller::{Axis, GameController};
|
|
use sdl2::event::Event;
|
|
use sdl2::keyboard::Keycode;
|
|
|
|
/// Trivially maps the "RetroPad" layout to the SDL_GameController API.
|
|
///
|
|
/// NOTE: This component is intended for exceedingly simple use-cases, and will *own* and
|
|
/// process an [sdl2::EventPump] if one hasn't been claimed from [sdl2::Sdl::event_pump]
|
|
/// at the time of the component's creation with [SimpleSdl2GamepadComponent::new].
|
|
///
|
|
/// This means *if you need to manage your own events*, you must
|
|
/// *instantiate your own [sdl2::EventPump] before constructing this component*.
|
|
/// (If you do this, you are of course responsible for pumping the event queue yourself.)
|
|
///
|
|
/// It opens all connected controllers recognized by the [sdl2::Sdl] context and presents them
|
|
/// to the core in the order provided by the OS. Port and button remapping are not supported.
|
|
pub struct SimpleSdl2GamepadComponent {
|
|
preferred_pad: Option<u32>,
|
|
|
|
gamepad_subsys: sdl2::GameControllerSubsystem,
|
|
gamepads: Vec<GameController>,
|
|
event_pump: Option<sdl2::EventPump>,
|
|
}
|
|
|
|
impl RetroCallbacks for SimpleSdl2GamepadComponent {
|
|
fn input_poll(&mut self) {
|
|
self.gamepad_subsys.update();
|
|
}
|
|
|
|
fn input_state(&mut self, port: u32, device: InputDeviceId, index: InputIndex) -> i16 {
|
|
match self.gamepads.get(port as usize) {
|
|
Some(gamepad) => {
|
|
match device {
|
|
InputDeviceId::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,
|
|
}
|
|
}
|
|
}
|
|
InputDeviceId::Analog(axis) => gamepad.axis(axis_map(index, axis)),
|
|
_ => 0,
|
|
}
|
|
}
|
|
None => 0,
|
|
}
|
|
}
|
|
|
|
fn get_variable(&mut self, key: &str) -> Option<String> {
|
|
match key {
|
|
"beetle_saturn_analog_stick_deadzone" => Some("15%".to_string()),
|
|
"parallel-n64-astick-deadzone" => Some("15%".to_string()),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
fn get_input_device_capabilities(&mut self) -> Option<u64> {
|
|
let bits = (1 << (DeviceType::Joypad as u32)) | (1 << (DeviceType::Analog as u32));
|
|
Some(bits as u64)
|
|
}
|
|
|
|
fn set_controller_info(&mut self, controller_info: &[ControllerDescription2]) -> Option<bool> {
|
|
for ci in controller_info {
|
|
// so we can have analog support in beetle/mednafen saturn
|
|
if ci.name.as_str() == "3D Control Pad" {
|
|
self.preferred_pad = Some(ci.device_id());
|
|
break;
|
|
}
|
|
}
|
|
Some(true)
|
|
}
|
|
}
|
|
|
|
impl RetroComponent for SimpleSdl2GamepadComponent {
|
|
fn pre_run(&mut self, _retro: &mut LibretroWrapper) -> ControlFlow {
|
|
if let Some(pump) = self.event_pump.as_mut() {
|
|
for event in pump.poll_iter() {
|
|
match event {
|
|
Event::Quit { .. }
|
|
| Event::KeyDown {
|
|
keycode: Some(Keycode::Escape),
|
|
..
|
|
} => return ControlFlow::Break,
|
|
_ => {}
|
|
}
|
|
}
|
|
}
|
|
ControlFlow::Continue
|
|
}
|
|
|
|
fn post_load_game(&mut self, retro: &mut LibretroWrapper, _rom: &Path) -> Result<(), Box<dyn Error>> {
|
|
if let Some(device) = self.preferred_pad {
|
|
for port in 0..self.gamepads.len() as u32 {
|
|
retro.set_controller_port_device(port, device);
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl SimpleSdl2GamepadComponent {
|
|
pub fn new(sdl_context: &mut Sdl) -> Self {
|
|
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 event_pump = sdl_context.event_pump().ok();
|
|
|
|
SimpleSdl2GamepadComponent {
|
|
preferred_pad: None,
|
|
gamepad_subsys,
|
|
gamepads,
|
|
event_pump,
|
|
}
|
|
}
|
|
}
|
|
|
|
fn button_map(retro_button: &JoypadButton) -> Option<sdl2::controller::Button> {
|
|
use sdl2::controller::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: InputIndex, axis: AnalogAxis) -> Axis {
|
|
match (index, axis) {
|
|
(InputIndex::Left, AnalogAxis::X) => Axis::LeftX,
|
|
(InputIndex::Left, AnalogAxis::Y) => Axis::LeftY,
|
|
(InputIndex::Right, AnalogAxis::X) => Axis::RightX,
|
|
(InputIndex::Right, AnalogAxis::Y) => Axis::RightY,
|
|
}
|
|
}
|