Home | History | Annotate | Download | only in gamepad
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include <cmath>
      6 #include <set>
      7 #include <vector>
      8 
      9 #include "base/bind.h"
     10 #include "base/logging.h"
     11 #include "base/message_loop/message_loop.h"
     12 #include "base/message_loop/message_loop_proxy.h"
     13 #include "base/threading/thread.h"
     14 #include "base/threading/thread_restrictions.h"
     15 #include "content/browser/gamepad/gamepad_data_fetcher.h"
     16 #include "content/browser/gamepad/gamepad_platform_data_fetcher.h"
     17 #include "content/browser/gamepad/gamepad_provider.h"
     18 #include "content/browser/gamepad/gamepad_service.h"
     19 #include "content/common/gamepad_hardware_buffer.h"
     20 #include "content/common/gamepad_messages.h"
     21 #include "content/common/gamepad_user_gesture.h"
     22 #include "content/public/browser/browser_thread.h"
     23 
     24 using blink::WebGamepad;
     25 using blink::WebGamepads;
     26 
     27 namespace content {
     28 
     29 GamepadProvider::ClosureAndThread::ClosureAndThread(
     30     const base::Closure& c,
     31     const scoped_refptr<base::MessageLoopProxy>& m)
     32     : closure(c),
     33       message_loop(m) {
     34 }
     35 
     36 GamepadProvider::ClosureAndThread::~ClosureAndThread() {
     37 }
     38 
     39 GamepadProvider::GamepadProvider()
     40     : is_paused_(true),
     41       have_scheduled_do_poll_(false),
     42       devices_changed_(true),
     43       ever_had_user_gesture_(false) {
     44   Initialize(scoped_ptr<GamepadDataFetcher>());
     45 }
     46 
     47 GamepadProvider::GamepadProvider(scoped_ptr<GamepadDataFetcher> fetcher)
     48     : is_paused_(true),
     49       have_scheduled_do_poll_(false),
     50       devices_changed_(true),
     51       ever_had_user_gesture_(false) {
     52   Initialize(fetcher.Pass());
     53 }
     54 
     55 GamepadProvider::~GamepadProvider() {
     56   base::SystemMonitor* monitor = base::SystemMonitor::Get();
     57   if (monitor)
     58     monitor->RemoveDevicesChangedObserver(this);
     59 
     60   // Use Stop() to join the polling thread, as there may be pending callbacks
     61   // which dereference |polling_thread_|.
     62   polling_thread_->Stop();
     63   data_fetcher_.reset();
     64 }
     65 
     66 base::SharedMemoryHandle GamepadProvider::GetSharedMemoryHandleForProcess(
     67     base::ProcessHandle process) {
     68   base::SharedMemoryHandle renderer_handle;
     69   gamepad_shared_memory_.ShareToProcess(process, &renderer_handle);
     70   return renderer_handle;
     71 }
     72 
     73 void GamepadProvider::GetCurrentGamepadData(WebGamepads* data) {
     74   const WebGamepads& pads = SharedMemoryAsHardwareBuffer()->buffer;
     75   base::AutoLock lock(shared_memory_lock_);
     76   *data = pads;
     77 }
     78 
     79 void GamepadProvider::Pause() {
     80   {
     81     base::AutoLock lock(is_paused_lock_);
     82     is_paused_ = true;
     83   }
     84   base::MessageLoop* polling_loop = polling_thread_->message_loop();
     85   polling_loop->PostTask(
     86       FROM_HERE,
     87       base::Bind(&GamepadProvider::SendPauseHint, Unretained(this), true));
     88 }
     89 
     90 void GamepadProvider::Resume() {
     91   {
     92     base::AutoLock lock(is_paused_lock_);
     93     if (!is_paused_)
     94         return;
     95     is_paused_ = false;
     96   }
     97 
     98   base::MessageLoop* polling_loop = polling_thread_->message_loop();
     99   polling_loop->PostTask(
    100       FROM_HERE,
    101       base::Bind(&GamepadProvider::SendPauseHint, Unretained(this), false));
    102   polling_loop->PostTask(
    103       FROM_HERE,
    104       base::Bind(&GamepadProvider::ScheduleDoPoll, Unretained(this)));
    105 }
    106 
    107 void GamepadProvider::RegisterForUserGesture(const base::Closure& closure) {
    108   base::AutoLock lock(user_gesture_lock_);
    109   user_gesture_observers_.push_back(ClosureAndThread(
    110       closure, base::MessageLoop::current()->message_loop_proxy()));
    111 }
    112 
    113 void GamepadProvider::OnDevicesChanged(base::SystemMonitor::DeviceType type) {
    114   base::AutoLock lock(devices_changed_lock_);
    115   devices_changed_ = true;
    116 }
    117 
    118 void GamepadProvider::Initialize(scoped_ptr<GamepadDataFetcher> fetcher) {
    119   size_t data_size = sizeof(GamepadHardwareBuffer);
    120   base::SystemMonitor* monitor = base::SystemMonitor::Get();
    121   if (monitor)
    122     monitor->AddDevicesChangedObserver(this);
    123   bool res = gamepad_shared_memory_.CreateAndMapAnonymous(data_size);
    124   CHECK(res);
    125   GamepadHardwareBuffer* hwbuf = SharedMemoryAsHardwareBuffer();
    126   memset(hwbuf, 0, sizeof(GamepadHardwareBuffer));
    127   pad_states_.reset(new PadState[WebGamepads::itemsLengthCap]);
    128 
    129   polling_thread_.reset(new base::Thread("Gamepad polling thread"));
    130 #if defined(OS_LINUX)
    131   // On Linux, the data fetcher needs to watch file descriptors, so the message
    132   // loop needs to be a libevent loop.
    133   const base::MessageLoop::Type kMessageLoopType = base::MessageLoop::TYPE_IO;
    134 #elif defined(OS_ANDROID)
    135   // On Android, keeping a message loop of default type.
    136   const base::MessageLoop::Type kMessageLoopType =
    137       base::MessageLoop::TYPE_DEFAULT;
    138 #else
    139   // On Mac, the data fetcher uses IOKit which depends on CFRunLoop, so the
    140   // message loop needs to be a UI-type loop. On Windows it must be a UI loop
    141   // to properly pump the MessageWindow that captures device state.
    142   const base::MessageLoop::Type kMessageLoopType = base::MessageLoop::TYPE_UI;
    143 #endif
    144   polling_thread_->StartWithOptions(base::Thread::Options(kMessageLoopType, 0));
    145 
    146   polling_thread_->message_loop()->PostTask(
    147       FROM_HERE,
    148       base::Bind(&GamepadProvider::DoInitializePollingThread,
    149                  base::Unretained(this),
    150                  base::Passed(&fetcher)));
    151 }
    152 
    153 void GamepadProvider::DoInitializePollingThread(
    154     scoped_ptr<GamepadDataFetcher> fetcher) {
    155   DCHECK(base::MessageLoop::current() == polling_thread_->message_loop());
    156   DCHECK(!data_fetcher_.get());  // Should only initialize once.
    157 
    158   if (!fetcher)
    159     fetcher.reset(new GamepadPlatformDataFetcher);
    160   data_fetcher_ = fetcher.Pass();
    161 }
    162 
    163 void GamepadProvider::SendPauseHint(bool paused) {
    164   DCHECK(base::MessageLoop::current() == polling_thread_->message_loop());
    165   if (data_fetcher_)
    166     data_fetcher_->PauseHint(paused);
    167 }
    168 
    169 bool GamepadProvider::PadState::Match(const WebGamepad& pad) const {
    170   return connected_ == pad.connected &&
    171          axes_length_ == pad.axesLength &&
    172          buttons_length_ == pad.buttonsLength &&
    173          memcmp(id_, pad.id, arraysize(id_)) == 0 &&
    174          memcmp(mapping_, pad.mapping, arraysize(mapping_)) == 0;
    175 }
    176 
    177 void GamepadProvider::PadState::SetPad(const WebGamepad& pad) {
    178   connected_ = pad.connected;
    179   axes_length_ = pad.axesLength;
    180   buttons_length_ = pad.buttonsLength;
    181   memcpy(id_, pad.id, arraysize(id_));
    182   memcpy(mapping_, pad.mapping, arraysize(mapping_));
    183 }
    184 
    185 void GamepadProvider::PadState::SetDisconnected() {
    186   connected_ = false;
    187   axes_length_ = 0;
    188   buttons_length_ = 0;
    189   memset(id_, 0, arraysize(id_));
    190   memset(mapping_, 0, arraysize(mapping_));
    191 }
    192 
    193 void GamepadProvider::PadState::AsWebGamepad(WebGamepad* pad) {
    194   pad->connected = connected_;
    195   pad->axesLength = axes_length_;
    196   pad->buttonsLength = buttons_length_;
    197   memcpy(pad->id, id_, arraysize(id_));
    198   memcpy(pad->mapping, mapping_, arraysize(mapping_));
    199   memset(pad->axes, 0, arraysize(pad->axes));
    200   memset(pad->buttons, 0, arraysize(pad->buttons));
    201 }
    202 
    203 void GamepadProvider::DoPoll() {
    204   DCHECK(base::MessageLoop::current() == polling_thread_->message_loop());
    205   DCHECK(have_scheduled_do_poll_);
    206   have_scheduled_do_poll_ = false;
    207 
    208   bool changed;
    209   GamepadHardwareBuffer* hwbuf = SharedMemoryAsHardwareBuffer();
    210 
    211   ANNOTATE_BENIGN_RACE_SIZED(
    212       &hwbuf->buffer,
    213       sizeof(WebGamepads),
    214       "Racey reads are discarded");
    215 
    216   {
    217     base::AutoLock lock(devices_changed_lock_);
    218     changed = devices_changed_;
    219     devices_changed_ = false;
    220   }
    221 
    222   {
    223     base::AutoLock lock(shared_memory_lock_);
    224 
    225     // Acquire the SeqLock. There is only ever one writer to this data.
    226     // See gamepad_hardware_buffer.h.
    227     hwbuf->sequence.WriteBegin();
    228     data_fetcher_->GetGamepadData(&hwbuf->buffer, changed);
    229     hwbuf->sequence.WriteEnd();
    230   }
    231 
    232   if (ever_had_user_gesture_) {
    233     for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) {
    234       WebGamepad& pad = hwbuf->buffer.items[i];
    235       PadState& state = pad_states_.get()[i];
    236       if (pad.connected && !state.connected()) {
    237         OnGamepadConnectionChange(true, i, pad);
    238       } else if (!pad.connected && state.connected()) {
    239         OnGamepadConnectionChange(false, i, pad);
    240       } else if (pad.connected && state.connected() && !state.Match(pad)) {
    241         WebGamepad old_pad;
    242         state.AsWebGamepad(&old_pad);
    243         OnGamepadConnectionChange(false, i, old_pad);
    244         OnGamepadConnectionChange(true, i, pad);
    245       }
    246     }
    247   }
    248 
    249   CheckForUserGesture();
    250 
    251   // Schedule our next interval of polling.
    252   ScheduleDoPoll();
    253 }
    254 
    255 void GamepadProvider::ScheduleDoPoll() {
    256   DCHECK(base::MessageLoop::current() == polling_thread_->message_loop());
    257   if (have_scheduled_do_poll_)
    258     return;
    259 
    260   {
    261     base::AutoLock lock(is_paused_lock_);
    262     if (is_paused_)
    263       return;
    264   }
    265 
    266   base::MessageLoop::current()->PostDelayedTask(
    267       FROM_HERE,
    268       base::Bind(&GamepadProvider::DoPoll, Unretained(this)),
    269       base::TimeDelta::FromMilliseconds(kDesiredSamplingIntervalMs));
    270   have_scheduled_do_poll_ = true;
    271 }
    272 
    273 void GamepadProvider::OnGamepadConnectionChange(
    274     bool connected, int index, const WebGamepad& pad) {
    275   PadState& state = pad_states_.get()[index];
    276   if (connected)
    277     state.SetPad(pad);
    278   else
    279     state.SetDisconnected();
    280 
    281   BrowserThread::PostTask(
    282       BrowserThread::IO,
    283       FROM_HERE,
    284       base::Bind(&GamepadProvider::DispatchGamepadConnectionChange,
    285                  base::Unretained(this),
    286                  connected,
    287                  index,
    288                  pad));
    289 }
    290 
    291 void GamepadProvider::DispatchGamepadConnectionChange(
    292     bool connected, int index, const WebGamepad& pad) {
    293   if (connected)
    294     GamepadService::GetInstance()->OnGamepadConnected(index, pad);
    295   else
    296     GamepadService::GetInstance()->OnGamepadDisconnected(index, pad);
    297 }
    298 
    299 GamepadHardwareBuffer* GamepadProvider::SharedMemoryAsHardwareBuffer() {
    300   void* mem = gamepad_shared_memory_.memory();
    301   CHECK(mem);
    302   return static_cast<GamepadHardwareBuffer*>(mem);
    303 }
    304 
    305 void GamepadProvider::CheckForUserGesture() {
    306   base::AutoLock lock(user_gesture_lock_);
    307   if (user_gesture_observers_.empty() && ever_had_user_gesture_)
    308     return;
    309 
    310   bool had_gesture_before = ever_had_user_gesture_;
    311   const WebGamepads& pads = SharedMemoryAsHardwareBuffer()->buffer;
    312   if (GamepadsHaveUserGesture(pads)) {
    313     ever_had_user_gesture_ = true;
    314     for (size_t i = 0; i < user_gesture_observers_.size(); i++) {
    315       user_gesture_observers_[i].message_loop->PostTask(FROM_HERE,
    316           user_gesture_observers_[i].closure);
    317     }
    318     user_gesture_observers_.clear();
    319   }
    320   if (!had_gesture_before && ever_had_user_gesture_) {
    321     // Initialize pad_states_ for the first time.
    322     for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) {
    323       pad_states_.get()[i].SetPad(pads.items[i]);
    324     }
    325   }
    326 }
    327 
    328 }  // namespace content
    329