Bug 1866894 - Update failing subtest for content-visibility-auto-resize.html. r=fredw
[gecko.git] / dom / vr / XRInputSource.cpp
blob8cf2849d1c727024ac1d24c532a3729951239df1
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/dom/XRInputSource.h"
8 #include "mozilla/dom/XRInputSourceEvent.h"
9 #include "XRNativeOriginViewer.h"
10 #include "XRNativeOriginTracker.h"
11 #include "XRInputSpace.h"
12 #include "VRDisplayClient.h"
14 #include "mozilla/dom/Gamepad.h"
15 #include "mozilla/dom/GamepadManager.h"
17 namespace mozilla::dom {
19 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(XRInputSource, mParent, mTargetRaySpace,
20 mGripSpace, mGamepad)
22 // Follow the controller profile ids from
23 // https://github.com/immersive-web/webxr-input-profiles.
24 nsTArray<nsString> GetInputSourceProfile(gfx::VRControllerType aType) {
25 nsTArray<nsString> profile;
26 nsString id;
28 switch (aType) {
29 case gfx::VRControllerType::HTCVive:
30 id.AssignLiteral("htc-vive");
31 profile.AppendElement(id);
32 id.AssignLiteral("generic-trigger-squeeze-touchpad");
33 profile.AppendElement(id);
34 break;
35 case gfx::VRControllerType::HTCViveCosmos:
36 id.AssignLiteral("htc-vive-cosmos");
37 profile.AppendElement(id);
38 id.AssignLiteral("generic-trigger-squeeze-thumbstick");
39 profile.AppendElement(id);
40 break;
41 case gfx::VRControllerType::HTCViveFocus:
42 id.AssignLiteral("htc-vive-focus");
43 profile.AppendElement(id);
44 id.AssignLiteral("generic-trigger-touchpad");
45 profile.AppendElement(id);
46 break;
47 case gfx::VRControllerType::HTCViveFocusPlus:
48 id.AssignLiteral("htc-vive-focus-plus");
49 profile.AppendElement(id);
50 id.AssignLiteral("generic-trigger-squeeze-touchpad");
51 profile.AppendElement(id);
52 break;
53 case gfx::VRControllerType::MSMR:
54 id.AssignLiteral("microsoft-mixed-reality");
55 profile.AppendElement(id);
56 id.AssignLiteral("generic-trigger-squeeze-touchpad-thumbstick");
57 profile.AppendElement(id);
58 break;
59 case gfx::VRControllerType::ValveIndex:
60 id.AssignLiteral("valve-index");
61 profile.AppendElement(id);
62 id.AssignLiteral("generic-trigger-squeeze-touchpad-thumbstick");
63 profile.AppendElement(id);
64 break;
65 case gfx::VRControllerType::OculusGo:
66 id.AssignLiteral("oculus-go");
67 profile.AppendElement(id);
68 id.AssignLiteral("generic-trigger-touchpad");
69 profile.AppendElement(id);
70 break;
71 case gfx::VRControllerType::OculusTouch:
72 id.AssignLiteral("oculus-touch");
73 profile.AppendElement(id);
74 id.AssignLiteral("generic-trigger-squeeze-thumbstick");
75 profile.AppendElement(id);
76 break;
77 case gfx::VRControllerType::OculusTouch2:
78 id.AssignLiteral("oculus-touch-v2");
79 profile.AppendElement(id);
80 id.AssignLiteral("oculus-touch");
81 profile.AppendElement(id);
82 id.AssignLiteral("generic-trigger-squeeze-thumbstick");
83 profile.AppendElement(id);
84 break;
85 case gfx::VRControllerType::OculusTouch3:
86 id.AssignLiteral("oculus-touch-v3");
87 profile.AppendElement(id);
88 id.AssignLiteral("oculus-touch-v2");
89 profile.AppendElement(id);
90 id.AssignLiteral("oculus-touch");
91 profile.AppendElement(id);
92 id.AssignLiteral("generic-trigger-squeeze-thumbstick");
93 profile.AppendElement(id);
94 break;
95 case gfx::VRControllerType::PicoGaze:
96 id.AssignLiteral("pico-gaze");
97 profile.AppendElement(id);
98 id.AssignLiteral("generic-button");
99 profile.AppendElement(id);
100 break;
101 case gfx::VRControllerType::PicoG2:
102 id.AssignLiteral("pico-g2");
103 profile.AppendElement(id);
104 id.AssignLiteral("generic-trigger-touchpad");
105 profile.AppendElement(id);
106 break;
107 case gfx::VRControllerType::PicoNeo2:
108 id.AssignLiteral("pico-neo2");
109 profile.AppendElement(id);
110 id.AssignLiteral("generic-trigger-squeeze-thumbstick");
111 profile.AppendElement(id);
112 break;
113 default:
114 NS_WARNING("Unsupported XR input source profile.\n");
115 break;
117 return profile;
120 XRInputSource::XRInputSource(nsISupports* aParent)
121 : mParent(aParent),
122 mGamepad(nullptr),
123 mIndex(-1),
124 mSelectAction(ActionState::ActionState_Released),
125 mSqueezeAction(ActionState::ActionState_Released) {}
127 XRInputSource::~XRInputSource() {
128 mTargetRaySpace = nullptr;
129 mGripSpace = nullptr;
130 mGamepad = nullptr;
133 JSObject* XRInputSource::WrapObject(JSContext* aCx,
134 JS::Handle<JSObject*> aGivenProto) {
135 return XRInputSource_Binding::Wrap(aCx, this, aGivenProto);
138 XRHandedness XRInputSource::Handedness() { return mHandedness; }
140 XRTargetRayMode XRInputSource::TargetRayMode() { return mTargetRayMode; }
142 XRSpace* XRInputSource::TargetRaySpace() { return mTargetRaySpace; }
144 XRSpace* XRInputSource::GetGripSpace() { return mGripSpace; }
146 void XRInputSource::GetProfiles(nsTArray<nsString>& aResult) {
147 aResult = mProfiles.Clone();
150 Gamepad* XRInputSource::GetGamepad() { return mGamepad; }
152 void XRInputSource::Setup(XRSession* aSession, uint32_t aIndex) {
153 MOZ_ASSERT(aSession);
154 gfx::VRDisplayClient* displayClient = aSession->GetDisplayClient();
155 if (!displayClient) {
156 MOZ_ASSERT(displayClient);
157 return;
159 const gfx::VRDisplayInfo& displayInfo = displayClient->GetDisplayInfo();
160 const gfx::VRControllerState& controllerState =
161 displayInfo.mControllerState[aIndex];
162 MOZ_ASSERT(controllerState.controllerName[0] != '\0');
164 mProfiles = GetInputSourceProfile(controllerState.type);
165 mHandedness = XRHandedness::None;
166 switch (controllerState.hand) {
167 case GamepadHand::_empty:
168 mHandedness = XRHandedness::None;
169 break;
170 case GamepadHand::Left:
171 mHandedness = XRHandedness::Left;
172 break;
173 case GamepadHand::Right:
174 mHandedness = XRHandedness::Right;
175 break;
176 default:
177 MOZ_ASSERT(false && "Unknown GamepadHand type.");
178 break;
181 RefPtr<XRNativeOrigin> nativeOriginTargetRay = nullptr;
182 mTargetRayMode = XRTargetRayMode::Tracked_pointer;
183 switch (controllerState.targetRayMode) {
184 case gfx::TargetRayMode::Gaze:
185 mTargetRayMode = XRTargetRayMode::Gaze;
186 nativeOriginTargetRay = new XRNativeOriginViewer(displayClient);
187 break;
188 case gfx::TargetRayMode::TrackedPointer:
189 mTargetRayMode = XRTargetRayMode::Tracked_pointer;
190 // We use weak pointers of poses in XRNativeOriginTracker to sync their
191 // data internally.
192 nativeOriginTargetRay =
193 new XRNativeOriginTracker(&controllerState.targetRayPose);
194 break;
195 case gfx::TargetRayMode::Screen:
196 mTargetRayMode = XRTargetRayMode::Screen;
197 break;
198 default:
199 MOZ_ASSERT(false && "Undefined TargetRayMode type.");
200 break;
203 mTargetRaySpace = new XRInputSpace(aSession->GetParentObject(), aSession,
204 nativeOriginTargetRay, aIndex);
206 const uint32_t gamepadHandleValue =
207 displayInfo.mDisplayID * gfx::kVRControllerMaxCount + aIndex;
209 const GamepadHandle gamepadHandle{gamepadHandleValue, GamepadHandleKind::VR};
211 mGamepad =
212 new Gamepad(mParent, NS_ConvertASCIItoUTF16(""), -1, gamepadHandle,
213 GamepadMappingType::Xr_standard, controllerState.hand,
214 displayInfo.mDisplayID, controllerState.numButtons,
215 controllerState.numAxes, controllerState.numHaptics, 0, 0);
216 mIndex = aIndex;
218 if (!mGripSpace) {
219 CreateGripSpace(aSession, controllerState);
223 void XRInputSource::SetGamepadIsConnected(bool aConnected,
224 XRSession* aSession) {
225 mGamepad->SetConnected(aConnected);
226 MOZ_ASSERT(aSession);
228 if (!aConnected) {
229 if (mSelectAction != ActionState::ActionState_Released) {
230 DispatchEvent(u"selectend"_ns, aSession);
231 mSelectAction = ActionState::ActionState_Released;
233 if (mSqueezeAction != ActionState::ActionState_Released) {
234 DispatchEvent(u"squeezeend"_ns, aSession);
235 mSqueezeAction = ActionState::ActionState_Released;
240 void XRInputSource::Update(XRSession* aSession) {
241 MOZ_ASSERT(aSession && mIndex >= 0 && mGamepad);
243 gfx::VRDisplayClient* displayClient = aSession->GetDisplayClient();
244 if (!displayClient) {
245 MOZ_ASSERT(displayClient);
246 return;
248 const gfx::VRDisplayInfo& displayInfo = displayClient->GetDisplayInfo();
249 const gfx::VRControllerState& controllerState =
250 displayInfo.mControllerState[mIndex];
251 MOZ_ASSERT(controllerState.controllerName[0] != '\0');
253 // OculusVR and OpenVR controllers need to wait until
254 // update functions to assign GamepadCapabilityFlags::Cap_GripSpacePosition
255 // flag.
256 if (!mGripSpace) {
257 CreateGripSpace(aSession, controllerState);
260 // Update button values.
261 nsTArray<RefPtr<GamepadButton>> buttons;
262 mGamepad->GetButtons(buttons);
263 for (uint32_t i = 0; i < buttons.Length(); ++i) {
264 const bool pressed = controllerState.buttonPressed & (1ULL << i);
265 const bool touched = controllerState.buttonTouched & (1ULL << i);
267 if (buttons[i]->Pressed() != pressed || buttons[i]->Touched() != touched ||
268 buttons[i]->Value() != controllerState.triggerValue[i]) {
269 mGamepad->SetButton(i, pressed, touched, controllerState.triggerValue[i]);
272 // Update axis values.
273 nsTArray<double> axes;
274 mGamepad->GetAxes(axes);
275 for (uint32_t i = 0; i < axes.Length(); ++i) {
276 if (axes[i] != controllerState.axisValue[i]) {
277 mGamepad->SetAxis(i, controllerState.axisValue[i]);
281 // We define 0.85f and 0.15f based on our current finding
282 // for better experience, we can adjust these values if we need.
283 const float completeThreshold = 0.90f;
284 const float startThreshold = 0.85f;
285 const float endThreshold = 0.15f;
286 const uint32_t selectIndex = 0;
287 const uint32_t squeezeIndex = 1;
289 // Checking selectstart, select, selectend
290 if (buttons.Length() > selectIndex) {
291 if (controllerState.selectActionStartFrameId >
292 controllerState.selectActionStopFrameId) {
293 if (mSelectAction == ActionState::ActionState_Released &&
294 controllerState.triggerValue[selectIndex] > endThreshold) {
295 DispatchEvent(u"selectstart"_ns, aSession);
296 mSelectAction = ActionState::ActionState_Pressing;
297 } else if (mSelectAction == ActionState::ActionState_Pressing &&
298 controllerState.triggerValue[selectIndex] >
299 completeThreshold) {
300 mSelectAction = ActionState::ActionState_Pressed;
301 } else if (mSelectAction == ActionState::ActionState_Pressed &&
302 controllerState.triggerValue[selectIndex] < startThreshold) {
303 DispatchEvent(u"select"_ns, aSession);
304 mSelectAction = ActionState::ActionState_Releasing;
305 } else if (mSelectAction <= ActionState::ActionState_Releasing &&
306 controllerState.triggerValue[selectIndex] < endThreshold) {
307 // For a select btn which only has pressed and unpressed status.
308 if (mSelectAction == ActionState::ActionState_Pressed) {
309 DispatchEvent(u"select"_ns, aSession);
311 DispatchEvent(u"selectend"_ns, aSession);
312 mSelectAction = ActionState::ActionState_Released;
314 } else if (mSelectAction <= ActionState::ActionState_Releasing) {
315 // For a select btn which only has pressed and unpressed status.
316 if (mSelectAction == ActionState::ActionState_Pressed) {
317 DispatchEvent(u"select"_ns, aSession);
319 DispatchEvent(u"selectend"_ns, aSession);
320 mSelectAction = ActionState::ActionState_Released;
324 // Checking squeezestart, squeeze, squeezeend
325 if (buttons.Length() > squeezeIndex) {
326 if (controllerState.squeezeActionStartFrameId >
327 controllerState.squeezeActionStopFrameId) {
328 if (mSqueezeAction == ActionState::ActionState_Released &&
329 controllerState.triggerValue[squeezeIndex] > endThreshold) {
330 DispatchEvent(u"squeezestart"_ns, aSession);
331 mSqueezeAction = ActionState::ActionState_Pressing;
332 } else if (mSqueezeAction == ActionState::ActionState_Pressing &&
333 controllerState.triggerValue[squeezeIndex] >
334 completeThreshold) {
335 mSqueezeAction = ActionState::ActionState_Pressed;
336 } else if (mSqueezeAction == ActionState::ActionState_Pressed &&
337 controllerState.triggerValue[squeezeIndex] < startThreshold) {
338 DispatchEvent(u"squeeze"_ns, aSession);
339 mSqueezeAction = ActionState::ActionState_Releasing;
340 } else if (mSqueezeAction <= ActionState::ActionState_Releasing &&
341 controllerState.triggerValue[squeezeIndex] < endThreshold) {
342 // For a squeeze btn which only has pressed and unpressed status.
343 if (mSqueezeAction == ActionState::ActionState_Pressed) {
344 DispatchEvent(u"squeeze"_ns, aSession);
346 DispatchEvent(u"squeezeend"_ns, aSession);
347 mSqueezeAction = ActionState::ActionState_Released;
349 } else if (mSqueezeAction <= ActionState::ActionState_Releasing) {
350 // For a squeeze btn which only has pressed and unpressed status.
351 if (mSqueezeAction == ActionState::ActionState_Pressed) {
352 DispatchEvent(u"squeeze"_ns, aSession);
354 DispatchEvent(u"squeezeend"_ns, aSession);
355 mSqueezeAction = ActionState::ActionState_Released;
360 int32_t XRInputSource::GetIndex() { return mIndex; }
362 void XRInputSource::DispatchEvent(const nsAString& aEvent,
363 XRSession* aSession) {
364 if (!GetParentObject() || !aSession) {
365 return;
367 // Create a XRFrame for its callbacks
368 RefPtr<XRFrame> frame = new XRFrame(GetParentObject(), aSession);
369 frame->StartInputSourceEvent();
371 XRInputSourceEventInit init;
372 init.mBubbles = false;
373 init.mCancelable = false;
374 init.mFrame = frame;
375 init.mInputSource = this;
377 RefPtr<XRInputSourceEvent> event =
378 XRInputSourceEvent::Constructor(aSession, aEvent, init);
380 event->SetTrusted(true);
381 aSession->DispatchEvent(*event);
382 frame->EndInputSourceEvent();
385 void XRInputSource::CreateGripSpace(
386 XRSession* aSession, const gfx::VRControllerState& controllerState) {
387 MOZ_ASSERT(!mGripSpace);
388 MOZ_ASSERT(aSession && mIndex >= 0 && mGamepad);
389 if (mTargetRayMode == XRTargetRayMode::Tracked_pointer &&
390 controllerState.flags & GamepadCapabilityFlags::Cap_GripSpacePosition) {
391 RefPtr<XRNativeOrigin> nativeOriginGrip = nullptr;
392 nativeOriginGrip = new XRNativeOriginTracker(&controllerState.pose);
393 mGripSpace = new XRInputSpace(aSession->GetParentObject(), aSession,
394 nativeOriginGrip, mIndex);
395 } else {
396 mGripSpace = nullptr;
400 } // namespace mozilla::dom