Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(312)

Side by Side Diff: content/browser/gamepad/gamepad_provider.cc

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

Powered by Google App Engine
This is Rietveld 408576698