1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
//! ECS input bundle

use crate::{BindingError, BindingTypes, Bindings, InputSystemDesc};
use amethyst_config::{Config, ConfigError};
use amethyst_core::{
    ecs::prelude::{DispatcherBuilder, World},
    SystemBundle, SystemDesc,
};
use amethyst_error::Error;
use derivative::Derivative;
use std::{error, fmt, path::Path};

#[cfg(feature = "sdl_controller")]
use crate::sdl_events_system::ControllerMappings;

/// Bundle for adding the `InputHandler`.
///
/// This also adds the Winit EventHandler and the `InputEvent<T>` EventHandler
/// where `T::Action` is the type for Actions you have assigned here.
///
/// ## Type parameters
///
/// T: The type used to identify input binding types.
///
/// String is appropriate for either of these if you don't know what to use.
///
/// ## Errors
///
/// No errors returned from this bundle.
///
#[derive(Debug, Derivative)]
#[derivative(Default(bound = ""))]
pub struct InputBundle<T: BindingTypes> {
    bindings: Option<Bindings<T>>,
    #[cfg(feature = "sdl_controller")]
    controller_mappings: Option<ControllerMappings>,
}

impl<T: BindingTypes> InputBundle<T> {
    /// Create a new input bundle with no bindings
    pub fn new() -> Self {
        Default::default()
    }

    /// Use the provided bindings with the `InputHandler`
    pub fn with_bindings(mut self, bindings: Bindings<T>) -> Self {
        self.bindings = Some(bindings);
        self
    }

    /// Load bindings from file
    pub fn with_bindings_from_file<P: AsRef<Path>>(
        self,
        file: P,
    ) -> Result<Self, BindingsFileError<T>>
    where
        Bindings<T>: Config,
    {
        let mut bindings = Bindings::load_no_fallback(file)?;
        bindings.check_invariants()?;
        Ok(self.with_bindings(bindings))
    }

    /// Load SDL controller mappings from file
    #[cfg(feature = "sdl_controller")]
    pub fn with_sdl_controller_mappings(mut self, mappings: String) -> Self {
        self.controller_mappings = Some(ControllerMappings::FromString(mappings));
        self
    }

    /// Load SDL controller mappings from file
    #[cfg(feature = "sdl_controller")]
    pub fn with_sdl_controller_mappings_from_file<P: AsRef<Path>>(mut self, file: P) -> Self {
        use std::path::PathBuf;

        let path_buf = PathBuf::from(file.as_ref());
        self.controller_mappings = Some(ControllerMappings::FromPath(path_buf));
        self
    }
}

impl<'a, 'b, T: BindingTypes> SystemBundle<'a, 'b> for InputBundle<T> {
    fn build(
        self,
        world: &mut World,
        builder: &mut DispatcherBuilder<'a, 'b>,
    ) -> Result<(), Error> {
        #[cfg(feature = "sdl_controller")]
        {
            use super::SdlEventsSystem;
            builder.add_thread_local(
                // TODO: improve errors when migrating to failure
                SdlEventsSystem::<T>::new(world, self.controller_mappings).unwrap(),
            );
        }
        builder.add(
            InputSystemDesc::<T>::new(self.bindings).build(world),
            "input_system",
            &[],
        );
        Ok(())
    }
}

/// An error occurred while loading the bindings file.
#[derive(Derivative)]
#[derivative(Debug(bound = ""))]
pub enum BindingsFileError<T: BindingTypes> {
    /// Problem in amethyst_config
    ConfigError(ConfigError),
    /// Problem with the bindings themselves.
    BindingError(BindingError<T>),
}

impl<T: BindingTypes> fmt::Display for BindingsFileError<T>
where
    T::Axis: fmt::Display,
    T::Action: fmt::Display,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            BindingsFileError::ConfigError(..) => write!(f, "Configuration error"),
            BindingsFileError::BindingError(..) => write!(f, "Binding error"),
        }
    }
}

impl<T: BindingTypes> error::Error for BindingsFileError<T>
where
    T::Axis: fmt::Display,
    T::Action: fmt::Display,
{
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        match self {
            BindingsFileError::ConfigError(ref e) => Some(e),
            BindingsFileError::BindingError(ref e) => Some(e),
        }
    }
}

impl<T: BindingTypes> From<BindingError<T>> for BindingsFileError<T> {
    fn from(error: BindingError<T>) -> Self {
        BindingsFileError::BindingError(error)
    }
}

impl<T: BindingTypes> From<ConfigError> for BindingsFileError<T> {
    fn from(error: ConfigError) -> Self {
        BindingsFileError::ConfigError(error)
    }
}