diff options
Diffstat (limited to 'Source/WebCore/Modules/gamepad/GamepadManager.cpp')
-rw-r--r-- | Source/WebCore/Modules/gamepad/GamepadManager.cpp | 245 |
1 files changed, 245 insertions, 0 deletions
diff --git a/Source/WebCore/Modules/gamepad/GamepadManager.cpp b/Source/WebCore/Modules/gamepad/GamepadManager.cpp new file mode 100644 index 000000000..b2ecba196 --- /dev/null +++ b/Source/WebCore/Modules/gamepad/GamepadManager.cpp @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "config.h" +#include "GamepadManager.h" + +#if ENABLE(GAMEPAD) + +#include "DOMWindow.h" +#include "Document.h" +#include "EventNames.h" +#include "Gamepad.h" +#include "GamepadEvent.h" +#include "GamepadProvider.h" +#include "Logging.h" +#include "NavigatorGamepad.h" +#include "PlatformGamepad.h" + +namespace WebCore { + +static NavigatorGamepad* navigatorGamepadFromDOMWindow(DOMWindow* window) +{ + Navigator* navigator = window->navigator(); + if (!navigator) + return nullptr; + + return NavigatorGamepad::from(navigator); +} + +GamepadManager& GamepadManager::singleton() +{ + static NeverDestroyed<GamepadManager> sharedManager; + return sharedManager; +} + +GamepadManager::GamepadManager() + : m_isMonitoringGamepads(false) +{ +} + +void GamepadManager::platformGamepadConnected(PlatformGamepad& platformGamepad) +{ + // Notify blind Navigators and Windows about all gamepads except for this one. + for (auto* gamepad : GamepadProvider::singleton().platformGamepads()) { + if (!gamepad || gamepad == &platformGamepad) + continue; + + makeGamepadVisible(*gamepad, m_gamepadBlindNavigators, m_gamepadBlindDOMWindows); + } + + m_gamepadBlindNavigators.clear(); + m_gamepadBlindDOMWindows.clear(); + + // Notify everyone of this new gamepad. + makeGamepadVisible(platformGamepad, m_navigators, m_domWindows); +} + +void GamepadManager::platformGamepadDisconnected(PlatformGamepad& platformGamepad) +{ + Vector<WeakPtr<DOMWindow>> weakWindows; + for (auto* domWindow : m_domWindows) + weakWindows.append(domWindow->createWeakPtr()); + + HashSet<NavigatorGamepad*> notifiedNavigators; + + // Handle the disconnect for all DOMWindows with event listeners and their Navigators. + for (auto& window : weakWindows) { + // Event dispatch might have made this window go away. + if (!window) + continue; + + // This DOMWindow's Navigator might not be accessible. e.g. The DOMWindow might be in the back/forward cache. + // If this happens the DOMWindow will not get this gamepaddisconnected event. + NavigatorGamepad* navigator = navigatorGamepadFromDOMWindow(window.get()); + if (!navigator) + continue; + + // If this Navigator hasn't seen gamepads yet then its Window should not get the disconnect event. + if (m_gamepadBlindNavigators.contains(navigator)) + continue; + + Ref<Gamepad> gamepad(navigator->gamepadFromPlatformGamepad(platformGamepad)); + + navigator->gamepadDisconnected(platformGamepad); + notifiedNavigators.add(navigator); + + window->dispatchEvent(GamepadEvent::create(eventNames().gamepaddisconnectedEvent, gamepad.get()), window->document()); + } + + // Notify all the Navigators that haven't already been notified. + for (auto* navigator : m_navigators) { + if (!notifiedNavigators.contains(navigator)) + navigator->gamepadDisconnected(platformGamepad); + } +} + +void GamepadManager::platformGamepadInputActivity(bool shouldMakeGamepadVisible) +{ + if (!shouldMakeGamepadVisible) + return; + + if (m_gamepadBlindNavigators.isEmpty() && m_gamepadBlindDOMWindows.isEmpty()) + return; + + for (auto* gamepad : GamepadProvider::singleton().platformGamepads()) { + if (gamepad) + makeGamepadVisible(*gamepad, m_gamepadBlindNavigators, m_gamepadBlindDOMWindows); + } + + m_gamepadBlindNavigators.clear(); + m_gamepadBlindDOMWindows.clear(); +} + +void GamepadManager::makeGamepadVisible(PlatformGamepad& platformGamepad, HashSet<NavigatorGamepad*>& navigatorSet, HashSet<DOMWindow*>& domWindowSet) +{ + if (navigatorSet.isEmpty() && domWindowSet.isEmpty()) + return; + + for (auto* navigator : navigatorSet) + navigator->gamepadConnected(platformGamepad); + + Vector<WeakPtr<DOMWindow>> weakWindows; + for (auto* domWindow : m_domWindows) + weakWindows.append(domWindow->createWeakPtr()); + + for (auto& window : weakWindows) { + // Event dispatch might have made this window go away. + if (!window) + continue; + + // This DOMWindow's Navigator might not be accessible. e.g. The DOMWindow might be in the back/forward cache. + // If this happens the DOMWindow will not get this gamepadconnected event. + // The new gamepad will still be visibile to it once it is restored from the back/forward cache. + NavigatorGamepad* navigator = navigatorGamepadFromDOMWindow(window.get()); + if (!navigator) + continue; + + Ref<Gamepad> gamepad(navigator->gamepadFromPlatformGamepad(platformGamepad)); + window->dispatchEvent(GamepadEvent::create(eventNames().gamepadconnectedEvent, gamepad.get()), window->document()); + } +} + +void GamepadManager::registerNavigator(NavigatorGamepad* navigator) +{ + LOG(Gamepad, "GamepadManager registering NavigatorGamepad %p", navigator); + + ASSERT(!m_navigators.contains(navigator)); + m_navigators.add(navigator); + m_gamepadBlindNavigators.add(navigator); + + maybeStartMonitoringGamepads(); +} + +void GamepadManager::unregisterNavigator(NavigatorGamepad* navigator) +{ + LOG(Gamepad, "GamepadManager unregistering NavigatorGamepad %p", navigator); + + ASSERT(m_navigators.contains(navigator)); + m_navigators.remove(navigator); + m_gamepadBlindNavigators.remove(navigator); + + maybeStopMonitoringGamepads(); +} + +void GamepadManager::registerDOMWindow(DOMWindow* window) +{ + LOG(Gamepad, "GamepadManager registering DOMWindow %p", window); + + ASSERT(!m_domWindows.contains(window)); + m_domWindows.add(window); + + // Anytime we register a DOMWindow, we should also double check that its NavigatorGamepad is registered. + NavigatorGamepad* navigator = navigatorGamepadFromDOMWindow(window); + ASSERT(navigator); + + if (m_navigators.add(navigator).isNewEntry) + m_gamepadBlindNavigators.add(navigator); + + // If this DOMWindow's NavigatorGamepad was already registered but was still blind, + // then this DOMWindow should be blind. + if (m_gamepadBlindNavigators.contains(navigator)) + m_gamepadBlindDOMWindows.add(window); + + maybeStartMonitoringGamepads(); +} + +void GamepadManager::unregisterDOMWindow(DOMWindow* window) +{ + LOG(Gamepad, "GamepadManager unregistering DOMWindow %p", window); + + ASSERT(m_domWindows.contains(window)); + m_domWindows.remove(window); + m_gamepadBlindDOMWindows.remove(window); + + maybeStopMonitoringGamepads(); +} + +void GamepadManager::maybeStartMonitoringGamepads() +{ + if (m_isMonitoringGamepads) + return; + + if (!m_navigators.isEmpty() || !m_domWindows.isEmpty()) { + LOG(Gamepad, "GamepadManager has %i NavigatorGamepads and %i DOMWindows registered, is starting gamepad monitoring", m_navigators.size(), m_domWindows.size()); + m_isMonitoringGamepads = true; + GamepadProvider::singleton().startMonitoringGamepads(*this); + } +} + +void GamepadManager::maybeStopMonitoringGamepads() +{ + if (!m_isMonitoringGamepads) + return; + + if (m_navigators.isEmpty() && m_domWindows.isEmpty()) { + LOG(Gamepad, "GamepadManager has no NavigatorGamepads or DOMWindows registered, is stopping gamepad monitoring"); + m_isMonitoringGamepads = false; + GamepadProvider::singleton().stopMonitoringGamepads(*this); + } +} + +} // namespace WebCore + +#endif // ENABLE(GAMEPAD) |