diff --git a/Cargo.lock b/Cargo.lock index d2c28f5205f..69f6bba5afb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = "0.0.1" dependencies = [ "ipc-channel 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "msg 0.0.1", - "rust-webvr 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rust-webvr 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2193,9 +2193,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "rust-webvr" -version = "0.2.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "gl_generator 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "libloading 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3495,7 +3496,7 @@ dependencies = [ "checksum ref_slice 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "825740057197b7d43025e7faf6477eaabc03434e153233da02d1f44602f71527" "checksum regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4278c17d0f6d62dfef0ab00028feb45bd7d2102843f80763474eeb1be8a10c01" "checksum regex-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9191b1f57603095f105d317e375d19b1c9c5c3185ea9633a99a6dcbed04457" -"checksum rust-webvr 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ae0560bf176cd49f08d3df2784f9bfe74df6f6346b71b98ca3358160316e271" +"checksum rust-webvr 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "454fc4c3a786029ab82c5528c14f01bf965f60f61b3f9b1ed51b4646223eab59" "checksum rustc-demangle 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3058a43ada2c2d0b92b3ae38007a2d0fa5e9db971be260e0171408a4ff471c95" "checksum rustc-serialize 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)" = "684ce48436d6465300c9ea783b6b14c4361d6b8dcbb1375b486a69cc19e2dfb0" "checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084" diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index 2fbcd6feefb..31942533990 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -102,7 +102,6 @@ use script_traits::{LayoutMsg as FromLayoutMsg, ScriptMsg as FromScriptMsg, Scri use script_traits::{LogEntry, ServiceWorkerMsg, webdriver_msg}; use script_traits::{MozBrowserErrorType, MozBrowserEvent, WebDriverCommandMsg, WindowSizeData}; use script_traits::{SWManagerMsg, ScopeThings, WindowSizeType}; -use script_traits::WebVREventMsg; use serde::{Deserialize, Serialize}; use servo_config::opts; use servo_config::prefs::PREFS; @@ -124,7 +123,7 @@ use style_traits::cursor::Cursor; use style_traits::viewport::ViewportConstraints; use timer_scheduler::TimerScheduler; use webrender_traits; -use webvr_traits::WebVRMsg; +use webvr_traits::{WebVREvent, WebVRMsg}; /// The `Constellation` itself. In the servo browser, there is one /// constellation, which maintains all of the browser global data. @@ -897,9 +896,9 @@ impl Constellation assert!(self.webvr_thread.is_none()); self.webvr_thread = Some(webvr_thread) } - FromCompositorMsg::WebVREvent(pipeline_ids, event) => { - debug!("constellation got WebVR event"); - self.handle_webvr_event(pipeline_ids, event); + FromCompositorMsg::WebVREvents(pipeline_ids, events) => { + debug!("constellation got {:?} WebVR events", events.len()); + self.handle_webvr_events(pipeline_ids, events); } } } @@ -1326,12 +1325,12 @@ impl Constellation } } - fn handle_webvr_event(&mut self, ids: Vec, event: WebVREventMsg) { + fn handle_webvr_events(&mut self, ids: Vec, events: Vec) { for id in ids { match self.pipelines.get_mut(&id) { Some(ref pipeline) => { // Notify script thread - let _ = pipeline.event_loop.send(ConstellationControlMsg::WebVREvent(id, event.clone())); + let _ = pipeline.event_loop.send(ConstellationControlMsg::WebVREvents(id, events.clone())); }, None => warn!("constellation got webvr event for dead pipeline") } diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index 890074974d3..9c798c42500 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -107,6 +107,7 @@ use time::Duration; use uuid::Uuid; use webrender_traits::{WebGLBufferId, WebGLError, WebGLFramebufferId, WebGLProgramId}; use webrender_traits::{WebGLRenderbufferId, WebGLShaderId, WebGLTextureId}; +use webvr_traits::WebVRGamepadHand; /// A trait to allow tracing (only) DOM objects. pub unsafe trait JSTraceable { @@ -381,6 +382,7 @@ unsafe_no_jsmanaged_fields!(WebGLRenderbufferId); unsafe_no_jsmanaged_fields!(WebGLShaderId); unsafe_no_jsmanaged_fields!(WebGLTextureId); unsafe_no_jsmanaged_fields!(MediaList); +unsafe_no_jsmanaged_fields!(WebVRGamepadHand); unsafe impl<'a> JSTraceable for &'a str { #[inline] diff --git a/components/script/dom/gamepad.rs b/components/script/dom/gamepad.rs new file mode 100644 index 00000000000..f9f28fc8c51 --- /dev/null +++ b/components/script/dom/gamepad.rs @@ -0,0 +1,206 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +use core::nonzero::NonZero; +use dom::bindings::codegen::Bindings::GamepadBinding; +use dom::bindings::codegen::Bindings::GamepadBinding::GamepadMethods; +use dom::bindings::inheritance::Castable; +use dom::bindings::js::{JS, Root}; +use dom::bindings::num::Finite; +use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object}; +use dom::bindings::str::DOMString; +use dom::event::Event; +use dom::eventtarget::EventTarget; +use dom::gamepadbuttonlist::GamepadButtonList; +use dom::gamepadevent::{GamepadEvent, GamepadEventType}; +use dom::globalscope::GlobalScope; +use dom::vrpose::VRPose; +use dom_struct::dom_struct; +use js::jsapi::{Heap, JSContext, JSObject}; +use js::typedarray::{Float64Array, CreateWith}; +use std::cell::Cell; +use std::ptr; +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, + axes: Heap<*mut JSObject>, + buttons: JS, + pose: Option>, + #[ignore_heap_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, + axes: *mut JSObject, + 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::new(axes), + buttons: JS::from_ref(buttons), + pose: pose.map(JS::from_ref), + hand: hand, + display_id: display_id + } + } + + #[allow(unsafe_code)] + pub fn new_from_vr(global: &GlobalScope, + index: i32, + data: &WebVRGamepadData, + state: &WebVRGamepadState) -> Root { + let buttons = GamepadButtonList::new_from_vr(&global, &state.buttons); + let pose = VRPose::new(&global, &state.pose); + let cx = global.get_cx(); + rooted!(in (cx) let mut axes = ptr::null_mut()); + unsafe { + let _ = Float64Array::create(cx, + CreateWith::Slice(&state.axes), + axes.handle_mut()); + } + + reflect_dom_object(box Gamepad::new_inherited(state.gamepad_id, + data.name.clone(), + index, + state.connected, + state.timestamp, + "".into(), + axes.get(), + &buttons, + Some(&pose), + data.hand.clone(), + data.display_id), + global, + GamepadBinding::Wrap) + + } +} + +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) -> NonZero<*mut JSObject> { + NonZero::new(self.axes.get()) + } + + // https://w3c.github.io/gamepad/#dom-gamepad-buttons + fn Buttons(&self) -> Root { + Root::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| Root::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::()); + } +} diff --git a/components/script/dom/gamepadbutton.rs b/components/script/dom/gamepadbutton.rs new file mode 100644 index 00000000000..ff0c7271e5b --- /dev/null +++ b/components/script/dom/gamepadbutton.rs @@ -0,0 +1,61 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +use dom::bindings::codegen::Bindings::GamepadButtonBinding; +use dom::bindings::codegen::Bindings::GamepadButtonBinding::GamepadButtonMethods; +use dom::bindings::js::Root; +use dom::bindings::num::Finite; +use dom::bindings::reflector::{Reflector, reflect_dom_object}; +use dom::globalscope::GlobalScope; +use dom_struct::dom_struct; +use std::cell::Cell; + +#[dom_struct] +pub struct GamepadButton { + reflector_: Reflector, + pressed: Cell, + touched: Cell, + value: Cell, +} + +impl GamepadButton { + pub fn new_inherited(pressed: bool, touched: bool) -> GamepadButton { + Self { + reflector_: Reflector::new(), + pressed: Cell::new(pressed), + touched: Cell::new(touched), + value: Cell::new(0.0), + } + } + + pub fn new(global: &GlobalScope, pressed: bool, touched: bool) -> Root { + reflect_dom_object(box GamepadButton::new_inherited(pressed, touched), + global, + GamepadButtonBinding::Wrap) + } +} + +impl GamepadButtonMethods for GamepadButton { + // https://www.w3.org/TR/gamepad/#widl-GamepadButton-pressed + fn Pressed(&self) -> bool { + self.pressed.get() + } + + // https://www.w3.org/TR/gamepad/#widl-GamepadButton-touched + fn Touched(&self) -> bool { + self.touched.get() + } + + // https://www.w3.org/TR/gamepad/#widl-GamepadButton-value + fn Value(&self) -> Finite { + Finite::wrap(self.value.get()) + } +} + +impl GamepadButton { + pub fn update(&self, pressed: bool, touched: bool) { + self.pressed.set(pressed); + self.touched.set(touched); + } +} diff --git a/components/script/dom/gamepadbuttonlist.rs b/components/script/dom/gamepadbuttonlist.rs new file mode 100644 index 00000000000..5ac25504009 --- /dev/null +++ b/components/script/dom/gamepadbuttonlist.rs @@ -0,0 +1,63 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +use dom::bindings::codegen::Bindings::GamepadButtonListBinding; +use dom::bindings::codegen::Bindings::GamepadButtonListBinding::GamepadButtonListMethods; +use dom::bindings::js::{JS, Root, RootedReference}; +use dom::bindings::reflector::{Reflector, reflect_dom_object}; +use dom::gamepadbutton::GamepadButton; +use dom::globalscope::GlobalScope; +use dom_struct::dom_struct; +use webvr_traits::WebVRGamepadButton; + +// https://w3c.github.io/gamepad/#gamepadbutton-interface +#[dom_struct] +pub struct GamepadButtonList { + reflector_: Reflector, + list: Vec> +} + +impl GamepadButtonList { + #[allow(unrooted_must_root)] + fn new_inherited(list: &[&GamepadButton]) -> GamepadButtonList { + GamepadButtonList { + reflector_: Reflector::new(), + list: list.iter().map(|button| JS::from_ref(*button)).collect(), + } + } + + pub fn new_from_vr(global: &GlobalScope, buttons: &[WebVRGamepadButton]) -> Root { + rooted_vec!(let list <- buttons.iter() + .map(|btn| GamepadButton::new(&global, btn.pressed, btn.touched))); + + reflect_dom_object(box GamepadButtonList::new_inherited(list.r()), + global, + GamepadButtonListBinding::Wrap) + } + + pub fn sync_from_vr(&self, vr_buttons: &[WebVRGamepadButton]) { + let mut index = 0; + for btn in vr_buttons { + self.list.get(index).as_ref().unwrap().update(btn.pressed, btn.touched); + index += 1; + } + } +} + +impl GamepadButtonListMethods for GamepadButtonList { + // https://w3c.github.io/gamepad/#dom-gamepad-buttons + fn Length(&self) -> u32 { + self.list.len() as u32 + } + + // https://w3c.github.io/gamepad/#dom-gamepad-buttons + fn Item(&self, index: u32) -> Option> { + self.list.get(index as usize).map(|button| Root::from_ref(&**button)) + } + + // https://w3c.github.io/gamepad/#dom-gamepad-buttons + fn IndexedGetter(&self, index: u32) -> Option> { + self.Item(index) + } +} diff --git a/components/script/dom/gamepadevent.rs b/components/script/dom/gamepadevent.rs new file mode 100644 index 00000000000..f6690981a57 --- /dev/null +++ b/components/script/dom/gamepadevent.rs @@ -0,0 +1,92 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +use dom::bindings::codegen::Bindings::EventBinding::EventBinding::EventMethods; +use dom::bindings::codegen::Bindings::GamepadEventBinding; +use dom::bindings::codegen::Bindings::GamepadEventBinding::GamepadEventMethods; +use dom::bindings::error::Fallible; +use dom::bindings::inheritance::Castable; +use dom::bindings::js::{JS, Root}; +use dom::bindings::reflector::{DomObject, reflect_dom_object}; +use dom::bindings::str::DOMString; +use dom::event::Event; +use dom::gamepad::Gamepad; +use dom::globalscope::GlobalScope; +use dom::window::Window; +use dom_struct::dom_struct; +use servo_atoms::Atom; + +#[dom_struct] +pub struct GamepadEvent { + event: Event, + gamepad: JS, +} + +pub enum GamepadEventType { + Connected, + Disconnected +} + +impl GamepadEvent { + fn new_inherited(gamepad: &Gamepad) -> GamepadEvent { + GamepadEvent { + event: Event::new_inherited(), + gamepad: JS::from_ref(gamepad), + } + } + + pub fn new(global: &GlobalScope, + type_: Atom, + bubbles: bool, + cancelable: bool, + gamepad: &Gamepad) + -> Root { + let ev = reflect_dom_object(box GamepadEvent::new_inherited(&gamepad), + global, + GamepadEventBinding::Wrap); + { + let event = ev.upcast::(); + event.init_event(type_, bubbles, cancelable); + } + ev + } + + pub fn new_with_type(global: &GlobalScope, event_type: GamepadEventType, gamepad: &Gamepad) + -> Root { + let name = match event_type { + GamepadEventType::Connected => "gamepadconnected", + GamepadEventType::Disconnected => "gamepaddisconnected" + }; + + GamepadEvent::new(&global, + name.into(), + false, + false, + &gamepad) + } + + // https://w3c.github.io/gamepad/#gamepadevent-interface + pub fn Constructor(window: &Window, + type_: DOMString, + init: &GamepadEventBinding::GamepadEventInit) + -> Fallible> { + Ok(GamepadEvent::new(&window.global(), + Atom::from(type_), + init.parent.bubbles, + init.parent.cancelable, + &init.gamepad)) + } +} + +impl GamepadEventMethods for GamepadEvent { + // https://w3c.github.io/gamepad/#gamepadevent-interface + fn Gamepad(&self) -> Root { + Root::from_ref(&*self.gamepad) + } + + // https://dom.spec.whatwg.org/#dom-event-istrusted + fn IsTrusted(&self) -> bool { + self.event.IsTrusted() + } +} diff --git a/components/script/dom/gamepadlist.rs b/components/script/dom/gamepadlist.rs new file mode 100644 index 00000000000..dd5bdd757d9 --- /dev/null +++ b/components/script/dom/gamepadlist.rs @@ -0,0 +1,61 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +use dom::bindings::cell::DOMRefCell; +use dom::bindings::codegen::Bindings::GamepadListBinding; +use dom::bindings::codegen::Bindings::GamepadListBinding::GamepadListMethods; +use dom::bindings::js::{JS, Root}; +use dom::bindings::reflector::{Reflector, reflect_dom_object}; +use dom::gamepad::Gamepad; +use dom::globalscope::GlobalScope; +use dom_struct::dom_struct; + +// https://www.w3.org/TR/gamepad/ +#[dom_struct] +pub struct GamepadList { + reflector_: Reflector, + list: DOMRefCell>> +} + +impl GamepadList { + fn new_inherited(list: &[&Gamepad]) -> GamepadList { + GamepadList { + reflector_: Reflector::new(), + list: DOMRefCell::new(list.iter().map(|g| JS::from_ref(&**g)).collect()) + } + } + + pub fn new(global: &GlobalScope, list: &[&Gamepad]) -> Root { + reflect_dom_object(box GamepadList::new_inherited(list), + global, + GamepadListBinding::Wrap) + } + + pub fn add_if_not_exists(&self, gamepads: &[Root]) { + for gamepad in gamepads { + if !self.list.borrow().iter().any(|g| g.gamepad_id() == gamepad.gamepad_id()) { + self.list.borrow_mut().push(JS::from_ref(&*gamepad)); + // Ensure that the gamepad has the correct index + gamepad.update_index(self.list.borrow().len() as i32 - 1); + } + } + } +} + +impl GamepadListMethods for GamepadList { + // https://w3c.github.io/gamepad/#dom-navigator-getgamepads + fn Length(&self) -> u32 { + self.list.borrow().len() as u32 + } + + // https://w3c.github.io/gamepad/#dom-navigator-getgamepads + fn Item(&self, index: u32) -> Option> { + self.list.borrow().get(index as usize).map(|gamepad| Root::from_ref(&**gamepad)) + } + + // https://w3c.github.io/gamepad/#dom-navigator-getgamepads + fn IndexedGetter(&self, index: u32) -> Option> { + self.Item(index) + } +} diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 2cfcfc894ea..83efe407f3e 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -290,6 +290,11 @@ pub mod filereadersync; pub mod focusevent; pub mod forcetouchevent; pub mod formdata; +pub mod gamepad; +pub mod gamepadbutton; +pub mod gamepadbuttonlist; +pub mod gamepadevent; +pub mod gamepadlist; pub mod globalscope; pub mod hashchangeevent; pub mod headers; diff --git a/components/script/dom/navigator.rs b/components/script/dom/navigator.rs index 118f53ac49b..32dd2900e17 100644 --- a/components/script/dom/navigator.rs +++ b/components/script/dom/navigator.rs @@ -8,6 +8,7 @@ use dom::bindings::js::{MutNullableJS, Root}; use dom::bindings::reflector::{Reflector, DomObject, reflect_dom_object}; use dom::bindings::str::DOMString; use dom::bluetooth::Bluetooth; +use dom::gamepadlist::GamepadList; use dom::mimetypearray::MimeTypeArray; use dom::navigatorinfo; use dom::permissions::Permissions; @@ -16,7 +17,6 @@ use dom::serviceworkercontainer::ServiceWorkerContainer; use dom::vr::VR; use dom::window::Window; use dom_struct::dom_struct; -use script_traits::WebVREventMsg; #[dom_struct] pub struct Navigator { @@ -26,6 +26,7 @@ pub struct Navigator { mime_types: MutNullableJS, service_worker: MutNullableJS, vr: MutNullableJS, + gamepads: MutNullableJS, permissions: MutNullableJS, } @@ -38,6 +39,7 @@ impl Navigator { mime_types: Default::default(), service_worker: Default::default(), vr: Default::default(), + gamepads: Default::default(), permissions: Default::default(), } } @@ -128,15 +130,19 @@ impl NavigatorMethods for Navigator { self.vr.or_init(|| VR::new(&self.global())) } + // https://www.w3.org/TR/gamepad/#navigator-interface-extension + fn GetGamepads(&self) -> Root { + let root = self.gamepads.or_init(|| { + GamepadList::new(&self.global(), &[]) + }); + + let vr_gamepads = self.Vr().get_gamepads(); + root.add_if_not_exists(&vr_gamepads); + // TODO: Add not VR related gamepads + root + } // https://w3c.github.io/permissions/#navigator-and-workernavigator-extension fn Permissions(&self) -> Root { self.permissions.or_init(|| Permissions::new(&self.global())) } } - -impl Navigator { - pub fn handle_webvr_event(&self, event: WebVREventMsg) { - self.vr.get().expect("Shouldn't arrive here with an empty VR instance") - .handle_webvr_event(event); - } -} diff --git a/components/script/dom/vr.rs b/components/script/dom/vr.rs index aea8230ac0e..9497405e8c9 100644 --- a/components/script/dom/vr.rs +++ b/components/script/dom/vr.rs @@ -5,12 +5,15 @@ use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::VRBinding; use dom::bindings::codegen::Bindings::VRBinding::VRMethods; +use dom::bindings::codegen::Bindings::VRDisplayBinding::VRDisplayMethods; use dom::bindings::error::Error; use dom::bindings::inheritance::Castable; use dom::bindings::js::{JS, Root}; use dom::bindings::reflector::{DomObject, reflect_dom_object}; use dom::event::Event; use dom::eventtarget::EventTarget; +use dom::gamepad::Gamepad; +use dom::gamepadevent::GamepadEventType; use dom::globalscope::GlobalScope; use dom::promise::Promise; use dom::vrdisplay::VRDisplay; @@ -18,22 +21,23 @@ use dom::vrdisplayevent::VRDisplayEvent; use dom_struct::dom_struct; use ipc_channel::ipc; use ipc_channel::ipc::IpcSender; -use script_traits::WebVREventMsg; use std::rc::Rc; -use webvr_traits::WebVRMsg; -use webvr_traits::webvr; +use webvr_traits::{WebVRDisplayData, WebVRDisplayEvent, WebVREvent, WebVRMsg}; +use webvr_traits::{WebVRGamepadData, WebVRGamepadEvent, WebVRGamepadState}; #[dom_struct] pub struct VR { eventtarget: EventTarget, - displays: DOMRefCell>> + displays: DOMRefCell>>, + gamepads: DOMRefCell>> } impl VR { fn new_inherited() -> VR { VR { eventtarget: EventTarget::new_inherited(), - displays: DOMRefCell::new(Vec::new()) + displays: DOMRefCell::new(Vec::new()), + gamepads: DOMRefCell::new(Vec::new()), } } @@ -95,10 +99,10 @@ impl VR { self.global().as_window().webvr_thread() } - fn find_display(&self, display_id: u64) -> Option> { + fn find_display(&self, display_id: u32) -> Option> { self.displays.borrow() .iter() - .find(|d| d.get_display_id() == display_id) + .find(|d| d.DisplayId() == display_id) .map(|d| Root::from_ref(&**d)) } @@ -116,7 +120,7 @@ impl VR { } } - fn sync_display(&self, display: &webvr::VRDisplayData) -> Root { + fn sync_display(&self, display: &WebVRDisplayData) -> Root { if let Some(existing) = self.find_display(display.display_id) { existing.update_display(&display); existing @@ -127,35 +131,121 @@ impl VR { } } - pub fn handle_webvr_event(&self, event: WebVREventMsg) { - let WebVREventMsg::DisplayEvent(event) = event; - match &event { - &webvr::VRDisplayEvent::Connect(ref display) => { + fn handle_display_event(&self, event: WebVRDisplayEvent) { + match event { + WebVRDisplayEvent::Connect(ref display) => { let display = self.sync_display(&display); display.handle_webvr_event(&event); - self.notify_event(&display, &event); + self.notify_display_event(&display, &event); }, - &webvr::VRDisplayEvent::Disconnect(id) => { + WebVRDisplayEvent::Disconnect(id) => { if let Some(display) = self.find_display(id) { display.handle_webvr_event(&event); - self.notify_event(&display, &event); + self.notify_display_event(&display, &event); } }, - &webvr::VRDisplayEvent::Activate(ref display, _) | - &webvr::VRDisplayEvent::Deactivate(ref display, _) | - &webvr::VRDisplayEvent::Blur(ref display) | - &webvr::VRDisplayEvent::Focus(ref display) | - &webvr::VRDisplayEvent::PresentChange(ref display, _) | - &webvr::VRDisplayEvent::Change(ref display) => { + WebVRDisplayEvent::Activate(ref display, _) | + WebVRDisplayEvent::Deactivate(ref display, _) | + WebVRDisplayEvent::Blur(ref display) | + WebVRDisplayEvent::Focus(ref display) | + WebVRDisplayEvent::PresentChange(ref display, _) | + WebVRDisplayEvent::Change(ref display) => { let display = self.sync_display(&display); display.handle_webvr_event(&event); } }; } - fn notify_event(&self, display: &VRDisplay, event: &webvr::VRDisplayEvent) { + fn handle_gamepad_event(&self, event: WebVRGamepadEvent) { + match event { + WebVRGamepadEvent::Connect(data, state) => { + if let Some(gamepad) = self.find_gamepad(state.gamepad_id) { + gamepad.update_from_vr(&state); + } else { + // new gamepad + self.sync_gamepad(Some(data), &state); + } + }, + WebVRGamepadEvent::Disconnect(id) => { + if let Some(gamepad) = self.find_gamepad(id) { + gamepad.update_connected(false); + } + } + }; + } + + pub fn handle_webvr_event(&self, event: WebVREvent) { + match event { + WebVREvent::Display(event) => { + self.handle_display_event(event); + }, + WebVREvent::Gamepad(event) => { + self.handle_gamepad_event(event); + } + }; + } + + pub fn handle_webvr_events(&self, events: Vec) { + for event in events { + self.handle_webvr_event(event); + } + } + + fn notify_display_event(&self, display: &VRDisplay, event: &WebVRDisplayEvent) { let event = VRDisplayEvent::new_from_webvr(&self.global(), &display, &event); event.upcast::().fire(self.upcast()); } } +// Gamepad +impl VR { + fn find_gamepad(&self, gamepad_id: u32) -> Option> { + self.gamepads.borrow() + .iter() + .find(|g| g.gamepad_id() == gamepad_id) + .map(|g| Root::from_ref(&**g)) + } + + fn sync_gamepad(&self, data: Option, state: &WebVRGamepadState) { + if let Some(existing) = self.find_gamepad(state.gamepad_id) { + existing.update_from_vr(&state); + } else { + let index = self.gamepads.borrow().len(); + let data = data.unwrap_or_default(); + let root = Gamepad::new_from_vr(&self.global(), + index as i32, + &data, + &state); + self.gamepads.borrow_mut().push(JS::from_ref(&*root)); + if state.connected { + root.notify_event(GamepadEventType::Connected); + } + } + } + + // Gamepads are synced immediately in response to the API call. + // The current approach allows the to sample gamepad state multiple times per frame. This + // guarantees that the gamepads always have a valid state and can be very useful for + // motion capture or drawing applications. + pub fn get_gamepads(&self) -> Vec> { + if let Some(wevbr_sender) = self.webvr_thread() { + let (sender, receiver) = ipc::channel().unwrap(); + let synced_ids = self.gamepads.borrow().iter().map(|g| g.gamepad_id()).collect(); + wevbr_sender.send(WebVRMsg::GetGamepads(synced_ids, sender)).unwrap(); + match receiver.recv().unwrap() { + Ok(gamepads) => { + // Sync displays + for gamepad in gamepads { + self.sync_gamepad(gamepad.0, &gamepad.1); + } + }, + Err(_) => {} + } + } + + // We can add other not VR related gamepad providers here + self.gamepads.borrow().iter() + .map(|g| Root::from_ref(&**g)) + .collect() + } +} diff --git a/components/script/dom/vrdisplay.rs b/components/script/dom/vrdisplay.rs index c05276c2349..b9b65264c44 100644 --- a/components/script/dom/vrdisplay.rs +++ b/components/script/dom/vrdisplay.rs @@ -161,7 +161,7 @@ impl VRDisplayMethods for VRDisplay { // https://w3c.github.io/webvr/#dom-vrdisplay-displayid fn DisplayId(&self) -> u32 { - self.display.borrow().display_id as u32 + self.display.borrow().display_id } // https://w3c.github.io/webvr/#dom-vrdisplay-displayname @@ -188,7 +188,7 @@ impl VRDisplayMethods for VRDisplay { // If not presenting we fetch inmediante VRFrameData let (sender, receiver) = ipc::channel().unwrap(); self.webvr_thread().send(WebVRMsg::GetFrameData(self.global().pipeline_id(), - self.get_display_id(), + self.DisplayId(), self.depth_near.get(), self.depth_far.get(), sender)).unwrap(); @@ -213,7 +213,7 @@ impl VRDisplayMethods for VRDisplay { fn ResetPose(&self) { let (sender, receiver) = ipc::channel().unwrap(); self.webvr_thread().send(WebVRMsg::ResetPose(self.global().pipeline_id(), - self.get_display_id(), + self.DisplayId(), sender)).unwrap(); if let Ok(data) = receiver.recv().unwrap() { // Some VRDisplay data might change after calling ResetPose() @@ -378,7 +378,7 @@ impl VRDisplayMethods for VRDisplay { } let api_sender = self.layer_ctx.get().unwrap().ipc_renderer(); - let display_id = self.display.borrow().display_id; + let display_id = self.display.borrow().display_id as u64; let layer = self.layer.borrow(); let msg = VRCompositorCommand::SubmitFrame(display_id, layer.left_bounds, layer.right_bounds); api_sender.send(CanvasMsg::WebVR(msg)).unwrap(); @@ -390,10 +390,6 @@ impl VRDisplay { self.global().as_window().webvr_thread().expect("Shouldn't arrive here with WebVR disabled") } - pub fn get_display_id(&self) -> u64 { - self.display.borrow().display_id - } - pub fn update_display(&self, display: &WebVRDisplayData) { *self.display.borrow_mut() = display.clone(); if let Some(ref stage) = display.stage_parameters { @@ -447,7 +443,7 @@ impl VRDisplay { let (sync_sender, sync_receiver) = ipc::channel().unwrap(); *self.frame_data_receiver.borrow_mut() = Some(sync_receiver); - let display_id = self.display.borrow().display_id; + let display_id = self.display.borrow().display_id as u64; let api_sender = self.layer_ctx.get().unwrap().ipc_renderer(); let js_sender = self.global().script_chan(); let address = Trusted::new(&*self); @@ -497,7 +493,7 @@ impl VRDisplay { *self.frame_data_receiver.borrow_mut() = None; let api_sender = self.layer_ctx.get().unwrap().ipc_renderer(); - let display_id = self.display.borrow().display_id; + let display_id = self.display.borrow().display_id as u64; let msg = VRCompositorCommand::Release(display_id); api_sender.send(CanvasMsg::WebVR(msg)).unwrap(); } diff --git a/components/script/dom/vrpose.rs b/components/script/dom/vrpose.rs index 542059ca799..ee035e2a0f1 100644 --- a/components/script/dom/vrpose.rs +++ b/components/script/dom/vrpose.rs @@ -32,7 +32,9 @@ unsafe fn update_or_create_typed_array(cx: *mut JSContext, match src { Some(data) => { if dst.get().is_null() { - let _ = Float32Array::create(cx, CreateWith::Slice(data), dst.handle_mut()); + rooted!(in (cx) let mut array = ptr::null_mut()); + let _ = Float32Array::create(cx, CreateWith::Slice(data), array.handle_mut()); + (*dst).set(array.get()); } else { typedarray!(in(cx) let array: Float32Array = dst.get()); if let Ok(mut array) = array { diff --git a/components/script/dom/webidls/Gamepad.webidl b/components/script/dom/webidls/Gamepad.webidl new file mode 100644 index 00000000000..0f666a495cf --- /dev/null +++ b/components/script/dom/webidls/Gamepad.webidl @@ -0,0 +1,26 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +// https://w3c.github.io/gamepad/#gamepad-interface +[Pref="dom.gamepad.enabled"] +interface Gamepad { + readonly attribute DOMString id; + readonly attribute long index; + readonly attribute boolean connected; + readonly attribute DOMHighResTimeStamp timestamp; + readonly attribute DOMString mapping; + readonly attribute Float64Array axes; + [SameObject] readonly attribute GamepadButtonList buttons; +}; + +// https://w3c.github.io/gamepad/extensions.html#dom-gamepad +partial interface Gamepad { + readonly attribute DOMString hand; + readonly attribute VRPose? pose; +}; + +// https://w3c.github.io/webvr/spec/1.1/#interface-gamepad +partial interface Gamepad { + readonly attribute unsigned long displayId; +}; diff --git a/components/script/dom/webidls/GamepadButton.webidl b/components/script/dom/webidls/GamepadButton.webidl new file mode 100644 index 00000000000..2fa04c8ba3c --- /dev/null +++ b/components/script/dom/webidls/GamepadButton.webidl @@ -0,0 +1,11 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +// https://w3c.github.io/gamepad/#gamepadbutton-interface +[Pref="dom.gamepad.enabled"] +interface GamepadButton { + readonly attribute boolean pressed; + readonly attribute boolean touched; + readonly attribute double value; +}; diff --git a/components/script/dom/webidls/GamepadButtonList.webidl b/components/script/dom/webidls/GamepadButtonList.webidl new file mode 100644 index 00000000000..c8fb75a4350 --- /dev/null +++ b/components/script/dom/webidls/GamepadButtonList.webidl @@ -0,0 +1,10 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +// https://w3c.github.io/gamepad/#dom-gamepad-buttons +[Pref="dom.gamepad.enabled"] +interface GamepadButtonList { + getter GamepadButton? item(unsigned long index); + readonly attribute unsigned long length; +}; diff --git a/components/script/dom/webidls/GamepadEvent.webidl b/components/script/dom/webidls/GamepadEvent.webidl new file mode 100644 index 00000000000..ea40fd4261c --- /dev/null +++ b/components/script/dom/webidls/GamepadEvent.webidl @@ -0,0 +1,13 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +// https://w3c.github.io/gamepad/#gamepadevent-interface +[Pref="dom.gamepad.enabled", Constructor(DOMString type, GamepadEventInit eventInitDict)] +interface GamepadEvent : Event { + readonly attribute Gamepad gamepad; +}; + +dictionary GamepadEventInit : EventInit { + required Gamepad gamepad; +}; diff --git a/components/script/dom/webidls/GamepadList.webidl b/components/script/dom/webidls/GamepadList.webidl new file mode 100644 index 00000000000..2d99e4e8f3c --- /dev/null +++ b/components/script/dom/webidls/GamepadList.webidl @@ -0,0 +1,10 @@ +/* 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 http://mozilla.org/MPL/2.0/. */ + +// https://w3c.github.io/gamepad/#navigator-interface-extension +[Pref="dom.gamepad.enabled"] +interface GamepadList { + getter Gamepad? item(unsigned long index); + readonly attribute unsigned long length; +}; diff --git a/components/script/dom/webidls/Navigator.webidl b/components/script/dom/webidls/Navigator.webidl index e04616ef40f..338753d6261 100644 --- a/components/script/dom/webidls/Navigator.webidl +++ b/components/script/dom/webidls/Navigator.webidl @@ -68,3 +68,8 @@ partial interface Navigator { partial interface Navigator { [Pref="dom.permissions.enabled"] readonly attribute Permissions permissions; }; + +// https://w3c.github.io/gamepad/#navigator-interface-extension +partial interface Navigator { + [Pref="dom.gamepad.enabled"] GamepadList getGamepads(); +}; diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 955887addf4..92537fa7abe 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -27,6 +27,7 @@ use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::CSSStyleDeclarationBinding::CSSStyleDeclarationMethods; use dom::bindings::codegen::Bindings::DocumentBinding::{DocumentMethods, DocumentReadyState}; use dom::bindings::codegen::Bindings::EventBinding::EventInit; +use dom::bindings::codegen::Bindings::NavigatorBinding::NavigatorMethods; use dom::bindings::codegen::Bindings::TransitionEventBinding::TransitionEventInit; use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use dom::bindings::conversions::{ConversionResult, FromJSValConvertible, StringificationBehavior}; @@ -91,7 +92,6 @@ use script_traits::{ScriptThreadFactory, TimerEvent, TimerSchedulerMsg, TimerSou use script_traits::{TouchEventType, TouchId, UntrustedNodeAddress, WindowSizeData, WindowSizeType}; use script_traits::CompositorEvent::{KeyEvent, MouseButtonEvent, MouseMoveEvent, ResizeEvent}; use script_traits::CompositorEvent::{TouchEvent, TouchpadPressureEvent}; -use script_traits::WebVREventMsg; use script_traits::webdriver_msg::WebDriverScriptCommand; use serviceworkerjob::{Job, JobQueue, AsyncJobHandler}; use servo_config::opts; @@ -119,7 +119,7 @@ use task_source::user_interaction::{UserInteractionTask, UserInteractionTaskSour use time::Tm; use url::Position; use webdriver_handlers; -use webvr_traits::WebVRMsg; +use webvr_traits::{WebVREvent, WebVRMsg}; pub type ImageCacheMsg = (PipelineId, PendingImageResponse); @@ -1070,8 +1070,8 @@ impl ScriptThread { self.handle_reload(pipeline_id), ConstellationControlMsg::ExitPipeline(pipeline_id, discard_browsing_context) => self.handle_exit_pipeline_msg(pipeline_id, discard_browsing_context), - ConstellationControlMsg::WebVREvent(pipeline_id, event) => - self.handle_webvr_event(pipeline_id, event), + ConstellationControlMsg::WebVREvents(pipeline_id, events) => + self.handle_webvr_events(pipeline_id, events), msg @ ConstellationControlMsg::AttachLayout(..) | msg @ ConstellationControlMsg::Viewport(..) | msg @ ConstellationControlMsg::SetScrollState(..) | @@ -2186,11 +2186,11 @@ impl ScriptThread { } } - fn handle_webvr_event(&self, pipeline_id: PipelineId, event: WebVREventMsg) { + fn handle_webvr_events(&self, pipeline_id: PipelineId, events: Vec) { let window = self.documents.borrow().find_window(pipeline_id); if let Some(window) = window { - let navigator = window.Navigator(); - navigator.handle_webvr_event(event); + let vr = window.Navigator().Vr(); + vr.handle_webvr_events(events); } } diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index 0c4ac7b3fa3..f7248466d39 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -71,7 +71,7 @@ use std::sync::Arc; use std::sync::mpsc::{Receiver, Sender}; use style_traits::{CSSPixel, UnsafeNode}; use webdriver_msg::{LoadStatus, WebDriverScriptCommand}; -use webvr_traits::{WebVRDisplayEvent, WebVRMsg}; +use webvr_traits::{WebVREvent, WebVRMsg}; pub use script_msg::{LayoutMsg, ScriptMsg, EventResult, LogEntry}; pub use script_msg::{ServiceWorkerMsg, ScopeThings, SWManagerMsg, SWManagerSenders, DOMMessage}; @@ -280,8 +280,8 @@ pub enum ConstellationControlMsg { ReportCSSError(PipelineId, String, usize, usize, String), /// Reload the given page. Reload(PipelineId), - /// Notifies the script thread of a WebVR device event - WebVREvent(PipelineId, WebVREventMsg) + /// Notifies the script thread of WebVR events. + WebVREvents(PipelineId, Vec) } impl fmt::Debug for ConstellationControlMsg { @@ -314,7 +314,7 @@ impl fmt::Debug for ConstellationControlMsg { FramedContentChanged(..) => "FramedContentChanged", ReportCSSError(..) => "ReportCSSError", Reload(..) => "Reload", - WebVREvent(..) => "WebVREvent", + WebVREvents(..) => "WebVREvents", }; write!(formatter, "ConstellationMsg::{}", variant) } @@ -751,16 +751,8 @@ pub enum ConstellationMsg { LogEntry(Option, Option, LogEntry), /// Set the WebVR thread channel. SetWebVRThread(IpcSender), - /// Dispatch a WebVR event to the subscribed script threads. - WebVREvent(Vec, WebVREventMsg), -} - -/// Messages to the constellation originating from the WebVR thread. -/// Used to dispatch VR Headset state events: connected, unconnected, and more. -#[derive(Deserialize, Serialize, Clone)] -pub enum WebVREventMsg { - /// Inform the constellation of a VR display event. - DisplayEvent(WebVRDisplayEvent) + /// Dispatch WebVR events to the subscribed script threads. + WebVREvents(Vec, Vec), } /// Resources required by workerglobalscopes diff --git a/components/webvr/webvr_thread.rs b/components/webvr/webvr_thread.rs index 5be84dd12c1..6d16a23b83a 100644 --- a/components/webvr/webvr_thread.rs +++ b/components/webvr/webvr_thread.rs @@ -5,7 +5,7 @@ use ipc_channel::ipc; use ipc_channel::ipc::{IpcReceiver, IpcSender}; use msg::constellation_msg::PipelineId; -use script_traits::{ConstellationMsg, WebVREventMsg}; +use script_traits::ConstellationMsg; use servo_config::prefs::PREFS; use std::{thread, time}; use std::collections::{HashMap, HashSet}; @@ -42,7 +42,7 @@ pub struct WebVRThread { constellation_chan: Sender, vr_compositor_chan: WebVRCompositorSender, polling_events: bool, - presenting: HashMap + presenting: HashMap } impl WebVRThread { @@ -108,6 +108,9 @@ impl WebVRThread { WebVRMsg::CreateCompositor(display_id) => { self.handle_create_compositor(display_id); }, + WebVRMsg::GetGamepads(synced_ids, sender) => { + self.handle_get_gamepads(synced_ids, sender); + } WebVRMsg::Exit => { break }, @@ -134,7 +137,7 @@ impl WebVRThread { fn handle_framedata(&mut self, pipeline: PipelineId, - display_id: u64, + display_id: u32, near: f64, far: f64, sender: IpcSender>) { @@ -148,7 +151,7 @@ impl WebVRThread { fn handle_reset_pose(&mut self, pipeline: PipelineId, - display_id: u64, + display_id: u32, sender: IpcSender>) { match self.access_check(pipeline, display_id) { Ok(display) => { @@ -166,7 +169,7 @@ impl WebVRThread { // while the user is having a VR experience in the current tab. // These security rules also avoid multithreading race conditions between WebVRThread and // Webrender thread. See WebVRCompositorHandler implementation notes for more details about this. - fn access_check(&self, pipeline: PipelineId, display_id: u64) -> Result<&VRDisplayPtr, &'static str> { + fn access_check(&self, pipeline: PipelineId, display_id: u32) -> Result<&VRDisplayPtr, &'static str> { if *self.presenting.get(&display_id).unwrap_or(&pipeline) != pipeline { return Err("No access granted to this Display because it's presenting on other JavaScript Tab"); } @@ -175,14 +178,14 @@ impl WebVRThread { fn handle_request_present(&mut self, pipeline: PipelineId, - display_id: u64, + display_id: u32, sender: IpcSender>) { match self.access_check(pipeline, display_id).map(|d| d.clone()) { Ok(display) => { self.presenting.insert(display_id, pipeline); let data = display.borrow().data(); sender.send(Ok(())).unwrap(); - self.notify_event(VRDisplayEvent::PresentChange(data, true)); + self.notify_event(VRDisplayEvent::PresentChange(data, true).into()); }, Err(msg) => { sender.send(Err(msg.into())).unwrap(); @@ -192,7 +195,7 @@ impl WebVRThread { fn handle_exit_present(&mut self, pipeline: PipelineId, - display_id: u64, + display_id: u32, sender: Option>>) { match self.access_check(pipeline, display_id).map(|d| d.clone()) { Ok(display) => { @@ -201,7 +204,7 @@ impl WebVRThread { sender.send(Ok(())).unwrap(); } let data = display.borrow().data(); - self.notify_event(VRDisplayEvent::PresentChange(data, false)); + self.notify_event(VRDisplayEvent::PresentChange(data, false).into()); }, Err(msg) => { if let Some(sender) = sender { @@ -211,11 +214,28 @@ impl WebVRThread { } } - fn handle_create_compositor(&mut self, display_id: u64) { + fn handle_create_compositor(&mut self, display_id: u32) { let compositor = self.service.get_display(display_id).map(|d| WebVRCompositor(d.as_ptr())); self.vr_compositor_chan.send(compositor).unwrap(); } + fn handle_get_gamepads(&mut self, + synced_ids: Vec, + sender: IpcSender, VRGamepadState)>>>) { + let gamepads = self.service.get_gamepads(); + let data = gamepads.iter().map(|g| { + let g = g.borrow(); + // Optimization, don't fetch and send gamepad static data when the gamepad is already synced. + let data = if synced_ids.iter().any(|v| *v == g.id()) { + None + } else { + Some(g.data()) + }; + (data, g.state()) + }).collect(); + sender.send(Ok(data)).unwrap(); + } + fn poll_events(&mut self, sender: IpcSender) { loop { let events = self.service.poll_events(); @@ -230,16 +250,13 @@ impl WebVRThread { sender.send(self.polling_events).unwrap(); } - fn notify_events(&self, events: Vec) { + fn notify_events(&self, events: Vec) { let pipeline_ids: Vec = self.contexts.iter().map(|c| *c).collect(); - for event in events { - let event = WebVREventMsg::DisplayEvent(event); - self.constellation_chan.send(ConstellationMsg::WebVREvent(pipeline_ids.clone(), event)).unwrap(); - } + self.constellation_chan.send(ConstellationMsg::WebVREvents(pipeline_ids.clone(), events)).unwrap(); } #[inline] - fn notify_event(&self, event: VRDisplayEvent) { + fn notify_event(&self, event: VREvent) { self.notify_events(vec![event]); } @@ -334,7 +351,8 @@ impl webrender_traits::VRCompositorHandler for WebVRCompositorHandler { let layer = VRLayer { texture_id: texture_id, left_bounds: left_bounds, - right_bounds: right_bounds + right_bounds: right_bounds, + texture_size: None }; unsafe { (*compositor.0).submit_frame(&layer); @@ -357,7 +375,7 @@ impl WebVRCompositorHandler { None => return, }; - sender.send(WebVRMsg::CreateCompositor(display_id)).unwrap(); + sender.send(WebVRMsg::CreateCompositor(display_id as u32)).unwrap(); let display = self.webvr_thread_receiver.recv().unwrap(); match display { diff --git a/components/webvr_traits/Cargo.toml b/components/webvr_traits/Cargo.toml index 9ec8e33462c..69ff590cbc7 100644 --- a/components/webvr_traits/Cargo.toml +++ b/components/webvr_traits/Cargo.toml @@ -12,6 +12,6 @@ path = "lib.rs" [dependencies] ipc-channel = "0.7" msg = {path = "../msg"} -rust-webvr = {version = "0.2", features = ["serde-serialization"]} +rust-webvr = {version = "0.3", features = ["serde-serialization"]} serde = "0.9" serde_derive = "0.9" diff --git a/components/webvr_traits/lib.rs b/components/webvr_traits/lib.rs index 81aaca9085e..55c3057fcb7 100644 --- a/components/webvr_traits/lib.rs +++ b/components/webvr_traits/lib.rs @@ -16,9 +16,15 @@ pub use webvr::VRDisplayData as WebVRDisplayData; pub use webvr::VRDisplayCapabilities as WebVRDisplayCapabilities; pub use webvr::VRDisplayEvent as WebVRDisplayEvent; pub use webvr::VRDisplayEventReason as WebVRDisplayEventReason; +pub use webvr::VREvent as WebVREvent; pub use webvr::VREye as WebVREye; pub use webvr::VREyeParameters as WebVREyeParameters; pub use webvr::VRFieldOfView as WebVRFieldOfView; +pub use webvr::VRGamepadButton as WebVRGamepadButton; +pub use webvr::VRGamepadData as WebVRGamepadData; +pub use webvr::VRGamepadEvent as WebVRGamepadEvent; +pub use webvr::VRGamepadHand as WebVRGamepadHand; +pub use webvr::VRGamepadState as WebVRGamepadState; pub use webvr::VRFrameData as WebVRFrameData; pub use webvr::VRLayer as WebVRLayer; pub use webvr::VRPose as WebVRPose; diff --git a/components/webvr_traits/webvr_traits.rs b/components/webvr_traits/webvr_traits.rs index d52dd20b9f7..10fc9576b90 100644 --- a/components/webvr_traits/webvr_traits.rs +++ b/components/webvr_traits/webvr_traits.rs @@ -15,10 +15,11 @@ pub enum WebVRMsg { UnregisterContext(PipelineId), PollEvents(IpcSender), GetDisplays(IpcSender>>), - GetFrameData(PipelineId, u64, f64, f64, IpcSender>), - ResetPose(PipelineId, u64, IpcSender>), - RequestPresent(PipelineId, u64, IpcSender>), - ExitPresent(PipelineId, u64, Option>>), - CreateCompositor(u64), + GetFrameData(PipelineId, u32, f64, f64, IpcSender>), + ResetPose(PipelineId, u32, IpcSender>), + RequestPresent(PipelineId, u32, IpcSender>), + ExitPresent(PipelineId, u32, Option>>), + CreateCompositor(u32), + GetGamepads(Vec, IpcSender, VRGamepadState)>>>), Exit, } diff --git a/resources/prefs.json b/resources/prefs.json index 021354d0cb6..04df51d0604 100644 --- a/resources/prefs.json +++ b/resources/prefs.json @@ -2,6 +2,7 @@ "dom.bluetooth.enabled": false, "dom.bluetooth.testing.enabled": false, "dom.forcetouch.enabled": false, + "dom.gamepad.enabled": false, "dom.mouseevent.which.enabled": false, "dom.mozbrowser.enabled": false, "dom.mutation_observer.enabled": false, diff --git a/tests/html/webvr/vr-controllers.html b/tests/html/webvr/vr-controllers.html new file mode 100644 index 00000000000..2220c04fdc6 --- /dev/null +++ b/tests/html/webvr/vr-controllers.html @@ -0,0 +1,420 @@ + + + + + + + + + + + + + XX - VR Controllers + + + + + + + + + + + + + + + + + + + + + + + + + + +