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