remove gilrs dependency for controller support
This commit is contained in:
parent
9932b1209f
commit
84d9dbf877
|
@ -59,7 +59,6 @@ cpal = "0.13"
|
||||||
directories = "3"
|
directories = "3"
|
||||||
downcast = "0.11"
|
downcast = "0.11"
|
||||||
funty = "=1.1.0" # https://github.com/bitvecto-rs/bitvec/issues/105
|
funty = "=1.1.0" # https://github.com/bitvecto-rs/bitvec/issues/105
|
||||||
gilrs = { version = "0.9.0", features = ["serde-serialize"] }
|
|
||||||
glutin = { git = "https://github.com/doukutsu-rs/glutin.git", rev = "8dd457b9adb7dbac7ade337246b6356c784272d9", optional = true, default_features = false, features = ["x11"] }
|
glutin = { git = "https://github.com/doukutsu-rs/glutin.git", rev = "8dd457b9adb7dbac7ade337246b6356c784272d9", optional = true, default_features = false, features = ["x11"] }
|
||||||
imgui = "0.8.0"
|
imgui = "0.8.0"
|
||||||
image = { version = "0.23", default-features = false, features = ["png", "bmp"] }
|
image = { version = "0.23", default-features = false, features = ["png", "bmp"] }
|
||||||
|
|
|
@ -9,6 +9,7 @@ use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use imgui::internal::RawWrapper;
|
use imgui::internal::RawWrapper;
|
||||||
use imgui::{ConfigFlags, DrawCmd, DrawData, DrawIdx, DrawVert, Key, MouseCursor, TextureId, Ui};
|
use imgui::{ConfigFlags, DrawCmd, DrawData, DrawIdx, DrawVert, Key, MouseCursor, TextureId, Ui};
|
||||||
|
use sdl2::controller::GameController;
|
||||||
use sdl2::event::{Event, WindowEvent};
|
use sdl2::event::{Event, WindowEvent};
|
||||||
use sdl2::keyboard::Scancode;
|
use sdl2::keyboard::Scancode;
|
||||||
use sdl2::mouse::{Cursor, SystemCursor};
|
use sdl2::mouse::{Cursor, SystemCursor};
|
||||||
|
@ -17,7 +18,7 @@ use sdl2::render::{Texture, TextureCreator, TextureQuery, WindowCanvas};
|
||||||
use sdl2::video::GLProfile;
|
use sdl2::video::GLProfile;
|
||||||
use sdl2::video::Window;
|
use sdl2::video::Window;
|
||||||
use sdl2::video::WindowContext;
|
use sdl2::video::WindowContext;
|
||||||
use sdl2::{keyboard, pixels, EventPump, Sdl, VideoSubsystem};
|
use sdl2::{controller, keyboard, pixels, EventPump, GameControllerSubsystem, Sdl, VideoSubsystem};
|
||||||
|
|
||||||
use crate::common::{Color, Rect};
|
use crate::common::{Color, Rect};
|
||||||
use crate::framework::backend::{
|
use crate::framework::backend::{
|
||||||
|
@ -25,6 +26,7 @@ use crate::framework::backend::{
|
||||||
};
|
};
|
||||||
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::gamepad::{Axis, Button};
|
||||||
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};
|
||||||
|
@ -42,6 +44,8 @@ pub struct SDL2Backend {
|
||||||
|
|
||||||
impl SDL2Backend {
|
impl SDL2Backend {
|
||||||
pub fn new(size_hint: (u16, u16)) -> GameResult<Box<dyn Backend>> {
|
pub fn new(size_hint: (u16, u16)) -> GameResult<Box<dyn Backend>> {
|
||||||
|
sdl2::hint::set("SDL_JOYSTICK_THREAD", "1");
|
||||||
|
|
||||||
let context = sdl2::init().map_err(GameError::WindowError)?;
|
let context = sdl2::init().map_err(GameError::WindowError)?;
|
||||||
|
|
||||||
let backend = SDL2Backend { context, size_hint };
|
let backend = SDL2Backend { context, size_hint };
|
||||||
|
@ -141,12 +145,15 @@ struct SDL2Context {
|
||||||
gl_context: Option<sdl2::video::GLContext>,
|
gl_context: Option<sdl2::video::GLContext>,
|
||||||
blend_mode: sdl2::render::BlendMode,
|
blend_mode: sdl2::render::BlendMode,
|
||||||
fullscreen_type: sdl2::video::FullscreenType,
|
fullscreen_type: sdl2::video::FullscreenType,
|
||||||
|
game_controller: GameControllerSubsystem,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SDL2EventLoop {
|
impl SDL2EventLoop {
|
||||||
pub fn new(sdl: &Sdl, size_hint: (u16, u16)) -> GameResult<Box<dyn BackendEventLoop>> {
|
pub fn new(sdl: &Sdl, size_hint: (u16, u16)) -> GameResult<Box<dyn BackendEventLoop>> {
|
||||||
let event_pump = sdl.event_pump().map_err(GameError::WindowError)?;
|
let event_pump = sdl.event_pump().map_err(GameError::WindowError)?;
|
||||||
let video = sdl.video().map_err(GameError::WindowError)?;
|
let video = sdl.video().map_err(GameError::WindowError)?;
|
||||||
|
let game_controller = sdl.game_controller().map_err(GameError::GamepadError)?;
|
||||||
|
|
||||||
let gl_attr = video.gl_attr();
|
let gl_attr = video.gl_attr();
|
||||||
|
|
||||||
gl_attr.set_context_profile(GLProfile::Compatibility);
|
gl_attr.set_context_profile(GLProfile::Compatibility);
|
||||||
|
@ -160,8 +167,8 @@ impl SDL2EventLoop {
|
||||||
window.opengl();
|
window.opengl();
|
||||||
|
|
||||||
let window = window.build().map_err(|e| GameError::WindowError(e.to_string()))?;
|
let window = window.build().map_err(|e| GameError::WindowError(e.to_string()))?;
|
||||||
|
|
||||||
let opengl_available = if let Ok(v) = std::env::var("CAVESTORY_NO_OPENGL") { v != "1" } else { true };
|
let opengl_available = if let Ok(v) = std::env::var("CAVESTORY_NO_OPENGL") { v != "1" } else { true };
|
||||||
|
|
||||||
let event_loop = SDL2EventLoop {
|
let event_loop = SDL2EventLoop {
|
||||||
event_pump,
|
event_pump,
|
||||||
refs: Rc::new(RefCell::new(SDL2Context {
|
refs: Rc::new(RefCell::new(SDL2Context {
|
||||||
|
@ -170,6 +177,7 @@ impl SDL2EventLoop {
|
||||||
gl_context: None,
|
gl_context: None,
|
||||||
blend_mode: sdl2::render::BlendMode::Blend,
|
blend_mode: sdl2::render::BlendMode::Blend,
|
||||||
fullscreen_type: sdl2::video::FullscreenType::Off,
|
fullscreen_type: sdl2::video::FullscreenType::Off,
|
||||||
|
game_controller,
|
||||||
})),
|
})),
|
||||||
opengl_available: RefCell::new(opengl_available),
|
opengl_available: RefCell::new(opengl_available),
|
||||||
};
|
};
|
||||||
|
@ -293,6 +301,39 @@ impl BackendEventLoop for SDL2EventLoop {
|
||||||
ctx.keyboard_context.set_key(drs_scan, false);
|
ctx.keyboard_context.set_key(drs_scan, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Event::ControllerDeviceAdded { which, .. } => {
|
||||||
|
let game_controller = &self.refs.borrow().game_controller;
|
||||||
|
|
||||||
|
if game_controller.is_game_controller(which) {
|
||||||
|
let controller = game_controller.open(which).unwrap();
|
||||||
|
log::info!("Connected gamepad: {} (ID: {})", controller.name(), controller.instance_id());
|
||||||
|
|
||||||
|
let axis_sensitivity = state.settings.get_gamepad_axis_sensitivity(which);
|
||||||
|
ctx.gamepad_context.add_gamepad(controller, axis_sensitivity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Event::ControllerDeviceRemoved { which, .. } => {
|
||||||
|
let game_controller = &self.refs.borrow().game_controller;
|
||||||
|
log::info!("Disconnected gamepad with ID {}", which);
|
||||||
|
ctx.gamepad_context.remove_gamepad(which);
|
||||||
|
}
|
||||||
|
Event::ControllerAxisMotion { which, axis, value, .. } => {
|
||||||
|
if let Some(drs_axis) = conv_gamepad_axis(axis) {
|
||||||
|
let new_value = (value as f64) / i16::MAX as f64;
|
||||||
|
ctx.gamepad_context.set_axis_value(which, drs_axis, new_value);
|
||||||
|
ctx.gamepad_context.update_axes(which);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Event::ControllerButtonDown { which, button, .. } => {
|
||||||
|
if let Some(drs_button) = conv_gamepad_button(button) {
|
||||||
|
ctx.gamepad_context.set_button(which, drs_button, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Event::ControllerButtonUp { which, button, .. } => {
|
||||||
|
if let Some(drs_button) = conv_gamepad_button(button) {
|
||||||
|
ctx.gamepad_context.set_button(which, drs_button, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1100,6 +1141,39 @@ fn conv_scancode(code: keyboard::Scancode) -> Option<ScanCode> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn conv_gamepad_button(code: controller::Button) -> Option<Button> {
|
||||||
|
match code {
|
||||||
|
controller::Button::A => Some(Button::South),
|
||||||
|
controller::Button::B => Some(Button::East),
|
||||||
|
controller::Button::X => Some(Button::West),
|
||||||
|
controller::Button::Y => Some(Button::North),
|
||||||
|
controller::Button::Back => Some(Button::Back),
|
||||||
|
controller::Button::Guide => Some(Button::Guide),
|
||||||
|
controller::Button::Start => Some(Button::Start),
|
||||||
|
controller::Button::LeftStick => Some(Button::LeftStick),
|
||||||
|
controller::Button::RightStick => Some(Button::RightStick),
|
||||||
|
controller::Button::LeftShoulder => Some(Button::LeftShoulder),
|
||||||
|
controller::Button::RightShoulder => Some(Button::RightShoulder),
|
||||||
|
controller::Button::DPadUp => Some(Button::DPadUp),
|
||||||
|
controller::Button::DPadDown => Some(Button::DPadDown),
|
||||||
|
controller::Button::DPadLeft => Some(Button::DPadLeft),
|
||||||
|
controller::Button::DPadRight => Some(Button::DPadRight),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn conv_gamepad_axis(code: controller::Axis) -> Option<Axis> {
|
||||||
|
match code {
|
||||||
|
controller::Axis::LeftX => Some(Axis::LeftX),
|
||||||
|
controller::Axis::LeftY => Some(Axis::LeftY),
|
||||||
|
controller::Axis::RightX => Some(Axis::RightX),
|
||||||
|
controller::Axis::RightY => Some(Axis::RightY),
|
||||||
|
controller::Axis::TriggerLeft => Some(Axis::TriggerLeft),
|
||||||
|
controller::Axis::TriggerRight => Some(Axis::TriggerRight),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// based on imgui-sdl2 crate
|
// based on imgui-sdl2 crate
|
||||||
pub struct ImguiSdl2 {
|
pub struct ImguiSdl2 {
|
||||||
mouse_press: [bool; 5],
|
mouse_press: [bool; 5],
|
||||||
|
|
|
@ -1,13 +1,25 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
use gilrs::{Axis, Button, Gamepad, GamepadId};
|
use sdl2::controller::GameController;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{framework::context::Context, settings::PlayerControllerInputType};
|
use crate::{framework::context::Context, settings::PlayerControllerInputType};
|
||||||
|
|
||||||
|
#[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
|
||||||
|
#[repr(u32)]
|
||||||
|
pub enum Axis {
|
||||||
|
LeftX,
|
||||||
|
LeftY,
|
||||||
|
RightX,
|
||||||
|
RightY,
|
||||||
|
TriggerLeft,
|
||||||
|
TriggerRight,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum AxisDirection {
|
pub enum AxisDirection {
|
||||||
None,
|
None,
|
||||||
|
Either,
|
||||||
Up,
|
Up,
|
||||||
Left,
|
Left,
|
||||||
Right,
|
Right,
|
||||||
|
@ -18,107 +30,169 @@ impl AxisDirection {
|
||||||
pub fn compare(&self, value: f64, axis_sensitivity: f64) -> bool {
|
pub fn compare(&self, value: f64, axis_sensitivity: f64) -> bool {
|
||||||
match self {
|
match self {
|
||||||
AxisDirection::None => false,
|
AxisDirection::None => false,
|
||||||
AxisDirection::Up => value > axis_sensitivity,
|
AxisDirection::Either => value.abs() > 0.0,
|
||||||
AxisDirection::Left => value < -axis_sensitivity,
|
AxisDirection::Down | AxisDirection::Right => value > axis_sensitivity,
|
||||||
AxisDirection::Right => value > axis_sensitivity,
|
AxisDirection::Up | AxisDirection::Left => value < -axis_sensitivity,
|
||||||
AxisDirection::Down => value < -axis_sensitivity,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Debug, Hash, Ord, PartialOrd, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
|
||||||
|
#[repr(u32)]
|
||||||
|
pub enum Button {
|
||||||
|
South,
|
||||||
|
East,
|
||||||
|
West,
|
||||||
|
North,
|
||||||
|
|
||||||
|
Back,
|
||||||
|
Guide,
|
||||||
|
Start,
|
||||||
|
LeftStick,
|
||||||
|
RightStick,
|
||||||
|
LeftShoulder,
|
||||||
|
RightShoulder,
|
||||||
|
DPadUp,
|
||||||
|
DPadDown,
|
||||||
|
DPadLeft,
|
||||||
|
DPadRight,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct GamepadData {
|
pub struct GamepadData {
|
||||||
|
controller: GameController,
|
||||||
|
|
||||||
left_x: f64,
|
left_x: f64,
|
||||||
left_y: f64,
|
left_y: f64,
|
||||||
right_x: f64,
|
right_x: f64,
|
||||||
right_y: f64,
|
right_y: f64,
|
||||||
|
trigger_left: f64,
|
||||||
|
trigger_right: f64,
|
||||||
|
|
||||||
axis_sensitivity: f64,
|
axis_sensitivity: f64,
|
||||||
|
|
||||||
|
pressed_buttons_set: HashSet<Button>,
|
||||||
|
axis_values: HashMap<Axis, f64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GamepadData {
|
impl GamepadData {
|
||||||
pub(crate) fn new(axis_sensitivity: f64) -> Self {
|
pub(crate) fn new(game_controller: GameController, axis_sensitivity: f64) -> Self {
|
||||||
GamepadData { left_x: 0.0, left_y: 0.0, right_x: 0.0, right_y: 0.0, axis_sensitivity }
|
GamepadData {
|
||||||
|
controller: game_controller,
|
||||||
|
|
||||||
|
left_x: 0.0,
|
||||||
|
left_y: 0.0,
|
||||||
|
right_x: 0.0,
|
||||||
|
right_y: 0.0,
|
||||||
|
trigger_left: 0.0,
|
||||||
|
trigger_right: 0.0,
|
||||||
|
|
||||||
|
axis_sensitivity,
|
||||||
|
|
||||||
|
pressed_buttons_set: HashSet::with_capacity(16),
|
||||||
|
axis_values: HashMap::with_capacity(8),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct GamepadContext {
|
pub struct GamepadContext {
|
||||||
gamepads: HashMap<GamepadId, GamepadData>,
|
gamepads: Vec<GamepadData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GamepadContext {
|
impl GamepadContext {
|
||||||
pub(crate) fn new() -> Self {
|
pub(crate) fn new() -> Self {
|
||||||
Self { gamepads: HashMap::new() }
|
Self { gamepads: Vec::new() }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gamepad_exists(&self, gamepad: &Gamepad) -> bool {
|
fn get_gamepad(&self, gamepad_id: u32) -> Option<&GamepadData> {
|
||||||
self.gamepads.contains_key(&gamepad.id())
|
self.gamepads.iter().find(|gamepad| gamepad.controller.instance_id() == gamepad_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn add_gamepad(&mut self, gamepad: &Gamepad, axis_sensitivity: f64) {
|
fn get_gamepad_by_index(&self, gamepad_index: usize) -> Option<&GamepadData> {
|
||||||
self.gamepads.insert(gamepad.id(), GamepadData::new(axis_sensitivity));
|
self.gamepads.get(gamepad_index)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn remove_gamepad(&mut self, gamepad: &Gamepad) {
|
fn get_gamepad_mut(&mut self, gamepad_id: u32) -> Option<&mut GamepadData> {
|
||||||
self.gamepads.remove(&gamepad.id());
|
self.gamepads.iter_mut().find(|gamepad| gamepad.controller.instance_id() == gamepad_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn add_gamepad(&mut self, game_controller: GameController, axis_sensitivity: f64) {
|
||||||
|
self.gamepads.push(GamepadData::new(game_controller, axis_sensitivity));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn remove_gamepad(&mut self, gamepad_id: u32) {
|
||||||
|
self.gamepads.retain(|data| data.controller.instance_id() != gamepad_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_button(&mut self, gamepad_id: u32, button: Button, pressed: bool) {
|
||||||
|
if let Some(gamepad) = self.get_gamepad_mut(gamepad_id) {
|
||||||
|
if pressed {
|
||||||
|
gamepad.pressed_buttons_set.insert(button);
|
||||||
|
} else {
|
||||||
|
gamepad.pressed_buttons_set.remove(&button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_axis_value(&mut self, gamepad_id: u32, axis: Axis, value: f64) {
|
||||||
|
if let Some(gamepad) = self.get_gamepad_mut(gamepad_id) {
|
||||||
|
gamepad.axis_values.insert(axis, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn is_active(
|
pub(crate) fn is_active(
|
||||||
&self,
|
&self,
|
||||||
gamepad: &Gamepad,
|
gamepad_index: u32,
|
||||||
input_type: &PlayerControllerInputType,
|
input_type: &PlayerControllerInputType,
|
||||||
axis_direction: AxisDirection,
|
axis_direction: AxisDirection,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
match input_type {
|
match input_type {
|
||||||
PlayerControllerInputType::ButtonInput(button) => self.is_button_active(gamepad, *button),
|
PlayerControllerInputType::ButtonInput(button) => self.is_button_active(gamepad_index, *button),
|
||||||
PlayerControllerInputType::AxisInput(axis) => self.is_axis_active(gamepad, *axis, axis_direction),
|
PlayerControllerInputType::AxisInput(axis) => self.is_axis_active(gamepad_index, *axis, axis_direction),
|
||||||
PlayerControllerInputType::Either(button, axis) => {
|
PlayerControllerInputType::Either(button, axis) => {
|
||||||
self.is_button_active(gamepad, *button) || self.is_axis_active(gamepad, *axis, axis_direction)
|
self.is_button_active(gamepad_index, *button)
|
||||||
|
|| self.is_axis_active(gamepad_index, *axis, axis_direction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn is_button_active(&self, gamepad: &Gamepad, button: Button) -> bool {
|
pub(crate) fn is_button_active(&self, gamepad_index: u32, button: Button) -> bool {
|
||||||
if !self.gamepad_exists(gamepad) {
|
if let Some(gamepad) = self.get_gamepad_by_index(gamepad_index as usize) {
|
||||||
return false;
|
return gamepad.pressed_buttons_set.contains(&button);
|
||||||
}
|
}
|
||||||
|
|
||||||
gamepad.is_pressed(button)
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn is_axis_active(&self, gamepad: &Gamepad, axis: Axis, direction: AxisDirection) -> bool {
|
pub(crate) fn is_axis_active(&self, gamepad_index: u32, axis: Axis, direction: AxisDirection) -> bool {
|
||||||
if !self.gamepad_exists(gamepad) {
|
if let Some(gamepad) = self.get_gamepad_by_index(gamepad_index as usize) {
|
||||||
return false;
|
return match axis {
|
||||||
|
Axis::LeftX => direction.compare(gamepad.left_x, gamepad.axis_sensitivity),
|
||||||
|
Axis::LeftY => direction.compare(gamepad.left_y, gamepad.axis_sensitivity),
|
||||||
|
Axis::RightX => direction.compare(gamepad.right_x, gamepad.axis_sensitivity),
|
||||||
|
Axis::RightY => direction.compare(gamepad.right_y, gamepad.axis_sensitivity),
|
||||||
|
Axis::TriggerLeft => direction.compare(gamepad.trigger_left, 0.0),
|
||||||
|
Axis::TriggerRight => direction.compare(gamepad.trigger_right, 0.0),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = self.gamepads.get(&gamepad.id()).unwrap();
|
false
|
||||||
|
|
||||||
match axis {
|
|
||||||
Axis::LeftStickX => direction.compare(data.left_x, data.axis_sensitivity),
|
|
||||||
Axis::LeftStickY => direction.compare(data.left_y, data.axis_sensitivity),
|
|
||||||
Axis::RightStickX => direction.compare(data.right_x, data.axis_sensitivity),
|
|
||||||
Axis::RightStickY => direction.compare(data.right_y, data.axis_sensitivity),
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn update_axes(&mut self, gamepad: &Gamepad) {
|
pub(crate) fn update_axes(&mut self, gamepad_id: u32) {
|
||||||
if !self.gamepad_exists(gamepad) {
|
if let Some(gamepad) = self.get_gamepad_mut(gamepad_id) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let data = self.gamepads.get_mut(&gamepad.id()).unwrap();
|
|
||||||
|
|
||||||
let mut axes = [
|
let mut axes = [
|
||||||
(&mut data.left_x, Axis::LeftStickX),
|
(&mut gamepad.left_x, Axis::LeftX),
|
||||||
(&mut data.left_y, Axis::LeftStickY),
|
(&mut gamepad.left_y, Axis::LeftY),
|
||||||
(&mut data.right_x, Axis::RightStickX),
|
(&mut gamepad.right_x, Axis::RightX),
|
||||||
(&mut data.right_y, Axis::RightStickY),
|
(&mut gamepad.right_y, Axis::RightY),
|
||||||
|
(&mut gamepad.trigger_left, Axis::TriggerLeft),
|
||||||
|
(&mut gamepad.trigger_right, Axis::TriggerRight),
|
||||||
];
|
];
|
||||||
|
|
||||||
for (axis_val, id) in axes.iter_mut() {
|
for (axis_val, id) in axes.iter_mut() {
|
||||||
if let Some(axis) = gamepad.axis_data(*id) {
|
if let Some(axis) = gamepad.axis_values.get(id) {
|
||||||
**axis_val = if axis.value().abs() < 0.12 { 0.0 } else { axis.value() } as f64;
|
**axis_val = if axis.abs() < 0.12 { 0.0 } else { *axis };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -130,31 +204,27 @@ impl Default for GamepadContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_gamepad(context: &mut Context, gamepad: &Gamepad, axis_sensitivity: f64) {
|
pub fn add_gamepad(context: &mut Context, game_controller: GameController, axis_sensitivity: f64) {
|
||||||
context.gamepad_context.add_gamepad(gamepad, axis_sensitivity);
|
context.gamepad_context.add_gamepad(game_controller, axis_sensitivity);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_gamepad(context: &mut Context, gamepad: &Gamepad) {
|
pub fn remove_gamepad(context: &mut Context, gamepad_id: u32) {
|
||||||
context.gamepad_context.remove_gamepad(gamepad);
|
context.gamepad_context.remove_gamepad(gamepad_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_active(
|
pub fn is_active(
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
gamepad: &Gamepad,
|
gamepad_index: u32,
|
||||||
input_type: &PlayerControllerInputType,
|
input_type: &PlayerControllerInputType,
|
||||||
axis_direction: AxisDirection,
|
axis_direction: AxisDirection,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
ctx.gamepad_context.is_active(gamepad, input_type, axis_direction)
|
ctx.gamepad_context.is_active(gamepad_index, input_type, axis_direction)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_button_active(ctx: &Context, gamepad: &Gamepad, button: Button) -> bool {
|
pub fn is_button_active(ctx: &Context, gamepad_index: u32, button: Button) -> bool {
|
||||||
ctx.gamepad_context.is_button_active(gamepad, button)
|
ctx.gamepad_context.is_button_active(gamepad_index, button)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_axis_active(ctx: &Context, gamepad: &Gamepad, axis: Axis, direction: AxisDirection) -> bool {
|
pub fn is_axis_active(ctx: &Context, gamepad_index: u32, axis: Axis, direction: AxisDirection) -> bool {
|
||||||
ctx.gamepad_context.is_axis_active(gamepad, axis, direction)
|
ctx.gamepad_context.is_axis_active(gamepad_index, axis, direction)
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update_axes(ctx: &mut Context, gamepad: &Gamepad) {
|
|
||||||
ctx.gamepad_context.update_axes(gamepad);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
use crate::framework::context::Context;
|
use crate::framework::context::Context;
|
||||||
use crate::framework::error::GameResult;
|
use crate::framework::error::GameResult;
|
||||||
use crate::framework::gamepad::{self, AxisDirection};
|
use crate::framework::gamepad::{self, AxisDirection, Button};
|
||||||
use crate::input::player_controller::PlayerController;
|
use crate::input::player_controller::PlayerController;
|
||||||
use crate::player::TargetPlayer;
|
use crate::player::TargetPlayer;
|
||||||
use crate::shared_game_state::SharedGameState;
|
use crate::shared_game_state::SharedGameState;
|
||||||
use crate::{bitfield, settings::PlayerControllerInputType};
|
use crate::{bitfield, settings::PlayerControllerInputType};
|
||||||
|
|
||||||
use gilrs::{Button, GamepadId};
|
|
||||||
|
|
||||||
bitfield! {
|
bitfield! {
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct KeyState(u16);
|
pub struct KeyState(u16);
|
||||||
|
@ -31,7 +29,7 @@ bitfield! {
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct GamepadController {
|
pub struct GamepadController {
|
||||||
gamepad_id: GamepadId,
|
gamepad_id: u32,
|
||||||
target: TargetPlayer,
|
target: TargetPlayer,
|
||||||
state: KeyState,
|
state: KeyState,
|
||||||
old_state: KeyState,
|
old_state: KeyState,
|
||||||
|
@ -39,7 +37,7 @@ pub struct GamepadController {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GamepadController {
|
impl GamepadController {
|
||||||
pub fn new(gamepad_id: GamepadId, target: TargetPlayer) -> GamepadController {
|
pub fn new(gamepad_id: u32, target: TargetPlayer) -> GamepadController {
|
||||||
GamepadController { gamepad_id, target, state: KeyState(0), old_state: KeyState(0), trigger: KeyState(0) }
|
GamepadController { gamepad_id, target, state: KeyState(0), old_state: KeyState(0), trigger: KeyState(0) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,41 +49,35 @@ impl PlayerController for GamepadController {
|
||||||
TargetPlayer::Player2 => &state.settings.player2_controller_button_map,
|
TargetPlayer::Player2 => &state.settings.player2_controller_button_map,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(gilrs) = &state.gilrs {
|
self.state.set_up(gamepad::is_active(ctx, self.gamepad_id, &button_map.up, AxisDirection::Up));
|
||||||
if let Some(gamepad) = gilrs.connected_gamepad(self.gamepad_id) {
|
self.state.set_down(gamepad::is_active(ctx, self.gamepad_id, &button_map.down, AxisDirection::Down));
|
||||||
gamepad::update_axes(ctx, &gamepad);
|
self.state.set_left(gamepad::is_active(ctx, self.gamepad_id, &button_map.left, AxisDirection::Left));
|
||||||
|
self.state.set_right(gamepad::is_active(ctx, self.gamepad_id, &button_map.right, AxisDirection::Right));
|
||||||
self.state.set_up(gamepad::is_active(ctx, &gamepad, &button_map.up, AxisDirection::Up));
|
self.state.set_map(gamepad::is_active(ctx, self.gamepad_id, &button_map.map, AxisDirection::None));
|
||||||
self.state.set_down(gamepad::is_active(ctx, &gamepad, &button_map.down, AxisDirection::Down));
|
self.state.set_inventory(gamepad::is_active(ctx, self.gamepad_id, &button_map.inventory, AxisDirection::None));
|
||||||
self.state.set_left(gamepad::is_active(ctx, &gamepad, &button_map.left, AxisDirection::Left));
|
self.state.set_jump(gamepad::is_active(ctx, self.gamepad_id, &button_map.jump, AxisDirection::None));
|
||||||
self.state.set_right(gamepad::is_active(ctx, &gamepad, &button_map.right, AxisDirection::Right));
|
self.state.set_shoot(gamepad::is_active(ctx, self.gamepad_id, &button_map.shoot, AxisDirection::None));
|
||||||
self.state.set_map(gamepad::is_active(ctx, &gamepad, &button_map.map, AxisDirection::None));
|
|
||||||
self.state.set_inventory(gamepad::is_active(ctx, &gamepad, &button_map.inventory, AxisDirection::None));
|
|
||||||
self.state.set_jump(gamepad::is_active(ctx, &gamepad, &button_map.jump, AxisDirection::None));
|
|
||||||
self.state.set_shoot(gamepad::is_active(ctx, &gamepad, &button_map.shoot, AxisDirection::None));
|
|
||||||
self.state.set_next_weapon(gamepad::is_active(
|
self.state.set_next_weapon(gamepad::is_active(
|
||||||
ctx,
|
ctx,
|
||||||
&gamepad,
|
self.gamepad_id,
|
||||||
&button_map.next_weapon,
|
&button_map.next_weapon,
|
||||||
AxisDirection::None,
|
AxisDirection::None,
|
||||||
));
|
));
|
||||||
self.state.set_prev_weapon(gamepad::is_active(
|
self.state.set_prev_weapon(gamepad::is_active(
|
||||||
ctx,
|
ctx,
|
||||||
&gamepad,
|
self.gamepad_id,
|
||||||
&button_map.prev_weapon,
|
&button_map.prev_weapon,
|
||||||
AxisDirection::None,
|
AxisDirection::None,
|
||||||
));
|
));
|
||||||
self.state.set_escape(gamepad::is_active(
|
self.state.set_escape(gamepad::is_active(
|
||||||
ctx,
|
ctx,
|
||||||
&gamepad,
|
self.gamepad_id,
|
||||||
&PlayerControllerInputType::ButtonInput(Button::Start),
|
&PlayerControllerInputType::ButtonInput(Button::Start),
|
||||||
AxisDirection::None,
|
AxisDirection::None,
|
||||||
));
|
));
|
||||||
self.state.set_enter(gamepad::is_active(ctx, &gamepad, &button_map.jump, AxisDirection::None));
|
self.state.set_enter(gamepad::is_active(ctx, self.gamepad_id, &button_map.jump, AxisDirection::None));
|
||||||
self.state.set_skip(gamepad::is_active(ctx, &gamepad, &button_map.skip, AxisDirection::None));
|
self.state.set_skip(gamepad::is_active(ctx, self.gamepad_id, &button_map.skip, AxisDirection::Either));
|
||||||
self.state.set_strafe(gamepad::is_active(ctx, &gamepad, &button_map.strafe, AxisDirection::None));
|
self.state.set_strafe(gamepad::is_active(ctx, self.gamepad_id, &button_map.strafe, AxisDirection::Either));
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
30
src/lib.rs
30
src/lib.rs
|
@ -11,8 +11,6 @@ use std::sync::Mutex;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use directories::ProjectDirs;
|
use directories::ProjectDirs;
|
||||||
use framework::gamepad;
|
|
||||||
use gilrs::EventType;
|
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
use crate::builtin_fs::BuiltinFS;
|
use crate::builtin_fs::BuiltinFS;
|
||||||
|
@ -112,34 +110,6 @@ impl Game {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, ctx: &mut Context) -> GameResult {
|
fn update(&mut self, ctx: &mut Context) -> GameResult {
|
||||||
{
|
|
||||||
let state_ref = unsafe { &mut *self.state.get() };
|
|
||||||
|
|
||||||
if let Some(gilrs) = &mut state_ref.gilrs {
|
|
||||||
while let Some(e) = gilrs.next_event() {
|
|
||||||
let gamepad = gilrs.gamepad(e.id);
|
|
||||||
|
|
||||||
match e.event {
|
|
||||||
EventType::Connected => {
|
|
||||||
log::info!("Gamepad connected: {} (ID: {})", gamepad.name(), gamepad.id());
|
|
||||||
|
|
||||||
let axis_sensitivity = state_ref.settings.get_gamepad_axis_sensitivity(gamepad.id());
|
|
||||||
gamepad::add_gamepad(ctx, &gamepad, axis_sensitivity);
|
|
||||||
|
|
||||||
// TODO: replace the controller of all players that use this gamepad from keyboard to gamepad
|
|
||||||
}
|
|
||||||
EventType::Disconnected => {
|
|
||||||
log::info!("Gamepad disconnected: {} (ID: {})", gamepad.name(), gamepad.id());
|
|
||||||
gamepad::remove_gamepad(ctx, &gamepad);
|
|
||||||
|
|
||||||
// TODO: fall back to keyboard for all players that use this gamepad
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(scene) = &mut self.scene {
|
if let Some(scene) = &mut self.scene {
|
||||||
let state_ref = unsafe { &mut *self.state.get() };
|
let state_ref = unsafe { &mut *self.state.get() };
|
||||||
|
|
||||||
|
|
|
@ -1773,7 +1773,7 @@ impl Scene for GameScene {
|
||||||
if !state.control_flags.control_enabled() && !state.textscript_vm.flags.cutscene_skip() =>
|
if !state.control_flags.control_enabled() && !state.textscript_vm.flags.cutscene_skip() =>
|
||||||
{
|
{
|
||||||
state.touch_controls.control_type = TouchControlType::Dialog;
|
state.touch_controls.control_type = TouchControlType::Dialog;
|
||||||
if self.player1.controller.inventory() {
|
if self.player1.controller.skip() {
|
||||||
self.skip_counter += 1;
|
self.skip_counter += 1;
|
||||||
if self.skip_counter >= CUTSCENE_SKIP_WAIT {
|
if self.skip_counter >= CUTSCENE_SKIP_WAIT {
|
||||||
state.textscript_vm.flags.set_cutscene_skip(true);
|
state.textscript_vm.flags.set_cutscene_skip(true);
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
use gilrs::{Axis, Button, GamepadId};
|
|
||||||
|
|
||||||
use crate::framework::context::Context;
|
use crate::framework::context::Context;
|
||||||
use crate::framework::error::GameResult;
|
use crate::framework::error::GameResult;
|
||||||
use crate::framework::filesystem::{user_create, user_open};
|
use crate::framework::filesystem::{user_create, user_open};
|
||||||
|
use crate::framework::gamepad::{Axis, Button};
|
||||||
use crate::framework::keyboard::ScanCode;
|
use crate::framework::keyboard::ScanCode;
|
||||||
use crate::graphics::VSyncMode;
|
use crate::graphics::VSyncMode;
|
||||||
use crate::input::gamepad_player_controller::GamepadController;
|
use crate::input::gamepad_player_controller::GamepadController;
|
||||||
|
@ -232,7 +231,7 @@ impl Settings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_gamepad_axis_sensitivity(&self, id: GamepadId) -> f64 {
|
pub fn get_gamepad_axis_sensitivity(&self, id: u32) -> f64 {
|
||||||
if self.player1_controller_type == ControllerType::Gamepad(id) {
|
if self.player1_controller_type == ControllerType::Gamepad(id) {
|
||||||
self.player1_controller_axis_sensitivity
|
self.player1_controller_axis_sensitivity
|
||||||
} else if self.player2_controller_type == ControllerType::Gamepad(id) {
|
} else if self.player2_controller_type == ControllerType::Gamepad(id) {
|
||||||
|
@ -337,7 +336,7 @@ fn p2_default_keymap() -> PlayerKeyMap {
|
||||||
#[derive(serde::Serialize, serde::Deserialize, Eq, PartialEq)]
|
#[derive(serde::Serialize, serde::Deserialize, Eq, PartialEq)]
|
||||||
pub enum ControllerType {
|
pub enum ControllerType {
|
||||||
Keyboard,
|
Keyboard,
|
||||||
Gamepad(GamepadId),
|
Gamepad(u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(serde::Serialize, serde::Deserialize)]
|
#[derive(serde::Serialize, serde::Deserialize)]
|
||||||
|
@ -366,16 +365,16 @@ pub struct PlayerControllerButtonMap {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn player_default_controller_button_map() -> PlayerControllerButtonMap {
|
pub fn player_default_controller_button_map() -> PlayerControllerButtonMap {
|
||||||
PlayerControllerButtonMap {
|
PlayerControllerButtonMap {
|
||||||
left: PlayerControllerInputType::Either(Button::DPadLeft, Axis::LeftStickX),
|
left: PlayerControllerInputType::Either(Button::DPadLeft, Axis::LeftX),
|
||||||
up: PlayerControllerInputType::Either(Button::DPadUp, Axis::LeftStickY),
|
up: PlayerControllerInputType::Either(Button::DPadUp, Axis::LeftY),
|
||||||
right: PlayerControllerInputType::Either(Button::DPadRight, Axis::LeftStickX),
|
right: PlayerControllerInputType::Either(Button::DPadRight, Axis::LeftX),
|
||||||
down: PlayerControllerInputType::Either(Button::DPadDown, Axis::LeftStickY),
|
down: PlayerControllerInputType::Either(Button::DPadDown, Axis::LeftY),
|
||||||
prev_weapon: PlayerControllerInputType::ButtonInput(Button::LeftTrigger),
|
prev_weapon: PlayerControllerInputType::ButtonInput(Button::LeftShoulder),
|
||||||
next_weapon: PlayerControllerInputType::ButtonInput(Button::RightTrigger),
|
next_weapon: PlayerControllerInputType::ButtonInput(Button::RightShoulder),
|
||||||
jump: PlayerControllerInputType::ButtonInput(Button::East),
|
jump: PlayerControllerInputType::ButtonInput(Button::South),
|
||||||
shoot: PlayerControllerInputType::ButtonInput(Button::South),
|
shoot: PlayerControllerInputType::ButtonInput(Button::East),
|
||||||
skip: PlayerControllerInputType::ButtonInput(Button::LeftTrigger2),
|
skip: PlayerControllerInputType::AxisInput(Axis::TriggerLeft),
|
||||||
strafe: PlayerControllerInputType::ButtonInput(Button::RightTrigger2),
|
strafe: PlayerControllerInputType::AxisInput(Axis::TriggerRight),
|
||||||
inventory: PlayerControllerInputType::ButtonInput(Button::North),
|
inventory: PlayerControllerInputType::ButtonInput(Button::North),
|
||||||
map: PlayerControllerInputType::ButtonInput(Button::West),
|
map: PlayerControllerInputType::ButtonInput(Button::West),
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ use crate::framework::error::GameResult;
|
||||||
use crate::framework::graphics::{create_texture_mutable, set_render_target};
|
use crate::framework::graphics::{create_texture_mutable, set_render_target};
|
||||||
use crate::framework::keyboard::ScanCode;
|
use crate::framework::keyboard::ScanCode;
|
||||||
use crate::framework::vfs::OpenOptions;
|
use crate::framework::vfs::OpenOptions;
|
||||||
use crate::framework::{filesystem, gamepad, graphics};
|
use crate::framework::{filesystem, graphics};
|
||||||
#[cfg(feature = "hooks")]
|
#[cfg(feature = "hooks")]
|
||||||
use crate::hooks::init_hooks;
|
use crate::hooks::init_hooks;
|
||||||
use crate::i18n::Locale;
|
use crate::i18n::Locale;
|
||||||
|
@ -38,8 +38,6 @@ use crate::stage::StageData;
|
||||||
use crate::texture_set::TextureSet;
|
use crate::texture_set::TextureSet;
|
||||||
use crate::vanilla::VanillaExtractor;
|
use crate::vanilla::VanillaExtractor;
|
||||||
|
|
||||||
use gilrs::Gilrs;
|
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Copy, Clone, serde::Serialize, serde::Deserialize)]
|
#[derive(PartialEq, Eq, Copy, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
pub enum TimingMode {
|
pub enum TimingMode {
|
||||||
_50Hz,
|
_50Hz,
|
||||||
|
@ -289,7 +287,6 @@ pub struct SharedGameState {
|
||||||
pub super_quake_counter: u16,
|
pub super_quake_counter: u16,
|
||||||
pub teleporter_slots: Vec<(u16, u16)>,
|
pub teleporter_slots: Vec<(u16, u16)>,
|
||||||
pub carets: Vec<Caret>,
|
pub carets: Vec<Caret>,
|
||||||
pub gilrs: Option<Gilrs>,
|
|
||||||
pub touch_controls: TouchControls,
|
pub touch_controls: TouchControls,
|
||||||
pub mod_path: Option<String>,
|
pub mod_path: Option<String>,
|
||||||
pub mod_list: ModList,
|
pub mod_list: ModList,
|
||||||
|
@ -334,16 +331,6 @@ impl SharedGameState {
|
||||||
let mut sound_manager = SoundManager::new(ctx)?;
|
let mut sound_manager = SoundManager::new(ctx)?;
|
||||||
let settings = Settings::load(ctx)?;
|
let settings = Settings::load(ctx)?;
|
||||||
let mod_requirements = ModRequirements::load(ctx)?;
|
let mod_requirements = ModRequirements::load(ctx)?;
|
||||||
let mut gilrs = Gilrs::new().ok();
|
|
||||||
|
|
||||||
if let Some(gilrs) = &mut gilrs {
|
|
||||||
for (id, gamepad) in gilrs.gamepads() {
|
|
||||||
log::info!("Found gamepad {} (ID {})", gamepad.name(), id);
|
|
||||||
|
|
||||||
let axis_sensitivity = settings.get_gamepad_axis_sensitivity(id);
|
|
||||||
gamepad::add_gamepad(ctx, &gamepad, axis_sensitivity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let vanilla_extractor = VanillaExtractor::from(ctx, "Doukutsu.exe".to_string());
|
let vanilla_extractor = VanillaExtractor::from(ctx, "Doukutsu.exe".to_string());
|
||||||
if vanilla_extractor.is_some() {
|
if vanilla_extractor.is_some() {
|
||||||
|
@ -437,7 +424,6 @@ impl SharedGameState {
|
||||||
super_quake_counter: 0,
|
super_quake_counter: 0,
|
||||||
teleporter_slots: Vec::with_capacity(8),
|
teleporter_slots: Vec::with_capacity(8),
|
||||||
carets: Vec::with_capacity(32),
|
carets: Vec::with_capacity(32),
|
||||||
gilrs,
|
|
||||||
touch_controls: TouchControls::new(),
|
touch_controls: TouchControls::new(),
|
||||||
mod_path: None,
|
mod_path: None,
|
||||||
mod_list,
|
mod_list,
|
||||||
|
|
Loading…
Reference in New Issue