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