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 webxr_api::{Handedness, InputFrame, InputId, InputSource, TargetRayMode};
12
13use crate::dom::bindings::codegen::Bindings::XRInputSourceBinding::{
14 XRHandedness, XRInputSourceMethods, XRTargetRayMode,
15};
16use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
17use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
18use crate::dom::gamepad::Gamepad;
19use crate::dom::globalscope::GlobalScope;
20use crate::dom::window::Window;
21use crate::dom::xrhand::XRHand;
22use crate::dom::xrsession::XRSession;
23use crate::dom::xrspace::XRSpace;
24use crate::realms::enter_realm;
25use crate::script_runtime::{CanGc, JSContext};
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: DomRoot<Gamepad>,
39}
40
41impl XRInputSource {
42 pub(crate) fn new_inherited(
43 window: &Window,
44 session: &XRSession,
45 info: InputSource,
46 can_gc: CanGc,
47 ) -> XRInputSource {
48 let gamepad = Gamepad::new(
50 window,
51 0,
52 "".into(),
53 "xr-standard".into(),
54 (-1.0, 1.0),
55 (0.0, 1.0),
56 GamepadSupportedHapticEffects {
57 supports_dual_rumble: false,
58 supports_trigger_rumble: false,
59 },
60 true,
61 can_gc,
62 );
63 XRInputSource {
64 reflector: Reflector::new(),
65 session: Dom::from_ref(session),
66 info,
67 target_ray_space: Default::default(),
68 grip_space: Default::default(),
69 hand: Default::default(),
70 profiles: Heap::default(),
71 gamepad,
72 }
73 }
74
75 pub(crate) fn new(
76 window: &Window,
77 session: &XRSession,
78 info: InputSource,
79 can_gc: CanGc,
80 ) -> DomRoot<XRInputSource> {
81 let source = reflect_dom_object(
82 Box::new(XRInputSource::new_inherited(window, session, info, can_gc)),
83 window,
84 can_gc,
85 );
86
87 let _ac = enter_realm(window);
88 let cx = GlobalScope::get_cx();
89 rooted!(in(*cx) let mut profiles = UndefinedValue());
90 source
91 .info
92 .profiles
93 .safe_to_jsval(cx, profiles.handle_mut(), can_gc);
94 source.profiles.set(profiles.get());
95 source
96 }
97
98 pub(crate) fn id(&self) -> InputId {
99 self.info.id
100 }
101
102 pub(crate) fn session(&self) -> &XRSession {
103 &self.session
104 }
105
106 pub(crate) fn update_gamepad_state(&self, frame: InputFrame) {
107 frame
108 .button_values
109 .iter()
110 .enumerate()
111 .for_each(|(i, value)| {
112 self.gamepad.map_and_normalize_buttons(i, *value as f64);
113 });
114 frame.axis_values.iter().enumerate().for_each(|(i, value)| {
115 self.gamepad.map_and_normalize_axes(i, *value as f64);
116 });
117 }
118
119 pub(crate) fn gamepad(&self) -> &DomRoot<Gamepad> {
120 &self.gamepad
121 }
122}
123
124impl XRInputSourceMethods<crate::DomTypeHolder> for XRInputSource {
125 fn Handedness(&self) -> XRHandedness {
127 match self.info.handedness {
128 Handedness::None => XRHandedness::None,
129 Handedness::Left => XRHandedness::Left,
130 Handedness::Right => XRHandedness::Right,
131 }
132 }
133
134 fn TargetRayMode(&self) -> XRTargetRayMode {
136 match self.info.target_ray_mode {
137 TargetRayMode::Gaze => XRTargetRayMode::Gaze,
138 TargetRayMode::TrackedPointer => XRTargetRayMode::Tracked_pointer,
139 TargetRayMode::Screen => XRTargetRayMode::Screen,
140 TargetRayMode::TransientPointer => XRTargetRayMode::Transient_pointer,
141 }
142 }
143
144 fn TargetRaySpace(&self) -> DomRoot<XRSpace> {
146 self.target_ray_space.or_init(|| {
147 let global = self.global();
148 XRSpace::new_inputspace(&global, &self.session, self, false, CanGc::note())
149 })
150 }
151
152 fn GetGripSpace(&self) -> Option<DomRoot<XRSpace>> {
154 if self.info.supports_grip {
155 Some(self.grip_space.or_init(|| {
156 let global = self.global();
157 XRSpace::new_inputspace(&global, &self.session, self, true, CanGc::note())
158 }))
159 } else {
160 None
161 }
162 }
163 fn Profiles(&self, _cx: JSContext, mut retval: MutableHandleValue) {
165 retval.set(self.profiles.get())
166 }
167
168 fn SkipRendering(&self) -> bool {
170 false
173 }
174
175 fn GetGamepad(&self) -> Option<DomRoot<Gamepad>> {
177 Some(DomRoot::from_ref(&*self.gamepad))
178 }
179
180 fn GetHand(&self) -> Option<DomRoot<XRHand>> {
182 self.info.hand_support.as_ref().map(|hand| {
183 self.hand
184 .or_init(|| XRHand::new(&self.global(), self, hand.clone(), CanGc::note()))
185 })
186 }
187}