use std::{fmt, marker::PhantomData, path::PathBuf};
use derivative::Derivative;
use derive_new::new;
use sdl2::{
self,
controller::{AddMappingError, Axis, Button, GameController},
event::Event,
EventPump, GameControllerSubsystem, Sdl,
};
use amethyst_core::{
ecs::prelude::{System, SystemData, World, Write},
shrev::EventChannel,
SystemDesc,
};
use super::{
controller::{ControllerAxis, ControllerButton, ControllerEvent},
BindingTypes, InputEvent, InputHandler,
};
#[derive(Debug)]
pub enum SdlSystemError {
ContextInit(String),
ControllerSubsystemInit(String),
AddMappingError(AddMappingError),
}
impl fmt::Display for SdlSystemError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
SdlSystemError::ContextInit(ref msg) => write!(f, "Failed to initialize SDL: {}", msg),
SdlSystemError::ControllerSubsystemInit(ref msg) => {
write!(f, "Failed to initialize SDL controller subsystem: {}", msg)
}
SdlSystemError::AddMappingError(ref err) => {
write!(f, "Failed to load controller mappings: {}", err)
}
}
}
}
#[derive(Debug)]
pub enum ControllerMappings {
FromPath(PathBuf),
FromString(String),
}
#[derive(Derivative, Debug, new)]
#[derivative(Default(bound = ""))]
pub struct SdlEventsSystemDesc<T>
where
T: BindingTypes,
{
mappings: Option<ControllerMappings>,
marker: PhantomData<T>,
}
impl<'a, 'b, T> SystemDesc<'a, 'b, SdlEventsSystem<T>> for SdlEventsSystemDesc<T>
where
T: BindingTypes,
{
fn build(self, world: &mut World) -> SdlEventsSystem<T> {
<SdlEventsSystem<T> as System<'_>>::SystemData::setup(world);
SdlEventsSystem::new(world, self.mappings)
.unwrap_or_else(|e| panic!("Failed to build SdlEventsSystem. Error: {}", e))
}
}
#[allow(missing_debug_implementations)]
pub struct SdlEventsSystem<T: BindingTypes> {
#[allow(dead_code)]
sdl_context: Sdl,
event_pump: Option<EventPump>,
controller_subsystem: GameControllerSubsystem,
opened_controllers: Vec<(u32, GameController)>,
marker: PhantomData<T>,
}
type SdlEventsData<'a, T> = (
Write<'a, InputHandler<T>>,
Write<'a, EventChannel<InputEvent<T>>>,
);
impl<'a, T: BindingTypes> System<'a> for SdlEventsSystem<T> {
type SystemData = SdlEventsData<'a, T>;
fn run(&mut self, (mut handler, mut output): Self::SystemData) {
let mut event_pump = self
.event_pump
.take()
.expect("Unreachable: `event_pump` is always reinserted after `take`");
for event in event_pump.poll_iter() {
self.handle_sdl_event(&event, &mut handler, &mut output);
}
self.event_pump = Some(event_pump);
}
}
impl<T: BindingTypes> SdlEventsSystem<T> {
pub fn new(
world: &mut World,
mappings: Option<ControllerMappings>,
) -> Result<Self, SdlSystemError> {
let sdl_context = sdl2::init().map_err(SdlSystemError::ContextInit)?;
let event_pump = sdl_context
.event_pump()
.map_err(SdlSystemError::ContextInit)?;
let controller_subsystem = sdl_context
.game_controller()
.map_err(SdlSystemError::ControllerSubsystemInit)?;
match mappings {
Some(ControllerMappings::FromPath(p)) => {
controller_subsystem
.load_mappings(p)
.map_err(SdlSystemError::AddMappingError)?;
}
Some(ControllerMappings::FromString(s)) => {
controller_subsystem
.add_mapping(s.as_str())
.map_err(SdlSystemError::AddMappingError)?;
}
None => {}
};
SdlEventsData::<T>::setup(world);
let mut sys = SdlEventsSystem {
sdl_context,
event_pump: Some(event_pump),
controller_subsystem,
opened_controllers: vec![],
marker: PhantomData,
};
let (mut handler, mut output) = SdlEventsData::fetch(world);
sys.initialize_controllers(&mut handler, &mut output);
Ok(sys)
}
fn handle_sdl_event(
&mut self,
event: &Event,
handler: &mut InputHandler<T>,
output: &mut EventChannel<InputEvent<T>>,
) {
use self::ControllerEvent::*;
match *event {
Event::ControllerAxisMotion {
which, axis, value, ..
} => {
handler.send_controller_event(
&ControllerAxisMoved {
which: which as u32,
axis: axis.into(),
value: if value > 0 {
f32::from(value) / 32767.0
} else {
f32::from(value) / 32768.0
},
},
output,
);
}
Event::ControllerButtonDown { which, button, .. } => {
handler.send_controller_event(
&ControllerButtonPressed {
which: which as u32,
button: button.into(),
},
output,
);
}
Event::ControllerButtonUp { which, button, .. } => {
handler.send_controller_event(
&ControllerButtonReleased {
which: which as u32,
button: button.into(),
},
output,
);
}
Event::ControllerDeviceRemoved { which, .. } => {
self.close_controller(which as u32);
handler.send_controller_event(
&ControllerDisconnected {
which: which as u32,
},
output,
);
}
Event::ControllerDeviceAdded { which, .. } => {
if let Some(idx) = self.open_controller(which) {
handler.send_controller_event(&ControllerConnected { which: idx }, output);
}
}
_ => {}
}
}
fn open_controller(&mut self, which: u32) -> Option<u32> {
if self.controller_subsystem.is_game_controller(which) {
self.controller_subsystem.open(which).ok().map(|c| {
let id = c.instance_id() as u32;
self.opened_controllers.push((which, c));
id
})
} else {
None
}
}
fn close_controller(&mut self, which: u32) {
let index = self
.opened_controllers
.iter()
.position(|(_, c)| c.instance_id() as u32 == which);
if let Some(i) = index {
self.opened_controllers.swap_remove(i);
}
}
fn initialize_controllers(
&mut self,
handler: &mut InputHandler<T>,
output: &mut EventChannel<InputEvent<T>>,
) {
use crate::controller::ControllerEvent::ControllerConnected;
if let Ok(available) = self.controller_subsystem.num_joysticks() {
for id in 0..available {
if let Some(idx) = self.open_controller(id) {
handler.send_controller_event(&ControllerConnected { which: idx }, output);
}
}
}
}
}
impl From<Button> for ControllerButton {
fn from(button: Button) -> Self {
match button {
Button::A => ControllerButton::A,
Button::B => ControllerButton::B,
Button::X => ControllerButton::X,
Button::Y => ControllerButton::Y,
Button::DPadDown => ControllerButton::DPadDown,
Button::DPadLeft => ControllerButton::DPadLeft,
Button::DPadRight => ControllerButton::DPadRight,
Button::DPadUp => ControllerButton::DPadUp,
Button::LeftShoulder => ControllerButton::LeftShoulder,
Button::RightShoulder => ControllerButton::RightShoulder,
Button::LeftStick => ControllerButton::LeftStick,
Button::RightStick => ControllerButton::RightStick,
Button::Back => ControllerButton::Back,
Button::Start => ControllerButton::Start,
Button::Guide => ControllerButton::Guide,
}
}
}
impl From<Axis> for ControllerAxis {
fn from(axis: Axis) -> Self {
match axis {
Axis::LeftX => ControllerAxis::LeftX,
Axis::LeftY => ControllerAxis::LeftY,
Axis::RightX => ControllerAxis::RightX,
Axis::RightY => ControllerAxis::RightY,
Axis::TriggerLeft => ControllerAxis::LeftTrigger,
Axis::TriggerRight => ControllerAxis::RightTrigger,
}
}
}