script/dom/webxr/
fakexrinputcontroller.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 base::generic_channel::GenericSender;
6use dom_struct::dom_struct;
7use webxr_api::{
8    Handedness, InputId, MockButton, MockButtonType, MockDeviceMsg, MockInputMsg, SelectEvent,
9    SelectKind, TargetRayMode,
10};
11
12use crate::conversions::Convert;
13use crate::dom::bindings::codegen::Bindings::FakeXRDeviceBinding::FakeXRRigidTransformInit;
14use crate::dom::bindings::codegen::Bindings::FakeXRInputControllerBinding::{
15    FakeXRButtonStateInit, FakeXRButtonType, FakeXRInputControllerMethods,
16};
17use crate::dom::bindings::codegen::Bindings::XRInputSourceBinding::{
18    XRHandedness, XRTargetRayMode,
19};
20use crate::dom::bindings::error::{Error, Fallible};
21use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
22use crate::dom::bindings::root::DomRoot;
23use crate::dom::bindings::str::DOMString;
24use crate::dom::fakexrdevice::get_origin;
25use crate::dom::globalscope::GlobalScope;
26use crate::script_runtime::CanGc;
27
28#[dom_struct]
29pub(crate) struct FakeXRInputController {
30    reflector: Reflector,
31    #[no_trace]
32    sender: GenericSender<MockDeviceMsg>,
33    #[no_trace]
34    id: InputId,
35}
36
37impl FakeXRInputController {
38    pub(crate) fn new_inherited(
39        sender: GenericSender<MockDeviceMsg>,
40        id: InputId,
41    ) -> FakeXRInputController {
42        FakeXRInputController {
43            reflector: Reflector::new(),
44            sender,
45            id,
46        }
47    }
48
49    pub(crate) fn new(
50        global: &GlobalScope,
51        sender: GenericSender<MockDeviceMsg>,
52        id: InputId,
53        can_gc: CanGc,
54    ) -> DomRoot<FakeXRInputController> {
55        reflect_dom_object(
56            Box::new(FakeXRInputController::new_inherited(sender, id)),
57            global,
58            can_gc,
59        )
60    }
61
62    fn send_message(&self, msg: MockInputMsg) {
63        let _ = self
64            .sender
65            .send(MockDeviceMsg::MessageInputSource(self.id, msg));
66    }
67}
68
69impl FakeXRInputControllerMethods<crate::DomTypeHolder> for FakeXRInputController {
70    /// <https://immersive-web.github.io/webxr-test-api/#dom-fakexrinputcontroller-setpointerorigin>
71    fn SetPointerOrigin(&self, origin: &FakeXRRigidTransformInit, _emulated: bool) -> Fallible<()> {
72        self.send_message(MockInputMsg::SetPointerOrigin(Some(get_origin(origin)?)));
73        Ok(())
74    }
75
76    /// <https://immersive-web.github.io/webxr-test-api/#dom-fakexrinputcontroller-setgriporigin>
77    fn SetGripOrigin(&self, origin: &FakeXRRigidTransformInit, _emulated: bool) -> Fallible<()> {
78        self.send_message(MockInputMsg::SetGripOrigin(Some(get_origin(origin)?)));
79        Ok(())
80    }
81
82    /// <https://immersive-web.github.io/webxr-test-api/#dom-fakexrinputcontroller-cleargriporigin>
83    fn ClearGripOrigin(&self) {
84        self.send_message(MockInputMsg::SetGripOrigin(None))
85    }
86
87    /// <https://immersive-web.github.io/webxr-test-api/#dom-fakexrinputcontroller-disconnect>
88    fn Disconnect(&self) {
89        self.send_message(MockInputMsg::Disconnect)
90    }
91
92    /// <https://immersive-web.github.io/webxr-test-api/#dom-fakexrinputcontroller-reconnect>
93    fn Reconnect(&self) {
94        self.send_message(MockInputMsg::Reconnect)
95    }
96
97    /// <https://immersive-web.github.io/webxr-test-api/#dom-fakexrinputcontroller-startselection>
98    fn StartSelection(&self) {
99        self.send_message(MockInputMsg::TriggerSelect(
100            SelectKind::Select,
101            SelectEvent::Start,
102        ))
103    }
104
105    /// <https://immersive-web.github.io/webxr-test-api/#dom-fakexrinputcontroller-endselection>
106    fn EndSelection(&self) {
107        self.send_message(MockInputMsg::TriggerSelect(
108            SelectKind::Select,
109            SelectEvent::End,
110        ))
111    }
112
113    /// <https://immersive-web.github.io/webxr-test-api/#dom-fakexrinputcontroller-simulateselect>
114    fn SimulateSelect(&self) {
115        self.send_message(MockInputMsg::TriggerSelect(
116            SelectKind::Select,
117            SelectEvent::Select,
118        ))
119    }
120
121    /// <https://immersive-web.github.io/webxr-test-api/#dom-fakexrinputcontroller-sethandedness>
122    fn SetHandedness(&self, handedness: XRHandedness) {
123        let h = match handedness {
124            XRHandedness::None => Handedness::None,
125            XRHandedness::Left => Handedness::Left,
126            XRHandedness::Right => Handedness::Right,
127        };
128        self.send_message(MockInputMsg::SetHandedness(h));
129    }
130
131    /// <https://immersive-web.github.io/webxr-test-api/#dom-fakexrinputcontroller-settargetraymode>
132    fn SetTargetRayMode(&self, target_ray_mode: XRTargetRayMode) {
133        let t = match target_ray_mode {
134            XRTargetRayMode::Gaze => TargetRayMode::Gaze,
135            XRTargetRayMode::Tracked_pointer => TargetRayMode::TrackedPointer,
136            XRTargetRayMode::Screen => TargetRayMode::Screen,
137            XRTargetRayMode::Transient_pointer => TargetRayMode::TransientPointer,
138        };
139        self.send_message(MockInputMsg::SetTargetRayMode(t));
140    }
141
142    /// <https://immersive-web.github.io/webxr-test-api/#dom-fakexrinputcontroller-setprofiles>
143    fn SetProfiles(&self, profiles: Vec<DOMString>) {
144        let t = profiles.into_iter().map(String::from).collect();
145        self.send_message(MockInputMsg::SetProfiles(t));
146    }
147
148    /// <https://immersive-web.github.io/webxr-test-api/#dom-fakexrinputcontroller-setsupportedbuttons>
149    fn SetSupportedButtons(&self, supported_buttons: Vec<FakeXRButtonStateInit>) {
150        let supported = init_to_mock_buttons(&supported_buttons);
151        self.send_message(MockInputMsg::SetSupportedButtons(supported));
152    }
153
154    /// <https://immersive-web.github.io/webxr-test-api/#dom-fakexrinputcontroller-updatebuttonstate>
155    fn UpdateButtonState(&self, button_state: &FakeXRButtonStateInit) -> Fallible<()> {
156        // https://immersive-web.github.io/webxr-test-api/#validate-a-button-state
157        if (button_state.pressed || *button_state.pressedValue > 0.0) && !button_state.touched {
158            return Err(Error::Type(c"Pressed button must also be touched".into()));
159        }
160        if *button_state.pressedValue < 0.0 {
161            return Err(Error::Type(c"Pressed value must be non-negative".into()));
162        }
163
164        // TODO: Steps 3-5 of updateButtonState
165        // Passing the one WPT test that utilizes this will require additional work
166        // to specify gamepad button/axes list lengths, as well as passing that info
167        // to the constructor of XRInputSource
168
169        Ok(())
170    }
171}
172
173impl Convert<MockButtonType> for FakeXRButtonType {
174    fn convert(self) -> MockButtonType {
175        match self {
176            FakeXRButtonType::Grip => MockButtonType::Grip,
177            FakeXRButtonType::Touchpad => MockButtonType::Touchpad,
178            FakeXRButtonType::Thumbstick => MockButtonType::Thumbstick,
179            FakeXRButtonType::Optional_button => MockButtonType::OptionalButton,
180            FakeXRButtonType::Optional_thumbstick => MockButtonType::OptionalThumbstick,
181        }
182    }
183}
184
185/// <https://immersive-web.github.io/webxr-test-api/#parse-supported-buttons>
186pub(crate) fn init_to_mock_buttons(buttons: &[FakeXRButtonStateInit]) -> Vec<MockButton> {
187    let supported: Vec<MockButton> = buttons
188        .iter()
189        .map(|b| MockButton {
190            button_type: b.buttonType.convert(),
191            pressed: b.pressed,
192            touched: b.touched,
193            pressed_value: *b.pressedValue,
194            x_value: *b.xValue,
195            y_value: *b.yValue,
196        })
197        .collect();
198    supported
199}