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

Side by Side Diff: device/gamepad/gamepad_provider.cc

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

Powered by Google App Engine
This is Rietveld 408576698