oxygengine_input/resources/
controller.rs

1use crate::device::InputDevice;
2use core::{ecs::Universe, Scalar};
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6#[derive(Debug, Default, Clone, Serialize, Deserialize)]
7pub struct InputMappings {
8    /// {device name: {name from: name to}}
9    #[serde(default)]
10    pub axes: HashMap<String, HashMap<String, String>>,
11    /// {device name: {name from: name to}}
12    #[serde(default)]
13    pub triggers: HashMap<String, HashMap<String, String>>,
14}
15
16#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
17pub enum TriggerState {
18    Idle,
19    Pressed,
20    Hold,
21    Released,
22}
23
24impl Default for TriggerState {
25    fn default() -> Self {
26        Self::Idle
27    }
28}
29
30impl TriggerState {
31    #[inline]
32    pub fn is_on(self) -> bool {
33        self == TriggerState::Pressed || self == TriggerState::Hold
34    }
35
36    #[inline]
37    pub fn is_off(self) -> bool {
38        !self.is_on()
39    }
40
41    #[inline]
42    pub fn is_idle(self) -> bool {
43        self == TriggerState::Idle
44    }
45
46    #[inline]
47    pub fn is_pressed(self) -> bool {
48        self == TriggerState::Pressed
49    }
50
51    #[inline]
52    pub fn is_hold(self) -> bool {
53        self == TriggerState::Hold
54    }
55
56    #[inline]
57    pub fn is_released(self) -> bool {
58        self == TriggerState::Released
59    }
60
61    #[inline]
62    pub fn press(self) -> Self {
63        match self {
64            Self::Idle => Self::Pressed,
65            Self::Pressed => Self::Hold,
66            Self::Hold => Self::Hold,
67            Self::Released => Self::Pressed,
68        }
69    }
70
71    #[inline]
72    pub fn release(self) -> Self {
73        match self {
74            Self::Idle => Self::Idle,
75            Self::Pressed => Self::Released,
76            Self::Hold => Self::Released,
77            Self::Released => Self::Idle,
78        }
79    }
80
81    #[inline]
82    pub fn progress(self, press: bool) -> Self {
83        if press {
84            self.press()
85        } else {
86            self.release()
87        }
88    }
89
90    #[inline]
91    pub fn priority(self) -> u8 {
92        match self {
93            Self::Idle => 0,
94            Self::Pressed => 1,
95            Self::Hold => 2,
96            Self::Released => 1,
97        }
98    }
99}
100
101#[derive(Default)]
102pub struct InputController {
103    devices: HashMap<String, Box<dyn InputDevice>>,
104    mapping_axes: HashMap<String, (String, String)>,
105    mapping_triggers: HashMap<String, (String, String)>,
106    axes: HashMap<String, Scalar>,
107    triggers: HashMap<String, TriggerState>,
108    text: String,
109}
110
111impl InputController {
112    pub fn register<D>(&mut self, mut device: D)
113    where
114        D: InputDevice + 'static,
115    {
116        device.on_register();
117        self.devices
118            .insert(device.name().to_owned(), Box::new(device));
119    }
120
121    pub fn unregister(&mut self, name: &str) -> Option<Box<dyn InputDevice>> {
122        if let Some(mut device) = self.devices.remove(name) {
123            device.on_unregister();
124            Some(device)
125        } else {
126            None
127        }
128    }
129
130    pub fn device(&self, id: &str) -> Option<&dyn InputDevice> {
131        self.devices.get(id).map(|device| device.as_ref())
132    }
133
134    pub fn as_device<T>(&self, id: &str) -> Option<&T>
135    where
136        T: InputDevice,
137    {
138        if let Some(device) = self.devices.get(id) {
139            device.as_any().downcast_ref::<T>()
140        } else {
141            None
142        }
143    }
144
145    pub fn mapping_axes(&self) -> impl Iterator<Item = (&str, (&str, &str))> {
146        self.mapping_axes
147            .iter()
148            .map(|(k, (a, b))| (k.as_str(), (a.as_str(), b.as_str())))
149    }
150
151    pub fn mapping_triggers(&self) -> impl Iterator<Item = (&str, (&str, &str))> {
152        self.mapping_triggers
153            .iter()
154            .map(|(k, (a, b))| (k.as_str(), (a.as_str(), b.as_str())))
155    }
156
157    pub fn map_config(&mut self, config: InputMappings) {
158        for (device, mappings) in config.axes {
159            for (name_from, name_to) in mappings {
160                self.map_axis(&name_from, &device, &name_to);
161            }
162        }
163        for (device, mappings) in config.triggers {
164            for (name_from, name_to) in mappings {
165                self.map_trigger(&name_from, &device, &name_to);
166            }
167        }
168    }
169
170    pub fn map_axis(&mut self, name_from: &str, device: &str, name_to: &str) {
171        self.mapping_axes.insert(
172            name_from.to_owned(),
173            (device.to_owned(), name_to.to_owned()),
174        );
175    }
176
177    pub fn unmap_axis(&mut self, name: &str) {
178        self.mapping_axes.remove(name);
179    }
180
181    pub fn map_trigger(&mut self, name_from: &str, device: &str, name_to: &str) {
182        self.mapping_triggers.insert(
183            name_from.to_owned(),
184            (device.to_owned(), name_to.to_owned()),
185        );
186    }
187
188    pub fn unmap_trigger(&mut self, name: &str) {
189        self.mapping_triggers.remove(name);
190    }
191
192    pub fn axes(&self) -> impl Iterator<Item = (&str, Scalar)> {
193        self.axes.iter().map(|(k, v)| (k.as_str(), *v))
194    }
195
196    pub fn triggers(&self) -> impl Iterator<Item = (&str, TriggerState)> {
197        self.triggers.iter().map(|(k, v)| (k.as_str(), *v))
198    }
199
200    pub fn axis(&self, name: &str) -> Option<Scalar> {
201        self.axes.get(name).cloned()
202    }
203
204    pub fn multi_axis<const N: usize>(&self, names: [&str; N]) -> [Option<Scalar>; N] {
205        let mut result = [None; N];
206        for i in 0..N {
207            result[i] = self.axis(names[i]);
208        }
209        result
210    }
211
212    pub fn mirror_multi_axis<const N: usize>(
213        &self,
214        names: [(&str, &str); N],
215    ) -> [Option<Scalar>; N] {
216        let mut result = [None; N];
217        for i in 0..N {
218            let name = names[i];
219            result[i] = match (self.axis(name.0), self.axis(name.1)) {
220                (None, None) => None,
221                (Some(v), None) => Some(-v),
222                (None, Some(v)) => Some(v),
223                (Some(a), Some(b)) => Some(b - a),
224            };
225        }
226        result
227    }
228
229    pub fn axis_or_default(&self, name: &str) -> Scalar {
230        self.axis(name).unwrap_or(0.0)
231    }
232
233    pub fn multi_axis_or_default<const N: usize>(&self, names: [&str; N]) -> [Scalar; N] {
234        let mut result = [Default::default(); N];
235        for i in 0..N {
236            result[i] = self.axis_or_default(names[i]);
237        }
238        result
239    }
240
241    pub fn mirror_multi_axis_or_default<const N: usize>(
242        &self,
243        names: [(&str, &str); N],
244    ) -> [Scalar; N] {
245        let mut result = [Default::default(); N];
246        for i in 0..N {
247            let name = names[i];
248            result[i] = self.axis_or_default(name.0) - self.axis_or_default(name.1);
249        }
250        result
251    }
252
253    pub fn set_axis(&mut self, name: &str, value: Scalar) {
254        self.axes.insert(name.to_owned(), value);
255    }
256
257    pub fn trigger(&self, name: &str) -> Option<TriggerState> {
258        self.triggers.get(name).cloned()
259    }
260
261    pub fn multi_trigger<const N: usize>(&self, names: [&str; N]) -> [Option<TriggerState>; N] {
262        let mut result = [None; N];
263        for i in 0..N {
264            result[i] = self.trigger(names[i]);
265        }
266        result
267    }
268
269    pub fn priority_trigger<const N: usize>(&self, names: [&str; N]) -> Option<TriggerState> {
270        let mut result = None;
271        for name in names.iter() {
272            result = match (result, self.trigger(name)) {
273                (None, v) => v,
274                (Some(a), Some(b)) => {
275                    if b.priority() > a.priority() {
276                        Some(b)
277                    } else {
278                        Some(a)
279                    }
280                }
281                _ => result,
282            };
283        }
284        result
285    }
286
287    pub fn trigger_or_default(&self, name: &str) -> TriggerState {
288        self.trigger(name).unwrap_or(TriggerState::Idle)
289    }
290
291    pub fn multi_trigger_or_default<const N: usize>(&self, names: [&str; N]) -> [TriggerState; N] {
292        let mut result = [Default::default(); N];
293        for i in 0..N {
294            result[i] = self.trigger_or_default(names[i]);
295        }
296        result
297    }
298
299    pub fn priority_trigger_or_default<const N: usize>(&self, names: [&str; N]) -> TriggerState {
300        let mut result = TriggerState::Idle;
301        for name in names.iter() {
302            let state = self.trigger_or_default(name);
303            if state.priority() > result.priority() {
304                result = state;
305            }
306        }
307        result
308    }
309
310    pub fn set_trigger(&mut self, name: &str, value: TriggerState) {
311        self.triggers.insert(name.to_owned(), value);
312    }
313
314    pub fn text(&self) -> &str {
315        &self.text
316    }
317
318    pub fn process(&mut self, universe: &mut Universe) {
319        for device in self.devices.values_mut() {
320            device.process(universe);
321        }
322        self.text.clear();
323        for device in self.devices.values() {
324            if let Some(text) = device.query_text() {
325                self.text.push_str(&text);
326            }
327        }
328        self.axes.clear();
329        for (name_from, (dev, name_to)) in &self.mapping_axes {
330            if let Some(device) = self.devices.get(dev) {
331                if let Some(value) = device.query_axis(name_to) {
332                    self.axes.insert(name_from.to_owned(), value);
333                }
334            }
335        }
336        for (name_from, (dev, name_to)) in &self.mapping_triggers {
337            if let Some(device) = self.devices.get(dev) {
338                if let Some(value) = device.query_trigger(name_to) {
339                    let prev = self.triggers.get(name_from).unwrap_or(&TriggerState::Idle);
340                    let next = prev.progress(value);
341                    self.triggers.insert(name_from.to_owned(), next);
342                }
343            }
344        }
345    }
346}