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 24using blink::WebGamepad; 25using blink::WebGamepads; 26 27namespace content { 28 29GamepadProvider::ClosureAndThread::ClosureAndThread( 30 const base::Closure& c, 31 const scoped_refptr<base::MessageLoopProxy>& m) 32 : closure(c), 33 message_loop(m) { 34} 35 36GamepadProvider::ClosureAndThread::~ClosureAndThread() { 37} 38 39GamepadProvider::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 47GamepadProvider::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 55GamepadProvider::~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 66base::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 73void GamepadProvider::GetCurrentGamepadData(WebGamepads* data) { 74 const WebGamepads& pads = SharedMemoryAsHardwareBuffer()->buffer; 75 base::AutoLock lock(shared_memory_lock_); 76 *data = pads; 77} 78 79void 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 90void 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 107void 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 113void GamepadProvider::OnDevicesChanged(base::SystemMonitor::DeviceType type) { 114 base::AutoLock lock(devices_changed_lock_); 115 devices_changed_ = true; 116} 117 118void 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 153void 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 163void GamepadProvider::SendPauseHint(bool paused) { 164 DCHECK(base::MessageLoop::current() == polling_thread_->message_loop()); 165 if (data_fetcher_) 166 data_fetcher_->PauseHint(paused); 167} 168 169bool 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 177void 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 185void 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 193void 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 203void 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 255void 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 273void 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 291void 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 299GamepadHardwareBuffer* GamepadProvider::SharedMemoryAsHardwareBuffer() { 300 void* mem = gamepad_shared_memory_.memory(); 301 CHECK(mem); 302 return static_cast<GamepadHardwareBuffer*>(mem); 303} 304 305void 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