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