gilrs/mapping/
mod.rs

1// Copyright 2016-2018 Mateusz Sieczko and other GilRs Developers
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7#![cfg_attr(target_os = "windows", allow(dead_code))]
8
9mod parser;
10
11use crate::ev::{self, Axis, AxisOrBtn, Button};
12use crate::utils::PATH_SEPARATOR;
13use gilrs_core::native_ev_codes as nec;
14use gilrs_core::EvCode;
15
16use std::collections::HashMap;
17use std::env;
18use std::error::Error;
19use std::fmt::{Display, Formatter, Result as FmtResult, Write as _};
20
21use fnv::FnvHashMap;
22use uuid::Uuid;
23use vec_map::VecMap;
24
25use self::parser::{Error as ParserError, ErrorKind as ParserErrorKind, Parser, Token};
26
27/// Platform name used by SDL mappings
28#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd"))]
29const SDL_PLATFORM_NAME: &str = "Linux";
30#[cfg(target_os = "macos")]
31const SDL_PLATFORM_NAME: &str = "Mac OS X";
32#[cfg(target_os = "windows")]
33const SDL_PLATFORM_NAME: &str = "Windows";
34#[cfg(all(
35    not(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd")),
36    not(target_os = "macos"),
37    not(target_os = "windows")
38))]
39const SDL_PLATFORM_NAME: &str = "Unknown";
40
41#[derive(Debug)]
42#[cfg_attr(test, derive(PartialEq))]
43/// Store mappings from one `EvCode` (`u16`) to another.
44///
45/// This struct is internal, `MappingData` is exported in public interface as `Mapping`.
46pub struct Mapping {
47    mappings: FnvHashMap<EvCode, AxisOrBtn>,
48    name: String,
49    default: bool,
50    hats_mapped: u8,
51}
52
53impl Mapping {
54    pub fn new() -> Self {
55        Mapping {
56            mappings: FnvHashMap::default(),
57            name: String::new(),
58            default: false,
59            hats_mapped: 0,
60        }
61    }
62
63    pub fn default(gamepad: &gilrs_core::Gamepad) -> Self {
64        use self::Axis as Ax;
65        use self::AxisOrBtn::*;
66
67        macro_rules! fnv_map {
68            ( $( $key:expr => $elem:expr ),* ) => {
69                {
70                    let mut map = FnvHashMap::default();
71                    $(
72                        map.insert($key, $elem);
73                    )*
74
75                    map
76                }
77            };
78        }
79
80        let mut mappings = fnv_map![
81            nec::BTN_SOUTH => Btn(Button::South),
82            nec::BTN_EAST => Btn(Button::East),
83            nec::BTN_C => Btn(Button::C),
84            nec::BTN_NORTH => Btn(Button::North),
85            nec::BTN_WEST => Btn(Button::West),
86            nec::BTN_Z => Btn(Button::Z),
87            nec::BTN_LT => Btn(Button::LeftTrigger),
88            nec::BTN_RT => Btn(Button::RightTrigger),
89            nec::BTN_LT2 => Btn(Button::LeftTrigger2),
90            nec::BTN_RT2 => Btn(Button::RightTrigger2),
91            nec::BTN_SELECT => Btn(Button::Select),
92            nec::BTN_START => Btn(Button::Start),
93            nec::BTN_MODE => Btn(Button::Mode),
94            nec::BTN_LTHUMB => Btn(Button::LeftThumb),
95            nec::BTN_RTHUMB => Btn(Button::RightThumb),
96            nec::BTN_DPAD_UP => Btn(Button::DPadUp),
97            nec::BTN_DPAD_DOWN => Btn(Button::DPadDown),
98            nec::BTN_DPAD_LEFT => Btn(Button::DPadLeft),
99            nec::BTN_DPAD_RIGHT => Btn(Button::DPadRight),
100
101            nec::AXIS_LT => Btn(Button::LeftTrigger),
102            nec::AXIS_RT => Btn(Button::RightTrigger),
103            nec::AXIS_LT2 => Btn(Button::LeftTrigger2),
104            nec::AXIS_RT2 => Btn(Button::RightTrigger2),
105
106            nec::AXIS_LSTICKX => Axis(Ax::LeftStickX),
107            nec::AXIS_LSTICKY => Axis(Ax::LeftStickY),
108            nec::AXIS_LEFTZ => Axis(Ax::LeftZ),
109            nec::AXIS_RSTICKX => Axis(Ax::RightStickX),
110            nec::AXIS_RSTICKY => Axis(Ax::RightStickY),
111            nec::AXIS_RIGHTZ => Axis(Ax::RightZ),
112            nec::AXIS_DPADX => Axis(Ax::DPadX),
113            nec::AXIS_DPADY => Axis(Ax::DPadY)
114        ];
115
116        // Remove all mappings that don't have corresponding element in gamepad. Partial fix to #83
117        let axes = [
118            nec::AXIS_DPADX,
119            nec::AXIS_DPADY,
120            nec::AXIS_LEFTZ,
121            nec::AXIS_LSTICKX,
122            nec::AXIS_LSTICKY,
123            nec::AXIS_RSTICKX,
124            nec::AXIS_RSTICKY,
125            nec::AXIS_LT,
126            nec::AXIS_LT2,
127            nec::AXIS_RT,
128            nec::AXIS_RT2,
129            nec::AXIS_RIGHTZ,
130        ];
131        let btns = [
132            nec::BTN_SOUTH,
133            nec::BTN_NORTH,
134            nec::BTN_WEST,
135            nec::BTN_WEST,
136            nec::BTN_C,
137            nec::BTN_Z,
138            nec::BTN_LT,
139            nec::BTN_LT2,
140            nec::BTN_RT,
141            nec::BTN_RT2,
142            nec::BTN_SELECT,
143            nec::BTN_START,
144            nec::BTN_MODE,
145            nec::BTN_LTHUMB,
146            nec::BTN_RTHUMB,
147            nec::BTN_DPAD_DOWN,
148            nec::BTN_DPAD_LEFT,
149            nec::BTN_DPAD_RIGHT,
150            nec::BTN_DPAD_UP,
151        ];
152
153        for axis in &axes {
154            if !gamepad.axes().contains(axis) {
155                mappings.remove(axis);
156            }
157        }
158
159        for btn in &btns {
160            if !gamepad.buttons().contains(btn) {
161                mappings.remove(btn);
162            }
163        }
164
165        Mapping {
166            mappings,
167            name: String::new(),
168            default: true,
169            hats_mapped: 0,
170        }
171    }
172
173    pub fn name(&self) -> &str {
174        &self.name
175    }
176
177    pub fn from_data(
178        data: &MappingData,
179        buttons: &[EvCode],
180        axes: &[EvCode],
181        name: &str,
182        uuid: Uuid,
183    ) -> Result<(Self, String), MappingError> {
184        use crate::constants::*;
185
186        if !Self::is_name_valid(name) {
187            return Err(MappingError::InvalidName);
188        }
189
190        let mut mappings = FnvHashMap::default();
191        let mut sdl_mappings = format!("{},{},", uuid.as_simple(), name);
192
193        {
194            let mut add_button = |ident, ev_code, mapped_btn| {
195                Self::add_button(
196                    ident,
197                    ev_code,
198                    mapped_btn,
199                    buttons,
200                    &mut sdl_mappings,
201                    &mut mappings,
202                )
203            };
204
205            for (button, &ev_code) in &data.buttons {
206                match button as u16 {
207                    BTN_SOUTH => add_button("a", ev_code, Button::South)?,
208                    BTN_EAST => add_button("b", ev_code, Button::East)?,
209                    BTN_WEST => add_button("x", ev_code, Button::West)?,
210                    BTN_NORTH => add_button("y", ev_code, Button::North)?,
211                    BTN_LT => add_button("leftshoulder", ev_code, Button::LeftTrigger)?,
212                    BTN_RT => add_button("rightshoulder", ev_code, Button::RightTrigger)?,
213                    BTN_LT2 => add_button("lefttrigger", ev_code, Button::LeftTrigger2)?,
214                    BTN_RT2 => add_button("righttrigger", ev_code, Button::RightTrigger2)?,
215                    BTN_SELECT => add_button("back", ev_code, Button::Select)?,
216                    BTN_START => add_button("start", ev_code, Button::Start)?,
217                    BTN_MODE => add_button("guide", ev_code, Button::Mode)?,
218                    BTN_LTHUMB => add_button("leftstick", ev_code, Button::LeftThumb)?,
219                    BTN_RTHUMB => add_button("rightstick", ev_code, Button::RightThumb)?,
220                    BTN_DPAD_UP => add_button("dpup", ev_code, Button::DPadUp)?,
221                    BTN_DPAD_DOWN => add_button("dpdown", ev_code, Button::DPadDown)?,
222                    BTN_DPAD_LEFT => add_button("dpleft", ev_code, Button::DPadLeft)?,
223                    BTN_DPAD_RIGHT => add_button("dpright", ev_code, Button::DPadRight)?,
224                    BTN_C => add_button("c", ev_code, Button::C)?,
225                    BTN_Z => add_button("z", ev_code, Button::Z)?,
226                    BTN_UNKNOWN => return Err(MappingError::UnknownElement),
227                    _ => unreachable!(),
228                }
229            }
230        }
231
232        {
233            let mut add_axis = |ident, ev_code, mapped_axis| {
234                Self::add_axis(
235                    ident,
236                    ev_code,
237                    mapped_axis,
238                    axes,
239                    &mut sdl_mappings,
240                    &mut mappings,
241                )
242            };
243
244            for (axis, &ev_code) in &data.axes {
245                match axis as u16 {
246                    AXIS_LSTICKX => add_axis("leftx", ev_code, Axis::LeftStickX)?,
247                    AXIS_LSTICKY => add_axis("lefty", ev_code, Axis::LeftStickY)?,
248                    AXIS_RSTICKX => add_axis("rightx", ev_code, Axis::RightStickX)?,
249                    AXIS_RSTICKY => add_axis("righty", ev_code, Axis::RightStickY)?,
250                    AXIS_LEFTZ => add_axis("leftz", ev_code, Axis::LeftZ)?,
251                    AXIS_RIGHTZ => add_axis("rightz", ev_code, Axis::RightZ)?,
252                    AXIS_UNKNOWN => return Err(MappingError::UnknownElement),
253                    _ => unreachable!(),
254                }
255            }
256        }
257
258        let mapping = Mapping {
259            mappings,
260            name: name.to_owned(),
261            default: false,
262            hats_mapped: 0,
263        };
264
265        Ok((mapping, sdl_mappings))
266    }
267
268    pub fn parse_sdl_mapping(
269        line: &str,
270        buttons: &[EvCode],
271        axes: &[EvCode],
272    ) -> Result<Self, ParseSdlMappingError> {
273        let mut mapping = Mapping::new();
274        let mut parser = Parser::new(line);
275
276        let mut uuid: Option<Uuid> = None;
277        while let Some(token) = parser.next_token() {
278            if let Err(ref e) = token {
279                if e.kind() == &ParserErrorKind::EmptyValue {
280                    continue;
281                }
282            }
283
284            let token = token?;
285
286            match token {
287                Token::Platform(platform) => {
288                    if platform != SDL_PLATFORM_NAME {
289                        warn!("Mappings for different platform – {}", platform);
290                    }
291                }
292                Token::Uuid(v) => uuid = Some(v),
293
294                Token::Name(name) => mapping.name = name.to_owned(),
295                Token::AxisMapping { from, to, .. } => {
296                    let axis = axes.get(from as usize).cloned();
297                    if let Some(axis) = axis {
298                        mapping.mappings.insert(axis, to);
299                    } else {
300                        warn!(
301                            "SDL-mapping {} {}: Unknown axis a{}",
302                            uuid.unwrap(),
303                            mapping.name,
304                            from
305                        )
306                    }
307                }
308                Token::ButtonMapping { from, to, .. } => {
309                    let btn = buttons.get(from as usize).cloned();
310
311                    if let Some(btn) = btn {
312                        mapping.mappings.insert(btn, to);
313                    } else {
314                        warn!(
315                            "SDL-mapping {} {}: Unknown button b{}",
316                            uuid.unwrap(),
317                            mapping.name,
318                            from
319                        )
320                    }
321                }
322                Token::HatMapping {
323                    hat, direction, to, ..
324                } => {
325                    if hat != 0 {
326                        warn!(
327                            "Hat mappings are only supported for dpads (requested to map hat \
328                             {}.{} to {:?}",
329                            hat, direction, to
330                        );
331                    } else {
332                        // We  don't have anything like "hat" in gilrs, so let's jus assume that
333                        // user want to map dpad axes.
334                        //
335                        // We have to add mappings for axes AND buttons, because axis_dpad_to_button
336                        // filter may transform event to button event.
337                        let (from_axis, from_btn) = match direction {
338                            1 => (nec::AXIS_DPADY, nec::BTN_DPAD_UP),
339                            4 => (nec::AXIS_DPADY, nec::BTN_DPAD_DOWN),
340                            2 => (nec::AXIS_DPADX, nec::BTN_DPAD_RIGHT),
341                            8 => (nec::AXIS_DPADX, nec::BTN_DPAD_LEFT),
342                            0 => continue, // FIXME: I have no idea what 0 means here
343                            _ => return Err(ParseSdlMappingError::UnknownHatDirection),
344                        };
345
346                        if to.is_button() {
347                            match to {
348                                AxisOrBtn::Btn(Button::DPadLeft | Button::DPadRight) => {
349                                    mapping
350                                        .mappings
351                                        .insert(from_axis, AxisOrBtn::Axis(Axis::DPadX));
352                                }
353                                AxisOrBtn::Btn(Button::DPadUp | Button::DPadDown) => {
354                                    mapping
355                                        .mappings
356                                        .insert(from_axis, AxisOrBtn::Axis(Axis::DPadY));
357                                }
358                                _ => (),
359                            }
360                            mapping.mappings.insert(from_btn, to);
361                        } else {
362                            mapping.mappings.insert(from_axis, to);
363                        }
364
365                        mapping.hats_mapped |= direction as u8;
366                    }
367                }
368            }
369        }
370
371        Ok(mapping)
372    }
373
374    fn add_button(
375        ident: &str,
376        ev_code: EvCode,
377        mapped_btn: Button,
378        buttons: &[EvCode],
379        sdl_mappings: &mut String,
380        mappings: &mut FnvHashMap<EvCode, AxisOrBtn>,
381    ) -> Result<(), MappingError> {
382        let n_btn = buttons
383            .iter()
384            .position(|&x| x == ev_code)
385            .ok_or(MappingError::InvalidCode(ev::Code(ev_code)))?;
386        let _ = write!(sdl_mappings, "{}:b{},", ident, n_btn);
387        mappings.insert(ev_code, AxisOrBtn::Btn(mapped_btn));
388        Ok(())
389    }
390
391    fn add_axis(
392        ident: &str,
393        ev_code: EvCode,
394        mapped_axis: Axis,
395        axes: &[EvCode],
396        sdl_mappings: &mut String,
397        mappings: &mut FnvHashMap<EvCode, AxisOrBtn>,
398    ) -> Result<(), MappingError> {
399        let n_axis = axes
400            .iter()
401            .position(|&x| x == ev_code)
402            .ok_or(MappingError::InvalidCode(ev::Code(ev_code)))?;
403        let _ = write!(sdl_mappings, "{}:a{},", ident, n_axis);
404        mappings.insert(ev_code, AxisOrBtn::Axis(mapped_axis));
405        Ok(())
406    }
407
408    fn is_name_valid(name: &str) -> bool {
409        !name.chars().any(|x| x == ',')
410    }
411
412    pub fn map(&self, code: &EvCode) -> Option<AxisOrBtn> {
413        self.mappings.get(code).cloned()
414    }
415
416    pub fn map_rev(&self, el: &AxisOrBtn) -> Option<EvCode> {
417        self.mappings.iter().find(|x| x.1 == el).map(|x| *x.0)
418    }
419
420    pub fn is_default(&self) -> bool {
421        self.default
422    }
423
424    /// Return bit field with mapped hats. Only for mappings created from SDL format this function
425    /// can return non-zero value.
426    pub fn hats_mapped(&self) -> u8 {
427        self.hats_mapped
428    }
429}
430
431#[derive(Clone, PartialEq, Eq, Debug)]
432pub enum ParseSdlMappingError {
433    UnknownHatDirection,
434    ParseError(ParserError),
435}
436
437impl From<ParserError> for ParseSdlMappingError {
438    fn from(f: ParserError) -> Self {
439        ParseSdlMappingError::ParseError(f)
440    }
441}
442
443impl Error for ParseSdlMappingError {
444    fn source(&self) -> Option<&(dyn Error + 'static)> {
445        if let ParseSdlMappingError::ParseError(ref err) = self {
446            Some(err)
447        } else {
448            None
449        }
450    }
451}
452
453impl Display for ParseSdlMappingError {
454    fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
455        match self {
456            ParseSdlMappingError::UnknownHatDirection => {
457                fmt.write_str("hat direction wasn't 1, 2, 4 or 8")
458            }
459            ParseSdlMappingError::ParseError(_) => fmt.write_str("parsing error"),
460        }
461    }
462}
463
464#[derive(Debug)]
465pub struct MappingDb {
466    mappings: HashMap<Uuid, String>,
467}
468
469impl MappingDb {
470    pub fn new() -> Self {
471        MappingDb {
472            mappings: HashMap::new(),
473        }
474    }
475
476    pub fn add_included_mappings(&mut self) {
477        self.insert(include_str!(concat!(
478            env!("OUT_DIR"),
479            PATH_SEPARATOR!(),
480            "gamecontrollerdb.txt"
481        )));
482    }
483
484    pub fn add_env_mappings(&mut self) {
485        if let Ok(mapping) = env::var("SDL_GAMECONTROLLERCONFIG") {
486            self.insert(&mapping);
487        }
488    }
489
490    pub fn insert(&mut self, s: &str) {
491        for mapping in s.lines() {
492            let pat = "platform:";
493            if let Some(offset) = mapping.find(pat).map(|o| o + pat.len()) {
494                let s = &mapping[offset..];
495                let end = s.find(',').unwrap_or(s.len());
496
497                if &s[..end] != SDL_PLATFORM_NAME {
498                    continue;
499                }
500            }
501
502            mapping
503                .split(',')
504                .next()
505                .and_then(|s| Uuid::parse_str(s).ok())
506                .and_then(|uuid| self.mappings.insert(uuid, mapping.to_owned()));
507        }
508    }
509
510    pub fn get(&self, uuid: Uuid) -> Option<&str> {
511        self.mappings.get(&uuid).map(String::as_ref)
512    }
513
514    pub fn len(&self) -> usize {
515        self.mappings.len()
516    }
517}
518
519/// Stores data used to map gamepad buttons and axes.
520///
521/// After you add all mappings, use
522/// [`Gamepad::set_mapping(…)`](struct.Gamepad.html#method.set_mapping) to change mapping of
523/// existing gamepad.
524///
525/// See `examples/mapping.rs` for more detailed example.
526#[derive(Debug, Clone, Default)]
527// Re-exported as Mapping
528pub struct MappingData {
529    buttons: VecMap<EvCode>,
530    axes: VecMap<EvCode>,
531}
532
533impl MappingData {
534    /// Creates new `Mapping`.
535    pub fn new() -> Self {
536        MappingData {
537            buttons: VecMap::with_capacity(18),
538            axes: VecMap::with_capacity(11),
539        }
540    }
541
542    /// Returns `EvCode` associated with button index.
543    pub fn button(&self, idx: Button) -> Option<ev::Code> {
544        self.buttons.get(idx as usize).cloned().map(ev::Code)
545    }
546
547    /// Returns `EvCode` associated with axis index.
548    pub fn axis(&self, idx: Axis) -> Option<ev::Code> {
549        self.axes.get(idx as usize).cloned().map(ev::Code)
550    }
551
552    /// Inserts new button mapping.
553    pub fn insert_btn(&mut self, from: ev::Code, to: Button) -> Option<ev::Code> {
554        self.buttons.insert(to as usize, from.0).map(ev::Code)
555    }
556
557    /// Inserts new axis mapping.
558    pub fn insert_axis(&mut self, from: ev::Code, to: Axis) -> Option<ev::Code> {
559        self.axes.insert(to as usize, from.0).map(ev::Code)
560    }
561
562    /// Removes button and returns associated `NativEvCode`.
563    pub fn remove_button(&mut self, idx: Button) -> Option<ev::Code> {
564        self.buttons.remove(idx as usize).map(ev::Code)
565    }
566
567    /// Removes axis and returns associated `NativEvCode`.
568    pub fn remove_axis(&mut self, idx: Axis) -> Option<ev::Code> {
569        self.axes.remove(idx as usize).map(ev::Code)
570    }
571}
572
573/// The error type for functions related to gamepad mapping.
574#[derive(Copy, Clone, Debug, PartialEq, Eq)]
575#[non_exhaustive]
576pub enum MappingError {
577    /// Gamepad does not have element referenced by `EvCode`.
578    InvalidCode(ev::Code),
579    /// Name contains comma (',').
580    InvalidName,
581    /// This function is not implemented for current platform.
582    NotImplemented,
583    /// Gamepad is not connected.
584    NotConnected,
585    /// Same gamepad element is referenced by axis and button.
586    DuplicatedEntry,
587    /// `Mapping` with `Button::Unknown` or `Axis::Unknown`.
588    UnknownElement,
589    /// `Mapping` have button or axis that are not present in SDL2.
590    NotSdl2Compatible,
591}
592
593impl Error for MappingError {}
594
595impl Display for MappingError {
596    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
597        let sbuf;
598        let s = match self {
599            MappingError::InvalidCode(code) => {
600                sbuf = format!("gamepad does not have element with {}", code);
601                sbuf.as_ref()
602            }
603            MappingError::InvalidName => "name can not contain comma",
604            MappingError::NotImplemented => {
605                "current platform does not implement setting custom mappings"
606            }
607            MappingError::NotConnected => "gamepad is not connected",
608            MappingError::DuplicatedEntry => {
609                "same gamepad element is referenced by axis and button"
610            }
611            MappingError::UnknownElement => "Button::Unknown and Axis::Unknown are not allowed",
612            MappingError::NotSdl2Compatible => "one of buttons or axes is not compatible with SDL2",
613        };
614
615        f.write_str(s)
616    }
617}
618
619#[cfg(test)]
620mod tests {
621    use super::*;
622    use crate::ev::{Axis, Button};
623    use gilrs_core::native_ev_codes as nec;
624    use gilrs_core::EvCode;
625    use uuid::Uuid;
626    // Do not include platform, mapping from (with UUID modified)
627    // https://github.com/gabomdq/SDL_GameControllerDB/blob/master/gamecontrollerdb.txt
628    const TEST_STR: &str = "03000000260900008888000000010001,GameCube {WiseGroup USB \
629                            box},a:b0,b:b2,y:b3,x:b1,start:b7,rightshoulder:b6,dpup:h0.1,dpleft:\
630                            h0.8,dpdown:h0.4,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,\
631                            lefttrigger:a4,righttrigger:a5,";
632
633    const BUTTONS: [EvCode; 15] = [
634        nec::BTN_SOUTH,
635        nec::BTN_EAST,
636        nec::BTN_C,
637        nec::BTN_NORTH,
638        nec::BTN_WEST,
639        nec::BTN_Z,
640        nec::BTN_LT,
641        nec::BTN_RT,
642        nec::BTN_LT2,
643        nec::BTN_RT2,
644        nec::BTN_SELECT,
645        nec::BTN_START,
646        nec::BTN_MODE,
647        nec::BTN_LTHUMB,
648        nec::BTN_RTHUMB,
649    ];
650
651    const AXES: [EvCode; 12] = [
652        nec::AXIS_LSTICKX,
653        nec::AXIS_LSTICKY,
654        nec::AXIS_LEFTZ,
655        nec::AXIS_RSTICKX,
656        nec::AXIS_RSTICKY,
657        nec::AXIS_RIGHTZ,
658        nec::AXIS_DPADX,
659        nec::AXIS_DPADY,
660        nec::AXIS_RT,
661        nec::AXIS_LT,
662        nec::AXIS_RT2,
663        nec::AXIS_LT2,
664    ];
665
666    #[test]
667    fn mapping() {
668        Mapping::parse_sdl_mapping(TEST_STR, &BUTTONS, &AXES).unwrap();
669    }
670
671    #[test]
672    fn from_data() {
673        let uuid = Uuid::nil();
674        let name = "Best Gamepad";
675        let buttons = BUTTONS.iter().cloned().map(ev::Code).collect::<Vec<_>>();
676        let axes = AXES.iter().cloned().map(ev::Code).collect::<Vec<_>>();
677
678        let mut data = MappingData::new();
679        data.insert_axis(axes[0], Axis::LeftStickX);
680        data.insert_axis(axes[1], Axis::LeftStickY);
681        data.insert_axis(axes[2], Axis::LeftZ);
682        data.insert_axis(axes[3], Axis::RightStickX);
683        data.insert_axis(axes[4], Axis::RightStickY);
684        data.insert_axis(axes[5], Axis::RightZ);
685
686        data.insert_btn(buttons[0], Button::South);
687        data.insert_btn(buttons[1], Button::East);
688        data.insert_btn(buttons[3], Button::North);
689        data.insert_btn(buttons[4], Button::West);
690        data.insert_btn(buttons[5], Button::Select);
691        data.insert_btn(buttons[6], Button::Start);
692        data.insert_btn(buttons[7], Button::DPadDown);
693        data.insert_btn(buttons[8], Button::DPadLeft);
694        data.insert_btn(buttons[9], Button::RightThumb);
695
696        let (mappings, sdl_mappings) =
697            Mapping::from_data(&data, &BUTTONS, &AXES, name, uuid).unwrap();
698        let sdl_mappings = Mapping::parse_sdl_mapping(&sdl_mappings, &BUTTONS, &AXES).unwrap();
699        assert_eq!(mappings, sdl_mappings);
700
701        let incorrect_mappings = Mapping::from_data(&data, &BUTTONS, &AXES, "Inval,id name", uuid);
702        assert_eq!(Err(MappingError::InvalidName), incorrect_mappings);
703
704        data.insert_btn(ev::Code(nec::BTN_DPAD_RIGHT), Button::DPadRight);
705        let incorrect_mappings = Mapping::from_data(&data, &BUTTONS, &AXES, name, uuid);
706        assert_eq!(
707            Err(MappingError::InvalidCode(ev::Code(nec::BTN_DPAD_RIGHT))),
708            incorrect_mappings
709        );
710
711        data.insert_btn(ev::Code(BUTTONS[3]), Button::Unknown);
712        let incorrect_mappings = Mapping::from_data(&data, &BUTTONS, &AXES, name, uuid);
713        assert_eq!(Err(MappingError::UnknownElement), incorrect_mappings);
714    }
715
716    #[test]
717    fn with_mappings() {
718        let mappings = format!(
719            "\nShould be ignored\nThis also should,be ignored\n\n{}",
720            TEST_STR
721        );
722        let mut db = MappingDb::new();
723        db.add_included_mappings();
724        db.insert(&mappings);
725
726        assert_eq!(
727            Some(TEST_STR),
728            db.get(Uuid::parse_str("03000000260900008888000000010001").unwrap())
729        );
730    }
731}