script/dom/webxr/
xrinputsource.rs1use dom_struct::dom_struct;
6use embedder_traits::GamepadSupportedHapticEffects;
7use js::context::JSContext;
8use js::jsapi::Heap;
9use js::jsval::{JSVal, UndefinedValue};
10use js::rust::MutableHandleValue;
11use script_bindings::conversions::SafeToJSValConvertible;
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 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
87 .info
88 .profiles
89 .safe_to_jsval(cx, profiles.handle_mut());
90 source.profiles.set(profiles.get());
91 source
92 }
93
94 pub(crate) fn id(&self) -> InputId {
95 self.info.id
96 }
97
98 pub(crate) fn session(&self) -> &XRSession {
99 &self.session
100 }
101
102 pub(crate) fn update_gamepad_state(&self, frame: InputFrame) {
103 frame
104 .button_values
105 .iter()
106 .enumerate()
107 .for_each(|(i, value)| {
108 self.gamepad.map_and_normalize_buttons(i, *value as f64);
109 });
110 frame.axis_values.iter().enumerate().for_each(|(i, value)| {
111 self.gamepad.map_and_normalize_axes(i, *value as f64);
112 });
113 }
114
115 pub(crate) fn gamepad(&self) -> &Gamepad {
116 &self.gamepad
117 }
118}
119
120impl XRInputSourceMethods<crate::DomTypeHolder> for XRInputSource {
121 fn Handedness(&self) -> XRHandedness {
123 match self.info.handedness {
124 Handedness::None => XRHandedness::None,
125 Handedness::Left => XRHandedness::Left,
126 Handedness::Right => XRHandedness::Right,
127 }
128 }
129
130 fn TargetRayMode(&self) -> XRTargetRayMode {
132 match self.info.target_ray_mode {
133 TargetRayMode::Gaze => XRTargetRayMode::Gaze,
134 TargetRayMode::TrackedPointer => XRTargetRayMode::Tracked_pointer,
135 TargetRayMode::Screen => XRTargetRayMode::Screen,
136 TargetRayMode::TransientPointer => XRTargetRayMode::Transient_pointer,
137 }
138 }
139
140 fn TargetRaySpace(&self, cx: &mut JSContext) -> DomRoot<XRSpace> {
142 self.target_ray_space.or_init(|| {
143 let global = self.global();
144 XRSpace::new_inputspace(cx, &global, &self.session, self, false)
145 })
146 }
147
148 fn GetGripSpace(&self, cx: &mut JSContext) -> Option<DomRoot<XRSpace>> {
150 if self.info.supports_grip {
151 Some(self.grip_space.or_init(|| {
152 let global = self.global();
153 XRSpace::new_inputspace(cx, &global, &self.session, self, true)
154 }))
155 } else {
156 None
157 }
158 }
159 fn Profiles(&self, mut retval: MutableHandleValue) {
161 retval.set(self.profiles.get())
162 }
163
164 fn SkipRendering(&self) -> bool {
166 false
169 }
170
171 fn GetGamepad(&self) -> Option<DomRoot<Gamepad>> {
173 Some(DomRoot::from_ref(&*self.gamepad))
174 }
175
176 fn GetHand(&self, cx: &mut JSContext) -> Option<DomRoot<XRHand>> {
178 self.info.hand_support.as_ref().map(|hand| {
179 self.hand
180 .or_init(|| XRHand::new(cx, &self.global(), self, hand.clone()))
181 })
182 }
183}