1use dom_struct::dom_struct;
6use js::context::JSContext;
7use js::jsapi::JSContext as RawJSContext;
8use js::rust::MutableHandleValue;
9use script_bindings::reflector::{Reflector, reflect_dom_object_with_cx};
10use webxr_api::{FingerJoint, Hand, Joint};
11
12use crate::dom::bindings::codegen::Bindings::XRHandBinding::{XRHandJoint, XRHandMethods};
13use crate::dom::bindings::conversions::ToJSValConvertible;
14use crate::dom::bindings::iterable::Iterable;
15use crate::dom::bindings::root::{Dom, DomRoot};
16use crate::dom::globalscope::GlobalScope;
17use crate::dom::xrinputsource::XRInputSource;
18use crate::dom::xrjointspace::XRJointSpace;
19
20const JOINT_SPACE_MAP: [(XRHandJoint, Joint); 25] = [
21 (XRHandJoint::Wrist, Joint::Wrist),
22 (XRHandJoint::Thumb_metacarpal, Joint::ThumbMetacarpal),
23 (
24 XRHandJoint::Thumb_phalanx_proximal,
25 Joint::ThumbPhalanxProximal,
26 ),
27 (XRHandJoint::Thumb_phalanx_distal, Joint::ThumbPhalanxDistal),
28 (XRHandJoint::Thumb_tip, Joint::ThumbPhalanxTip),
29 (
30 XRHandJoint::Index_finger_metacarpal,
31 Joint::Index(FingerJoint::Metacarpal),
32 ),
33 (
34 XRHandJoint::Index_finger_phalanx_proximal,
35 Joint::Index(FingerJoint::PhalanxProximal),
36 ),
37 (XRHandJoint::Index_finger_phalanx_intermediate, {
38 Joint::Index(FingerJoint::PhalanxIntermediate)
39 }),
40 (
41 XRHandJoint::Index_finger_phalanx_distal,
42 Joint::Index(FingerJoint::PhalanxDistal),
43 ),
44 (
45 XRHandJoint::Index_finger_tip,
46 Joint::Index(FingerJoint::PhalanxTip),
47 ),
48 (
49 XRHandJoint::Middle_finger_metacarpal,
50 Joint::Middle(FingerJoint::Metacarpal),
51 ),
52 (
53 XRHandJoint::Middle_finger_phalanx_proximal,
54 Joint::Middle(FingerJoint::PhalanxProximal),
55 ),
56 (XRHandJoint::Middle_finger_phalanx_intermediate, {
57 Joint::Middle(FingerJoint::PhalanxIntermediate)
58 }),
59 (
60 XRHandJoint::Middle_finger_phalanx_distal,
61 Joint::Middle(FingerJoint::PhalanxDistal),
62 ),
63 (
64 XRHandJoint::Middle_finger_tip,
65 Joint::Middle(FingerJoint::PhalanxTip),
66 ),
67 (
68 XRHandJoint::Ring_finger_metacarpal,
69 Joint::Ring(FingerJoint::Metacarpal),
70 ),
71 (
72 XRHandJoint::Ring_finger_phalanx_proximal,
73 Joint::Ring(FingerJoint::PhalanxProximal),
74 ),
75 (XRHandJoint::Ring_finger_phalanx_intermediate, {
76 Joint::Ring(FingerJoint::PhalanxIntermediate)
77 }),
78 (
79 XRHandJoint::Ring_finger_phalanx_distal,
80 Joint::Ring(FingerJoint::PhalanxDistal),
81 ),
82 (
83 XRHandJoint::Ring_finger_tip,
84 Joint::Ring(FingerJoint::PhalanxTip),
85 ),
86 (
87 XRHandJoint::Pinky_finger_metacarpal,
88 Joint::Little(FingerJoint::Metacarpal),
89 ),
90 (
91 XRHandJoint::Pinky_finger_phalanx_proximal,
92 Joint::Little(FingerJoint::PhalanxProximal),
93 ),
94 (XRHandJoint::Pinky_finger_phalanx_intermediate, {
95 Joint::Little(FingerJoint::PhalanxIntermediate)
96 }),
97 (
98 XRHandJoint::Pinky_finger_phalanx_distal,
99 Joint::Little(FingerJoint::PhalanxDistal),
100 ),
101 (
102 XRHandJoint::Pinky_finger_tip,
103 Joint::Little(FingerJoint::PhalanxTip),
104 ),
105];
106
107#[dom_struct]
108pub(crate) struct XRHand {
109 reflector_: Reflector,
110 source: Dom<XRInputSource>,
111 #[custom_trace]
112 spaces: Hand<Dom<XRJointSpace>>,
113}
114
115impl XRHand {
116 fn new_inherited(source: &XRInputSource, spaces: &Hand<DomRoot<XRJointSpace>>) -> XRHand {
117 XRHand {
118 reflector_: Reflector::new(),
119 source: Dom::from_ref(source),
120 spaces: spaces.map(&mut (), |_, j, _| j.as_ref().map(|j| Dom::from_ref(&**j))),
121 }
122 }
123
124 pub(crate) fn new(
125 cx: &mut JSContext,
126 global: &GlobalScope,
127 source: &XRInputSource,
128 support: Hand<()>,
129 ) -> DomRoot<XRHand> {
130 let id = source.id();
131 let session = source.session();
132 let spaces = support.map(cx, |cx, field, joint| {
133 let hand_joint = JOINT_SPACE_MAP
134 .iter()
135 .find(|&&(_, value)| value == joint)
136 .map(|&(hand_joint, _)| hand_joint)
137 .expect("Invalid joint name");
138 field.map(|_| XRJointSpace::new(cx, global, session, id, joint, hand_joint))
139 });
140 reflect_dom_object_with_cx(Box::new(XRHand::new_inherited(source, &spaces)), global, cx)
141 }
142}
143
144impl XRHandMethods<crate::DomTypeHolder> for XRHand {
145 fn Size(&self) -> u32 {
147 XRHandJoint::Pinky_finger_tip as u32 + 1
148 }
149
150 fn Get(&self, joint_name: XRHandJoint) -> DomRoot<XRJointSpace> {
152 let joint = JOINT_SPACE_MAP
153 .iter()
154 .find(|&&(key, _)| key == joint_name)
155 .map(|&(_, joint)| joint)
156 .expect("Invalid joint name");
157 self.spaces
158 .get(joint)
159 .map(|j| DomRoot::from_ref(&**j))
160 .expect("Failed to get joint pose")
161 }
162}
163
164pub(crate) struct ValueWrapper(pub DomRoot<XRJointSpace>);
167
168impl ToJSValConvertible for ValueWrapper {
169 #[expect(unsafe_code)]
170 unsafe fn to_jsval(&self, cx: *mut RawJSContext, rval: MutableHandleValue) {
171 unsafe { self.0.to_jsval(cx, rval) }
172 }
173}
174
175impl Iterable for XRHand {
176 type Key = XRHandJoint;
177 type Value = ValueWrapper;
178
179 fn get_iterable_length(&self) -> u32 {
180 JOINT_SPACE_MAP.len() as u32
181 }
182
183 fn get_value_at_index(&self, n: u32) -> ValueWrapper {
184 let joint = JOINT_SPACE_MAP[n as usize].1;
185 self.spaces
186 .get(joint)
187 .map(|j| ValueWrapper(DomRoot::from_ref(&**j)))
188 .expect("Failed to get joint pose")
189 }
190
191 fn get_key_at_index(&self, n: u32) -> XRHandJoint {
192 JOINT_SPACE_MAP[n as usize].0
193 }
194}