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 WebCore {
     41 
     42 template<typename T>
     43 static void sampleGamepad(unsigned index, T& gamepad, const blink::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     blink::WebGamepads gamepads;
     58 
     59     GamepadDispatcher::instance().sampleGamepads(gamepads);
     60 
     61     for (unsigned i = 0; i < blink::WebGamepads::itemsLengthCap; ++i) {
     62         blink::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     WillBeHeapSupplement<Navigator>::trace(visitor);
    130 }
    131 
    132 void NavigatorGamepad::didUpdateData()
    133 {
    134     // We should stop listening once we detached.
    135     ASSERT(window());
    136 
    137     // We register to the dispatcher before sampling gamepads so we need to check if we actually have an event listener.
    138     if (!m_hasEventListener)
    139         return;
    140 
    141     if (window()->document()->activeDOMObjectsAreStopped() || window()->document()->activeDOMObjectsAreSuspended())
    142         return;
    143 
    144     const GamepadDispatcher::ConnectionChange& change = GamepadDispatcher::instance().latestConnectionChange();
    145 
    146     if (!m_gamepads)
    147         m_gamepads = GamepadList::create();
    148 
    149     Gamepad* gamepad = m_gamepads->item(change.index);
    150     if (!gamepad)
    151         gamepad = Gamepad::create();
    152     sampleGamepad(change.index, *gamepad, change.pad);
    153     m_gamepads->set(change.index, gamepad);
    154 
    155     const AtomicString& eventName = change.pad.connected ? EventTypeNames::gamepadconnected : EventTypeNames::gamepaddisconnected;
    156     window()->dispatchEvent(GamepadEvent::create(eventName, false, true, gamepad));
    157 }
    158 
    159 NavigatorGamepad::NavigatorGamepad(LocalFrame* frame)
    160     : DOMWindowProperty(frame)
    161     , DeviceEventControllerBase(frame ? frame->page() : 0)
    162     , DOMWindowLifecycleObserver(frame ? frame->domWindow() : 0)
    163 {
    164 }
    165 
    166 NavigatorGamepad::~NavigatorGamepad()
    167 {
    168 }
    169 
    170 const char* NavigatorGamepad::supplementName()
    171 {
    172     return "NavigatorGamepad";
    173 }
    174 
    175 void NavigatorGamepad::willDestroyGlobalObjectInFrame()
    176 {
    177     stopUpdating();
    178     DOMWindowProperty::willDestroyGlobalObjectInFrame();
    179 }
    180 
    181 void NavigatorGamepad::willDetachGlobalObjectFromFrame()
    182 {
    183     stopUpdating();
    184     DOMWindowProperty::willDetachGlobalObjectFromFrame();
    185 }
    186 
    187 void NavigatorGamepad::registerWithDispatcher()
    188 {
    189     GamepadDispatcher::instance().addController(this);
    190 }
    191 
    192 void NavigatorGamepad::unregisterWithDispatcher()
    193 {
    194     GamepadDispatcher::instance().removeController(this);
    195 }
    196 
    197 bool NavigatorGamepad::hasLastData()
    198 {
    199     // Gamepad data is polled instead of pushed.
    200     return false;
    201 }
    202 
    203 static bool isGamepadEvent(const AtomicString& eventType)
    204 {
    205     return eventType == EventTypeNames::gamepadconnected || eventType == EventTypeNames::gamepaddisconnected;
    206 }
    207 
    208 void NavigatorGamepad::didAddEventListener(LocalDOMWindow*, const AtomicString& eventType)
    209 {
    210     if (RuntimeEnabledFeatures::gamepadEnabled() && isGamepadEvent(eventType)) {
    211         if (page() && page()->visibilityState() == PageVisibilityStateVisible)
    212             startUpdating();
    213         m_hasEventListener = true;
    214     }
    215 }
    216 
    217 void NavigatorGamepad::didRemoveEventListener(LocalDOMWindow* window, const AtomicString& eventType)
    218 {
    219     if (isGamepadEvent(eventType)
    220         && !window->hasEventListeners(EventTypeNames::gamepadconnected)
    221         && !window->hasEventListeners(EventTypeNames::gamepaddisconnected)) {
    222         m_hasEventListener = false;
    223     }
    224 }
    225 
    226 void NavigatorGamepad::didRemoveAllEventListeners(LocalDOMWindow*)
    227 {
    228     m_hasEventListener = false;
    229 }
    230 
    231 } // namespace WebCore
    232