script/dom/webxr/
xrinputsource.rs1use dom_struct::dom_struct;
6use embedder_traits::GamepadSupportedHapticEffects;
7use js::jsapi::Heap;
8use js::jsval::{JSVal, UndefinedValue};
9use js::rust::MutableHandleValue;
10use script_bindings::conversions::SafeToJSValConvertible;
11use script_bindings::reflector::{Reflector, reflect_dom_object};
12use webxr_api::{Handedness, InputFrame, InputId, InputSource, TargetRayMode};
13
14use crate::dom::bindings::codegen::Bindings::XRInputSourceBinding::{
15 XRHandedness, XRInputSourceMethods, XRTargetRayMode,
16};
17use crate::dom::bindings::reflector::DomGlobal;
18use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
19use crate::dom::gamepad::Gamepad;
20use crate::dom::globalscope::GlobalScope;
21use crate::dom::window::Window;
22use crate::dom::xrhand::XRHand;
23use crate::dom::xrsession::XRSession;
24use crate::dom::xrspace::XRSpace;
25use crate::realms::enter_realm;
26use crate::script_runtime::{CanGc, JSContext};
27
28#[dom_struct]
29pub(crate) struct XRInputSource {
30 reflector: Reflector,
31 session: Dom<XRSession>,
32 #[no_trace]
33 info: InputSource,
34 target_ray_space: MutNullableDom<XRSpace>,
35 grip_space: MutNullableDom<XRSpace>,
36 hand: MutNullableDom<XRHand>,
37 #[ignore_malloc_size_of = "mozjs"]
38 profiles: Heap<JSVal>,
39 gamepad: DomRoot<Gamepad>,
40}
41
42impl XRInputSource {
43 pub(crate) fn new_inherited(
44 window: &Window,
45 session: &XRSession,
46 info: InputSource,
47 can_gc: CanGc,
48 ) -> XRInputSource {
49 let gamepad = Gamepad::new(
51 window,
52 0,
53 "".into(),
54 "xr-standard".into(),
55 (-1.0, 1.0),
56 (0.0, 1.0),
57 GamepadSupportedHapticEffects {
58 supports_dual_rumble: false,
59 supports_trigger_rumble: false,
60 },
61 true,
62 can_gc,
63 );
64 XRInputSource {
65 reflector: Reflector::new(),
66 session: Dom::from_ref(session),
67 info,
68 target_ray_space: Default::default(),
69 grip_space: Default::default(),
70 hand: Default::default(),
71 profiles: Heap::default(),
72 gamepad,
73 }
74 }
75
76 pub(crate) fn new(
77 window: &Window,
78 session: &XRSession,
79 info: InputSource,
80 can_gc: CanGc,
81 ) -> DomRoot<XRInputSource> {
82 let source = reflect_dom_object(
83 Box::new(XRInputSource::new_inherited(window, session, info, can_gc)),
84 window,
85 can_gc,
86 );
87
88 let _ac = enter_realm(window);
89 let cx = GlobalScope::get_cx();
90 rooted!(in(*cx) let mut profiles = UndefinedValue());
91 source
92 .info
93 .profiles
94 .safe_to_jsval(cx, profiles.handle_mut(), can_gc);
95 source.profiles.set(profiles.get());
96 source
97 }
98
99 pub(crate) fn id(&self) -> InputId {
100 self.info.id
101 }
102
103 pub(crate) fn session(&self) -> &XRSession {
104 &self.session
105 }
106
107 pub(crate) fn update_gamepad_state(&self, frame: InputFrame) {
108 frame
109 .button_values
110 .iter()
111 .enumerate()
112 .for_each(|(i, value)| {
113 self.gamepad.map_and_normalize_buttons(i, *value as f64);
114 });
115 frame.axis_values.iter().enumerate().for_each(|(i, value)| {
116 self.gamepad.map_and_normalize_axes(i, *value as f64);
117 });
118 }
119
120 pub(crate) fn gamepad(&self) -> &DomRoot<Gamepad> {
121 &self.gamepad
122 }
123}
124
125impl XRInputSourceMethods<crate::DomTypeHolder> for XRInputSource {
126 fn Handedness(&self) -> XRHandedness {
128 match self.info.handedness {
129 Handedness::None => XRHandedness::None,
130 Handedness::Left => XRHandedness::Left,
131 Handedness::Right => XRHandedness::Right,
132 }
133 }
134
135 fn TargetRayMode(&self) -> XRTargetRayMode {
137 match self.info.target_ray_mode {
138 TargetRayMode::Gaze => XRTargetRayMode::Gaze,
139 TargetRayMode::TrackedPointer => XRTargetRayMode::Tracked_pointer,
140 TargetRayMode::Screen => XRTargetRayMode::Screen,
141 TargetRayMode::TransientPointer => XRTargetRayMode::Transient_pointer,
142 }
143 }
144
145 fn TargetRaySpace(&self) -> DomRoot<XRSpace> {
147 self.target_ray_space.or_init(|| {
148 let global = self.global();
149 XRSpace::new_inputspace(
150 &global,
151 &self.session,
152 self,
153 false,
154 CanGc::deprecated_note(),
155 )
156 })
157 }
158
159 fn GetGripSpace(&self) -> Option<DomRoot<XRSpace>> {
161 if self.info.supports_grip {
162 Some(self.grip_space.or_init(|| {
163 let global = self.global();
164 XRSpace::new_inputspace(
165 &global,
166 &self.session,
167 self,
168 true,
169 CanGc::deprecated_note(),
170 )
171 }))
172 } else {
173 None
174 }
175 }
176 fn Profiles(&self, _cx: JSContext, mut retval: MutableHandleValue) {
178 retval.set(self.profiles.get())
179 }
180
181 fn SkipRendering(&self) -> bool {
183 false
186 }
187
188 fn GetGamepad(&self) -> Option<DomRoot<Gamepad>> {
190 Some(DomRoot::from_ref(&*self.gamepad))
191 }
192
193 fn GetHand(&self) -> Option<DomRoot<XRHand>> {
195 self.info.hand_support.as_ref().map(|hand| {
196 self.hand.or_init(|| {
197 XRHand::new(&self.global(), self, hand.clone(), CanGc::deprecated_note())
198 })
199 })
200 }
201}