mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
Implement Gamepad API
This commit is contained in:
parent
69eda6a60c
commit
0158b5b2af
27 changed files with 1193 additions and 96 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -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<Message, LTF, STF> Constellation<Message, LTF, STF>
|
|||
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<Message, LTF, STF> Constellation<Message, LTF, STF>
|
|||
}
|
||||
}
|
||||
|
||||
fn handle_webvr_event(&mut self, ids: Vec<PipelineId>, event: WebVREventMsg) {
|
||||
fn handle_webvr_events(&mut self, ids: Vec<PipelineId>, events: Vec<WebVREvent>) {
|
||||
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")
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
|
|
206
components/script/dom/gamepad.rs
Normal file
206
components/script/dom/gamepad.rs
Normal file
|
@ -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<i32>,
|
||||
connected: Cell<bool>,
|
||||
timestamp: Cell<f64>,
|
||||
mapping_type: String,
|
||||
axes: Heap<*mut JSObject>,
|
||||
buttons: JS<GamepadButtonList>,
|
||||
pose: Option<JS<VRPose>>,
|
||||
#[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<Gamepad> {
|
||||
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<f64> {
|
||||
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<GamepadButtonList> {
|
||||
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<Root<VRPose>> {
|
||||
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::<Event>().fire(self.global().as_window().upcast::<EventTarget>());
|
||||
}
|
||||
}
|
61
components/script/dom/gamepadbutton.rs
Normal file
61
components/script/dom/gamepadbutton.rs
Normal file
|
@ -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<bool>,
|
||||
touched: Cell<bool>,
|
||||
value: Cell<f64>,
|
||||
}
|
||||
|
||||
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<GamepadButton> {
|
||||
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<f64> {
|
||||
Finite::wrap(self.value.get())
|
||||
}
|
||||
}
|
||||
|
||||
impl GamepadButton {
|
||||
pub fn update(&self, pressed: bool, touched: bool) {
|
||||
self.pressed.set(pressed);
|
||||
self.touched.set(touched);
|
||||
}
|
||||
}
|
63
components/script/dom/gamepadbuttonlist.rs
Normal file
63
components/script/dom/gamepadbuttonlist.rs
Normal file
|
@ -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<JS<GamepadButton>>
|
||||
}
|
||||
|
||||
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<GamepadButtonList> {
|
||||
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<Root<GamepadButton>> {
|
||||
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<Root<GamepadButton>> {
|
||||
self.Item(index)
|
||||
}
|
||||
}
|
92
components/script/dom/gamepadevent.rs
Normal file
92
components/script/dom/gamepadevent.rs
Normal file
|
@ -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<Gamepad>,
|
||||
}
|
||||
|
||||
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<GamepadEvent> {
|
||||
let ev = reflect_dom_object(box GamepadEvent::new_inherited(&gamepad),
|
||||
global,
|
||||
GamepadEventBinding::Wrap);
|
||||
{
|
||||
let event = ev.upcast::<Event>();
|
||||
event.init_event(type_, bubbles, cancelable);
|
||||
}
|
||||
ev
|
||||
}
|
||||
|
||||
pub fn new_with_type(global: &GlobalScope, event_type: GamepadEventType, gamepad: &Gamepad)
|
||||
-> Root<GamepadEvent> {
|
||||
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<Root<GamepadEvent>> {
|
||||
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<Gamepad> {
|
||||
Root::from_ref(&*self.gamepad)
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-event-istrusted
|
||||
fn IsTrusted(&self) -> bool {
|
||||
self.event.IsTrusted()
|
||||
}
|
||||
}
|
61
components/script/dom/gamepadlist.rs
Normal file
61
components/script/dom/gamepadlist.rs
Normal file
|
@ -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<Vec<JS<Gamepad>>>
|
||||
}
|
||||
|
||||
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<GamepadList> {
|
||||
reflect_dom_object(box GamepadList::new_inherited(list),
|
||||
global,
|
||||
GamepadListBinding::Wrap)
|
||||
}
|
||||
|
||||
pub fn add_if_not_exists(&self, gamepads: &[Root<Gamepad>]) {
|
||||
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<Root<Gamepad>> {
|
||||
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<Root<Gamepad>> {
|
||||
self.Item(index)
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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<MimeTypeArray>,
|
||||
service_worker: MutNullableJS<ServiceWorkerContainer>,
|
||||
vr: MutNullableJS<VR>,
|
||||
gamepads: MutNullableJS<GamepadList>,
|
||||
permissions: MutNullableJS<Permissions>,
|
||||
}
|
||||
|
||||
|
@ -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<GamepadList> {
|
||||
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<Permissions> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Vec<JS<VRDisplay>>>
|
||||
displays: DOMRefCell<Vec<JS<VRDisplay>>>,
|
||||
gamepads: DOMRefCell<Vec<JS<Gamepad>>>
|
||||
}
|
||||
|
||||
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<Root<VRDisplay>> {
|
||||
fn find_display(&self, display_id: u32) -> Option<Root<VRDisplay>> {
|
||||
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<VRDisplay> {
|
||||
fn sync_display(&self, display: &WebVRDisplayData) -> Root<VRDisplay> {
|
||||
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<WebVREvent>) {
|
||||
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::<Event>().fire(self.upcast());
|
||||
}
|
||||
}
|
||||
|
||||
// Gamepad
|
||||
impl VR {
|
||||
fn find_gamepad(&self, gamepad_id: u32) -> Option<Root<Gamepad>> {
|
||||
self.gamepads.borrow()
|
||||
.iter()
|
||||
.find(|g| g.gamepad_id() == gamepad_id)
|
||||
.map(|g| Root::from_ref(&**g))
|
||||
}
|
||||
|
||||
fn sync_gamepad(&self, data: Option<WebVRGamepadData>, 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<Root<Gamepad>> {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
26
components/script/dom/webidls/Gamepad.webidl
Normal file
26
components/script/dom/webidls/Gamepad.webidl
Normal file
|
@ -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;
|
||||
};
|
11
components/script/dom/webidls/GamepadButton.webidl
Normal file
11
components/script/dom/webidls/GamepadButton.webidl
Normal file
|
@ -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;
|
||||
};
|
10
components/script/dom/webidls/GamepadButtonList.webidl
Normal file
10
components/script/dom/webidls/GamepadButtonList.webidl
Normal file
|
@ -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;
|
||||
};
|
13
components/script/dom/webidls/GamepadEvent.webidl
Normal file
13
components/script/dom/webidls/GamepadEvent.webidl
Normal file
|
@ -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;
|
||||
};
|
10
components/script/dom/webidls/GamepadList.webidl
Normal file
10
components/script/dom/webidls/GamepadList.webidl
Normal file
|
@ -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;
|
||||
};
|
|
@ -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();
|
||||
};
|
||||
|
|
|
@ -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<WebVREvent>) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<WebVREvent>)
|
||||
}
|
||||
|
||||
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<FrameId>, Option<String>, LogEntry),
|
||||
/// Set the WebVR thread channel.
|
||||
SetWebVRThread(IpcSender<WebVRMsg>),
|
||||
/// Dispatch a WebVR event to the subscribed script threads.
|
||||
WebVREvent(Vec<PipelineId>, 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<PipelineId>, Vec<WebVREvent>),
|
||||
}
|
||||
|
||||
/// Resources required by workerglobalscopes
|
||||
|
|
|
@ -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<ConstellationMsg>,
|
||||
vr_compositor_chan: WebVRCompositorSender,
|
||||
polling_events: bool,
|
||||
presenting: HashMap<u64, PipelineId>
|
||||
presenting: HashMap<u32, PipelineId>
|
||||
}
|
||||
|
||||
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<WebVRResult<VRFrameData>>) {
|
||||
|
@ -148,7 +151,7 @@ impl WebVRThread {
|
|||
|
||||
fn handle_reset_pose(&mut self,
|
||||
pipeline: PipelineId,
|
||||
display_id: u64,
|
||||
display_id: u32,
|
||||
sender: IpcSender<WebVRResult<VRDisplayData>>) {
|
||||
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<WebVRResult<()>>) {
|
||||
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<IpcSender<WebVRResult<()>>>) {
|
||||
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<u32>,
|
||||
sender: IpcSender<WebVRResult<Vec<(Option<VRGamepadData>, 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<bool>) {
|
||||
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<VRDisplayEvent>) {
|
||||
fn notify_events(&self, events: Vec<VREvent>) {
|
||||
let pipeline_ids: Vec<PipelineId> = 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 {
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -15,10 +15,11 @@ pub enum WebVRMsg {
|
|||
UnregisterContext(PipelineId),
|
||||
PollEvents(IpcSender<bool>),
|
||||
GetDisplays(IpcSender<WebVRResult<Vec<VRDisplayData>>>),
|
||||
GetFrameData(PipelineId, u64, f64, f64, IpcSender<WebVRResult<VRFrameData>>),
|
||||
ResetPose(PipelineId, u64, IpcSender<WebVRResult<VRDisplayData>>),
|
||||
RequestPresent(PipelineId, u64, IpcSender<WebVRResult<()>>),
|
||||
ExitPresent(PipelineId, u64, Option<IpcSender<WebVRResult<()>>>),
|
||||
CreateCompositor(u64),
|
||||
GetFrameData(PipelineId, u32, f64, f64, IpcSender<WebVRResult<VRFrameData>>),
|
||||
ResetPose(PipelineId, u32, IpcSender<WebVRResult<VRDisplayData>>),
|
||||
RequestPresent(PipelineId, u32, IpcSender<WebVRResult<()>>),
|
||||
ExitPresent(PipelineId, u32, Option<IpcSender<WebVRResult<()>>>),
|
||||
CreateCompositor(u32),
|
||||
GetGamepads(Vec<u32>, IpcSender<WebVRResult<Vec<(Option<VRGamepadData>, VRGamepadState)>>>),
|
||||
Exit,
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
420
tests/html/webvr/vr-controllers.html
Normal file
420
tests/html/webvr/vr-controllers.html
Normal file
|
@ -0,0 +1,420 @@
|
|||
<!doctype html>
|
||||
<!--
|
||||
Copyright 2016 The Chromium Authors. All rights reserved.
|
||||
Use of this source code is governed by a BSD-style license that can be
|
||||
found in the LICENSE file.
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
|
||||
<!--Chrome origin trial header-->
|
||||
|
||||
<meta http-equiv="origin-trial" data-feature="WebVR" data-expires="2017-04-13" content="AtpEVUJTjLpU/IMdKA/u8TRWqVUKfA6aJQsonwi01IPxqA16zX7L4BMa9E4g4DdJW80v3N6jqde4pXeqd2GYCg4AAABJeyJvcmlnaW4iOiJodHRwczovL3dlYnZyLmluZm86NDQzIiwiZmVhdHVyZSI6IldlYlZSIiwiZXhwaXJ5IjoxNDkyMTAzODUyfQ==">
|
||||
<title>XX - VR Controllers</title>
|
||||
|
||||
<!--
|
||||
This sample demonstrates how to handle gamepads with 6DoF support, such as
|
||||
the Vive controllers or Oculus touch. PLEASE NOTE: The additions to the
|
||||
gamepad API used here are not yet part of the standard, and subject to
|
||||
change at any time!
|
||||
-->
|
||||
|
||||
<style>
|
||||
#webgl-canvas {
|
||||
box-sizing: border-box;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- This entire block in only to facilitate dynamically enabling and
|
||||
disabling the WebVR polyfill, and is not necessary for most WebVR apps.
|
||||
If you want to use the polyfill in your app, just include the js file and
|
||||
everything will work the way you want it to by default. -->
|
||||
<script>
|
||||
var WebVRConfig = {
|
||||
// Prevents the polyfill from initializing automatically.
|
||||
DEFER_INITIALIZATION: true,
|
||||
// Ensures the polyfill is always active when initialized, even if the
|
||||
// native API is available. This is probably NOT what most pages want.
|
||||
POLYFILL_MODE: "ALWAYS",
|
||||
// Polyfill optimizations
|
||||
DIRTY_SUBMIT_FRAME_BINDINGS: true,
|
||||
BUFFER_SCALE: 0.75,
|
||||
};
|
||||
</script>
|
||||
<script src="js/third-party/webvr-polyfill.js"></script>
|
||||
<script src="js/third-party/wglu/wglu-url.js"></script>
|
||||
<script>
|
||||
// Dynamically turn the polyfill on if requested by the query args.
|
||||
if (WGLUUrl.getBool('polyfill', false)) {
|
||||
InitializeWebVRPolyfill();
|
||||
} else {
|
||||
// Shim for migration from older version of WebVR. Shouldn't be necessary for very long.
|
||||
InitializeSpecShim();
|
||||
}
|
||||
</script>
|
||||
<!-- End sample polyfill enabling logic -->
|
||||
|
||||
<script src="js/third-party/gl-matrix-min.js"></script>
|
||||
|
||||
<script src="js/third-party/wglu/wglu-debug-geometry.js"></script>
|
||||
<script src="js/third-party/wglu/wglu-program.js"></script>
|
||||
<script src="js/third-party/wglu/wglu-stats.js"></script>
|
||||
<script src="js/third-party/wglu/wglu-texture.js"></script>
|
||||
|
||||
<script src="js/vr-cube-island.js"></script>
|
||||
<script src="js/vr-samples-util.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="webgl-canvas"></canvas>
|
||||
<script>
|
||||
/* global mat4, vec3, VRCubeIsland, WGLUDebugGeometry, WGLUStats, WGLUTextureLoader, VRSamplesUtil */
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
var PLAYER_HEIGHT = 1.65;
|
||||
|
||||
var vrDisplay = null;
|
||||
var frameData = null;
|
||||
var projectionMat = mat4.create();
|
||||
var viewMat = mat4.create();
|
||||
var poseMat = mat4.create();
|
||||
var gamepadMat = mat4.create();
|
||||
var gamepadMat2 = mat4.create();
|
||||
var gamepadColor = vec4.create();
|
||||
var standingPosition = vec3.create();
|
||||
var vrPresentButton = null;
|
||||
var orientation = [0, 0, 0, 1];
|
||||
var position = [0, 0, 0];
|
||||
|
||||
|
||||
window.addEventListener("gamepadconnected", function(ev) {
|
||||
var gamepad = ev.gamepad;
|
||||
console.log("Gamepad connected", gamepad.index, gamepad.id, gamepad.displayId);
|
||||
});
|
||||
|
||||
window.addEventListener("gamepaddisconnected", function(ev) {
|
||||
var gamepad = ev.gamepad;
|
||||
console.log("Gamepad disconnected", gamepad.index, gamepad.id, gamepad.displayId);
|
||||
});
|
||||
|
||||
// ===================================================
|
||||
// WebGL scene setup. This code is not WebVR specific.
|
||||
// ===================================================
|
||||
|
||||
// WebGL setup.
|
||||
var webglCanvas = document.getElementById("webgl-canvas");
|
||||
var gl = null;
|
||||
var cubeIsland = null;
|
||||
var stats = null;
|
||||
var debugGeom = null;
|
||||
|
||||
function initWebGL (preserveDrawingBuffer) {
|
||||
var glAttribs = {
|
||||
alpha: false,
|
||||
antialias: false,
|
||||
preserveDrawingBuffer: false
|
||||
};
|
||||
gl = webglCanvas.getContext("webgl", glAttribs);
|
||||
if (!gl) {
|
||||
gl = webglCanvas.getContext("experimental-webgl", glAttribs);
|
||||
if (!gl) {
|
||||
VRSamplesUtil.addError("Your browser does not support WebGL.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
//gl.clearColor(0.1, 0.2, 0.3, 1.0);
|
||||
gl.clearColor(0.0, 119.0/255.0, 51.0/255.0, 1.0);
|
||||
gl.enable(gl.DEPTH_TEST);
|
||||
gl.enable(gl.CULL_FACE);
|
||||
|
||||
var textureLoader = new WGLUTextureLoader(gl);
|
||||
var texture = textureLoader.loadTexture("media/textures/cube-sea.png");
|
||||
|
||||
cubeIsland = new VRCubeIsland(gl, texture, 2, 2);
|
||||
|
||||
stats = new WGLUStats(gl);
|
||||
debugGeom = new WGLUDebugGeometry(gl);
|
||||
|
||||
// Wait until we have a WebGL context to resize and start rendering.
|
||||
window.addEventListener("resize", onResize, false);
|
||||
onResize();
|
||||
window.requestAnimationFrame(onAnimationFrame);
|
||||
}
|
||||
|
||||
// ================================
|
||||
// WebVR-specific code begins here.
|
||||
// ================================
|
||||
|
||||
function onVRRequestPresent () {
|
||||
vrDisplay.requestPresent([{ source: webglCanvas }]).then(function () {
|
||||
}, function (err) {
|
||||
var errMsg = "requestPresent failed.";
|
||||
if (err && err.message) {
|
||||
errMsg += "<br/>" + err.message
|
||||
}
|
||||
VRSamplesUtil.addError(errMsg, 2000);
|
||||
});
|
||||
}
|
||||
|
||||
function onVRExitPresent () {
|
||||
if (!vrDisplay.isPresenting)
|
||||
return;
|
||||
|
||||
vrDisplay.exitPresent().then(function () {
|
||||
}, function () {
|
||||
VRSamplesUtil.addError("exitPresent failed.", 2000);
|
||||
});
|
||||
}
|
||||
|
||||
function onVRPresentChange () {
|
||||
onResize();
|
||||
|
||||
if (vrDisplay.isPresenting) {
|
||||
if (vrDisplay.capabilities.hasExternalDisplay) {
|
||||
VRSamplesUtil.removeButton(vrPresentButton);
|
||||
vrPresentButton = VRSamplesUtil.addButton("Exit VR", "E", "media/icons/cardboard64.png", onVRExitPresent);
|
||||
}
|
||||
} else {
|
||||
if (vrDisplay.capabilities.hasExternalDisplay) {
|
||||
VRSamplesUtil.removeButton(vrPresentButton);
|
||||
vrPresentButton = VRSamplesUtil.addButton("Enter VR", "E", "media/icons/cardboard64.png", onVRRequestPresent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (navigator.vr || navigator.getVRDisplays) {
|
||||
frameData = new VRFrameData();
|
||||
navigator.vr.getDisplays().then(function (displays) {
|
||||
// navigator.getVRDisplays().then(function (displays) {
|
||||
if (displays.length > 0) {
|
||||
vrDisplay = displays[displays.length - 1];
|
||||
vrDisplay.depthNear = 0.1;
|
||||
vrDisplay.depthFar = 1024.0;
|
||||
|
||||
initWebGL(true);
|
||||
|
||||
if (vrDisplay.stageParameters &&
|
||||
vrDisplay.stageParameters.sizeX > 0 &&
|
||||
vrDisplay.stageParameters.sizeZ > 0) {
|
||||
cubeIsland.resize(vrDisplay.stageParameters.sizeX, vrDisplay.stageParameters.sizeZ);
|
||||
}
|
||||
|
||||
VRSamplesUtil.addButton("Reset Pose", "R", null, function () { vrDisplay.resetPose(); });
|
||||
|
||||
if (vrDisplay.capabilities.canPresent)
|
||||
vrPresentButton = VRSamplesUtil.addButton("Enter VR", "E", "media/icons/cardboard64.png", onVRRequestPresent);
|
||||
|
||||
vrDisplay.addEventListener('presentchange', onVRPresentChange, false);
|
||||
//vrDisplay.addEventListener('vrdisplayactivate', onVRRequestPresent, false);
|
||||
//vrDisplay.addEventListener('vrdisplaydeactivate', onVRExitPresent, false);
|
||||
console.log(navigator.getGamepads());
|
||||
setTimeout(function(){
|
||||
onVRRequestPresent();
|
||||
}, 5);
|
||||
|
||||
} else {
|
||||
initWebGL(false);
|
||||
VRSamplesUtil.addInfo("WebVR supported, but no VRDisplays found.", 3000);
|
||||
}
|
||||
});
|
||||
} else if (navigator.getVRDevices) {
|
||||
initWebGL(false);
|
||||
VRSamplesUtil.addError("Your browser supports WebVR but not the latest version. See <a href='http://webvr.info'>webvr.info</a> for more info.");
|
||||
} else {
|
||||
initWebGL(false);
|
||||
VRSamplesUtil.addError("Your browser does not support WebVR. See <a href='http://webvr.info'>webvr.info</a> for assistance.");
|
||||
}
|
||||
|
||||
function onResize () {
|
||||
if (vrDisplay && vrDisplay.isPresenting) {
|
||||
var leftEye = vrDisplay.getEyeParameters("left");
|
||||
var rightEye = vrDisplay.getEyeParameters("right");
|
||||
|
||||
webglCanvas.width = Math.max(leftEye.renderWidth, rightEye.renderWidth) * 2;
|
||||
webglCanvas.height = Math.max(leftEye.renderHeight, rightEye.renderHeight);
|
||||
} else {
|
||||
webglCanvas.width = window.innerWidth * 2.0 * window.devicePixelRatio;
|
||||
webglCanvas.height = window.innerHeight * 2.0 * window.devicePixelRatio;
|
||||
}
|
||||
}
|
||||
|
||||
function onClick() {
|
||||
//onVRRequestPresent();
|
||||
}
|
||||
webglCanvas.addEventListener("click", onClick, false);
|
||||
|
||||
function getStandingViewMatrix (out, view) {
|
||||
if (vrDisplay.stageParameters) {
|
||||
mat4.invert(out, vrDisplay.stageParameters.sittingToStandingTransform);
|
||||
mat4.multiply(out, view, out);
|
||||
} else {
|
||||
mat4.identity(out);
|
||||
mat4.translate(out, out, [0, PLAYER_HEIGHT, 0]);
|
||||
mat4.invert(out, out);
|
||||
mat4.multiply(out, view, out);
|
||||
}
|
||||
}
|
||||
|
||||
function getPoseMatrix (out, pose, isGamepad) {
|
||||
orientation = pose.orientation;
|
||||
position = pose.position;
|
||||
if (!orientation) { orientation = [0, 0, 0, 1]; }
|
||||
if (!position) {
|
||||
// If this is a gamepad without a pose set it out in front of us so
|
||||
// we can see it.
|
||||
position = isGamepad ? [0.1, -0.1, -0.5] : [0, 0, 0];
|
||||
}
|
||||
|
||||
|
||||
if (vrDisplay.stageParameters) {
|
||||
mat4.fromRotationTranslation(out, orientation, position);
|
||||
mat4.multiply(out, vrDisplay.stageParameters.sittingToStandingTransform, out);
|
||||
} else {
|
||||
vec3.add(standingPosition, position, [0, PLAYER_HEIGHT, 0]);
|
||||
mat4.fromRotationTranslation(out, orientation, standingPosition);
|
||||
}
|
||||
}
|
||||
|
||||
function renderSceneView (projection, view, gamepads) {
|
||||
cubeIsland.render(projection, view, stats);
|
||||
|
||||
debugGeom.bind(projection, view);
|
||||
|
||||
// Render every gamepad with a pose we found
|
||||
for (var i = 0; i < gamepads.length; ++i) {
|
||||
var gamepad = gamepads[i];
|
||||
|
||||
// Because this sample is done in standing space we need to apply
|
||||
// the same transformation to the gamepad pose as we did the
|
||||
// VRDisplay's pose.
|
||||
getPoseMatrix(gamepadMat, gamepad.pose, true);
|
||||
|
||||
// Loop through all the gamepad's axes and scale the gamepad geometry
|
||||
// by their value.
|
||||
var scale = [0.1, 0.1, 0.1];
|
||||
for (var j = 0; j < gamepad.axes.length; ++j) {
|
||||
switch (j%3) {
|
||||
case 0:
|
||||
scale[0] *= 1.0 + gamepad.axes[j];
|
||||
break;
|
||||
case 1:
|
||||
scale[1] *= 1.0 + gamepad.axes[j];
|
||||
break;
|
||||
case 2:
|
||||
scale[2] *= 1.0 + gamepad.axes[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Scaled down to from 1 meter to be something closer to the size of
|
||||
// a hand.
|
||||
mat4.scale(gamepadMat, gamepadMat, scale);
|
||||
|
||||
// Rotate -90 deg so the point of the cone faces "forward"
|
||||
mat4.rotateX(gamepadMat, gamepadMat, -Math.PI * 0.5);
|
||||
|
||||
// Show the gamepad's cube as red if any buttons are pressed, blue
|
||||
// otherwise.
|
||||
vec4.set(gamepadColor, 0, 0, 1, 1);
|
||||
var buttons = gamepad.buttons;
|
||||
for (var j = 0; j < buttons.length; ++j) {
|
||||
if (buttons[j].pressed) {
|
||||
vec4.set(gamepadColor, buttons[j].value || 1.0, 0, 0, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
debugGeom.drawConeWithMatrix(gamepadMat, gamepadColor);
|
||||
|
||||
// Draw a "handle" for the gamepad
|
||||
mat4.identity(gamepadMat2);
|
||||
mat4.translate(gamepadMat2, gamepadMat2, [0, -0.5, -0.3]);
|
||||
mat4.rotateX(gamepadMat2, gamepadMat2, -Math.PI * 0.2);
|
||||
mat4.scale(gamepadMat2, gamepadMat2, [0.25, 0.25, 0.5]);
|
||||
|
||||
mat4.multiply(gamepadMat, gamepadMat, gamepadMat2);
|
||||
|
||||
debugGeom.drawBoxWithMatrix(gamepadMat, gamepadColor);
|
||||
}
|
||||
}
|
||||
|
||||
function onAnimationFrame (t) {
|
||||
stats.begin();
|
||||
|
||||
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
||||
|
||||
if (vrDisplay) {
|
||||
vrDisplay.requestAnimationFrame(onAnimationFrame);
|
||||
|
||||
vrDisplay.getFrameData(frameData);
|
||||
|
||||
// Loop over every gamepad and if we find any that have a pose use it.
|
||||
var vrGamepads = [];
|
||||
var gamepads = navigator.getGamepads();
|
||||
for (var i = 0; i < gamepads.length; ++i) {
|
||||
var gamepad = gamepads[i];
|
||||
// The array may contain undefined gamepads, so check for that as
|
||||
// well as a non-null pose.
|
||||
if (gamepad) {
|
||||
if (gamepad.pose)
|
||||
vrGamepads.push(gamepad);
|
||||
if ("hapticActuators" in gamepad && gamepad.hapticActuators.length > 0) {
|
||||
for (var j = 0; j < gamepad.buttons.length; ++j) {
|
||||
if (gamepad.buttons[j].pressed) {
|
||||
// Vibrate the gamepad using to the value of the button as
|
||||
// the vibration intensity.
|
||||
gamepad.hapticActuators[0].pulse(gamepad.buttons[j].value, 100);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (vrDisplay.isPresenting) {
|
||||
gl.viewport(0, 0, webglCanvas.width * 0.5, webglCanvas.height);
|
||||
getStandingViewMatrix(viewMat, frameData.leftViewMatrix);
|
||||
renderSceneView(frameData.leftProjectionMatrix, viewMat, vrGamepads);
|
||||
|
||||
gl.viewport(webglCanvas.width * 0.5, 0, webglCanvas.width * 0.5, webglCanvas.height);
|
||||
getStandingViewMatrix(viewMat, frameData.rightViewMatrix);
|
||||
renderSceneView(frameData.rightProjectionMatrix, viewMat, vrGamepads);
|
||||
|
||||
vrDisplay.submitFrame();
|
||||
} else {
|
||||
gl.viewport(0, 0, webglCanvas.width, webglCanvas.height);
|
||||
mat4.perspective(projectionMat, Math.PI*0.4, webglCanvas.width / webglCanvas.height, 0.1, 1024.0);
|
||||
getStandingViewMatrix(viewMat, frameData.leftViewMatrix);
|
||||
renderSceneView(projectionMat, viewMat, vrGamepads);
|
||||
stats.renderOrtho();
|
||||
}
|
||||
} else {
|
||||
window.requestAnimationFrame(onAnimationFrame);
|
||||
|
||||
// No VRDisplay found.
|
||||
gl.viewport(0, 0, webglCanvas.width, webglCanvas.height);
|
||||
mat4.perspective(projectionMat, Math.PI*0.4, webglCanvas.width / webglCanvas.height, 0.1, 1024.0);
|
||||
mat4.identity(viewMat);
|
||||
mat4.translate(viewMat, viewMat, [0, -PLAYER_HEIGHT, 0]);
|
||||
cubeIsland.render(projectionMat, viewMat, stats);
|
||||
|
||||
stats.renderOrtho();
|
||||
}
|
||||
|
||||
stats.end();
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Loading…
Add table
Add a link
Reference in a new issue