1use dom_struct::dom_struct;
6use js::jsapi::JSContext;
7use js::rust::MutableHandleValue;
8use webxr_api::{FingerJoint, Hand, Joint};
9
10use crate::dom::bindings::codegen::Bindings::XRHandBinding::{XRHandJoint, XRHandMethods};
11use crate::dom::bindings::conversions::ToJSValConvertible;
12use crate::dom::bindings::iterable::Iterable;
13use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
14use crate::dom::bindings::root::{Dom, DomRoot};
15use crate::dom::globalscope::GlobalScope;
16use crate::dom::xrinputsource::XRInputSource;
17use crate::dom::xrjointspace::XRJointSpace;
18use crate::script_runtime::CanGc;
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(|j, _| j.as_ref().map(|j| Dom::from_ref(&**j))),
121 }
122 }
123
124 pub(crate) fn new(
125 global: &GlobalScope,
126 source: &XRInputSource,
127 support: Hand<()>,
128 can_gc: CanGc,
129 ) -> DomRoot<XRHand> {
130 let id = source.id();
131 let session = source.session();
132 let spaces = support.map(|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(|_| {
139 XRJointSpace::new(
140 global,
141 session,
142 id,
143 joint,
144 hand_joint,
145 CanGc::deprecated_note(),
146 )
147 })
148 });
149 reflect_dom_object(
150 Box::new(XRHand::new_inherited(source, &spaces)),
151 global,
152 can_gc,
153 )
154 }
155}
156
157impl XRHandMethods<crate::DomTypeHolder> for XRHand {
158 fn Size(&self) -> u32 {
160 XRHandJoint::Pinky_finger_tip as u32 + 1
161 }
162
163 fn Get(&self, joint_name: XRHandJoint) -> DomRoot<XRJointSpace> {
165 let joint = JOINT_SPACE_MAP
166 .iter()
167 .find(|&&(key, _)| key == joint_name)
168 .map(|&(_, joint)| joint)
169 .expect("Invalid joint name");
170 self.spaces
171 .get(joint)
172 .map(|j| DomRoot::from_ref(&**j))
173 .expect("Failed to get joint pose")
174 }
175}
176
177pub(crate) struct ValueWrapper(pub DomRoot<XRJointSpace>);
180
181impl ToJSValConvertible for ValueWrapper {
182 #[expect(unsafe_code)]
183 unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
184 unsafe { self.0.to_jsval(cx, rval) }
185 }
186}
187
188impl Iterable for XRHand {
189 type Key = XRHandJoint;
190 type Value = ValueWrapper;
191
192 fn get_iterable_length(&self) -> u32 {
193 JOINT_SPACE_MAP.len() as u32
194 }
195
196 fn get_value_at_index(&self, n: u32) -> ValueWrapper {
197 let joint = JOINT_SPACE_MAP[n as usize].1;
198 self.spaces
199 .get(joint)
200 .map(|j| ValueWrapper(DomRoot::from_ref(&**j)))
201 .expect("Failed to get joint pose")
202 }
203
204 fn get_key_at_index(&self, n: u32) -> XRHandJoint {
205 JOINT_SPACE_MAP[n as usize].0
206 }
207}