Home | History | Annotate | Download | only in gamepad
      1 /*
      2  * Copyright (C) 2011, Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are met:
      6  *
      7  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
     14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     16  * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
     17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     19  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
     20  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
     23  * DAMAGE.
     24  */
     25 
     26 #include "config.h"
     27 #include "modules/gamepad/NavigatorGamepad.h"
     28 
     29 #include "core/dom/Document.h"
     30 #include "core/frame/LocalDOMWindow.h"
     31 #include "core/frame/LocalFrame.h"
     32 #include "core/frame/Navigator.h"
     33 #include "core/page/Page.h"
     34 #include "modules/gamepad/GamepadDispatcher.h"
     35 #include "modules/gamepad/GamepadEvent.h"
     36 #include "modules/gamepad/GamepadList.h"
     37 #include "modules/gamepad/WebKitGamepadList.h"
     38 #include "platform/RuntimeEnabledFeatures.h"
     39 
     40 namespace blink {
     41 
     42 template<typename T>
     43 static void sampleGamepad(unsigned index, T& gamepad, const WebGamepad& webGamepad)
     44 {
     45     gamepad.setId(webGamepad.id);
     46     gamepad.setIndex(index);
     47     gamepad.setConnected(webGamepad.connected);
     48     gamepad.setTimestamp(webGamepad.timestamp);
     49     gamepad.setMapping(webGamepad.mapping);
     50     gamepad.setAxes(webGamepad.axesLength, webGamepad.axes);
     51     gamepad.setButtons(webGamepad.buttonsLength, webGamepad.buttons);
     52 }
     53 
     54 template<typename GamepadType, typename ListType>
     55 static void sampleGamepads(ListType* into)
     56 {
     57     WebGamepads gamepads;
     58 
     59     GamepadDispatcher::instance().sampleGamepads(gamepads);
     60 
     61     for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) {
     62         WebGamepad& webGamepad = gamepads.items[i];
     63         if (i < gamepads.length && webGamepad.connected) {
     64             GamepadType* gamepad = into->item(i);
     65             if (!gamepad)
     66                 gamepad = GamepadType::create();
     67             sampleGamepad(i, *gamepad, webGamepad);
     68             into->set(i, gamepad);
     69         } else {
     70             into->set(i, 0);
     71         }
     72     }
     73 }
     74 
     75 NavigatorGamepad* NavigatorGamepad::from(Document& document)
     76 {
     77     if (!document.frame() || !document.frame()->domWindow())
     78         return 0;
     79     Navigator& navigator = document.frame()->domWindow()->navigator();
     80     return &from(navigator);
     81 }
     82 
     83 NavigatorGamepad& NavigatorGamepad::from(Navigator& navigator)
     84 {
     85     NavigatorGamepad* supplement = static_cast<NavigatorGamepad*>(WillBeHeapSupplement<Navigator>::from(navigator, supplementName()));
     86     if (!supplement) {
     87         supplement = new NavigatorGamepad(navigator.frame());
     88         provideTo(navigator, supplementName(), adoptPtrWillBeNoop(supplement));
     89     }
     90     return *supplement;
     91 }
     92 
     93 WebKitGamepadList* NavigatorGamepad::webkitGetGamepads(Navigator& navigator)
     94 {
     95     return NavigatorGamepad::from(navigator).webkitGamepads();
     96 }
     97 
     98 GamepadList* NavigatorGamepad::getGamepads(Navigator& navigator)
     99 {
    100     return NavigatorGamepad::from(navigator).gamepads();
    101 }
    102 
    103 WebKitGamepadList* NavigatorGamepad::webkitGamepads()
    104 {
    105     if (!m_webkitGamepads)
    106         m_webkitGamepads = WebKitGamepadList::create();
    107     if (window()) {
    108         startUpdating();
    109         sampleGamepads<WebKitGamepad>(m_webkitGamepads.get());
    110     }
    111     return m_webkitGamepads.get();
    112 }
    113 
    114 GamepadList* NavigatorGamepad::gamepads()
    115 {
    116     if (!m_gamepads)
    117         m_gamepads = GamepadList::create();
    118     if (window()) {
    119         startUpdating();
    120         sampleGamepads<Gamepad>(m_gamepads.get());
    121     }
    122     return m_gamepads.get();
    123 }
    124 
    125 void NavigatorGamepad::trace(Visitor* visitor)
    126 {
    127     visitor->trace(m_gamepads);
    128     visitor->trace(m_webkitGamepads);
    129     visitor->trace(m_pendingEvents);
    130     WillBeHeapSupplement<Navigator>::trace(visitor);
    131     DOMWindowProperty::trace(visitor);
    132 }
    133 
    134 void NavigatorGamepad::didUpdateData()
    135 {
    136     // We should stop listening once we detached.
    137     ASSERT(window());
    138 
    139     // We register to the dispatcher before sampling gamepads so we need to check if we actually have an event listener.
    140     if (!m_hasEventListener)
    141         return;
    142 
    143     if (window()->document()->activeDOMObjectsAreStopped() || window()->document()->activeDOMObjectsAreSuspended())
    144         return;
    145 
    146     const GamepadDispatcher::ConnectionChange& change = GamepadDispatcher::instance().latestConnectionChange();
    147 
    148     if (!m_gamepads)
    149         m_gamepads = GamepadList::create();
    150 
    151     Gamepad* gamepad = m_gamepads->item(change.index);
    152     if (!gamepad)
    153         gamepad = Gamepad::create();
    154     sampleGamepad(change.index, *gamepad, change.pad);
    155     m_gamepads->set(change.index, gamepad);
    156 
    157     m_pendingEvents.append(gamepad);
    158     m_dispatchOneEventRunner.runAsync();
    159 }
    160 
    161 void NavigatorGamepad::dispatchOneEvent()
    162 {
    163     ASSERT(window());
    164     ASSERT(!m_pendingEvents.isEmpty());
    165 
    166     Gamepad* gamepad = m_pendingEvents.takeFirst();
    167     const AtomicString& eventName = gamepad->connected() ? EventTypeNames::gamepadconnected : EventTypeNames::gamepaddisconnected;
    168     window()->dispatchEvent(GamepadEvent::create(eventName, false, true, gamepad));
    169 
    170     if (!m_pendingEvents.isEmpty())
    171         m_dispatchOneEventRunner.runAsync();
    172 }
    173 
    174 NavigatorGamepad::NavigatorGamepad(LocalFrame* frame)
    175     : DOMWindowProperty(frame)
    176     , PlatformEventController(frame ? frame->page() : 0)
    177     , DOMWindowLifecycleObserver(frame ? frame->domWindow() : 0)
    178     , m_dispatchOneEventRunner(this, &NavigatorGamepad::dispatchOneEvent)
    179 {
    180 }
    181 
    182 NavigatorGamepad::~NavigatorGamepad()
    183 {
    184 #if ENABLE(OILPAN)
    185     stopUpdating();
    186 #endif
    187 }
    188 
    189 const char* NavigatorGamepad::supplementName()
    190 {
    191     return "NavigatorGamepad";
    192 }
    193 
    194 void NavigatorGamepad::willDestroyGlobalObjectInFrame()
    195 {
    196     stopUpdating();
    197     DOMWindowProperty::willDestroyGlobalObjectInFrame();
    198 }
    199 
    200 void NavigatorGamepad::willDetachGlobalObjectFromFrame()
    201 {
    202     stopUpdating();
    203     DOMWindowProperty::willDetachGlobalObjectFromFrame();
    204 }
    205 
    206 void NavigatorGamepad::registerWithDispatcher()
    207 {
    208     GamepadDispatcher::instance().addController(this);
    209     m_dispatchOneEventRunner.resume();
    210 }
    211 
    212 void NavigatorGamepad::unregisterWithDispatcher()
    213 {
    214     m_dispatchOneEventRunner.suspend();
    215     GamepadDispatcher::instance().removeController(this);
    216 }
    217 
    218 bool NavigatorGamepad::hasLastData()
    219 {
    220     // Gamepad data is polled instead of pushed.
    221     return false;
    222 }
    223 
    224 static bool isGamepadEvent(const AtomicString& eventType)
    225 {
    226     return eventType == EventTypeNames::gamepadconnected || eventType == EventTypeNames::gamepaddisconnected;
    227 }
    228 
    229 void NavigatorGamepad::didAddEventListener(LocalDOMWindow*, const AtomicString& eventType)
    230 {
    231     if (RuntimeEnabledFeatures::gamepadEnabled() && isGamepadEvent(eventType)) {
    232         if (page() && page()->visibilityState() == PageVisibilityStateVisible)
    233             startUpdating();
    234         m_hasEventListener = true;
    235     }
    236 }
    237 
    238 void NavigatorGamepad::didRemoveEventListener(LocalDOMWindow* window, const AtomicString& eventType)
    239 {
    240     if (isGamepadEvent(eventType)
    241         && !window->hasEventListeners(EventTypeNames::gamepadconnected)
    242         && !window->hasEventListeners(EventTypeNames::gamepaddisconnected)) {
    243         didRemoveGamepadEventListeners();
    244     }
    245 }
    246 
    247 void NavigatorGamepad::didRemoveAllEventListeners(LocalDOMWindow*)
    248 {
    249     didRemoveGamepadEventListeners();
    250 }
    251 
    252 void NavigatorGamepad::didRemoveGamepadEventListeners()
    253 {
    254     m_hasEventListener = false;
    255     m_dispatchOneEventRunner.stop();
    256     m_pendingEvents.clear();
    257 }
    258 
    259 void NavigatorGamepad::pageVisibilityChanged()
    260 {
    261     // Inform the embedder whether it needs to provide gamepad data for us.
    262     bool visible = page()->visibilityState() == PageVisibilityStateVisible;
    263     if (visible && (m_hasEventListener || m_gamepads || m_webkitGamepads))
    264         startUpdating();
    265     else
    266         stopUpdating();
    267 
    268     if (!visible || !m_hasEventListener)
    269         return;
    270 
    271     // Tell the page what has changed. m_gamepads contains the state before we became hidden.
    272     // We create a new snapshot and compare them.
    273     GamepadList* oldGamepads = m_gamepads.release();
    274     gamepads();
    275     GamepadList* newGamepads = m_gamepads.get();
    276     ASSERT(newGamepads);
    277 
    278     for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) {
    279         Gamepad* oldGamepad = oldGamepads ? oldGamepads->item(i) : 0;
    280         Gamepad* newGamepad = newGamepads->item(i);
    281         bool oldWasConnected = oldGamepad && oldGamepad->connected();
    282         bool newIsConnected = newGamepad && newGamepad->connected();
    283         bool connectedGamepadChanged = oldWasConnected && newIsConnected && oldGamepad->id() != newGamepad->id();
    284         if (connectedGamepadChanged || (oldWasConnected && !newIsConnected)) {
    285             oldGamepad->setConnected(false);
    286             m_pendingEvents.append(oldGamepad);
    287         }
    288         if (connectedGamepadChanged || (!oldWasConnected && newIsConnected)) {
    289             m_pendingEvents.append(newGamepad);
    290         }
    291     }
    292 
    293     if (!m_pendingEvents.isEmpty())
    294         m_dispatchOneEventRunner.runAsync();
    295 }
    296 
    297 } // namespace blink
    298