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(blink::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