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
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include <math.h>
8 
9 #include "prlink.h"
10 #include "prenv.h"
11 
12 #include "nsIGlobalObject.h"
13 #include "nsRefPtrHashtable.h"
14 #include "nsString.h"
15 #include "mozilla/dom/GamepadManager.h"
16 #include "mozilla/dom/Gamepad.h"
17 #include "mozilla/dom/XRSession.h"
18 #include "mozilla/dom/XRInputSourceArray.h"
19 #include "mozilla/Preferences.h"
20 #include "mozilla/Unused.h"
21 #include "mozilla/Telemetry.h"
22 #include "mozilla/dom/WebXRBinding.h"
23 #include "nsServiceManagerUtils.h"
24 
25 #ifdef XP_WIN
26 #  include "../layers/d3d11/CompositorD3D11.h"
27 #endif
28 
29 #include "VRDisplayClient.h"
30 #include "VRDisplayPresentation.h"
31 #include "VRManagerChild.h"
32 #include "VRLayerChild.h"
33 
34 using namespace mozilla;
35 using namespace mozilla::gfx;
36 
VRDisplayClient(const VRDisplayInfo & aDisplayInfo)37 VRDisplayClient::VRDisplayClient(const VRDisplayInfo& aDisplayInfo)
38     : mDisplayInfo(aDisplayInfo),
39       bLastEventWasMounted(false),
40       bLastEventWasPresenting(false),
41       mPresentationCount(0),
42       mLastEventFrameId(0),
43       mLastPresentingGeneration(0),
44       mLastEventControllerState{},
45       // For now WebVR is default to prevent a VRDisplay restore bug in WebVR
46       // compatibility mode. See Bug 1630512
47       mAPIMode(VRAPIMode::WebVR) {
48   MOZ_COUNT_CTOR(VRDisplayClient);
49 }
50 
~VRDisplayClient()51 VRDisplayClient::~VRDisplayClient() { MOZ_COUNT_DTOR(VRDisplayClient); }
52 
UpdateDisplayInfo(const VRDisplayInfo & aDisplayInfo)53 void VRDisplayClient::UpdateDisplayInfo(const VRDisplayInfo& aDisplayInfo) {
54   mDisplayInfo = aDisplayInfo;
55   FireEvents();
56 }
57 
BeginPresentation(const nsTArray<mozilla::dom::VRLayer> & aLayers,uint32_t aGroup)58 already_AddRefed<VRDisplayPresentation> VRDisplayClient::BeginPresentation(
59     const nsTArray<mozilla::dom::VRLayer>& aLayers, uint32_t aGroup) {
60   PresentationCreated();
61   RefPtr<VRDisplayPresentation> presentation =
62       new VRDisplayPresentation(this, aLayers, aGroup);
63   return presentation.forget();
64 }
65 
PresentationCreated()66 void VRDisplayClient::PresentationCreated() { ++mPresentationCount; }
67 
PresentationDestroyed()68 void VRDisplayClient::PresentationDestroyed() { --mPresentationCount; }
69 
SessionStarted(dom::XRSession * aSession)70 void VRDisplayClient::SessionStarted(dom::XRSession* aSession) {
71   PresentationCreated();
72   MakePresentationGenerationCurrent();
73   mSessions.AppendElement(aSession);
74 }
SessionEnded(dom::XRSession * aSession)75 void VRDisplayClient::SessionEnded(dom::XRSession* aSession) {
76   mSessions.RemoveElement(aSession);
77   PresentationDestroyed();
78 }
79 
StartFrame()80 void VRDisplayClient::StartFrame() {
81   RefPtr<VRManagerChild> vm = VRManagerChild::Get();
82   vm->RunFrameRequestCallbacks();
83 
84   nsTArray<RefPtr<dom::XRSession>> sessions;
85   sessions.AppendElements(mSessions);
86   for (auto session : sessions) {
87     session->StartFrame();
88   }
89 }
90 
SetGroupMask(uint32_t aGroupMask)91 void VRDisplayClient::SetGroupMask(uint32_t aGroupMask) {
92   VRManagerChild* vm = VRManagerChild::Get();
93   vm->SendSetGroupMask(mDisplayInfo.mDisplayID, aGroupMask);
94 }
95 
IsPresentationGenerationCurrent() const96 bool VRDisplayClient::IsPresentationGenerationCurrent() const {
97   if (mLastPresentingGeneration !=
98       mDisplayInfo.mDisplayState.presentingGeneration) {
99     return false;
100   }
101 
102   return true;
103 }
104 
MakePresentationGenerationCurrent()105 void VRDisplayClient::MakePresentationGenerationCurrent() {
106   mLastPresentingGeneration = mDisplayInfo.mDisplayState.presentingGeneration;
107 }
108 
GetXRAPIMode() const109 gfx::VRAPIMode VRDisplayClient::GetXRAPIMode() const { return mAPIMode; }
110 
SetXRAPIMode(gfx::VRAPIMode aMode)111 void VRDisplayClient::SetXRAPIMode(gfx::VRAPIMode aMode) {
112   mAPIMode = aMode;
113   Telemetry::Accumulate(Telemetry::WEBXR_API_MODE,
114                         static_cast<uint32_t>(mAPIMode));
115 }
116 
FireEvents()117 void VRDisplayClient::FireEvents() {
118   RefPtr<VRManagerChild> vm = VRManagerChild::Get();
119   // Only fire these events for non-chrome VR sessions
120   bool isPresenting = (mDisplayInfo.mPresentingGroups & kVRGroupContent) != 0;
121 
122   // Check if we need to trigger onVRDisplayPresentChange event
123   if (bLastEventWasPresenting != isPresenting) {
124     bLastEventWasPresenting = isPresenting;
125     vm->FireDOMVRDisplayPresentChangeEvent(mDisplayInfo.mDisplayID);
126   }
127 
128   // Check if we need to trigger onvrdisplayactivate event
129   if (!bLastEventWasMounted && mDisplayInfo.mDisplayState.isMounted) {
130     bLastEventWasMounted = true;
131     if (StaticPrefs::dom_vr_autoactivate_enabled()) {
132       vm->FireDOMVRDisplayMountedEvent(mDisplayInfo.mDisplayID);
133     }
134   }
135 
136   // Check if we need to trigger onvrdisplaydeactivate event
137   if (bLastEventWasMounted && !mDisplayInfo.mDisplayState.isMounted) {
138     bLastEventWasMounted = false;
139     if (StaticPrefs::dom_vr_autoactivate_enabled()) {
140       vm->FireDOMVRDisplayUnmountedEvent(mDisplayInfo.mDisplayID);
141     }
142   }
143 
144   if (mLastPresentingGeneration !=
145       mDisplayInfo.mDisplayState.presentingGeneration) {
146     mLastPresentingGeneration = mDisplayInfo.mDisplayState.presentingGeneration;
147     vm->NotifyPresentationGenerationChanged(mDisplayInfo.mDisplayID);
148   }
149 
150   // In WebXR spec, Gamepad instances returned by an XRInputSource's gamepad
151   // attribute MUST NOT be included in the array returned by
152   // navigator.getGamepads().
153   if (mAPIMode == VRAPIMode::WebVR) {
154     FireGamepadEvents();
155   }
156   // Update controller states into XRInputSourceArray.
157   for (auto& session : mSessions) {
158     dom::XRInputSourceArray* inputs = session->InputSources();
159     if (inputs) {
160       inputs->Update(session);
161     }
162   }
163 
164   // Check if we need to trigger VRDisplay.requestAnimationFrame
165   if (mLastEventFrameId != mDisplayInfo.mFrameId) {
166     mLastEventFrameId = mDisplayInfo.mFrameId;
167     StartFrame();
168   }
169 }
170 
GamepadMappingForWebVR(VRControllerState & aControllerState)171 void VRDisplayClient::GamepadMappingForWebVR(
172     VRControllerState& aControllerState) {
173   float triggerValue[kVRControllerMaxButtons];
174   memcpy(triggerValue, aControllerState.triggerValue,
175          sizeof(aControllerState.triggerValue));
176   const uint64_t buttonPressed = aControllerState.buttonPressed;
177   const uint64_t buttonTouched = aControllerState.buttonTouched;
178 
179   auto SetTriggerValue = [&](uint64_t newSlot, uint64_t oldSlot) {
180     aControllerState.triggerValue[newSlot] = triggerValue[oldSlot];
181   };
182   auto ShiftButtonBitForNewSlot = [&](uint64_t newSlot, uint64_t oldSlot,
183                                       bool aIsTouch = false) {
184     if (aIsTouch) {
185       return ((buttonTouched & (1ULL << oldSlot)) != 0) * (1ULL << newSlot);
186     }
187     SetTriggerValue(newSlot, oldSlot);
188     return ((buttonPressed & (1ULL << oldSlot)) != 0) * (1ULL << newSlot);
189   };
190 
191   switch (aControllerState.type) {
192     case VRControllerType::HTCVive:
193       aControllerState.buttonPressed =
194           ShiftButtonBitForNewSlot(1, 0) | ShiftButtonBitForNewSlot(2, 1) |
195           ShiftButtonBitForNewSlot(0, 2) | ShiftButtonBitForNewSlot(3, 4);
196       aControllerState.buttonTouched = ShiftButtonBitForNewSlot(0, 1, true) |
197                                        ShiftButtonBitForNewSlot(2, 1, true) |
198                                        ShiftButtonBitForNewSlot(0, 2, true) |
199                                        ShiftButtonBitForNewSlot(3, 4, true);
200       aControllerState.numButtons = 4;
201       aControllerState.numAxes = 2;
202       break;
203     case VRControllerType::MSMR:
204       aControllerState.buttonPressed =
205           ShiftButtonBitForNewSlot(1, 0) | ShiftButtonBitForNewSlot(2, 1) |
206           ShiftButtonBitForNewSlot(0, 2) | ShiftButtonBitForNewSlot(3, 3) |
207           ShiftButtonBitForNewSlot(4, 4);
208       aControllerState.buttonTouched = ShiftButtonBitForNewSlot(1, 0, true) |
209                                        ShiftButtonBitForNewSlot(2, 1, true) |
210                                        ShiftButtonBitForNewSlot(0, 2, true) |
211                                        ShiftButtonBitForNewSlot(3, 3, true) |
212                                        ShiftButtonBitForNewSlot(4, 4, true);
213       aControllerState.numButtons = 5;
214       aControllerState.numAxes = 4;
215       break;
216     case VRControllerType::HTCViveCosmos:
217       aControllerState.buttonPressed =
218           ShiftButtonBitForNewSlot(0, 0) | ShiftButtonBitForNewSlot(1, 1) |
219           ShiftButtonBitForNewSlot(4, 3) | ShiftButtonBitForNewSlot(2, 4) |
220           ShiftButtonBitForNewSlot(3, 5) | ShiftButtonBitForNewSlot(5, 6);
221       aControllerState.buttonTouched = ShiftButtonBitForNewSlot(0, 0, true) |
222                                        ShiftButtonBitForNewSlot(1, 1, true) |
223                                        ShiftButtonBitForNewSlot(4, 3, true) |
224                                        ShiftButtonBitForNewSlot(2, 4, true) |
225                                        ShiftButtonBitForNewSlot(3, 5, true) |
226                                        ShiftButtonBitForNewSlot(5, 6, true);
227       aControllerState.axisValue[0] = aControllerState.axisValue[2];
228       aControllerState.axisValue[1] = aControllerState.axisValue[3];
229       aControllerState.numButtons = 6;
230       aControllerState.numAxes = 2;
231       break;
232     case VRControllerType::HTCViveFocus:
233       aControllerState.buttonPressed =
234           ShiftButtonBitForNewSlot(0, 2) | ShiftButtonBitForNewSlot(1, 0);
235       aControllerState.buttonTouched = ShiftButtonBitForNewSlot(0, 2, true) |
236                                        ShiftButtonBitForNewSlot(1, 0, true);
237       aControllerState.numButtons = 2;
238       aControllerState.numAxes = 2;
239       break;
240     case VRControllerType::HTCViveFocusPlus: {
241       aControllerState.buttonPressed = ShiftButtonBitForNewSlot(0, 2) |
242                                        ShiftButtonBitForNewSlot(1, 0) |
243                                        ShiftButtonBitForNewSlot(2, 1);
244       aControllerState.buttonTouched = ShiftButtonBitForNewSlot(0, 2, true) |
245                                        ShiftButtonBitForNewSlot(1, 0, true) |
246                                        ShiftButtonBitForNewSlot(2, 1, true);
247       aControllerState.numButtons = 3;
248       aControllerState.numAxes = 2;
249 
250       static Matrix4x4 focusPlusTransform;
251       Matrix4x4 originalMtx;
252       if (focusPlusTransform.IsIdentity()) {
253         focusPlusTransform.RotateX(-0.70f);
254         focusPlusTransform.PostTranslate(0.0f, 0.0f, 0.01f);
255         focusPlusTransform.Inverse();
256       }
257       gfx::Quaternion quat(aControllerState.pose.orientation[0],
258                            aControllerState.pose.orientation[1],
259                            aControllerState.pose.orientation[2],
260                            aControllerState.pose.orientation[3]);
261       // We need to invert its quaternion here because we did an invertion
262       // in FxR WaveVR delegate.
263       originalMtx.SetRotationFromQuaternion(quat.Invert());
264       originalMtx._41 = aControllerState.pose.position[0];
265       originalMtx._42 = aControllerState.pose.position[1];
266       originalMtx._43 = aControllerState.pose.position[2];
267       originalMtx = focusPlusTransform * originalMtx;
268 
269       gfx::Point3D pos, scale;
270       originalMtx.Decompose(pos, quat, scale);
271 
272       quat.Invert();
273       aControllerState.pose.position[0] = pos.x;
274       aControllerState.pose.position[1] = pos.y;
275       aControllerState.pose.position[2] = pos.z;
276 
277       aControllerState.pose.orientation[0] = quat.x;
278       aControllerState.pose.orientation[1] = quat.y;
279       aControllerState.pose.orientation[2] = quat.z;
280       aControllerState.pose.orientation[3] = quat.w;
281       break;
282     }
283     case VRControllerType::OculusGo: {
284       aControllerState.buttonPressed =
285           ShiftButtonBitForNewSlot(0, 2) | ShiftButtonBitForNewSlot(1, 0);
286       aControllerState.buttonTouched = ShiftButtonBitForNewSlot(0, 2, true) |
287                                        ShiftButtonBitForNewSlot(1, 0, true);
288       aControllerState.numButtons = 2;
289       aControllerState.numAxes = 2;
290 
291       static Matrix4x4 goTransform;
292       Matrix4x4 originalMtx;
293 
294       if (goTransform.IsIdentity()) {
295         goTransform.RotateX(-0.60f);
296         goTransform.Inverse();
297       }
298       gfx::Quaternion quat(aControllerState.pose.orientation[0],
299                            aControllerState.pose.orientation[1],
300                            aControllerState.pose.orientation[2],
301                            aControllerState.pose.orientation[3]);
302       // We need to invert its quaternion here because we did an invertion
303       // in FxR OculusVR delegate.
304       originalMtx.SetRotationFromQuaternion(quat.Invert());
305       originalMtx._41 = aControllerState.pose.position[0];
306       originalMtx._42 = aControllerState.pose.position[1];
307       originalMtx._43 = aControllerState.pose.position[2];
308       originalMtx = goTransform * originalMtx;
309 
310       gfx::Point3D pos, scale;
311       originalMtx.Decompose(pos, quat, scale);
312 
313       quat.Invert();
314       aControllerState.pose.position[0] = pos.x;
315       aControllerState.pose.position[1] = pos.y;
316       aControllerState.pose.position[2] = pos.z;
317 
318       aControllerState.pose.orientation[0] = quat.x;
319       aControllerState.pose.orientation[1] = quat.y;
320       aControllerState.pose.orientation[2] = quat.z;
321       aControllerState.pose.orientation[3] = quat.w;
322       break;
323     }
324     case VRControllerType::OculusTouch:
325       aControllerState.buttonPressed =
326           ShiftButtonBitForNewSlot(0, 3) | ShiftButtonBitForNewSlot(1, 0) |
327           ShiftButtonBitForNewSlot(2, 1) | ShiftButtonBitForNewSlot(3, 4) |
328           ShiftButtonBitForNewSlot(4, 5) | ShiftButtonBitForNewSlot(5, 6);
329       aControllerState.buttonTouched = ShiftButtonBitForNewSlot(0, 3, true) |
330                                        ShiftButtonBitForNewSlot(1, 0, true) |
331                                        ShiftButtonBitForNewSlot(2, 1, true) |
332                                        ShiftButtonBitForNewSlot(3, 4, true) |
333                                        ShiftButtonBitForNewSlot(4, 5, true) |
334                                        ShiftButtonBitForNewSlot(5, 6, true);
335       aControllerState.axisValue[0] = aControllerState.axisValue[2];
336       aControllerState.axisValue[1] = aControllerState.axisValue[3];
337       aControllerState.numButtons = 6;
338       aControllerState.numAxes = 2;
339       break;
340     case VRControllerType::OculusTouch2: {
341       aControllerState.buttonPressed =
342           ShiftButtonBitForNewSlot(0, 3) | ShiftButtonBitForNewSlot(1, 0) |
343           ShiftButtonBitForNewSlot(2, 1) | ShiftButtonBitForNewSlot(3, 4) |
344           ShiftButtonBitForNewSlot(4, 5) | ShiftButtonBitForNewSlot(5, 6);
345       aControllerState.buttonTouched = ShiftButtonBitForNewSlot(0, 3, true) |
346                                        ShiftButtonBitForNewSlot(1, 0, true) |
347                                        ShiftButtonBitForNewSlot(2, 1, true) |
348                                        ShiftButtonBitForNewSlot(3, 4, true) |
349                                        ShiftButtonBitForNewSlot(4, 5, true) |
350                                        ShiftButtonBitForNewSlot(5, 6, true);
351       aControllerState.axisValue[0] = aControllerState.axisValue[2];
352       aControllerState.axisValue[1] = aControllerState.axisValue[3];
353       aControllerState.numButtons = 6;
354       aControllerState.numAxes = 2;
355 
356       static Matrix4x4 touch2Transform;
357       Matrix4x4 originalMtx;
358 
359       if (touch2Transform.IsIdentity()) {
360         touch2Transform.RotateX(-0.77f);
361         touch2Transform.PostTranslate(0.0f, 0.0f, -0.025f);
362         touch2Transform.Inverse();
363       }
364       gfx::Quaternion quat(aControllerState.pose.orientation[0],
365                            aControllerState.pose.orientation[1],
366                            aControllerState.pose.orientation[2],
367                            aControllerState.pose.orientation[3]);
368       // We need to invert its quaternion here because we did an invertion
369       // in FxR OculusVR delegate.
370       originalMtx.SetRotationFromQuaternion(quat.Invert());
371       originalMtx._41 = aControllerState.pose.position[0];
372       originalMtx._42 = aControllerState.pose.position[1];
373       originalMtx._43 = aControllerState.pose.position[2];
374       originalMtx = touch2Transform * originalMtx;
375 
376       gfx::Point3D pos, scale;
377       originalMtx.Decompose(pos, quat, scale);
378 
379       quat.Invert();
380       aControllerState.pose.position[0] = pos.x;
381       aControllerState.pose.position[1] = pos.y;
382       aControllerState.pose.position[2] = pos.z;
383 
384       aControllerState.pose.orientation[0] = quat.x;
385       aControllerState.pose.orientation[1] = quat.y;
386       aControllerState.pose.orientation[2] = quat.z;
387       aControllerState.pose.orientation[3] = quat.w;
388       break;
389     }
390     case VRControllerType::ValveIndex:
391       aControllerState.buttonPressed =
392           ShiftButtonBitForNewSlot(1, 0) | ShiftButtonBitForNewSlot(2, 1) |
393           ShiftButtonBitForNewSlot(0, 2) | ShiftButtonBitForNewSlot(5, 3) |
394           ShiftButtonBitForNewSlot(3, 4) | ShiftButtonBitForNewSlot(4, 5) |
395           ShiftButtonBitForNewSlot(6, 6) | ShiftButtonBitForNewSlot(7, 7) |
396           ShiftButtonBitForNewSlot(8, 8) | ShiftButtonBitForNewSlot(9, 9);
397       aControllerState.buttonTouched = ShiftButtonBitForNewSlot(1, 0, true) |
398                                        ShiftButtonBitForNewSlot(2, 1, true) |
399                                        ShiftButtonBitForNewSlot(0, 2, true) |
400                                        ShiftButtonBitForNewSlot(5, 3, true) |
401                                        ShiftButtonBitForNewSlot(3, 4, true) |
402                                        ShiftButtonBitForNewSlot(4, 5, true) |
403                                        ShiftButtonBitForNewSlot(6, 6, true) |
404                                        ShiftButtonBitForNewSlot(7, 7, true) |
405                                        ShiftButtonBitForNewSlot(8, 8, true) |
406                                        ShiftButtonBitForNewSlot(9, 9, true);
407       aControllerState.numButtons = 10;
408       aControllerState.numAxes = 4;
409       break;
410     case VRControllerType::PicoGaze:
411       aControllerState.buttonPressed = ShiftButtonBitForNewSlot(0, 0);
412       aControllerState.buttonTouched = ShiftButtonBitForNewSlot(0, 0, true);
413       aControllerState.numButtons = 1;
414       aControllerState.numAxes = 0;
415       break;
416     case VRControllerType::PicoG2:
417       aControllerState.buttonPressed =
418           ShiftButtonBitForNewSlot(0, 2) | ShiftButtonBitForNewSlot(1, 0);
419       aControllerState.buttonTouched = ShiftButtonBitForNewSlot(0, 2, true) |
420                                        ShiftButtonBitForNewSlot(1, 0, true);
421       aControllerState.numButtons = 2;
422       aControllerState.numAxes = 2;
423       break;
424     case VRControllerType::PicoNeo2:
425       aControllerState.buttonPressed =
426           ShiftButtonBitForNewSlot(0, 3) | ShiftButtonBitForNewSlot(1, 0) |
427           ShiftButtonBitForNewSlot(2, 1) | ShiftButtonBitForNewSlot(3, 4) |
428           ShiftButtonBitForNewSlot(4, 5) | ShiftButtonBitForNewSlot(5, 6);
429       aControllerState.buttonTouched = ShiftButtonBitForNewSlot(0, 3, true) |
430                                        ShiftButtonBitForNewSlot(1, 0, true) |
431                                        ShiftButtonBitForNewSlot(2, 1, true) |
432                                        ShiftButtonBitForNewSlot(3, 4, true) |
433                                        ShiftButtonBitForNewSlot(4, 5, true) |
434                                        ShiftButtonBitForNewSlot(5, 6, true);
435       aControllerState.axisValue[0] = aControllerState.axisValue[2];
436       aControllerState.axisValue[1] = aControllerState.axisValue[3];
437       aControllerState.numButtons = 6;
438       aControllerState.numAxes = 2;
439       break;
440     default:
441       // Undefined controller types, we will keep its the same order.
442       break;
443   }
444 }
445 
FireGamepadEvents()446 void VRDisplayClient::FireGamepadEvents() {
447   RefPtr<dom::GamepadManager> gamepadManager(dom::GamepadManager::GetService());
448   if (!gamepadManager) {
449     return;
450   }
451   for (int stateIndex = 0; stateIndex < kVRControllerMaxCount; stateIndex++) {
452     VRControllerState state = {}, lastState = {};
453     memcpy(&state, &mDisplayInfo.mControllerState[stateIndex],
454            sizeof(VRControllerState));
455     memcpy(&lastState, &mLastEventControllerState[stateIndex],
456            sizeof(VRControllerState));
457     GamepadMappingForWebVR(state);
458     GamepadMappingForWebVR(lastState);
459 
460     uint32_t gamepadId =
461         mDisplayInfo.mDisplayID * kVRControllerMaxCount + stateIndex;
462     bool bIsNew = false;
463 
464     // Send events to notify that controllers are removed
465     if (state.controllerName[0] == '\0') {
466       // Controller is not present
467       if (lastState.controllerName[0] != '\0') {
468         // Controller has been removed
469         dom::GamepadRemoved info;
470         dom::GamepadChangeEventBody body(info);
471         dom::GamepadChangeEvent event(gamepadId, dom::GamepadServiceType::VR,
472                                       body);
473         gamepadManager->Update(event);
474       }
475       // Do not process any further events for removed controllers
476       continue;
477     }
478 
479     // Send events to notify that new controllers are added
480     RefPtr<dom::Gamepad> existing =
481         gamepadManager->GetGamepad(gamepadId, dom::GamepadServiceType::VR);
482     // ControllerState in OpenVR action-based API gets delay to query btn and
483     // axis count. So, we need to check if they are more than zero.
484     if ((lastState.controllerName[0] == '\0' || !existing) &&
485         (state.numButtons > 0 || state.numAxes > 0)) {
486       dom::GamepadAdded info(NS_ConvertUTF8toUTF16(state.controllerName),
487                              dom::GamepadMappingType::_empty, state.hand,
488                              mDisplayInfo.mDisplayID, state.numButtons,
489                              state.numAxes, state.numHaptics, 0, 0);
490       dom::GamepadChangeEventBody body(info);
491       dom::GamepadChangeEvent event(gamepadId, dom::GamepadServiceType::VR,
492                                     body);
493       gamepadManager->Update(event);
494       bIsNew = true;
495     }
496 
497     // Send events for handedness changes
498     if (state.hand != lastState.hand) {
499       dom::GamepadHandInformation info(state.hand);
500       dom::GamepadChangeEventBody body(info);
501       dom::GamepadChangeEvent event(gamepadId, dom::GamepadServiceType::VR,
502                                     body);
503       gamepadManager->Update(event);
504     }
505 
506     // Send events for axis value changes
507     for (uint32_t axisIndex = 0; axisIndex < state.numAxes; axisIndex++) {
508       if (state.axisValue[axisIndex] != lastState.axisValue[axisIndex]) {
509         dom::GamepadAxisInformation info(axisIndex, state.axisValue[axisIndex]);
510         dom::GamepadChangeEventBody body(info);
511         dom::GamepadChangeEvent event(gamepadId, dom::GamepadServiceType::VR,
512                                       body);
513         gamepadManager->Update(event);
514       }
515     }
516 
517     // Send events for trigger, touch, and button value changes
518     if (!bIsNew) {
519       // When a new controller is added, we do not emit button events for
520       // the initial state of the inputs.
521       for (uint32_t buttonIndex = 0; buttonIndex < state.numButtons;
522            buttonIndex++) {
523         bool bPressed = (state.buttonPressed & (1ULL << buttonIndex)) != 0;
524         bool bTouched = (state.buttonTouched & (1ULL << buttonIndex)) != 0;
525         bool bLastPressed =
526             (lastState.buttonPressed & (1ULL << buttonIndex)) != 0;
527         bool bLastTouched =
528             (lastState.buttonTouched & (1ULL << buttonIndex)) != 0;
529 
530         if (state.triggerValue[buttonIndex] !=
531                 lastState.triggerValue[buttonIndex] ||
532             bPressed != bLastPressed || bTouched != bLastTouched) {
533           dom::GamepadButtonInformation info(
534               buttonIndex, state.triggerValue[buttonIndex], bPressed, bTouched);
535           dom::GamepadChangeEventBody body(info);
536           dom::GamepadChangeEvent event(gamepadId, dom::GamepadServiceType::VR,
537                                         body);
538           gamepadManager->Update(event);
539         }
540       }
541     }
542 
543     // Send events for pose changes
544     // Note that VRPose is asserted to be a POD type so memcmp is safe
545     if (state.flags != lastState.flags ||
546         state.isPositionValid != lastState.isPositionValid ||
547         state.isOrientationValid != lastState.isOrientationValid ||
548         memcmp(&state.pose, &lastState.pose, sizeof(VRPose)) != 0) {
549       // Convert pose to GamepadPoseState
550       dom::GamepadPoseState poseState;
551       poseState.Clear();
552       poseState.flags = state.flags;
553 
554       // Orientation values
555       poseState.isOrientationValid = state.isOrientationValid;
556       poseState.orientation[0] = state.pose.orientation[0];
557       poseState.orientation[1] = state.pose.orientation[1];
558       poseState.orientation[2] = state.pose.orientation[2];
559       poseState.orientation[3] = state.pose.orientation[3];
560       poseState.angularVelocity[0] = state.pose.angularVelocity[0];
561       poseState.angularVelocity[1] = state.pose.angularVelocity[1];
562       poseState.angularVelocity[2] = state.pose.angularVelocity[2];
563       poseState.angularAcceleration[0] = state.pose.angularAcceleration[0];
564       poseState.angularAcceleration[1] = state.pose.angularAcceleration[1];
565       poseState.angularAcceleration[2] = state.pose.angularAcceleration[2];
566 
567       // Position values
568       poseState.isPositionValid = state.isPositionValid;
569       poseState.position[0] = state.pose.position[0];
570       poseState.position[1] = state.pose.position[1];
571       poseState.position[2] = state.pose.position[2];
572       poseState.linearVelocity[0] = state.pose.linearVelocity[0];
573       poseState.linearVelocity[1] = state.pose.linearVelocity[1];
574       poseState.linearVelocity[2] = state.pose.linearVelocity[2];
575       poseState.linearAcceleration[0] = state.pose.linearAcceleration[0];
576       poseState.linearAcceleration[1] = state.pose.linearAcceleration[1];
577       poseState.linearAcceleration[2] = state.pose.linearAcceleration[2];
578 
579       // Send the event
580       dom::GamepadPoseInformation info(poseState);
581       dom::GamepadChangeEventBody body(info);
582       dom::GamepadChangeEvent event(gamepadId, dom::GamepadServiceType::VR,
583                                     body);
584       gamepadManager->Update(event);
585     }
586   }
587 
588   // Note that VRControllerState is asserted to be a POD type and memcpy is
589   // safe.
590   memcpy(mLastEventControllerState, mDisplayInfo.mControllerState,
591          sizeof(VRControllerState) * kVRControllerMaxCount);
592 }
593 
GetSensorState() const594 const VRHMDSensorState& VRDisplayClient::GetSensorState() const {
595   return mDisplayInfo.GetSensorState();
596 }
597 
GetIsConnected() const598 bool VRDisplayClient::GetIsConnected() const {
599   return mDisplayInfo.GetIsConnected();
600 }
601 
IsPresenting()602 bool VRDisplayClient::IsPresenting() {
603   return mDisplayInfo.mPresentingGroups != 0;
604 }
605 
NotifyDisconnected()606 void VRDisplayClient::NotifyDisconnected() {
607   mDisplayInfo.mDisplayState.isConnected = false;
608 }
609 
UpdateSubmitFrameResult(const VRSubmitFrameResultInfo & aResult)610 void VRDisplayClient::UpdateSubmitFrameResult(
611     const VRSubmitFrameResultInfo& aResult) {
612   mSubmitFrameResult = aResult;
613 }
614 
GetSubmitFrameResult(VRSubmitFrameResultInfo & aResult)615 void VRDisplayClient::GetSubmitFrameResult(VRSubmitFrameResultInfo& aResult) {
616   aResult = mSubmitFrameResult;
617 }
618 
StartVRNavigation()619 void VRDisplayClient::StartVRNavigation() {
620   /**
621    * A VR-to-VR site navigation has started, notify VRManager
622    * so we don't drop out of VR during the transition
623    */
624   VRManagerChild* vm = VRManagerChild::Get();
625   vm->SendStartVRNavigation(mDisplayInfo.mDisplayID);
626 }
627 
StopVRNavigation(const TimeDuration & aTimeout)628 void VRDisplayClient::StopVRNavigation(const TimeDuration& aTimeout) {
629   /**
630    * A VR-to-VR site navigation has ended and the new site
631    * has received a vrdisplayactivate event.
632    * Don't actually consider the navigation transition over
633    * until aTimeout has elapsed.
634    * This may be called multiple times, in which case the timeout
635    * should be reset to aTimeout.
636    * When aTimeout is TimeDuration(0), we should consider the
637    * transition immediately ended.
638    */
639   VRManagerChild* vm = VRManagerChild::Get();
640   vm->SendStopVRNavigation(mDisplayInfo.mDisplayID, aTimeout);
641 }
642 
IsReferenceSpaceTypeSupported(dom::XRReferenceSpaceType aType) const643 bool VRDisplayClient::IsReferenceSpaceTypeSupported(
644     dom::XRReferenceSpaceType aType) const {
645   /**
646    * https://immersive-web.github.io/webxr/#reference-space-is-supported
647    *
648    * We do not yet support local or local-floor for inline sessions.
649    * This could be expanded if we later support WebXR for inline-ar
650    * sessions on Firefox Fenix.
651    *
652    * We do not yet support unbounded reference spaces.
653    */
654   switch (aType) {
655     case dom::XRReferenceSpaceType::Viewer:
656       // Viewer is always supported, for both inline and immersive sessions
657       return true;
658     case dom::XRReferenceSpaceType::Local:
659     case dom::XRReferenceSpaceType::Local_floor:
660       // Local and Local_Floor are always supported for immersive sessions
661       return bool(mDisplayInfo.GetCapabilities() &
662                   (VRDisplayCapabilityFlags::Cap_ImmersiveVR |
663                    VRDisplayCapabilityFlags::Cap_ImmersiveAR));
664     case dom::XRReferenceSpaceType::Bounded_floor:
665       return bool(mDisplayInfo.GetCapabilities() &
666                   VRDisplayCapabilityFlags::Cap_StageParameters);
667     default:
668       NS_WARNING(
669           "Unknown XRReferenceSpaceType passed to "
670           "VRDisplayClient::IsReferenceSpaceTypeSupported");
671       return false;
672   }
673 }
674