/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use crate::dom::bindings::codegen::Bindings::GamepadBinding; use crate::dom::bindings::codegen::Bindings::GamepadBinding::GamepadMethods; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::num::Finite; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector}; use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::bindings::str::DOMString; use crate::dom::event::Event; use crate::dom::eventtarget::EventTarget; use crate::dom::gamepadbuttonlist::GamepadButtonList; use crate::dom::gamepadevent::{GamepadEvent, GamepadEventType}; use crate::dom::globalscope::GlobalScope; use crate::dom::vrpose::VRPose; use dom_struct::dom_struct; use js::jsapi::{Heap, JSContext, JSObject}; use js::typedarray::{CreateWith, Float64Array}; use std::cell::Cell; use std::ptr; use std::ptr::NonNull; use webvr_traits::{WebVRGamepadData, WebVRGamepadHand, WebVRGamepadState}; #[dom_struct] pub struct Gamepad { reflector_: Reflector, gamepad_id: u32, id: String, index: Cell, connected: Cell, timestamp: Cell, mapping_type: String, #[ignore_malloc_size_of = "mozjs"] axes: Heap<*mut JSObject>, buttons: Dom, pose: Option>, #[ignore_malloc_size_of = "Defined in rust-webvr"] hand: WebVRGamepadHand, display_id: u32, } impl Gamepad { fn new_inherited( gamepad_id: u32, id: String, index: i32, connected: bool, timestamp: f64, mapping_type: String, buttons: &GamepadButtonList, pose: Option<&VRPose>, hand: WebVRGamepadHand, display_id: u32, ) -> Gamepad { Self { reflector_: Reflector::new(), gamepad_id: gamepad_id, id: id, index: Cell::new(index), connected: Cell::new(connected), timestamp: Cell::new(timestamp), mapping_type: mapping_type, axes: Heap::default(), buttons: Dom::from_ref(buttons), pose: pose.map(Dom::from_ref), hand: hand, display_id: display_id, } } #[allow(unsafe_code)] pub fn new_from_vr( global: &GlobalScope, index: i32, data: &WebVRGamepadData, state: &WebVRGamepadState, ) -> DomRoot { let buttons = GamepadButtonList::new_from_vr(&global, &state.buttons); let pose = VRPose::new(&global, &state.pose); let gamepad = reflect_dom_object( Box::new(Gamepad::new_inherited( state.gamepad_id, data.name.clone(), index, state.connected, state.timestamp, "".into(), &buttons, Some(&pose), data.hand.clone(), data.display_id, )), global, GamepadBinding::Wrap, ); let cx = global.get_cx(); rooted!(in (cx) let mut array = ptr::null_mut::()); unsafe { let _ = Float64Array::create(cx, CreateWith::Slice(&state.axes), array.handle_mut()); } gamepad.axes.set(array.get()); gamepad } } impl GamepadMethods for Gamepad { // https://w3c.github.io/gamepad/#dom-gamepad-id fn Id(&self) -> DOMString { DOMString::from(self.id.clone()) } // https://w3c.github.io/gamepad/#dom-gamepad-index fn Index(&self) -> i32 { self.index.get() } // https://w3c.github.io/gamepad/#dom-gamepad-connected fn Connected(&self) -> bool { self.connected.get() } // https://w3c.github.io/gamepad/#dom-gamepad-timestamp fn Timestamp(&self) -> Finite { Finite::wrap(self.timestamp.get()) } // https://w3c.github.io/gamepad/#dom-gamepad-mapping fn Mapping(&self) -> DOMString { DOMString::from(self.mapping_type.clone()) } #[allow(unsafe_code)] // https://w3c.github.io/gamepad/#dom-gamepad-axes unsafe fn Axes(&self, _cx: *mut JSContext) -> NonNull { NonNull::new_unchecked(self.axes.get()) } // https://w3c.github.io/gamepad/#dom-gamepad-buttons fn Buttons(&self) -> DomRoot { DomRoot::from_ref(&*self.buttons) } // https://w3c.github.io/gamepad/extensions.html#gamepadhand-enum fn Hand(&self) -> DOMString { let value = match self.hand { WebVRGamepadHand::Unknown => "", WebVRGamepadHand::Left => "left", WebVRGamepadHand::Right => "right", }; value.into() } // https://w3c.github.io/gamepad/extensions.html#dom-gamepad-pose fn GetPose(&self) -> Option> { self.pose.as_ref().map(|p| DomRoot::from_ref(&**p)) } // https://w3c.github.io/webvr/spec/1.1/#gamepad-getvrdisplays-attribute fn DisplayId(&self) -> u32 { self.display_id } } impl Gamepad { #[allow(unsafe_code)] pub fn update_from_vr(&self, state: &WebVRGamepadState) { self.timestamp.set(state.timestamp); unsafe { let cx = self.global().get_cx(); typedarray!(in(cx) let axes: Float64Array = self.axes.get()); if let Ok(mut array) = axes { array.update(&state.axes); } } self.buttons.sync_from_vr(&state.buttons); if let Some(ref pose) = self.pose { pose.update(&state.pose); } self.update_connected(state.connected); } pub fn gamepad_id(&self) -> u32 { self.gamepad_id } pub fn update_connected(&self, connected: bool) { if self.connected.get() == connected { return; } self.connected.set(connected); let event_type = if connected { GamepadEventType::Connected } else { GamepadEventType::Disconnected }; self.notify_event(event_type); } pub fn update_index(&self, index: i32) { self.index.set(index); } pub fn notify_event(&self, event_type: GamepadEventType) { let event = GamepadEvent::new_with_type(&self.global(), event_type, &self); event .upcast::() .fire(self.global().as_window().upcast::()); } }