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/common/gamepad_hardware_buffer.h"
     19 #include "content/common/gamepad_messages.h"
     20 #include "content/common/gamepad_user_gesture.h"
     21 
     22 namespace content {
     23 
     24 GamepadProvider::ClosureAndThread::ClosureAndThread(
     25     const base::Closure& c,
     26     const scoped_refptr<base::MessageLoopProxy>& m)
     27     : closure(c),
     28       message_loop(m) {
     29 }
     30 
     31 GamepadProvider::ClosureAndThread::~ClosureAndThread() {
     32 }
     33 
     34 GamepadProvider::GamepadProvider()
     35     : is_paused_(true),
     36       have_scheduled_do_poll_(false),
     37       devices_changed_(true) {
     38   Initialize(scoped_ptr<GamepadDataFetcher>());
     39 }
     40 
     41 GamepadProvider::GamepadProvider(scoped_ptr<GamepadDataFetcher> fetcher)
     42     : is_paused_(true),
     43       have_scheduled_do_poll_(false),
     44       devices_changed_(true) {
     45   Initialize(fetcher.Pass());
     46 }
     47 
     48 GamepadProvider::~GamepadProvider() {
     49   base::SystemMonitor* monitor = base::SystemMonitor::Get();
     50   if (monitor)
     51     monitor->RemoveDevicesChangedObserver(this);
     52 
     53   // Use Stop() to join the polling thread, as there may be pending callbacks
     54   // which dereference |polling_thread_|.
     55   polling_thread_->Stop();
     56   data_fetcher_.reset();
     57 }
     58 
     59 base::SharedMemoryHandle GamepadProvider::GetSharedMemoryHandleForProcess(
     60     base::ProcessHandle process) {
     61   base::SharedMemoryHandle renderer_handle;
     62   gamepad_shared_memory_.ShareToProcess(process, &renderer_handle);
     63   return renderer_handle;
     64 }
     65 
     66 void GamepadProvider::Pause() {
     67   {
     68     base::AutoLock lock(is_paused_lock_);
     69     is_paused_ = true;
     70   }
     71   base::MessageLoop* polling_loop = polling_thread_->message_loop();
     72   polling_loop->PostTask(
     73       FROM_HERE,
     74       base::Bind(&GamepadProvider::SendPauseHint, Unretained(this), true));
     75 }
     76 
     77 void GamepadProvider::Resume() {
     78   {
     79     base::AutoLock lock(is_paused_lock_);
     80     if (!is_paused_)
     81         return;
     82     is_paused_ = false;
     83   }
     84 
     85   base::MessageLoop* polling_loop = polling_thread_->message_loop();
     86   polling_loop->PostTask(
     87       FROM_HERE,
     88       base::Bind(&GamepadProvider::SendPauseHint, Unretained(this), false));
     89   polling_loop->PostTask(
     90       FROM_HERE,
     91       base::Bind(&GamepadProvider::ScheduleDoPoll, Unretained(this)));
     92 }
     93 
     94 void GamepadProvider::RegisterForUserGesture(const base::Closure& closure) {
     95   base::AutoLock lock(user_gesture_lock_);
     96   user_gesture_observers_.push_back(ClosureAndThread(
     97       closure, base::MessageLoop::current()->message_loop_proxy()));
     98 }
     99 
    100 void GamepadProvider::OnDevicesChanged(base::SystemMonitor::DeviceType type) {
    101   base::AutoLock lock(devices_changed_lock_);
    102   devices_changed_ = true;
    103 }
    104 
    105 void GamepadProvider::Initialize(scoped_ptr<GamepadDataFetcher> fetcher) {
    106   size_t data_size = sizeof(GamepadHardwareBuffer);
    107   base::SystemMonitor* monitor = base::SystemMonitor::Get();
    108   if (monitor)
    109     monitor->AddDevicesChangedObserver(this);
    110   bool res = gamepad_shared_memory_.CreateAndMapAnonymous(data_size);
    111   CHECK(res);
    112   GamepadHardwareBuffer* hwbuf = SharedMemoryAsHardwareBuffer();
    113   memset(hwbuf, 0, sizeof(GamepadHardwareBuffer));
    114 
    115   polling_thread_.reset(new base::Thread("Gamepad polling thread"));
    116 #if defined(OS_MACOSX)
    117   // On Mac, the data fetcher uses IOKit which depends on CFRunLoop, so the
    118   // message loop needs to be a UI-type loop.
    119   const base::MessageLoop::Type kMessageLoopType = base::MessageLoop::TYPE_UI;
    120 #else
    121   // On Linux, the data fetcher needs to watch file descriptors, so the message
    122   // loop needs to be a libevent loop. On Windows it doesn't matter what the
    123   // loop is.
    124   const base::MessageLoop::Type kMessageLoopType = base::MessageLoop::TYPE_IO;
    125 #endif
    126   polling_thread_->StartWithOptions(base::Thread::Options(kMessageLoopType, 0));
    127 
    128   polling_thread_->message_loop()->PostTask(
    129       FROM_HERE,
    130       base::Bind(&GamepadProvider::DoInitializePollingThread,
    131                  base::Unretained(this),
    132                  base::Passed(&fetcher)));
    133 }
    134 
    135 void GamepadProvider::DoInitializePollingThread(
    136     scoped_ptr<GamepadDataFetcher> fetcher) {
    137   DCHECK(base::MessageLoop::current() == polling_thread_->message_loop());
    138   DCHECK(!data_fetcher_.get());  // Should only initialize once.
    139 
    140   if (!fetcher)
    141     fetcher.reset(new GamepadPlatformDataFetcher);
    142   data_fetcher_ = fetcher.Pass();
    143 }
    144 
    145 void GamepadProvider::SendPauseHint(bool paused) {
    146   DCHECK(base::MessageLoop::current() == polling_thread_->message_loop());
    147   if (data_fetcher_)
    148     data_fetcher_->PauseHint(paused);
    149 }
    150 
    151 void GamepadProvider::DoPoll() {
    152   DCHECK(base::MessageLoop::current() == polling_thread_->message_loop());
    153   DCHECK(have_scheduled_do_poll_);
    154   have_scheduled_do_poll_ = false;
    155 
    156   bool changed;
    157   GamepadHardwareBuffer* hwbuf = SharedMemoryAsHardwareBuffer();
    158 
    159   ANNOTATE_BENIGN_RACE_SIZED(
    160       &hwbuf->buffer,
    161       sizeof(WebKit::WebGamepads),
    162       "Racey reads are discarded");
    163 
    164   {
    165     base::AutoLock lock(devices_changed_lock_);
    166     changed = devices_changed_;
    167     devices_changed_ = false;
    168   }
    169 
    170   // Acquire the SeqLock. There is only ever one writer to this data.
    171   // See gamepad_hardware_buffer.h.
    172   hwbuf->sequence.WriteBegin();
    173   data_fetcher_->GetGamepadData(&hwbuf->buffer, changed);
    174   hwbuf->sequence.WriteEnd();
    175 
    176   CheckForUserGesture();
    177 
    178   // Schedule our next interval of polling.
    179   ScheduleDoPoll();
    180 }
    181 
    182 void GamepadProvider::ScheduleDoPoll() {
    183   DCHECK(base::MessageLoop::current() == polling_thread_->message_loop());
    184   if (have_scheduled_do_poll_)
    185     return;
    186 
    187   {
    188     base::AutoLock lock(is_paused_lock_);
    189     if (is_paused_)
    190       return;
    191   }
    192 
    193   base::MessageLoop::current()->PostDelayedTask(
    194       FROM_HERE,
    195       base::Bind(&GamepadProvider::DoPoll, Unretained(this)),
    196       base::TimeDelta::FromMilliseconds(kDesiredSamplingIntervalMs));
    197   have_scheduled_do_poll_ = true;
    198 }
    199 
    200 GamepadHardwareBuffer* GamepadProvider::SharedMemoryAsHardwareBuffer() {
    201   void* mem = gamepad_shared_memory_.memory();
    202   CHECK(mem);
    203   return static_cast<GamepadHardwareBuffer*>(mem);
    204 }
    205 
    206 void GamepadProvider::CheckForUserGesture() {
    207   base::AutoLock lock(user_gesture_lock_);
    208   if (user_gesture_observers_.empty())
    209     return;  // Don't need to check if nobody is listening.
    210 
    211   if (GamepadsHaveUserGesture(SharedMemoryAsHardwareBuffer()->buffer)) {
    212     for (size_t i = 0; i < user_gesture_observers_.size(); i++) {
    213       user_gesture_observers_[i].message_loop->PostTask(FROM_HERE,
    214           user_gesture_observers_[i].closure);
    215     }
    216     user_gesture_observers_.clear();
    217   }
    218 }
    219 
    220 }  // namespace content
    221