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