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

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

Issue 1586663006: Refactoring gamepad polling to support dynamically added sources (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Avoid crash on Android content_unittests Created 4 years, 11 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
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 "content/browser/gamepad/gamepad_provider.h" 5 #include "content/browser/gamepad/gamepad_provider.h"
6 6
7 #include <stddef.h> 7 #include <stddef.h>
8 #include <string.h> 8 #include <string.h>
9 #include <cmath> 9 #include <cmath>
10 #include <set>
11 #include <utility> 10 #include <utility>
12 #include <vector> 11 #include <vector>
13 12
14 #include "base/bind.h" 13 #include "base/bind.h"
15 #include "base/location.h" 14 #include "base/location.h"
16 #include "base/logging.h" 15 #include "base/logging.h"
17 #include "base/single_thread_task_runner.h" 16 #include "base/single_thread_task_runner.h"
18 #include "base/third_party/dynamic_annotations/dynamic_annotations.h" 17 #include "base/third_party/dynamic_annotations/dynamic_annotations.h"
19 #include "base/thread_task_runner_handle.h" 18 #include "base/thread_task_runner_handle.h"
20 #include "base/threading/thread.h" 19 #include "base/threading/thread.h"
21 #include "base/threading/thread_restrictions.h" 20 #include "base/threading/thread_restrictions.h"
22 #include "build/build_config.h" 21 #include "build/build_config.h"
23 #include "content/browser/gamepad/gamepad_data_fetcher.h" 22 #include "content/browser/gamepad/gamepad_data_fetcher.h"
24 #include "content/browser/gamepad/gamepad_platform_data_fetcher.h" 23 #include "content/browser/gamepad/gamepad_platform_data_fetcher.h"
25 #include "content/browser/gamepad/gamepad_service.h" 24 #include "content/browser/gamepad/gamepad_service.h"
26 #include "content/common/gamepad_hardware_buffer.h" 25 #include "content/common/gamepad_hardware_buffer.h"
27 #include "content/common/gamepad_messages.h" 26 #include "content/common/gamepad_messages.h"
28 #include "content/common/gamepad_user_gesture.h" 27 #include "content/common/gamepad_user_gesture.h"
29 #include "content/public/browser/browser_thread.h" 28 #include "content/public/browser/browser_thread.h"
30 29
31 using blink::WebGamepad; 30 using blink::WebGamepad;
32 using blink::WebGamepads; 31 using blink::WebGamepads;
33 32
33 namespace {
34
35 const float kMinAxisResetValue = 0.1f;
36
37 } // namespace
38
34 namespace content { 39 namespace content {
35 40
36 GamepadProvider::ClosureAndThread::ClosureAndThread( 41 GamepadProvider::ClosureAndThread::ClosureAndThread(
37 const base::Closure& c, 42 const base::Closure& c,
38 const scoped_refptr<base::SingleThreadTaskRunner>& m) 43 const scoped_refptr<base::SingleThreadTaskRunner>& m)
39 : closure(c), task_runner(m) { 44 : closure(c), task_runner(m) {
40 } 45 }
41 46
42 GamepadProvider::ClosureAndThread::~ClosureAndThread() { 47 GamepadProvider::ClosureAndThread::~ClosureAndThread() {
43 } 48 }
44 49
45 GamepadProvider::GamepadProvider() 50 GamepadProvider::GamepadProvider()
46 : is_paused_(true), 51 : is_paused_(true),
47 have_scheduled_do_poll_(false), 52 have_scheduled_do_poll_(false),
48 devices_changed_(true), 53 devices_changed_(true),
49 ever_had_user_gesture_(false) { 54 ever_had_user_gesture_(false),
55 sanitize_(true) {
50 Initialize(scoped_ptr<GamepadDataFetcher>()); 56 Initialize(scoped_ptr<GamepadDataFetcher>());
51 } 57 }
52 58
53 GamepadProvider::GamepadProvider(scoped_ptr<GamepadDataFetcher> fetcher) 59 GamepadProvider::GamepadProvider(scoped_ptr<GamepadDataFetcher> fetcher)
54 : is_paused_(true), 60 : is_paused_(true),
55 have_scheduled_do_poll_(false), 61 have_scheduled_do_poll_(false),
56 devices_changed_(true), 62 devices_changed_(true),
57 ever_had_user_gesture_(false) { 63 ever_had_user_gesture_(false) {
58 Initialize(std::move(fetcher)); 64 Initialize(std::move(fetcher));
59 } 65 }
60 66
61 GamepadProvider::~GamepadProvider() { 67 GamepadProvider::~GamepadProvider() {
62 base::SystemMonitor* monitor = base::SystemMonitor::Get(); 68 base::SystemMonitor* monitor = base::SystemMonitor::Get();
63 if (monitor) 69 if (monitor)
64 monitor->RemoveDevicesChangedObserver(this); 70 monitor->RemoveDevicesChangedObserver(this);
65 71
66 // Use Stop() to join the polling thread, as there may be pending callbacks 72 // Use Stop() to join the polling thread, as there may be pending callbacks
67 // which dereference |polling_thread_|. 73 // which dereference |polling_thread_|.
68 polling_thread_->Stop(); 74 polling_thread_->Stop();
69 data_fetcher_.reset();
70 } 75 }
71 76
72 base::SharedMemoryHandle GamepadProvider::GetSharedMemoryHandleForProcess( 77 base::SharedMemoryHandle GamepadProvider::GetSharedMemoryHandleForProcess(
73 base::ProcessHandle process) { 78 base::ProcessHandle process) {
74 base::SharedMemoryHandle renderer_handle; 79 base::SharedMemoryHandle renderer_handle;
75 gamepad_shared_memory_.ShareToProcess(process, &renderer_handle); 80 gamepad_shared_memory_.ShareToProcess(process, &renderer_handle);
76 return renderer_handle; 81 return renderer_handle;
77 } 82 }
78 83
79 void GamepadProvider::GetCurrentGamepadData(WebGamepads* data) { 84 void GamepadProvider::GetCurrentGamepadData(WebGamepads* data) {
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
125 size_t data_size = sizeof(GamepadHardwareBuffer); 130 size_t data_size = sizeof(GamepadHardwareBuffer);
126 base::SystemMonitor* monitor = base::SystemMonitor::Get(); 131 base::SystemMonitor* monitor = base::SystemMonitor::Get();
127 if (monitor) 132 if (monitor)
128 monitor->AddDevicesChangedObserver(this); 133 monitor->AddDevicesChangedObserver(this);
129 bool res = gamepad_shared_memory_.CreateAndMapAnonymous(data_size); 134 bool res = gamepad_shared_memory_.CreateAndMapAnonymous(data_size);
130 CHECK(res); 135 CHECK(res);
131 GamepadHardwareBuffer* hwbuf = SharedMemoryAsHardwareBuffer(); 136 GamepadHardwareBuffer* hwbuf = SharedMemoryAsHardwareBuffer();
132 memset(hwbuf, 0, sizeof(GamepadHardwareBuffer)); 137 memset(hwbuf, 0, sizeof(GamepadHardwareBuffer));
133 pad_states_.reset(new PadState[WebGamepads::itemsLengthCap]); 138 pad_states_.reset(new PadState[WebGamepads::itemsLengthCap]);
134 139
140 for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i)
141 ClearPadState(pad_states_.get()[i]);
142
135 polling_thread_.reset(new base::Thread("Gamepad polling thread")); 143 polling_thread_.reset(new base::Thread("Gamepad polling thread"));
136 #if defined(OS_LINUX) 144 #if defined(OS_LINUX)
137 // On Linux, the data fetcher needs to watch file descriptors, so the message 145 // On Linux, the data fetcher needs to watch file descriptors, so the message
138 // loop needs to be a libevent loop. 146 // loop needs to be a libevent loop.
139 const base::MessageLoop::Type kMessageLoopType = base::MessageLoop::TYPE_IO; 147 const base::MessageLoop::Type kMessageLoopType = base::MessageLoop::TYPE_IO;
140 #elif defined(OS_ANDROID) 148 #elif defined(OS_ANDROID)
141 // On Android, keeping a message loop of default type. 149 // On Android, keeping a message loop of default type.
142 const base::MessageLoop::Type kMessageLoopType = 150 const base::MessageLoop::Type kMessageLoopType =
143 base::MessageLoop::TYPE_DEFAULT; 151 base::MessageLoop::TYPE_DEFAULT;
144 #else 152 #else
145 // On Mac, the data fetcher uses IOKit which depends on CFRunLoop, so the 153 // On Mac, the data fetcher uses IOKit which depends on CFRunLoop, so the
146 // message loop needs to be a UI-type loop. On Windows it must be a UI loop 154 // message loop needs to be a UI-type loop. On Windows it must be a UI loop
147 // to properly pump the MessageWindow that captures device state. 155 // to properly pump the MessageWindow that captures device state.
148 const base::MessageLoop::Type kMessageLoopType = base::MessageLoop::TYPE_UI; 156 const base::MessageLoop::Type kMessageLoopType = base::MessageLoop::TYPE_UI;
149 #endif 157 #endif
150 polling_thread_->StartWithOptions(base::Thread::Options(kMessageLoopType, 0)); 158 polling_thread_->StartWithOptions(base::Thread::Options(kMessageLoopType, 0));
151 159
160 if (fetcher) {
161 AddGamepadDataFetcher(std::move(fetcher));
162 } else {
163 AddGamepadPlatformDataFetchers(this);
164 }
165 }
166
167 void GamepadProvider::AddGamepadDataFetcher(
168 scoped_ptr<GamepadDataFetcher> fetcher) {
152 polling_thread_->task_runner()->PostTask( 169 polling_thread_->task_runner()->PostTask(
153 FROM_HERE, base::Bind(&GamepadProvider::DoInitializePollingThread, 170 FROM_HERE, base::Bind(&GamepadProvider::DoAddGamepadDataFetcher,
154 base::Unretained(this), base::Passed(&fetcher))); 171 base::Unretained(this), base::Passed(&fetcher)));
155 } 172 }
156 173
157 void GamepadProvider::DoInitializePollingThread( 174 void GamepadProvider::DoAddGamepadDataFetcher(
158 scoped_ptr<GamepadDataFetcher> fetcher) { 175 scoped_ptr<GamepadDataFetcher> fetcher) {
159 DCHECK(base::MessageLoop::current() == polling_thread_->message_loop()); 176 DCHECK(base::MessageLoop::current() == polling_thread_->message_loop());
160 DCHECK(!data_fetcher_.get()); // Should only initialize once.
161 177
162 if (!fetcher) 178 fetcher->InitializeProvider(this);
163 fetcher.reset(new GamepadPlatformDataFetcher); 179
164 data_fetcher_ = std::move(fetcher); 180 data_fetchers_.push_back(std::move(fetcher));
165 } 181 }
166 182
167 void GamepadProvider::SendPauseHint(bool paused) { 183 void GamepadProvider::SendPauseHint(bool paused) {
168 DCHECK(base::MessageLoop::current() == polling_thread_->message_loop()); 184 DCHECK(base::MessageLoop::current() == polling_thread_->message_loop());
169 if (data_fetcher_) 185 for (const auto& it : data_fetchers_) {
170 data_fetcher_->PauseHint(paused); 186 it->PauseHint(paused);
171 } 187 }
172
173 bool GamepadProvider::PadState::Match(const WebGamepad& pad) const {
174 return connected_ == pad.connected &&
175 axes_length_ == pad.axesLength &&
176 buttons_length_ == pad.buttonsLength &&
177 memcmp(id_, pad.id, sizeof(id_)) == 0 &&
178 memcmp(mapping_, pad.mapping, sizeof(mapping_)) == 0;
179 }
180
181 void GamepadProvider::PadState::SetPad(const WebGamepad& pad) {
182 connected_ = pad.connected;
183 axes_length_ = pad.axesLength;
184 buttons_length_ = pad.buttonsLength;
185 memcpy(id_, pad.id, sizeof(id_));
186 memcpy(mapping_, pad.mapping, sizeof(mapping_));
187 }
188
189 void GamepadProvider::PadState::SetDisconnected() {
190 connected_ = false;
191 axes_length_ = 0;
192 buttons_length_ = 0;
193 memset(id_, 0, sizeof(id_));
194 memset(mapping_, 0, sizeof(mapping_));
195 }
196
197 void GamepadProvider::PadState::AsWebGamepad(WebGamepad* pad) {
198 pad->connected = connected_;
199 pad->axesLength = axes_length_;
200 pad->buttonsLength = buttons_length_;
201 memcpy(pad->id, id_, sizeof(id_));
202 memcpy(pad->mapping, mapping_, sizeof(mapping_));
203 memset(pad->axes, 0, sizeof(pad->axes));
204 memset(pad->buttons, 0, sizeof(pad->buttons));
205 } 188 }
206 189
207 void GamepadProvider::DoPoll() { 190 void GamepadProvider::DoPoll() {
208 DCHECK(base::MessageLoop::current() == polling_thread_->message_loop()); 191 DCHECK(base::MessageLoop::current() == polling_thread_->message_loop());
209 DCHECK(have_scheduled_do_poll_); 192 DCHECK(have_scheduled_do_poll_);
210 have_scheduled_do_poll_ = false; 193 have_scheduled_do_poll_ = false;
211 194
212 bool changed; 195 bool changed;
213 GamepadHardwareBuffer* hwbuf = SharedMemoryAsHardwareBuffer(); 196 GamepadHardwareBuffer* hwbuf = SharedMemoryAsHardwareBuffer();
214 197
215 ANNOTATE_BENIGN_RACE_SIZED( 198 ANNOTATE_BENIGN_RACE_SIZED(
216 &hwbuf->buffer, 199 &hwbuf->buffer,
217 sizeof(WebGamepads), 200 sizeof(WebGamepads),
218 "Racey reads are discarded"); 201 "Racey reads are discarded");
219 202
220 { 203 {
221 base::AutoLock lock(devices_changed_lock_); 204 base::AutoLock lock(devices_changed_lock_);
222 changed = devices_changed_; 205 changed = devices_changed_;
223 devices_changed_ = false; 206 devices_changed_ = false;
224 } 207 }
225 208
209 // Loop through each registered data fetcher and poll its gamepad data.
210 // It's expected that GetGamepadData will mark each gamepad as active (via
211 // GetPadState). If a gamepad is not marked as active during the calls to
212 // GetGamepadData then it's assumed to be disconnected.
213 for (const auto& it : data_fetchers_) {
214 it->GetGamepadData(changed);
215 }
216
217 // Send out disconnect events using the last polled data before we wipe it out
218 // in the mapping step.
219 if (ever_had_user_gesture_) {
220 for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) {
221 PadState& state = pad_states_.get()[i];
222
223 if (!state.active_state && state.source != GAMEPAD_SOURCE_NONE) {
224 OnGamepadConnectionChange(false, i, hwbuf->buffer.items[i]);
225 ClearPadState(state);
226 }
227 }
228 }
229
226 { 230 {
227 base::AutoLock lock(shared_memory_lock_); 231 base::AutoLock lock(shared_memory_lock_);
228 232
229 // Acquire the SeqLock. There is only ever one writer to this data. 233 // Acquire the SeqLock. There is only ever one writer to this data.
230 // See gamepad_hardware_buffer.h. 234 // See gamepad_hardware_buffer.h.
231 hwbuf->sequence.WriteBegin(); 235 hwbuf->sequence.WriteBegin();
232 data_fetcher_->GetGamepadData(&hwbuf->buffer, changed); 236 hwbuf->buffer.length = 0;
237 for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) {
238 PadState& state = pad_states_.get()[i];
239 // Must run through the map+sanitize here or CheckForUserGesture may fail.
240 MapAndSanitizeGamepadData(&state, &hwbuf->buffer.items[i]);
241 if (state.active_state)
242 hwbuf->buffer.length++;
243 }
233 hwbuf->sequence.WriteEnd(); 244 hwbuf->sequence.WriteEnd();
234 } 245 }
235 246
236 if (ever_had_user_gesture_) { 247 if (ever_had_user_gesture_) {
237 for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) { 248 for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) {
238 WebGamepad& pad = hwbuf->buffer.items[i];
239 PadState& state = pad_states_.get()[i]; 249 PadState& state = pad_states_.get()[i];
240 if (pad.connected && !state.connected()) { 250
241 OnGamepadConnectionChange(true, i, pad); 251 if (state.active_state) {
242 } else if (!pad.connected && state.connected()) { 252 if (state.active_state == GAMEPAD_NEWLY_ACTIVE)
243 OnGamepadConnectionChange(false, i, pad); 253 OnGamepadConnectionChange(true, i, hwbuf->buffer.items[i]);
244 } else if (pad.connected && state.connected() && !state.Match(pad)) { 254 state.active_state = GAMEPAD_INACTIVE;
245 WebGamepad old_pad;
246 state.AsWebGamepad(&old_pad);
247 OnGamepadConnectionChange(false, i, old_pad);
248 OnGamepadConnectionChange(true, i, pad);
249 } 255 }
250 } 256 }
251 } 257 }
252 258
253 CheckForUserGesture(); 259 CheckForUserGesture();
254 260
255 // Schedule our next interval of polling. 261 // Schedule our next interval of polling.
256 ScheduleDoPoll(); 262 ScheduleDoPoll();
257 } 263 }
258 264
259 void GamepadProvider::ScheduleDoPoll() { 265 void GamepadProvider::ScheduleDoPoll() {
260 DCHECK(base::MessageLoop::current() == polling_thread_->message_loop()); 266 DCHECK(base::MessageLoop::current() == polling_thread_->message_loop());
261 if (have_scheduled_do_poll_) 267 if (have_scheduled_do_poll_)
262 return; 268 return;
263 269
264 { 270 {
265 base::AutoLock lock(is_paused_lock_); 271 base::AutoLock lock(is_paused_lock_);
266 if (is_paused_) 272 if (is_paused_)
267 return; 273 return;
268 } 274 }
269 275
270 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( 276 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
271 FROM_HERE, base::Bind(&GamepadProvider::DoPoll, Unretained(this)), 277 FROM_HERE, base::Bind(&GamepadProvider::DoPoll, Unretained(this)),
272 base::TimeDelta::FromMilliseconds(kDesiredSamplingIntervalMs)); 278 base::TimeDelta::FromMilliseconds(kDesiredSamplingIntervalMs));
273 have_scheduled_do_poll_ = true; 279 have_scheduled_do_poll_ = true;
274 } 280 }
275 281
276 void GamepadProvider::OnGamepadConnectionChange( 282 void GamepadProvider::OnGamepadConnectionChange(
277 bool connected, int index, const WebGamepad& pad) { 283 bool connected, int index, const WebGamepad& pad) {
278 PadState& state = pad_states_.get()[index];
279 if (connected)
280 state.SetPad(pad);
281 else
282 state.SetDisconnected();
283
284 BrowserThread::PostTask( 284 BrowserThread::PostTask(
285 BrowserThread::IO, 285 BrowserThread::IO,
286 FROM_HERE, 286 FROM_HERE,
287 base::Bind(&GamepadProvider::DispatchGamepadConnectionChange, 287 base::Bind(&GamepadProvider::DispatchGamepadConnectionChange,
288 base::Unretained(this), 288 base::Unretained(this),
289 connected, 289 connected,
290 index, 290 index,
291 pad)); 291 pad));
292 } 292 }
293 293
294 void GamepadProvider::DispatchGamepadConnectionChange( 294 void GamepadProvider::DispatchGamepadConnectionChange(
295 bool connected, int index, const WebGamepad& pad) { 295 bool connected, int index, const WebGamepad& pad) {
296 if (connected) 296 if (connected)
297 GamepadService::GetInstance()->OnGamepadConnected(index, pad); 297 GamepadService::GetInstance()->OnGamepadConnected(index, pad);
298 else 298 else
299 GamepadService::GetInstance()->OnGamepadDisconnected(index, pad); 299 GamepadService::GetInstance()->OnGamepadDisconnected(index, pad);
300 } 300 }
301 301
302 GamepadHardwareBuffer* GamepadProvider::SharedMemoryAsHardwareBuffer() { 302 GamepadHardwareBuffer* GamepadProvider::SharedMemoryAsHardwareBuffer() {
303 void* mem = gamepad_shared_memory_.memory(); 303 void* mem = gamepad_shared_memory_.memory();
304 CHECK(mem); 304 CHECK(mem);
305 return static_cast<GamepadHardwareBuffer*>(mem); 305 return static_cast<GamepadHardwareBuffer*>(mem);
306 } 306 }
307 307
308 void GamepadProvider::CheckForUserGesture() { 308 void GamepadProvider::CheckForUserGesture() {
309 base::AutoLock lock(user_gesture_lock_); 309 base::AutoLock lock(user_gesture_lock_);
310 if (user_gesture_observers_.empty() && ever_had_user_gesture_) 310 if (user_gesture_observers_.empty() && ever_had_user_gesture_)
311 return; 311 return;
312 312
313 bool had_gesture_before = ever_had_user_gesture_;
314 const WebGamepads& pads = SharedMemoryAsHardwareBuffer()->buffer; 313 const WebGamepads& pads = SharedMemoryAsHardwareBuffer()->buffer;
315 if (GamepadsHaveUserGesture(pads)) { 314 if (GamepadsHaveUserGesture(pads)) {
316 ever_had_user_gesture_ = true; 315 ever_had_user_gesture_ = true;
317 for (size_t i = 0; i < user_gesture_observers_.size(); i++) { 316 for (size_t i = 0; i < user_gesture_observers_.size(); i++) {
318 user_gesture_observers_[i].task_runner->PostTask( 317 user_gesture_observers_[i].task_runner->PostTask(
319 FROM_HERE, user_gesture_observers_[i].closure); 318 FROM_HERE, user_gesture_observers_[i].closure);
320 } 319 }
321 user_gesture_observers_.clear(); 320 user_gesture_observers_.clear();
322 } 321 }
323 if (!had_gesture_before && ever_had_user_gesture_) { 322 }
324 // Initialize pad_states_ for the first time. 323
325 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) { 324 void GamepadProvider::ClearPadState(PadState& state) {
326 pad_states_.get()[i].SetPad(pads.items[i]); 325 memset(&state, 0, sizeof(PadState));
326 }
327
328 PadState* GamepadProvider::GetPadState(GamepadSource source, int source_id) {
329 // Check to see if the device already has a reserved slot
330 PadState* empty_slot = nullptr;
331 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) {
332 PadState& state = pad_states_.get()[i];
333 if (state.source == source &&
334 state.source_id == source_id) {
335 // Retrieving the pad state marks this gamepad as active.
336 state.active_state = GAMEPAD_ACTIVE;
337 return &state;
338 }
339 if (!empty_slot && state.source == GAMEPAD_SOURCE_NONE)
340 empty_slot = &state;
341 }
342 if (empty_slot) {
343 empty_slot->source = source;
344 empty_slot->source_id = source_id;
345 empty_slot->active_state = GAMEPAD_NEWLY_ACTIVE;
346 }
347 return empty_slot;
348 }
349
350 void GamepadProvider::MapAndSanitizeGamepadData(
351 PadState* pad_state, WebGamepad* pad) {
352 DCHECK(pad_state);
353 DCHECK(pad);
354
355 if (!pad_state->active_state) {
356 memset(pad, 0, sizeof(WebGamepad));
357 return;
358 }
359
360 // Copy the current state to the output buffer, using the mapping
361 // function, if there is one available.
362 if (pad_state->mapper)
363 pad_state->mapper(pad_state->data, pad);
364 else
365 *pad = pad_state->data;
366
367 pad->connected = true;
368
369 if (!sanitize_)
370 return;
371
372 // About sanitization: Gamepads may report input event if the user is not
373 // interacting with it, due to hardware problems or environmental ones (pad
374 // has something heavy leaning against an axis.) This may cause user gestures
375 // to be detected erroniously, exposing gamepad information when the user had
376 // no intention of doing so. To avoid this we require that each button or axis
377 // report being at rest (zero) at least once before exposing its value to the
378 // Gamepad API. This state is tracked by the axis_mask and button_mask
379 // bitfields. If the bit for an axis or button is 0 it means the axis has
380 // never reported being at rest, and the value will be forced to zero.
381
382 // We can skip axis sanitation if all available axes have been masked.
383 uint32_t full_axis_mask = (1 << pad->axesLength) - 1;
384 if (pad_state->axis_mask != full_axis_mask) {
385 for (size_t axis = 0; axis < pad->axesLength; ++axis) {
386 if (!(pad_state->axis_mask & 1 << axis)) {
387 if (fabs(pad->axes[axis]) < kMinAxisResetValue) {
388 pad_state->axis_mask |= 1 << axis;
389 } else {
390 pad->axes[axis] = 0.0f;
391 }
392 }
393 }
394 }
395
396 // We can skip button sanitation if all available buttons have been masked.
397 uint32_t full_button_mask = (1 << pad->buttonsLength) - 1;
398 if (pad_state->button_mask != full_button_mask) {
399 for (size_t button = 0; button < pad->buttonsLength; ++button) {
400 if (!(pad_state->button_mask & 1 << button)) {
401 if (!pad->buttons[button].pressed) {
402 pad_state->button_mask |= 1 << button;
403 } else {
404 pad->buttons[button].pressed = false;
405 pad->buttons[button].value = 0.0f;
406 }
407 }
327 } 408 }
328 } 409 }
329 } 410 }
330 411
331 } // namespace content 412 } // namespace content
OLDNEW
« no previous file with comments | « content/browser/gamepad/gamepad_provider.h ('k') | content/browser/gamepad/gamepad_provider_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698