mirror of
https://github.com/servo/servo.git
synced 2025-08-02 04:00:32 +01:00
Implement Gamepad API
This commit is contained in:
parent
69eda6a60c
commit
0158b5b2af
27 changed files with 1193 additions and 96 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue