mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
Implement WebXR Gamepads Module (#32860)
* Expose gamepad attribute on XRInputSource Signed-off-by: Daniel Adams <msub2official@gmail.com> * Tidy, add spec links Signed-off-by: Daniel Adams <msub2official@gmail.com> * Update WPT test expectations Signed-off-by: Daniel Adams <msub2official@gmail.com> * Update gamepad state on InputChanged event Signed-off-by: Daniel Adams <msub2official@gmail.com> * Pin webxr commit Signed-off-by: Daniel Adams <msub2official@gmail.com> * Apply gamepad updates during frame updates Signed-off-by: Daniel Adams <msub2official@gmail.com> * Drain input frame map Signed-off-by: Daniel Adams <msub2official@gmail.com> * Don't store gamepad as option Signed-off-by: Daniel Adams <msub2official@gmail.com> --------- Signed-off-by: Daniel Adams <msub2official@gmail.com>
This commit is contained in:
parent
0672eca749
commit
fd83281657
8 changed files with 90 additions and 32 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -7745,7 +7745,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "webxr"
|
||||
version = "0.0.1"
|
||||
source = "git+https://github.com/servo/webxr#93ee726f84dc3feb9aa47f9a61fdedd784d14938"
|
||||
source = "git+https://github.com/servo/webxr#790f50587d651fd865736cfd8c70cab0dea5dc7f"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"euclid",
|
||||
|
@ -7762,7 +7762,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "webxr-api"
|
||||
version = "0.0.1"
|
||||
source = "git+https://github.com/servo/webxr#93ee726f84dc3feb9aa47f9a61fdedd784d14938"
|
||||
source = "git+https://github.com/servo/webxr#790f50587d651fd865736cfd8c70cab0dea5dc7f"
|
||||
dependencies = [
|
||||
"euclid",
|
||||
"ipc-channel",
|
||||
|
|
|
@ -92,6 +92,7 @@ impl Gamepad {
|
|||
global: &GlobalScope,
|
||||
gamepad_id: u32,
|
||||
id: String,
|
||||
mapping_type: String,
|
||||
axis_bounds: (f64, f64),
|
||||
button_bounds: (f64, f64),
|
||||
supported_haptic_effects: GamepadSupportedHapticEffects,
|
||||
|
@ -100,6 +101,7 @@ impl Gamepad {
|
|||
global,
|
||||
gamepad_id,
|
||||
id,
|
||||
mapping_type,
|
||||
axis_bounds,
|
||||
button_bounds,
|
||||
supported_haptic_effects,
|
||||
|
@ -115,6 +117,7 @@ impl Gamepad {
|
|||
global: &GlobalScope,
|
||||
gamepad_id: u32,
|
||||
id: String,
|
||||
mapping_type: String,
|
||||
axis_bounds: (f64, f64),
|
||||
button_bounds: (f64, f64),
|
||||
supported_haptic_effects: GamepadSupportedHapticEffects,
|
||||
|
@ -129,7 +132,7 @@ impl Gamepad {
|
|||
0,
|
||||
true,
|
||||
0.,
|
||||
String::from("standard"),
|
||||
mapping_type,
|
||||
&button_list,
|
||||
None,
|
||||
GamepadHand::_empty,
|
||||
|
|
|
@ -3214,22 +3214,29 @@ impl GlobalScope {
|
|||
// TODO: 2. If document is not null and is not allowed to use the "gamepad" permission,
|
||||
// then abort these steps.
|
||||
let this = Trusted::new(self);
|
||||
self.gamepad_task_source().queue_with_canceller(
|
||||
task!(gamepad_connected: move || {
|
||||
let global = this.root();
|
||||
self.gamepad_task_source()
|
||||
.queue_with_canceller(
|
||||
task!(gamepad_connected: move || {
|
||||
let global = this.root();
|
||||
|
||||
if let Some(window) = global.downcast::<Window>() {
|
||||
let navigator = window.Navigator();
|
||||
let selected_index = navigator.select_gamepad_index();
|
||||
let gamepad = Gamepad::new(
|
||||
&global, selected_index, name, axis_bounds, button_bounds, supported_haptic_effects
|
||||
);
|
||||
navigator.set_gamepad(selected_index as usize, &gamepad);
|
||||
}
|
||||
}),
|
||||
&self.task_canceller(TaskSourceName::Gamepad)
|
||||
)
|
||||
.expect("Failed to queue gamepad connected task.");
|
||||
if let Some(window) = global.downcast::<Window>() {
|
||||
let navigator = window.Navigator();
|
||||
let selected_index = navigator.select_gamepad_index();
|
||||
let gamepad = Gamepad::new(
|
||||
&global,
|
||||
selected_index,
|
||||
name,
|
||||
"standard".into(),
|
||||
axis_bounds,
|
||||
button_bounds,
|
||||
supported_haptic_effects
|
||||
);
|
||||
navigator.set_gamepad(selected_index as usize, &gamepad);
|
||||
}
|
||||
}),
|
||||
&self.task_canceller(TaskSourceName::Gamepad),
|
||||
)
|
||||
.expect("Failed to queue gamepad connected task.");
|
||||
}
|
||||
|
||||
/// <https://www.w3.org/TR/gamepad/#dfn-gamepaddisconnected>
|
||||
|
|
|
@ -22,7 +22,7 @@ interface XRInputSource {
|
|||
readonly attribute XRTargetRayMode targetRayMode;
|
||||
[SameObject] readonly attribute XRSpace targetRaySpace;
|
||||
[SameObject] readonly attribute XRSpace? gripSpace;
|
||||
// [SameObject] readonly attribute Gamepad? gamepad;
|
||||
[SameObject] readonly attribute Gamepad? gamepad;
|
||||
/* [SameObject] */ readonly attribute /* FrozenArray<DOMString> */ any profiles;
|
||||
|
||||
[Pref="dom.webxr.hands.enabled"]
|
||||
|
|
|
@ -6,13 +6,15 @@ use dom_struct::dom_struct;
|
|||
use js::conversions::ToJSValConvertible;
|
||||
use js::jsapi::Heap;
|
||||
use js::jsval::{JSVal, UndefinedValue};
|
||||
use webxr_api::{Handedness, InputId, InputSource, TargetRayMode};
|
||||
use script_traits::GamepadSupportedHapticEffects;
|
||||
use webxr_api::{Handedness, InputFrame, InputId, InputSource, TargetRayMode};
|
||||
|
||||
use crate::dom::bindings::codegen::Bindings::XRInputSourceBinding::{
|
||||
XRHandedness, XRInputSourceMethods, XRTargetRayMode,
|
||||
};
|
||||
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
|
||||
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
|
||||
use crate::dom::gamepad::Gamepad;
|
||||
use crate::dom::globalscope::GlobalScope;
|
||||
use crate::dom::xrhand::XRHand;
|
||||
use crate::dom::xrsession::XRSession;
|
||||
|
@ -32,10 +34,28 @@ pub struct XRInputSource {
|
|||
hand: MutNullableDom<XRHand>,
|
||||
#[ignore_malloc_size_of = "mozjs"]
|
||||
profiles: Heap<JSVal>,
|
||||
gamepad: DomRoot<Gamepad>,
|
||||
}
|
||||
|
||||
impl XRInputSource {
|
||||
pub fn new_inherited(session: &XRSession, info: InputSource) -> XRInputSource {
|
||||
pub fn new_inherited(
|
||||
global: &GlobalScope,
|
||||
session: &XRSession,
|
||||
info: InputSource,
|
||||
) -> XRInputSource {
|
||||
// <https://www.w3.org/TR/webxr-gamepads-module-1/#gamepad-differences>
|
||||
let gamepad = Gamepad::new(
|
||||
global,
|
||||
0,
|
||||
"".into(),
|
||||
"xr-standard".into(),
|
||||
(-1.0, 1.0),
|
||||
(0.0, 1.0),
|
||||
GamepadSupportedHapticEffects {
|
||||
supports_dual_rumble: false,
|
||||
supports_trigger_rumble: false,
|
||||
},
|
||||
);
|
||||
XRInputSource {
|
||||
reflector: Reflector::new(),
|
||||
session: Dom::from_ref(session),
|
||||
|
@ -44,6 +64,7 @@ impl XRInputSource {
|
|||
grip_space: Default::default(),
|
||||
hand: Default::default(),
|
||||
profiles: Heap::default(),
|
||||
gamepad,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,7 +75,7 @@ impl XRInputSource {
|
|||
info: InputSource,
|
||||
) -> DomRoot<XRInputSource> {
|
||||
let source = reflect_dom_object(
|
||||
Box::new(XRInputSource::new_inherited(session, info)),
|
||||
Box::new(XRInputSource::new_inherited(global, session, info)),
|
||||
global,
|
||||
);
|
||||
|
||||
|
@ -75,6 +96,21 @@ impl XRInputSource {
|
|||
pub fn session(&self) -> &XRSession {
|
||||
&self.session
|
||||
}
|
||||
|
||||
pub fn update_gamepad_state(&self, frame: InputFrame) {
|
||||
frame
|
||||
.button_values
|
||||
.iter()
|
||||
.enumerate()
|
||||
.for_each(|(i, value)| {
|
||||
self.gamepad
|
||||
.map_and_normalize_buttons(i as usize, *value as f64);
|
||||
});
|
||||
frame.axis_values.iter().enumerate().for_each(|(i, value)| {
|
||||
self.gamepad
|
||||
.map_and_normalize_axes(i as usize, *value as f64);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl XRInputSourceMethods for XRInputSource {
|
||||
|
@ -120,6 +156,11 @@ impl XRInputSourceMethods for XRInputSource {
|
|||
self.profiles.get()
|
||||
}
|
||||
|
||||
/// <https://www.w3.org/TR/webxr-gamepads-module-1/#xrinputsource-interface>
|
||||
fn GetGamepad(&self) -> Option<DomRoot<Gamepad>> {
|
||||
Some(DomRoot::from_ref(&*self.gamepad))
|
||||
}
|
||||
|
||||
// https://github.com/immersive-web/webxr-hands-input/blob/master/explainer.md
|
||||
fn GetHand(&self) -> Option<DomRoot<XRHand>> {
|
||||
self.info.hand_support.as_ref().map(|hand| {
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::collections::HashMap;
|
||||
use std::f64::consts::{FRAC_PI_2, PI};
|
||||
use std::mem;
|
||||
use std::rc::Rc;
|
||||
|
@ -15,8 +16,8 @@ use metrics::ToMs;
|
|||
use profile_traits::ipc;
|
||||
use webxr_api::{
|
||||
self, util, ApiSpace, ContextId as WebXRContextId, Display, EntityTypes, EnvironmentBlendMode,
|
||||
Event as XREvent, Frame, FrameUpdateEvent, HitTestId, HitTestSource, Ray, SelectEvent,
|
||||
SelectKind, Session, SessionId, View, Viewer, Visibility,
|
||||
Event as XREvent, Frame, FrameUpdateEvent, HitTestId, HitTestSource, InputFrame, InputId, Ray,
|
||||
SelectEvent, SelectKind, Session, SessionId, View, Viewer, Visibility,
|
||||
};
|
||||
|
||||
use super::bindings::trace::HashMapTracedValues;
|
||||
|
@ -92,6 +93,9 @@ pub struct XRSession {
|
|||
/// Opaque framebuffers need to know the session is "outside of a requestAnimationFrame"
|
||||
/// <https://immersive-web.github.io/webxr/#opaque-framebuffer>
|
||||
outside_raf: Cell<bool>,
|
||||
#[ignore_malloc_size_of = "defined in webxr"]
|
||||
#[no_trace]
|
||||
input_frames: DomRefCell<HashMap<InputId, InputFrame>>,
|
||||
}
|
||||
|
||||
impl XRSession {
|
||||
|
@ -122,6 +126,7 @@ impl XRSession {
|
|||
next_hit_test_id: Cell::new(HitTestId(0)),
|
||||
pending_hit_test_promises: DomRefCell::new(HashMapTracedValues::new()),
|
||||
outside_raf: Cell::new(true),
|
||||
input_frames: DomRefCell::new(HashMap::new()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -350,6 +355,9 @@ impl XRSession {
|
|||
XREvent::UpdateInput(id, source) => {
|
||||
self.input_sources.add_remove_input_source(self, id, source);
|
||||
},
|
||||
XREvent::InputChanged(id, frame) => {
|
||||
self.input_frames.borrow_mut().insert(id, frame);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -510,7 +518,13 @@ impl XRSession {
|
|||
|
||||
/// <https://immersive-web.github.io/webxr/#xrframe-apply-frame-updates>
|
||||
fn apply_frame_updates(&self, _frame: &XRFrame) {
|
||||
// TODO: add a comment about why this is empty right now!
|
||||
// <https://www.w3.org/TR/webxr-gamepads-module-1/#xrframe-apply-gamepad-frame-updates>
|
||||
for (id, frame) in self.input_frames.borrow_mut().drain() {
|
||||
let source = self.input_sources.find(id);
|
||||
if let Some(source) = source {
|
||||
source.update_gamepad_state(frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_frame_event(&self, event: FrameUpdateEvent) {
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
[idlharness.https.window.html]
|
||||
[XRInputSource interface: attribute gamepad]
|
||||
expected: FAIL
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
[idlharness.https.window.html]
|
||||
[XRInputSource interface: attribute gamepad]
|
||||
expected: FAIL
|
Loading…
Add table
Add a link
Reference in a new issue