Line data Source code
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/. */
6 :
7 : #include "mozilla/dom/GamepadManager.h"
8 :
9 : #include "mozilla/dom/Gamepad.h"
10 : #include "mozilla/dom/GamepadAxisMoveEvent.h"
11 : #include "mozilla/dom/GamepadButtonEvent.h"
12 : #include "mozilla/dom/GamepadEvent.h"
13 : #include "mozilla/dom/GamepadEventChannelChild.h"
14 : #include "mozilla/dom/GamepadMonitoring.h"
15 :
16 : #include "mozilla/ipc/BackgroundChild.h"
17 : #include "mozilla/ipc/PBackgroundChild.h"
18 : #include "mozilla/ClearOnShutdown.h"
19 : #include "mozilla/Preferences.h"
20 : #include "mozilla/StaticPtr.h"
21 :
22 : #include "nsAutoPtr.h"
23 : #include "nsContentUtils.h"
24 : #include "nsGlobalWindow.h"
25 : #include "nsIDOMEvent.h"
26 : #include "nsIDOMDocument.h"
27 : #include "nsIDOMWindow.h"
28 : #include "nsIObserver.h"
29 : #include "nsIObserverService.h"
30 : #include "nsIServiceManager.h"
31 : #include "nsThreadUtils.h"
32 : #include "VRManagerChild.h"
33 : #include "mozilla/Services.h"
34 : #include "mozilla/Unused.h"
35 :
36 : #include <cstddef>
37 :
38 : using namespace mozilla::ipc;
39 :
40 : namespace mozilla {
41 : namespace dom {
42 :
43 : namespace {
44 :
45 : const char* kGamepadEnabledPref = "dom.gamepad.enabled";
46 : const char* kGamepadEventsEnabledPref =
47 : "dom.gamepad.non_standard_events.enabled";
48 :
49 : const nsTArray<RefPtr<nsGlobalWindow>>::index_type NoIndex =
50 : nsTArray<RefPtr<nsGlobalWindow>>::NoIndex;
51 :
52 : bool sShutdown = false;
53 :
54 3 : StaticRefPtr<GamepadManager> gGamepadManagerSingleton;
55 : const uint32_t VR_GAMEPAD_IDX_OFFSET = 0x01 << 16;
56 :
57 : } // namespace
58 :
59 0 : NS_IMPL_ISUPPORTS(GamepadManager, nsIObserver)
60 :
61 0 : GamepadManager::GamepadManager()
62 : : mEnabled(false),
63 : mNonstandardEventsEnabled(false),
64 : mShuttingDown(false),
65 0 : mPromiseID(0)
66 0 : {}
67 :
68 : nsresult
69 0 : GamepadManager::Init()
70 : {
71 0 : mEnabled = IsAPIEnabled();
72 0 : mNonstandardEventsEnabled =
73 0 : Preferences::GetBool(kGamepadEventsEnabledPref, false);
74 : nsCOMPtr<nsIObserverService> observerService =
75 0 : mozilla::services::GetObserverService();
76 :
77 0 : if (NS_WARN_IF(!observerService)) {
78 0 : return NS_ERROR_FAILURE;
79 : }
80 :
81 : nsresult rv;
82 0 : rv = observerService->AddObserver(this,
83 : NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID,
84 0 : false);
85 :
86 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
87 0 : return rv;
88 : }
89 :
90 0 : return NS_OK;
91 : }
92 :
93 : NS_IMETHODIMP
94 0 : GamepadManager::Observe(nsISupports* aSubject,
95 : const char* aTopic,
96 : const char16_t* aData)
97 : {
98 : nsCOMPtr<nsIObserverService> observerService =
99 0 : mozilla::services::GetObserverService();
100 0 : if (observerService) {
101 0 : observerService->RemoveObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID);
102 : }
103 0 : BeginShutdown();
104 0 : return NS_OK;
105 : }
106 :
107 : void
108 0 : GamepadManager::StopMonitoring()
109 : {
110 0 : for (uint32_t i = 0; i < mChannelChildren.Length(); ++i) {
111 0 : mChannelChildren[i]->SendGamepadListenerRemoved();
112 : }
113 0 : mChannelChildren.Clear();
114 0 : mGamepads.Clear();
115 :
116 0 : if (gfx::VRManagerChild::IsCreated()) {
117 0 : gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
118 0 : vm->SendControllerListenerRemoved();
119 : }
120 0 : }
121 :
122 : void
123 0 : GamepadManager::BeginShutdown()
124 : {
125 0 : mShuttingDown = true;
126 0 : StopMonitoring();
127 : // Don't let windows call back to unregister during shutdown
128 0 : for (uint32_t i = 0; i < mListeners.Length(); i++) {
129 0 : mListeners[i]->SetHasGamepadEventListener(false);
130 : }
131 0 : mListeners.Clear();
132 0 : sShutdown = true;
133 0 : }
134 :
135 : void
136 0 : GamepadManager::AddListener(nsGlobalWindow* aWindow)
137 : {
138 0 : MOZ_ASSERT(aWindow);
139 0 : MOZ_ASSERT(aWindow->IsInnerWindow());
140 0 : MOZ_ASSERT(NS_IsMainThread());
141 :
142 : // IPDL child has not been created
143 0 : if (mChannelChildren.IsEmpty()) {
144 0 : PBackgroundChild *actor = BackgroundChild::GetForCurrentThread();
145 : //Try to get the PBackground Child actor
146 0 : if (actor) {
147 0 : ActorCreated(actor);
148 : } else {
149 0 : Unused << BackgroundChild::GetOrCreateForCurrentThread(this);
150 : }
151 : }
152 :
153 0 : if (!mEnabled || mShuttingDown || nsContentUtils::ShouldResistFingerprinting()) {
154 0 : return;
155 : }
156 :
157 0 : if (mListeners.IndexOf(aWindow) != NoIndex) {
158 0 : return; // already exists
159 : }
160 :
161 0 : mListeners.AppendElement(aWindow);
162 : }
163 :
164 : void
165 0 : GamepadManager::RemoveListener(nsGlobalWindow* aWindow)
166 : {
167 0 : MOZ_ASSERT(aWindow);
168 0 : MOZ_ASSERT(aWindow->IsInnerWindow());
169 :
170 0 : if (mShuttingDown) {
171 : // Doesn't matter at this point. It's possible we're being called
172 : // as a result of our own destructor here, so just bail out.
173 0 : return;
174 : }
175 :
176 0 : if (mListeners.IndexOf(aWindow) == NoIndex) {
177 0 : return; // doesn't exist
178 : }
179 :
180 0 : for (auto iter = mGamepads.Iter(); !iter.Done(); iter.Next()) {
181 0 : aWindow->RemoveGamepad(iter.Key());
182 : }
183 :
184 0 : mListeners.RemoveElement(aWindow);
185 :
186 0 : if (mListeners.IsEmpty()) {
187 0 : StopMonitoring();
188 : }
189 : }
190 :
191 : already_AddRefed<Gamepad>
192 0 : GamepadManager::GetGamepad(uint32_t aIndex) const
193 : {
194 0 : RefPtr<Gamepad> gamepad;
195 0 : if (mGamepads.Get(aIndex, getter_AddRefs(gamepad))) {
196 0 : return gamepad.forget();
197 : }
198 :
199 0 : return nullptr;
200 : }
201 :
202 0 : uint32_t GamepadManager::GetGamepadIndexWithServiceType(uint32_t aIndex,
203 : GamepadServiceType aServiceType)
204 : {
205 0 : uint32_t newIndex = 0;
206 :
207 0 : switch (aServiceType) {
208 : case GamepadServiceType::Standard:
209 0 : MOZ_ASSERT(aIndex <= VR_GAMEPAD_IDX_OFFSET);
210 0 : newIndex = aIndex;
211 0 : break;
212 : case GamepadServiceType::VR:
213 0 : newIndex = aIndex + VR_GAMEPAD_IDX_OFFSET;
214 0 : break;
215 : default:
216 0 : MOZ_ASSERT(false);
217 : break;
218 : }
219 :
220 0 : return newIndex;
221 : }
222 :
223 : void
224 0 : GamepadManager::AddGamepad(uint32_t aIndex,
225 : const nsAString& aId,
226 : GamepadMappingType aMapping,
227 : GamepadHand aHand,
228 : GamepadServiceType aServiceType,
229 : uint32_t aNumButtons,
230 : uint32_t aNumAxes,
231 : uint32_t aNumHaptics)
232 : {
233 0 : uint32_t newIndex = GetGamepadIndexWithServiceType(aIndex, aServiceType);
234 :
235 : //TODO: bug 852258: get initial button/axis state
236 : RefPtr<Gamepad> gamepad =
237 : new Gamepad(nullptr,
238 : aId,
239 : 0, // index is set by global window
240 : newIndex,
241 : aMapping,
242 : aHand,
243 : aNumButtons,
244 : aNumAxes,
245 0 : aNumHaptics);
246 :
247 : // We store the gamepad related to its index given by the parent process,
248 : // and no duplicate index is allowed.
249 0 : MOZ_ASSERT(!mGamepads.Get(newIndex, nullptr));
250 0 : mGamepads.Put(newIndex, gamepad);
251 0 : NewConnectionEvent(newIndex, true);
252 0 : }
253 :
254 : void
255 0 : GamepadManager::RemoveGamepad(uint32_t aIndex, GamepadServiceType aServiceType)
256 : {
257 0 : uint32_t newIndex = GetGamepadIndexWithServiceType(aIndex, aServiceType);
258 :
259 0 : RefPtr<Gamepad> gamepad = GetGamepad(newIndex);
260 0 : if (!gamepad) {
261 0 : NS_WARNING("Trying to delete gamepad with invalid index");
262 0 : return;
263 : }
264 0 : gamepad->SetConnected(false);
265 0 : NewConnectionEvent(newIndex, false);
266 0 : mGamepads.Remove(newIndex);
267 : }
268 :
269 : void
270 0 : GamepadManager::FireButtonEvent(EventTarget* aTarget,
271 : Gamepad* aGamepad,
272 : uint32_t aButton,
273 : double aValue)
274 : {
275 0 : nsString name = aValue == 1.0L ? NS_LITERAL_STRING("gamepadbuttondown") :
276 0 : NS_LITERAL_STRING("gamepadbuttonup");
277 0 : GamepadButtonEventInit init;
278 0 : init.mBubbles = false;
279 0 : init.mCancelable = false;
280 0 : init.mGamepad = aGamepad;
281 0 : init.mButton = aButton;
282 : RefPtr<GamepadButtonEvent> event =
283 0 : GamepadButtonEvent::Constructor(aTarget, name, init);
284 :
285 0 : event->SetTrusted(true);
286 :
287 0 : bool defaultActionEnabled = true;
288 0 : aTarget->DispatchEvent(event, &defaultActionEnabled);
289 0 : }
290 :
291 : void
292 0 : GamepadManager::FireAxisMoveEvent(EventTarget* aTarget,
293 : Gamepad* aGamepad,
294 : uint32_t aAxis,
295 : double aValue)
296 : {
297 0 : GamepadAxisMoveEventInit init;
298 0 : init.mBubbles = false;
299 0 : init.mCancelable = false;
300 0 : init.mGamepad = aGamepad;
301 0 : init.mAxis = aAxis;
302 0 : init.mValue = aValue;
303 : RefPtr<GamepadAxisMoveEvent> event =
304 0 : GamepadAxisMoveEvent::Constructor(aTarget,
305 0 : NS_LITERAL_STRING("gamepadaxismove"),
306 0 : init);
307 :
308 0 : event->SetTrusted(true);
309 :
310 0 : bool defaultActionEnabled = true;
311 0 : aTarget->DispatchEvent(event, &defaultActionEnabled);
312 0 : }
313 :
314 : void
315 0 : GamepadManager::NewConnectionEvent(uint32_t aIndex, bool aConnected)
316 : {
317 : // Do not fire gamepadconnected and gamepaddisconnected events when
318 : // privacy.resistFingerprinting is true.
319 0 : if (nsContentUtils::ShouldResistFingerprinting()) {
320 0 : return;
321 : }
322 :
323 0 : if (mShuttingDown) {
324 0 : return;
325 : }
326 :
327 0 : RefPtr<Gamepad> gamepad = GetGamepad(aIndex);
328 0 : if (!gamepad) {
329 0 : return;
330 : }
331 :
332 : // Hold on to listeners in a separate array because firing events
333 : // can mutate the mListeners array.
334 0 : nsTArray<RefPtr<nsGlobalWindow>> listeners(mListeners);
335 0 : MOZ_ASSERT(!listeners.IsEmpty());
336 :
337 0 : if (aConnected) {
338 0 : for (uint32_t i = 0; i < listeners.Length(); i++) {
339 :
340 0 : MOZ_ASSERT(listeners[i]->IsInnerWindow());
341 :
342 : // Only send events to non-background windows
343 0 : if (!listeners[i]->AsInner()->IsCurrentInnerWindow() ||
344 0 : listeners[i]->GetOuterWindow()->IsBackground()) {
345 0 : continue;
346 : }
347 :
348 : // We don't fire a connected event here unless the window
349 : // has seen input from at least one device.
350 0 : if (!listeners[i]->HasSeenGamepadInput()) {
351 0 : continue;
352 : }
353 :
354 0 : SetWindowHasSeenGamepad(listeners[i], aIndex);
355 :
356 0 : RefPtr<Gamepad> listenerGamepad = listeners[i]->GetGamepad(aIndex);
357 0 : if (listenerGamepad) {
358 : // Fire event
359 0 : FireConnectionEvent(listeners[i], listenerGamepad, aConnected);
360 : }
361 : }
362 : } else {
363 : // For disconnection events, fire one at every window that has received
364 : // data from this gamepad.
365 0 : for (uint32_t i = 0; i < listeners.Length(); i++) {
366 :
367 : // Even background windows get these events, so we don't have to
368 : // deal with the hassle of syncing the state of removed gamepads.
369 :
370 0 : if (WindowHasSeenGamepad(listeners[i], aIndex)) {
371 0 : RefPtr<Gamepad> listenerGamepad = listeners[i]->GetGamepad(aIndex);
372 0 : if (listenerGamepad) {
373 0 : listenerGamepad->SetConnected(false);
374 : // Fire event
375 0 : FireConnectionEvent(listeners[i], listenerGamepad, false);
376 0 : listeners[i]->RemoveGamepad(aIndex);
377 : }
378 : }
379 : }
380 : }
381 : }
382 :
383 : void
384 0 : GamepadManager::FireConnectionEvent(EventTarget* aTarget,
385 : Gamepad* aGamepad,
386 : bool aConnected)
387 : {
388 0 : nsString name = aConnected ? NS_LITERAL_STRING("gamepadconnected") :
389 0 : NS_LITERAL_STRING("gamepaddisconnected");
390 0 : GamepadEventInit init;
391 0 : init.mBubbles = false;
392 0 : init.mCancelable = false;
393 0 : init.mGamepad = aGamepad;
394 : RefPtr<GamepadEvent> event =
395 0 : GamepadEvent::Constructor(aTarget, name, init);
396 :
397 0 : event->SetTrusted(true);
398 :
399 0 : bool defaultActionEnabled = true;
400 0 : aTarget->DispatchEvent(event, &defaultActionEnabled);
401 0 : }
402 :
403 : void
404 0 : GamepadManager::SyncGamepadState(uint32_t aIndex, Gamepad* aGamepad)
405 : {
406 0 : if (mShuttingDown || !mEnabled || nsContentUtils::ShouldResistFingerprinting()) {
407 0 : return;
408 : }
409 :
410 0 : RefPtr<Gamepad> gamepad = GetGamepad(aIndex);
411 0 : if (!gamepad) {
412 0 : return;
413 : }
414 :
415 0 : aGamepad->SyncState(gamepad);
416 : }
417 :
418 : // static
419 : bool
420 0 : GamepadManager::IsServiceRunning()
421 : {
422 0 : return !!gGamepadManagerSingleton;
423 : }
424 :
425 : // static
426 : already_AddRefed<GamepadManager>
427 0 : GamepadManager::GetService()
428 : {
429 0 : if (sShutdown) {
430 0 : return nullptr;
431 : }
432 :
433 0 : if (!gGamepadManagerSingleton) {
434 0 : RefPtr<GamepadManager> manager = new GamepadManager();
435 0 : nsresult rv = manager->Init();
436 0 : if(NS_WARN_IF(NS_FAILED(rv))) {
437 0 : return nullptr;
438 : }
439 0 : gGamepadManagerSingleton = manager;
440 0 : ClearOnShutdown(&gGamepadManagerSingleton);
441 : }
442 :
443 0 : RefPtr<GamepadManager> service(gGamepadManagerSingleton);
444 0 : return service.forget();
445 : }
446 :
447 : // static
448 : bool
449 0 : GamepadManager::IsAPIEnabled() {
450 0 : return Preferences::GetBool(kGamepadEnabledPref, false);
451 : }
452 :
453 : bool
454 0 : GamepadManager::MaybeWindowHasSeenGamepad(nsGlobalWindow* aWindow, uint32_t aIndex)
455 : {
456 0 : if (!WindowHasSeenGamepad(aWindow, aIndex)) {
457 : // This window hasn't seen this gamepad before, so
458 : // send a connection event first.
459 0 : SetWindowHasSeenGamepad(aWindow, aIndex);
460 0 : return true;
461 : }
462 0 : return false;
463 : }
464 :
465 : bool
466 0 : GamepadManager::WindowHasSeenGamepad(nsGlobalWindow* aWindow, uint32_t aIndex) const
467 : {
468 0 : RefPtr<Gamepad> gamepad = aWindow->GetGamepad(aIndex);
469 0 : return gamepad != nullptr;
470 : }
471 :
472 : void
473 0 : GamepadManager::SetWindowHasSeenGamepad(nsGlobalWindow* aWindow,
474 : uint32_t aIndex,
475 : bool aHasSeen)
476 : {
477 0 : MOZ_ASSERT(aWindow);
478 0 : MOZ_ASSERT(aWindow->IsInnerWindow());
479 :
480 0 : if (mListeners.IndexOf(aWindow) == NoIndex) {
481 : // This window isn't even listening for gamepad events.
482 0 : return;
483 : }
484 :
485 0 : if (aHasSeen) {
486 0 : aWindow->SetHasSeenGamepadInput(true);
487 0 : nsCOMPtr<nsISupports> window = ToSupports(aWindow);
488 0 : RefPtr<Gamepad> gamepad = GetGamepad(aIndex);
489 0 : if (!gamepad) {
490 0 : return;
491 : }
492 0 : RefPtr<Gamepad> clonedGamepad = gamepad->Clone(window);
493 0 : aWindow->AddGamepad(aIndex, clonedGamepad);
494 : } else {
495 0 : aWindow->RemoveGamepad(aIndex);
496 : }
497 : }
498 :
499 : void
500 0 : GamepadManager::Update(const GamepadChangeEvent& aEvent)
501 : {
502 0 : if (!mEnabled || mShuttingDown || nsContentUtils::ShouldResistFingerprinting()) {
503 0 : return;
504 : }
505 :
506 0 : if (aEvent.type() == GamepadChangeEvent::TGamepadAdded) {
507 0 : const GamepadAdded& a = aEvent.get_GamepadAdded();
508 0 : AddGamepad(a.index(), a.id(),
509 0 : static_cast<GamepadMappingType>(a.mapping()),
510 0 : static_cast<GamepadHand>(a.hand()),
511 0 : a.service_type(),
512 0 : a.num_buttons(), a.num_axes(),
513 0 : a.num_haptics());
514 0 : return;
515 : }
516 0 : if (aEvent.type() == GamepadChangeEvent::TGamepadRemoved) {
517 0 : const GamepadRemoved& a = aEvent.get_GamepadRemoved();
518 0 : RemoveGamepad(a.index(), a.service_type());
519 0 : return;
520 : }
521 :
522 0 : if (!SetGamepadByEvent(aEvent)) {
523 0 : return;
524 : }
525 :
526 : // Hold on to listeners in a separate array because firing events
527 : // can mutate the mListeners array.
528 0 : nsTArray<RefPtr<nsGlobalWindow>> listeners(mListeners);
529 0 : MOZ_ASSERT(!listeners.IsEmpty());
530 :
531 0 : for (uint32_t i = 0; i < listeners.Length(); i++) {
532 0 : MOZ_ASSERT(listeners[i]->IsInnerWindow());
533 :
534 : // Only send events to non-background windows
535 0 : if (!listeners[i]->AsInner()->IsCurrentInnerWindow() ||
536 0 : listeners[i]->GetOuterWindow()->IsBackground()) {
537 0 : continue;
538 : }
539 :
540 0 : SetGamepadByEvent(aEvent, listeners[i]);
541 0 : MaybeConvertToNonstandardGamepadEvent(aEvent, listeners[i]);
542 : }
543 : }
544 :
545 : void
546 0 : GamepadManager::MaybeConvertToNonstandardGamepadEvent(const GamepadChangeEvent& aEvent,
547 : nsGlobalWindow* aWindow)
548 : {
549 0 : MOZ_ASSERT(aWindow);
550 :
551 0 : if (!mNonstandardEventsEnabled) {
552 0 : return;
553 : }
554 :
555 0 : RefPtr<Gamepad> gamepad;
556 :
557 0 : switch (aEvent.type()) {
558 : case GamepadChangeEvent::TGamepadButtonInformation:
559 : {
560 0 : const GamepadButtonInformation& a = aEvent.get_GamepadButtonInformation();
561 0 : gamepad = aWindow->GetGamepad(a.index());
562 0 : if (gamepad) {
563 0 : FireButtonEvent(aWindow, gamepad, a.button(), a.value());
564 : }
565 : }
566 0 : break;
567 : case GamepadChangeEvent::TGamepadAxisInformation:
568 : {
569 0 : const GamepadAxisInformation& a = aEvent.get_GamepadAxisInformation();
570 0 : gamepad = aWindow->GetGamepad(a.index());
571 0 : if (gamepad) {
572 0 : FireAxisMoveEvent(aWindow, gamepad, a.axis(), a.value());
573 : }
574 : }
575 0 : break;
576 : default:
577 0 : break;
578 : }
579 : }
580 :
581 : bool
582 0 : GamepadManager::SetGamepadByEvent(const GamepadChangeEvent& aEvent, nsGlobalWindow *aWindow)
583 : {
584 : uint32_t index;
585 0 : RefPtr<Gamepad> gamepad;
586 0 : bool ret = false;
587 0 : bool firstTime = false;
588 :
589 0 : switch (aEvent.type()) {
590 : case GamepadChangeEvent::TGamepadButtonInformation:
591 : {
592 0 : const GamepadButtonInformation& a = aEvent.get_GamepadButtonInformation();
593 0 : index = GetGamepadIndexWithServiceType(a.index(), a.service_type());
594 0 : if (aWindow) {
595 0 : firstTime = MaybeWindowHasSeenGamepad(aWindow, index);
596 : }
597 0 : gamepad = aWindow ? aWindow->GetGamepad(index) : GetGamepad(index);
598 0 : if (gamepad) {
599 0 : gamepad->SetButton(a.button(), a.pressed(), a.touched(), a.value());
600 0 : ret = true;
601 : }
602 0 : } break;
603 : case GamepadChangeEvent::TGamepadAxisInformation:
604 : {
605 0 : const GamepadAxisInformation& a = aEvent.get_GamepadAxisInformation();
606 0 : index = GetGamepadIndexWithServiceType(a.index(), a.service_type());
607 0 : if (aWindow) {
608 0 : firstTime = MaybeWindowHasSeenGamepad(aWindow, index);
609 : }
610 0 : gamepad = aWindow ? aWindow->GetGamepad(index) : GetGamepad(index);
611 0 : if (gamepad) {
612 0 : gamepad->SetAxis(a.axis(), a.value());
613 0 : ret = true;
614 : }
615 0 : } break;
616 : case GamepadChangeEvent::TGamepadPoseInformation:
617 : {
618 0 : const GamepadPoseInformation& a = aEvent.get_GamepadPoseInformation();
619 0 : index = GetGamepadIndexWithServiceType(a.index(), a.service_type());
620 0 : if (aWindow) {
621 0 : firstTime = MaybeWindowHasSeenGamepad(aWindow, index);
622 : }
623 0 : gamepad = aWindow ? aWindow->GetGamepad(index) : GetGamepad(index);
624 0 : if (gamepad) {
625 0 : gamepad->SetPose(a.pose_state());
626 0 : ret = true;
627 : }
628 0 : } break;
629 : case GamepadChangeEvent::TGamepadHandInformation:
630 : {
631 0 : const GamepadHandInformation& a = aEvent.get_GamepadHandInformation();
632 0 : index = GetGamepadIndexWithServiceType(a.index(), a.service_type());
633 0 : if (aWindow) {
634 0 : firstTime = MaybeWindowHasSeenGamepad(aWindow, index);
635 : }
636 0 : gamepad = aWindow ? aWindow->GetGamepad(index) : GetGamepad(index);
637 0 : if (gamepad) {
638 0 : gamepad->SetHand(a.hand());
639 0 : ret = true;
640 : }
641 0 : } break;
642 : default:
643 0 : MOZ_ASSERT(false);
644 : break;
645 : }
646 :
647 0 : if (aWindow && firstTime) {
648 0 : FireConnectionEvent(aWindow, gamepad, true);
649 : }
650 :
651 0 : return ret;
652 : }
653 :
654 : already_AddRefed<Promise>
655 0 : GamepadManager::VibrateHaptic(uint32_t aControllerIdx, uint32_t aHapticIndex,
656 : double aIntensity, double aDuration,
657 : nsIGlobalObject* aGlobal, ErrorResult& aRv)
658 : {
659 0 : RefPtr<Promise> promise = Promise::Create(aGlobal, aRv);
660 0 : if (NS_WARN_IF(aRv.Failed())) {
661 0 : aRv.Throw(NS_ERROR_FAILURE);
662 0 : return nullptr;
663 : }
664 :
665 0 : if (aControllerIdx >= VR_GAMEPAD_IDX_OFFSET) {
666 0 : if (gfx::VRManagerChild::IsCreated()) {
667 0 : const uint32_t index = aControllerIdx - VR_GAMEPAD_IDX_OFFSET;
668 0 : gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
669 0 : vm->AddPromise(mPromiseID, promise);
670 0 : vm->SendVibrateHaptic(index, aHapticIndex,
671 : aIntensity, aDuration,
672 0 : mPromiseID);
673 : }
674 : } else {
675 0 : for (const auto& channelChild: mChannelChildren) {
676 0 : channelChild->AddPromise(mPromiseID, promise);
677 0 : channelChild->SendVibrateHaptic(aControllerIdx, aHapticIndex,
678 : aIntensity, aDuration,
679 0 : mPromiseID);
680 : }
681 : }
682 :
683 0 : ++mPromiseID;
684 0 : return promise.forget();
685 : }
686 :
687 : void
688 0 : GamepadManager::StopHaptics()
689 : {
690 0 : for (auto iter = mGamepads.Iter(); !iter.Done(); iter.Next()) {
691 0 : const uint32_t gamepadIndex = iter.UserData()->HashKey();
692 0 : if (gamepadIndex >= VR_GAMEPAD_IDX_OFFSET) {
693 0 : if (gfx::VRManagerChild::IsCreated()) {
694 0 : const uint32_t index = gamepadIndex - VR_GAMEPAD_IDX_OFFSET;
695 0 : gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
696 0 : vm->SendStopVibrateHaptic(index);
697 : }
698 : } else {
699 0 : for (auto& channelChild : mChannelChildren) {
700 0 : channelChild->SendStopVibrateHaptic(gamepadIndex);
701 : }
702 : }
703 : }
704 0 : }
705 :
706 : //Override nsIIPCBackgroundChildCreateCallback
707 : void
708 0 : GamepadManager::ActorCreated(PBackgroundChild *aActor)
709 : {
710 0 : MOZ_ASSERT(aActor);
711 0 : GamepadEventChannelChild *child = new GamepadEventChannelChild();
712 : PGamepadEventChannelChild *initedChild =
713 0 : aActor->SendPGamepadEventChannelConstructor(child);
714 0 : if (NS_WARN_IF(!initedChild)) {
715 0 : ActorFailed();
716 0 : return;
717 : }
718 0 : MOZ_ASSERT(initedChild == child);
719 0 : child->SendGamepadListenerAdded();
720 0 : mChannelChildren.AppendElement(child);
721 :
722 0 : if (gfx::VRManagerChild::IsCreated()) {
723 : // Construct VRManagerChannel and ask adding the connected
724 : // VR controllers to GamepadManager
725 0 : gfx::VRManagerChild* vm = gfx::VRManagerChild::Get();
726 0 : vm->SendControllerListenerAdded();
727 : }
728 : }
729 :
730 : //Override nsIIPCBackgroundChildCreateCallback
731 : void
732 0 : GamepadManager::ActorFailed()
733 : {
734 0 : MOZ_CRASH("Gamepad IPC actor create failed!");
735 : }
736 :
737 : } // namespace dom
738 : } // namespace mozilla
|