Skip to main content

script/dom/webxr/
xrinputsource.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use dom_struct::dom_struct;
6use embedder_traits::GamepadSupportedHapticEffects;
7use js::context::JSContext;
8use js::conversions::ToJSValConvertible;
9use js::jsapi::Heap;
10use js::jsval::{JSVal, UndefinedValue};
11use js::rust::MutableHandleValue;
12use script_bindings::reflector::{Reflector, reflect_dom_object_with_cx};
13use webxr_api::{Handedness, InputFrame, InputId, InputSource, TargetRayMode};
14
15use crate::dom::bindings::codegen::Bindings::XRInputSourceBinding::{
16    XRHandedness, XRInputSourceMethods, XRTargetRayMode,
17};
18use crate::dom::bindings::reflector::DomGlobal;
19use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
20use crate::dom::gamepad::Gamepad;
21use crate::dom::window::Window;
22use crate::dom::xrhand::XRHand;
23use crate::dom::xrsession::XRSession;
24use crate::dom::xrspace::XRSpace;
25use crate::realms::enter_auto_realm;
26
27#[dom_struct]
28pub(crate) struct XRInputSource {
29    reflector: Reflector,
30    session: Dom<XRSession>,
31    #[no_trace]
32    info: InputSource,
33    target_ray_space: MutNullableDom<XRSpace>,
34    grip_space: MutNullableDom<XRSpace>,
35    hand: MutNullableDom<XRHand>,
36    #[ignore_malloc_size_of = "mozjs"]
37    profiles: Heap<JSVal>,
38    gamepad: Dom<Gamepad>,
39}
40
41impl XRInputSource {
42    fn new_inherited(session: &XRSession, info: InputSource, gamepad: &Gamepad) -> XRInputSource {
43        XRInputSource {
44            reflector: Reflector::new(),
45            session: Dom::from_ref(session),
46            info,
47            target_ray_space: Default::default(),
48            grip_space: Default::default(),
49            hand: Default::default(),
50            profiles: Heap::default(),
51            gamepad: Dom::from_ref(gamepad),
52        }
53    }
54
55    pub(crate) fn new(
56        cx: &mut JSContext,
57        window: &Window,
58        session: &XRSession,
59        info: InputSource,
60    ) -> DomRoot<XRInputSource> {
61        // <https://www.w3.org/TR/webxr-gamepads-module-1/#gamepad-differences>
62        let gamepad = Gamepad::new(
63            cx,
64            window,
65            0,
66            "".into(),
67            "xr-standard".into(),
68            (-1.0, 1.0),
69            (0.0, 1.0),
70            GamepadSupportedHapticEffects {
71                supports_dual_rumble: false,
72                supports_trigger_rumble: false,
73            },
74            true,
75        );
76
77        let source = reflect_dom_object_with_cx(
78            Box::new(XRInputSource::new_inherited(session, info, &gamepad)),
79            window,
80            cx,
81        );
82
83        let mut realm = enter_auto_realm(cx, window);
84        let cx = &mut realm.current_realm();
85        rooted!(&in(cx) let mut profiles = UndefinedValue());
86        source.info.profiles.to_jsval(cx, profiles.handle_mut());
87        source.profiles.set(profiles.get());
88        source
89    }
90
91    pub(crate) fn id(&self) -> InputId {
92        self.info.id
93    }
94
95    pub(crate) fn session(&self) -> &XRSession {
96        &self.session
97    }
98
99    pub(crate) fn update_gamepad_state(&self, frame: InputFrame) {
100        frame
101            .button_values
102            .iter()
103            .enumerate()
104            .for_each(|(i, value)| {
105                self.gamepad.map_and_normalize_buttons(i, *value as f64);
106            });
107        frame.axis_values.iter().enumerate().for_each(|(i, value)| {
108            self.gamepad.map_and_normalize_axes(i, *value as f64);
109        });
110    }
111
112    pub(crate) fn gamepad(&self) -> &Gamepad {
113        &self.gamepad
114    }
115}
116
117impl XRInputSourceMethods<crate::DomTypeHolder> for XRInputSource {
118    /// <https://immersive-web.github.io/webxr/#dom-xrinputsource-handedness>
119    fn Handedness(&self) -> XRHandedness {
120        match self.info.handedness {
121            Handedness::None => XRHandedness::None,
122            Handedness::Left => XRHandedness::Left,
123            Handedness::Right => XRHandedness::Right,
124        }
125    }
126
127    /// <https://immersive-web.github.io/webxr/#dom-xrinputsource-targetraymode>
128    fn TargetRayMode(&self) -> XRTargetRayMode {
129        match self.info.target_ray_mode {
130            TargetRayMode::Gaze => XRTargetRayMode::Gaze,
131            TargetRayMode::TrackedPointer => XRTargetRayMode::Tracked_pointer,
132            TargetRayMode::Screen => XRTargetRayMode::Screen,
133            TargetRayMode::TransientPointer => XRTargetRayMode::Transient_pointer,
134        }
135    }
136
137    /// <https://immersive-web.github.io/webxr/#dom-xrinputsource-targetrayspace>
138    fn TargetRaySpace(&self, cx: &mut JSContext) -> DomRoot<XRSpace> {
139        self.target_ray_space.or_init(|| {
140            let global = self.global();
141            XRSpace::new_inputspace(cx, &global, &self.session, self, false)
142        })
143    }
144
145    /// <https://immersive-web.github.io/webxr/#dom-xrinputsource-gripspace>
146    fn GetGripSpace(&self, cx: &mut JSContext) -> Option<DomRoot<XRSpace>> {
147        if self.info.supports_grip {
148            Some(self.grip_space.or_init(|| {
149                let global = self.global();
150                XRSpace::new_inputspace(cx, &global, &self.session, self, true)
151            }))
152        } else {
153            None
154        }
155    }
156    /// <https://immersive-web.github.io/webxr/#dom-xrinputsource-profiles>
157    fn Profiles(&self, mut retval: MutableHandleValue) {
158        retval.set(self.profiles.get())
159    }
160
161    /// <https://www.w3.org/TR/webxr/#dom-xrinputsource-skiprendering>
162    fn SkipRendering(&self) -> bool {
163        // Servo is not currently supported anywhere that would allow for skipped
164        // controller rendering.
165        false
166    }
167
168    /// <https://www.w3.org/TR/webxr-gamepads-module-1/#xrinputsource-interface>
169    fn GetGamepad(&self) -> Option<DomRoot<Gamepad>> {
170        Some(DomRoot::from_ref(&*self.gamepad))
171    }
172
173    /// <https://github.com/immersive-web/webxr-hands-input/blob/master/explainer.md>
174    fn GetHand(&self, cx: &mut JSContext) -> Option<DomRoot<XRHand>> {
175        self.info.hand_support.as_ref().map(|hand| {
176            self.hand
177                .or_init(|| XRHand::new(cx, &self.global(), self, hand.clone()))
178        })
179    }
180}