OLD | NEW |
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 Loading... |
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 Loading... |
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 |
OLD | NEW |