abstract gamepad away from SDL

This commit is contained in:
Alula 2022-11-21 15:12:45 +01:00
parent e74b586dd1
commit 2860938b9a
No known key found for this signature in database
GPG Key ID: 3E00485503A1D8BA
4 changed files with 115 additions and 52 deletions

View File

@ -95,6 +95,12 @@ pub trait BackendTexture {
fn as_any(&self) -> &dyn Any; fn as_any(&self) -> &dyn Any;
} }
pub trait BackendGamepad {
fn set_rumble(&mut self, low_freq: u16, high_freq: u16, duration_ms: u32) -> GameResult;
fn instance_id(&self) -> u32;
}
#[allow(unreachable_code)] #[allow(unreachable_code)]
pub fn init_backend(headless: bool, size_hint: (u16, u16)) -> GameResult<Box<dyn Backend>> { pub fn init_backend(headless: bool, size_hint: (u16, u16)) -> GameResult<Box<dyn Backend>> {
if headless { if headless {

View File

@ -22,13 +22,11 @@ use sdl2::video::Window;
use sdl2::video::WindowContext; use sdl2::video::WindowContext;
use crate::common::{Color, Rect}; use crate::common::{Color, Rect};
use crate::framework::backend::{ use crate::framework::backend::{Backend, BackendEventLoop, BackendGamepad, BackendRenderer, BackendShader, BackendTexture, SpriteBatchCommand, VertexData};
Backend, BackendEventLoop, BackendRenderer, BackendShader, BackendTexture, SpriteBatchCommand, VertexData,
};
use crate::framework::context::Context; use crate::framework::context::Context;
use crate::framework::error::{GameError, GameResult}; use crate::framework::error::{GameError, GameResult};
use crate::framework::filesystem; use crate::framework::filesystem;
use crate::framework::gamepad::{Axis, Button}; use crate::framework::gamepad::{Axis, Button, GamepadType};
use crate::framework::graphics::BlendMode; use crate::framework::graphics::BlendMode;
use crate::framework::keyboard::ScanCode; use crate::framework::keyboard::ScanCode;
use crate::framework::render_opengl::{GLContext, OpenGLRenderer}; use crate::framework::render_opengl::{GLContext, OpenGLRenderer};
@ -320,10 +318,10 @@ impl BackendEventLoop for SDL2EventLoop {
log::info!("Connected gamepad: {} (ID: {})", controller.name(), id); log::info!("Connected gamepad: {} (ID: {})", controller.name(), id);
let axis_sensitivity = state.settings.get_gamepad_axis_sensitivity(which); let axis_sensitivity = state.settings.get_gamepad_axis_sensitivity(which);
ctx.gamepad_context.add_gamepad(controller, axis_sensitivity); ctx.gamepad_context.add_gamepad(SDL2Gamepad::new(controller), axis_sensitivity);
unsafe { unsafe {
let controller_type = sdl2_sys::SDL_GameControllerTypeForIndex(id as _); let controller_type = get_game_controller_type(sdl2_sys::SDL_GameControllerTypeForIndex(id as _));
ctx.gamepad_context.set_gamepad_type(id, controller_type); ctx.gamepad_context.set_gamepad_type(id, controller_type);
} }
} }
@ -471,6 +469,46 @@ impl BackendEventLoop for SDL2EventLoop {
} }
} }
fn get_game_controller_type(ctype: sdl2_sys::SDL_GameControllerType) -> GamepadType {
match ctype as i32 {
1 => GamepadType::Xbox360,
2 => GamepadType::XboxOne,
3 => GamepadType::PS3,
4 => GamepadType::PS4,
5 => GamepadType::NintendoSwitchPro,
6 => GamepadType::Virtual,
7 => GamepadType::PS5,
8 => GamepadType::AmazonLuma,
9 => GamepadType::GoogleStadia,
10 => GamepadType::NVIDIAShield,
11 => GamepadType::NintendoSwitchJoyConLeft,
12 => GamepadType::NintendoSwitchJoyConRight,
13 => GamepadType::NintendoSwitchJoyConPair,
_ => GamepadType::Unknown,
}
}
struct SDL2Gamepad {
inner: GameController,
}
impl SDL2Gamepad {
pub fn new(inner: GameController) -> Box<dyn BackendGamepad> {
Box::new(SDL2Gamepad { inner })
}
}
impl BackendGamepad for SDL2Gamepad {
fn set_rumble(&mut self, low_freq: u16, high_freq: u16, duration_ms: u32) -> GameResult {
self.inner.set_rumble(low_freq, high_freq, duration_ms)
.map_err(|e| GameError::GamepadError(e.to_string()))
}
fn instance_id(&self) -> u32 {
self.inner.instance_id()
}
}
struct SDL2Renderer { struct SDL2Renderer {
refs: Rc<RefCell<SDL2Context>>, refs: Rc<RefCell<SDL2Context>>,
imgui: Rc<RefCell<imgui::Context>>, imgui: Rc<RefCell<imgui::Context>>,

View File

@ -1,17 +1,57 @@
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use sdl2::controller::GameController;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{common::Rect, engine_constants::EngineConstants, framework::context::Context}; use crate::framework::backend::BackendGamepad;
use crate::framework::error::GameResult; use crate::framework::error::GameResult;
use crate::game::shared_game_state::SharedGameState; use crate::game::shared_game_state::SharedGameState;
use crate::{common::Rect, engine_constants::EngineConstants, framework::context::Context};
const QUAKE_RUMBLE_LOW_FREQ: u16 = 0x3000; const QUAKE_RUMBLE_LOW_FREQ: u16 = 0x3000;
const QUAKE_RUMBLE_HI_FREQ: u16 = 0; const QUAKE_RUMBLE_HI_FREQ: u16 = 0;
const SUPER_QUAKE_RUMBLE_LOW_FREQ: u16 = 0x5000; const SUPER_QUAKE_RUMBLE_LOW_FREQ: u16 = 0x5000;
const SUPER_QUAKE_RUMBLE_HI_FREQ: u16 = 0; const SUPER_QUAKE_RUMBLE_HI_FREQ: u16 = 0;
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum GamepadType {
Unknown,
Xbox360,
XboxOne,
PS3,
PS4,
NintendoSwitchPro,
Virtual,
PS5,
AmazonLuma,
GoogleStadia,
NVIDIAShield,
NintendoSwitchJoyConLeft,
NintendoSwitchJoyConRight,
NintendoSwitchJoyConPair,
}
impl GamepadType {
pub fn get_name(&self) -> &str {
match self {
GamepadType::Unknown => "Unknown controller",
GamepadType::Xbox360 => "Xbox 360 controller",
GamepadType::XboxOne => "Xbox One controller",
GamepadType::PS3 => "PlayStation 3 controller",
GamepadType::PS4 => "PlayStation 4 controller",
GamepadType::NintendoSwitchPro => "Nintendo Switch Pro controller",
GamepadType::Virtual => "Virtual controller",
GamepadType::PS5 => "PlayStation 5 controller",
GamepadType::AmazonLuma => "Amazon Luma controller",
GamepadType::GoogleStadia => "Google Stadia controller",
GamepadType::NVIDIAShield => "NVIDIA Shield controller",
GamepadType::NintendoSwitchJoyConLeft => "Nintendo Switch Joy-Con (left)",
GamepadType::NintendoSwitchJoyConRight => "Nintendo Switch Joy-Con (right)",
GamepadType::NintendoSwitchJoyConPair => "Nintendo Switch Joy-Con (pair)",
}
}
}
#[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)] #[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
#[repr(u32)] #[repr(u32)]
pub enum Axis { pub enum Axis {
@ -118,8 +158,8 @@ impl PlayerControllerInputType {
} }
pub struct GamepadData { pub struct GamepadData {
controller: GameController, controller: Box<dyn BackendGamepad>,
controller_type: Option<sdl2_sys::SDL_GameControllerType>, controller_type: GamepadType,
left_x: f64, left_x: f64,
left_y: f64, left_y: f64,
@ -135,10 +175,10 @@ pub struct GamepadData {
} }
impl GamepadData { impl GamepadData {
pub(crate) fn new(game_controller: GameController, axis_sensitivity: f64) -> Self { pub(crate) fn new(game_controller: Box<dyn BackendGamepad>, axis_sensitivity: f64) -> Self {
GamepadData { GamepadData {
controller: game_controller, controller: game_controller,
controller_type: None, controller_type: GamepadType::Unknown,
left_x: 0.0, left_x: 0.0,
left_y: 0.0, left_y: 0.0,
@ -154,50 +194,29 @@ impl GamepadData {
} }
} }
pub(crate) fn set_gamepad_type(&mut self, controller_type: sdl2_sys::SDL_GameControllerType) { pub(crate) fn set_gamepad_type(&mut self, controller_type: GamepadType) {
self.controller_type = Some(controller_type); self.controller_type = controller_type;
} }
pub(crate) fn get_gamepad_sprite_offset(&self) -> usize { pub(crate) fn get_gamepad_sprite_offset(&self) -> usize {
if let Some(controller_type) = self.controller_type { match self.controller_type {
return match controller_type { GamepadType::PS3 | GamepadType::PS4 | GamepadType::PS5 => 0,
sdl2_sys::SDL_GameControllerType::SDL_CONTROLLER_TYPE_PS3 GamepadType::Xbox360 | GamepadType::XboxOne => 1,
| sdl2_sys::SDL_GameControllerType::SDL_CONTROLLER_TYPE_PS4 GamepadType::NintendoSwitchPro
| sdl2_sys::SDL_GameControllerType::SDL_CONTROLLER_TYPE_PS5 => 0, | GamepadType::NintendoSwitchJoyConLeft
sdl2_sys::SDL_GameControllerType::SDL_CONTROLLER_TYPE_XBOX360 | GamepadType::NintendoSwitchJoyConRight
| sdl2_sys::SDL_GameControllerType::SDL_CONTROLLER_TYPE_XBOXONE => 1, | GamepadType::NintendoSwitchJoyConPair => 3,
sdl2_sys::SDL_GameControllerType::SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO => 3, _ => 1,
_ => 1,
};
} }
1
} }
pub fn get_gamepad_name(&self) -> String { pub fn get_gamepad_name(&self) -> String {
let name = if let Some(controller_type) = self.controller_type { self.controller_type.get_name().to_owned()
match controller_type {
sdl2_sys::SDL_GameControllerType::SDL_CONTROLLER_TYPE_PS3
| sdl2_sys::SDL_GameControllerType::SDL_CONTROLLER_TYPE_PS4
| sdl2_sys::SDL_GameControllerType::SDL_CONTROLLER_TYPE_PS5 => "PlayStation Controller".to_string(),
sdl2_sys::SDL_GameControllerType::SDL_CONTROLLER_TYPE_XBOX360
| sdl2_sys::SDL_GameControllerType::SDL_CONTROLLER_TYPE_XBOXONE => "Xbox Controller".to_string(),
sdl2_sys::SDL_GameControllerType::SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO => {
"Nintendo Switch Controller".to_string()
}
_ => "Unknown Controller".to_string(),
}
} else {
"Unknown controller".to_string()
};
name
} }
pub fn set_rumble(&mut self, state: &SharedGameState, low_freq: u16, hi_freq: u16, ticks: u32) -> GameResult { pub fn set_rumble(&mut self, state: &SharedGameState, low_freq: u16, hi_freq: u16, ticks: u32) -> GameResult {
let duration_ms = (ticks as f32 / state.settings.timing_mode.get_tps() as f32 * 1000.0) as u32; let duration_ms = (ticks as f32 / state.settings.timing_mode.get_tps() as f32 * 1000.0) as u32;
self.controller.set_rumble(low_freq, hi_freq, duration_ms); self.controller.set_rumble(low_freq, hi_freq, duration_ms)
Ok(())
} }
} }
@ -226,7 +245,7 @@ impl GamepadContext {
self.gamepads.get_mut(gamepad_index) self.gamepads.get_mut(gamepad_index)
} }
pub(crate) fn add_gamepad(&mut self, game_controller: GameController, axis_sensitivity: f64) { pub(crate) fn add_gamepad(&mut self, game_controller: Box<dyn BackendGamepad>, axis_sensitivity: f64) {
self.gamepads.push(GamepadData::new(game_controller, axis_sensitivity)); self.gamepads.push(GamepadData::new(game_controller, axis_sensitivity));
} }
@ -234,7 +253,7 @@ impl GamepadContext {
self.gamepads.retain(|data| data.controller.instance_id() != gamepad_id); self.gamepads.retain(|data| data.controller.instance_id() != gamepad_id);
} }
pub(crate) fn set_gamepad_type(&mut self, gamepad_id: u32, controller_type: sdl2_sys::SDL_GameControllerType) { pub(crate) fn set_gamepad_type(&mut self, gamepad_id: u32, controller_type: GamepadType) {
if let Some(gamepad) = self.get_gamepad_mut(gamepad_id) { if let Some(gamepad) = self.get_gamepad_mut(gamepad_id) {
gamepad.set_gamepad_type(controller_type); gamepad.set_gamepad_type(controller_type);
} }
@ -377,7 +396,7 @@ impl Default for GamepadContext {
} }
} }
pub fn add_gamepad(context: &mut Context, game_controller: GameController, axis_sensitivity: f64) { pub fn add_gamepad(context: &mut Context, game_controller: Box<dyn BackendGamepad>, axis_sensitivity: f64) {
context.gamepad_context.add_gamepad(game_controller, axis_sensitivity); context.gamepad_context.add_gamepad(game_controller, axis_sensitivity);
} }
@ -385,7 +404,7 @@ pub fn remove_gamepad(context: &mut Context, gamepad_id: u32) {
context.gamepad_context.remove_gamepad(gamepad_id); context.gamepad_context.remove_gamepad(gamepad_id);
} }
pub fn set_gamepad_type(context: &mut Context, gamepad_id: u32, controller_type: sdl2_sys::SDL_GameControllerType) { pub fn set_gamepad_type(context: &mut Context, gamepad_id: u32, controller_type: GamepadType) {
context.gamepad_context.set_gamepad_type(gamepad_id, controller_type); context.gamepad_context.set_gamepad_type(gamepad_id, controller_type);
} }

View File

@ -24,15 +24,15 @@ pub mod caret;
pub mod frame; pub mod frame;
pub mod inventory; pub mod inventory;
pub mod map; pub mod map;
pub mod npc;
pub mod physics; pub mod physics;
pub mod player;
pub mod profile; pub mod profile;
pub mod scripting;
pub mod settings; pub mod settings;
pub mod shared_game_state; pub mod shared_game_state;
pub mod stage; pub mod stage;
pub mod npc;
pub mod player;
pub mod weapon; pub mod weapon;
pub mod scripting;
pub struct LaunchOptions { pub struct LaunchOptions {
pub server_mode: bool, pub server_mode: bool,