1 use webcore::try_from::{
2     TryFrom,
3     TryInto,
4 };
5 use webcore::value::{
6     ConversionError,
7     Reference,
8     Value,
9 };
10 
11 /// The set of known gamepad layout mappings.
12 ///
13 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Gamepad/mapping)
14 // https://w3c.github.io/gamepad/#dom-gamepadmappingtype
15 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
16 pub enum GamepadMappingType {
17     /// No mapping is in use for this gamepad
18     NoMapping,
19     /// This gamepad is mapped to the [Standard Gamepad layout](https://w3c.github.io/gamepad/#remapping)
20     Standard,
21 }
22 
23 impl TryFrom<Value> for GamepadMappingType {
24     type Error = ConversionError;
25 
try_from(v: Value) -> Result<Self, Self::Error>26     fn try_from(v: Value) -> Result<Self, Self::Error> {
27         let value: String = v.try_into()?;
28         match value.as_ref() {
29             "" => Ok(GamepadMappingType::NoMapping),
30             "standard" => Ok(GamepadMappingType::Standard),
31             s => Err(ConversionError::Custom(format!("invalid gamepad mapping type \"{}\"", s))),
32         }
33     }
34 }
35 
36 /// The state of an individual button on a gamepad device.
37 ///
38 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/GamepadButton)
39 // https://w3c.github.io/gamepad/#gamepadbutton-interface
40 #[derive(Clone, Debug, Eq, PartialEq, ReferenceType)]
41 #[reference(instance_of = "GamepadButton")]
42 pub struct GamepadButton( Reference );
43 
44 impl GamepadButton {
45 
46     /// Is the button currently pressed?
47     ///
48     /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/GamepadButton/pressed)
49     // https://w3c.github.io/gamepad/#dom-gamepadbutton-pressed
50     #[inline]
pressed(&self) -> bool51     pub fn pressed(&self) -> bool {
52         js!(
53             return @{self.as_ref()}.pressed;
54         ).try_into().unwrap()
55     }
56 
57     /// Is the button currently touched?
58     ///
59     /// MDN does not document this. Firefox supports it, but Chrome (as of v65) does not.
60     // https://w3c.github.io/gamepad/#dom-gamepadbutton-touched
61     #[inline]
touched(&self) -> bool62     pub fn touched(&self) -> bool {
63         js!(
64             return @{self.as_ref()}.touched;
65         ).try_into().unwrap()
66     }
67 
68     /// The amount which the button has been pressed, between 0.0 (not pressed), and 1.0 (fully pressed).
69     ///
70     /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/GamepadButton/value)
71     // https://w3c.github.io/gamepad/#dom-gamepadbutton-value
72     #[inline]
value(&self) -> f6473     pub fn value(&self) -> f64 {
74         js!(
75             return @{self.as_ref()}.value;
76         ).try_into().unwrap()
77     }
78 }
79 
80 /// An individual gamepad/controller.
81 ///
82 /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Gamepad)
83 // https://w3c.github.io/gamepad/#gamepad-interface
84 #[derive(Clone, Debug, Eq, PartialEq, ReferenceType)]
85 #[reference(instance_of = "Gamepad")]
86 pub struct Gamepad( Reference );
87 
88 impl Gamepad {
89 
90     /// A string containing some information about this gamepad.
91     ///
92     /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Gamepad/id)
93     // https://w3c.github.io/gamepad/#dom-gamepad-id
94     #[inline]
id(&self) -> String95     pub fn id(&self) -> String {
96         js!(
97             return @{self.as_ref()}.id;
98         ).try_into().unwrap()
99     }
100 
101     /// An auto-incrementing integer to uniquely identify a connected Gamepad.
102     ///
103     /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Gamepad/index)
104     // https://w3c.github.io/gamepad/#dom-gamepad-index
105     #[inline]
index(&self) -> i32106     pub fn index(&self) -> i32 {
107         js!(
108             return @{self.as_ref()}.index;
109         ).try_into().unwrap()
110     }
111 
112     /// Is this gamepad connected to the system, powered on, and usable?
113     ///
114     /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Gamepad/connected)
115     // https://w3c.github.io/gamepad/#dom-gamepad-connected
116     #[inline]
connected(&self) -> bool117     pub fn connected(&self) -> bool {
118         js!(
119             return @{self.as_ref()}.connected;
120         ).try_into().unwrap()
121     }
122 
123     /// Monotonically increasing time this gamepad was updated.
124     ///
125     /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Gamepad/timestamp)
126     // https://w3c.github.io/gamepad/#dom-gamepad-timestamp
127     #[inline]
timestamp(&self) -> f64128     pub fn timestamp(&self) -> f64 {
129         js!(
130             return @{self.as_ref()}.timestamp;
131         ).try_into().unwrap()
132     }
133 
134     /// The mapping in use for this device.
135     ///
136     /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Gamepad/mapping)
137     // https://w3c.github.io/gamepad/#dom-gamepad-mapping
138     #[inline]
mapping(&self) -> GamepadMappingType139     pub fn mapping(&self) -> GamepadMappingType {
140         js!(
141             return @{self.as_ref()}.mapping;
142         ).try_into().unwrap()
143     }
144 
145     /// Array of values for all axes of the gamepad.
146     ///
147     /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Gamepad/axes)
148     // https://w3c.github.io/gamepad/#dom-gamepad-axes
149     #[inline]
axes(&self) -> Vec<f64>150     pub fn axes(&self) -> Vec<f64> {
151         js!(
152             return @{self.as_ref()}.axes;
153         ).try_into().unwrap()
154     }
155 
156     /// Array of button states for all buttons of the gamepad.
157     ///
158     /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Gamepad/buttons)
159     // https://w3c.github.io/gamepad/#dom-gamepad-buttons
160     #[inline]
buttons(&self) -> Vec<GamepadButton>161     pub fn buttons(&self) -> Vec<GamepadButton> {
162         js!(
163             return @{self.as_ref()}.buttons;
164         ).try_into().unwrap()
165     }
166 
167     /// Retrieve all connected gamepads, in an array indexed by each gamepad's `index` member.
168     ///
169     /// Chrome doesn't update Gamepad state automatically, you must call this function each frame.
170     ///
171     /// [(JavaScript docs)](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/getGamepads)
172     // https://w3c.github.io/gamepad/#dom-navigator-getgamepads
get_all() -> Vec<Option<Gamepad>>173     pub fn get_all() -> Vec<Option<Gamepad>> {
174         js!(
175             return Array.from(navigator.getGamepads());
176         ).try_into().unwrap()
177     }
178 }
179 
180 #[cfg(test)]
181 mod tests {
182     use super::GamepadMappingType;
183 
184     use webcore::try_from::TryInto;
185     use webcore::value::{ConversionError, Value};
186 
187     #[test]
test_value_into_gamepad_mapping()188     fn test_value_into_gamepad_mapping() {
189 
190         let to_mapping = |v: Value| -> Result<GamepadMappingType, ConversionError> {
191             v.try_into()
192         };
193 
194         assert_eq!(to_mapping("standard".into()), Ok(GamepadMappingType::Standard));
195         assert_eq!(to_mapping("".into()), Ok(GamepadMappingType::NoMapping));
196         assert!(to_mapping("fakemapping".into()).is_err());
197         assert!(to_mapping(Value::Null).is_err());
198     }
199 
200     // most of the Gamepad API is not testable,
201     // because Gamepad and GamepadButton are not constructible
202 }
203