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 "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> |
10 #include <utility> | 11 #include <utility> |
11 #include <vector> | 12 #include <vector> |
12 | 13 |
13 #include "base/bind.h" | 14 #include "base/bind.h" |
14 #include "base/location.h" | 15 #include "base/location.h" |
15 #include "base/logging.h" | 16 #include "base/logging.h" |
16 #include "base/single_thread_task_runner.h" | 17 #include "base/single_thread_task_runner.h" |
17 #include "base/third_party/dynamic_annotations/dynamic_annotations.h" | 18 #include "base/third_party/dynamic_annotations/dynamic_annotations.h" |
18 #include "base/thread_task_runner_handle.h" | 19 #include "base/thread_task_runner_handle.h" |
19 #include "base/threading/thread.h" | 20 #include "base/threading/thread.h" |
20 #include "base/threading/thread_restrictions.h" | 21 #include "base/threading/thread_restrictions.h" |
21 #include "build/build_config.h" | 22 #include "build/build_config.h" |
22 #include "content/browser/gamepad/gamepad_data_fetcher.h" | 23 #include "content/browser/gamepad/gamepad_data_fetcher.h" |
23 #include "content/browser/gamepad/gamepad_platform_data_fetcher.h" | 24 #include "content/browser/gamepad/gamepad_platform_data_fetcher.h" |
24 #include "content/browser/gamepad/gamepad_service.h" | 25 #include "content/browser/gamepad/gamepad_service.h" |
25 #include "content/common/gamepad_hardware_buffer.h" | 26 #include "content/common/gamepad_hardware_buffer.h" |
26 #include "content/common/gamepad_messages.h" | 27 #include "content/common/gamepad_messages.h" |
27 #include "content/common/gamepad_user_gesture.h" | 28 #include "content/common/gamepad_user_gesture.h" |
28 #include "content/public/browser/browser_thread.h" | 29 #include "content/public/browser/browser_thread.h" |
29 | 30 |
30 using blink::WebGamepad; | 31 using blink::WebGamepad; |
31 using blink::WebGamepads; | 32 using blink::WebGamepads; |
32 | 33 |
33 namespace { | |
34 | |
35 const float kMinAxisResetValue = 0.1f; | |
36 | |
37 } // namespace | |
38 | |
39 namespace content { | 34 namespace content { |
40 | 35 |
41 GamepadProvider::ClosureAndThread::ClosureAndThread( | 36 GamepadProvider::ClosureAndThread::ClosureAndThread( |
42 const base::Closure& c, | 37 const base::Closure& c, |
43 const scoped_refptr<base::SingleThreadTaskRunner>& m) | 38 const scoped_refptr<base::SingleThreadTaskRunner>& m) |
44 : closure(c), task_runner(m) { | 39 : closure(c), task_runner(m) { |
45 } | 40 } |
46 | 41 |
47 GamepadProvider::ClosureAndThread::~ClosureAndThread() { | 42 GamepadProvider::ClosureAndThread::~ClosureAndThread() { |
48 } | 43 } |
49 | 44 |
50 GamepadProvider::GamepadProvider() | 45 GamepadProvider::GamepadProvider() |
51 : is_paused_(true), | 46 : is_paused_(true), |
52 have_scheduled_do_poll_(false), | 47 have_scheduled_do_poll_(false), |
53 devices_changed_(true), | 48 devices_changed_(true), |
54 ever_had_user_gesture_(false), | 49 ever_had_user_gesture_(false) { |
55 sanitize_(true) { | |
56 Initialize(scoped_ptr<GamepadDataFetcher>()); | 50 Initialize(scoped_ptr<GamepadDataFetcher>()); |
57 } | 51 } |
58 | 52 |
59 GamepadProvider::GamepadProvider(scoped_ptr<GamepadDataFetcher> fetcher) | 53 GamepadProvider::GamepadProvider(scoped_ptr<GamepadDataFetcher> fetcher) |
60 : is_paused_(true), | 54 : is_paused_(true), |
61 have_scheduled_do_poll_(false), | 55 have_scheduled_do_poll_(false), |
62 devices_changed_(true), | 56 devices_changed_(true), |
63 ever_had_user_gesture_(false) { | 57 ever_had_user_gesture_(false) { |
64 Initialize(std::move(fetcher)); | 58 Initialize(std::move(fetcher)); |
65 } | 59 } |
66 | 60 |
67 GamepadProvider::~GamepadProvider() { | 61 GamepadProvider::~GamepadProvider() { |
68 base::SystemMonitor* monitor = base::SystemMonitor::Get(); | 62 base::SystemMonitor* monitor = base::SystemMonitor::Get(); |
69 if (monitor) | 63 if (monitor) |
70 monitor->RemoveDevicesChangedObserver(this); | 64 monitor->RemoveDevicesChangedObserver(this); |
71 | 65 |
72 // Use Stop() to join the polling thread, as there may be pending callbacks | 66 // Use Stop() to join the polling thread, as there may be pending callbacks |
73 // which dereference |polling_thread_|. | 67 // which dereference |polling_thread_|. |
74 polling_thread_->Stop(); | 68 polling_thread_->Stop(); |
| 69 data_fetcher_.reset(); |
75 } | 70 } |
76 | 71 |
77 base::SharedMemoryHandle GamepadProvider::GetSharedMemoryHandleForProcess( | 72 base::SharedMemoryHandle GamepadProvider::GetSharedMemoryHandleForProcess( |
78 base::ProcessHandle process) { | 73 base::ProcessHandle process) { |
79 base::SharedMemoryHandle renderer_handle; | 74 base::SharedMemoryHandle renderer_handle; |
80 gamepad_shared_memory_.ShareToProcess(process, &renderer_handle); | 75 gamepad_shared_memory_.ShareToProcess(process, &renderer_handle); |
81 return renderer_handle; | 76 return renderer_handle; |
82 } | 77 } |
83 | 78 |
84 void GamepadProvider::GetCurrentGamepadData(WebGamepads* data) { | 79 void GamepadProvider::GetCurrentGamepadData(WebGamepads* data) { |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
130 size_t data_size = sizeof(GamepadHardwareBuffer); | 125 size_t data_size = sizeof(GamepadHardwareBuffer); |
131 base::SystemMonitor* monitor = base::SystemMonitor::Get(); | 126 base::SystemMonitor* monitor = base::SystemMonitor::Get(); |
132 if (monitor) | 127 if (monitor) |
133 monitor->AddDevicesChangedObserver(this); | 128 monitor->AddDevicesChangedObserver(this); |
134 bool res = gamepad_shared_memory_.CreateAndMapAnonymous(data_size); | 129 bool res = gamepad_shared_memory_.CreateAndMapAnonymous(data_size); |
135 CHECK(res); | 130 CHECK(res); |
136 GamepadHardwareBuffer* hwbuf = SharedMemoryAsHardwareBuffer(); | 131 GamepadHardwareBuffer* hwbuf = SharedMemoryAsHardwareBuffer(); |
137 memset(hwbuf, 0, sizeof(GamepadHardwareBuffer)); | 132 memset(hwbuf, 0, sizeof(GamepadHardwareBuffer)); |
138 pad_states_.reset(new PadState[WebGamepads::itemsLengthCap]); | 133 pad_states_.reset(new PadState[WebGamepads::itemsLengthCap]); |
139 | 134 |
140 for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) | |
141 ClearPadState(pad_states_.get()[i]); | |
142 | |
143 polling_thread_.reset(new base::Thread("Gamepad polling thread")); | 135 polling_thread_.reset(new base::Thread("Gamepad polling thread")); |
144 #if defined(OS_LINUX) | 136 #if defined(OS_LINUX) |
145 // On Linux, the data fetcher needs to watch file descriptors, so the message | 137 // On Linux, the data fetcher needs to watch file descriptors, so the message |
146 // loop needs to be a libevent loop. | 138 // loop needs to be a libevent loop. |
147 const base::MessageLoop::Type kMessageLoopType = base::MessageLoop::TYPE_IO; | 139 const base::MessageLoop::Type kMessageLoopType = base::MessageLoop::TYPE_IO; |
148 #elif defined(OS_ANDROID) | 140 #elif defined(OS_ANDROID) |
149 // On Android, keeping a message loop of default type. | 141 // On Android, keeping a message loop of default type. |
150 const base::MessageLoop::Type kMessageLoopType = | 142 const base::MessageLoop::Type kMessageLoopType = |
151 base::MessageLoop::TYPE_DEFAULT; | 143 base::MessageLoop::TYPE_DEFAULT; |
152 #else | 144 #else |
153 // On Mac, the data fetcher uses IOKit which depends on CFRunLoop, so the | 145 // On Mac, the data fetcher uses IOKit which depends on CFRunLoop, so the |
154 // message loop needs to be a UI-type loop. On Windows it must be a UI loop | 146 // message loop needs to be a UI-type loop. On Windows it must be a UI loop |
155 // to properly pump the MessageWindow that captures device state. | 147 // to properly pump the MessageWindow that captures device state. |
156 const base::MessageLoop::Type kMessageLoopType = base::MessageLoop::TYPE_UI; | 148 const base::MessageLoop::Type kMessageLoopType = base::MessageLoop::TYPE_UI; |
157 #endif | 149 #endif |
158 polling_thread_->StartWithOptions(base::Thread::Options(kMessageLoopType, 0)); | 150 polling_thread_->StartWithOptions(base::Thread::Options(kMessageLoopType, 0)); |
159 | 151 |
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) { | |
169 polling_thread_->task_runner()->PostTask( | 152 polling_thread_->task_runner()->PostTask( |
170 FROM_HERE, base::Bind(&GamepadProvider::DoAddGamepadDataFetcher, | 153 FROM_HERE, base::Bind(&GamepadProvider::DoInitializePollingThread, |
171 base::Unretained(this), base::Passed(&fetcher))); | 154 base::Unretained(this), base::Passed(&fetcher))); |
172 } | 155 } |
173 | 156 |
174 void GamepadProvider::DoAddGamepadDataFetcher( | 157 void GamepadProvider::DoInitializePollingThread( |
175 scoped_ptr<GamepadDataFetcher> fetcher) { | 158 scoped_ptr<GamepadDataFetcher> fetcher) { |
176 DCHECK(base::MessageLoop::current() == polling_thread_->message_loop()); | 159 DCHECK(base::MessageLoop::current() == polling_thread_->message_loop()); |
| 160 DCHECK(!data_fetcher_.get()); // Should only initialize once. |
177 | 161 |
178 fetcher->InitializeProvider(this); | 162 if (!fetcher) |
179 | 163 fetcher.reset(new GamepadPlatformDataFetcher); |
180 data_fetchers_.push_back(std::move(fetcher)); | 164 data_fetcher_ = std::move(fetcher); |
181 } | 165 } |
182 | 166 |
183 void GamepadProvider::SendPauseHint(bool paused) { | 167 void GamepadProvider::SendPauseHint(bool paused) { |
184 DCHECK(base::MessageLoop::current() == polling_thread_->message_loop()); | 168 DCHECK(base::MessageLoop::current() == polling_thread_->message_loop()); |
185 for (const auto& it : data_fetchers_) { | 169 if (data_fetcher_) |
186 it->PauseHint(paused); | 170 data_fetcher_->PauseHint(paused); |
187 } | 171 } |
| 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)); |
188 } | 205 } |
189 | 206 |
190 void GamepadProvider::DoPoll() { | 207 void GamepadProvider::DoPoll() { |
191 DCHECK(base::MessageLoop::current() == polling_thread_->message_loop()); | 208 DCHECK(base::MessageLoop::current() == polling_thread_->message_loop()); |
192 DCHECK(have_scheduled_do_poll_); | 209 DCHECK(have_scheduled_do_poll_); |
193 have_scheduled_do_poll_ = false; | 210 have_scheduled_do_poll_ = false; |
194 | 211 |
195 bool changed; | 212 bool changed; |
196 GamepadHardwareBuffer* hwbuf = SharedMemoryAsHardwareBuffer(); | 213 GamepadHardwareBuffer* hwbuf = SharedMemoryAsHardwareBuffer(); |
197 | 214 |
198 ANNOTATE_BENIGN_RACE_SIZED( | 215 ANNOTATE_BENIGN_RACE_SIZED( |
199 &hwbuf->buffer, | 216 &hwbuf->buffer, |
200 sizeof(WebGamepads), | 217 sizeof(WebGamepads), |
201 "Racey reads are discarded"); | 218 "Racey reads are discarded"); |
202 | 219 |
203 { | 220 { |
204 base::AutoLock lock(devices_changed_lock_); | 221 base::AutoLock lock(devices_changed_lock_); |
205 changed = devices_changed_; | 222 changed = devices_changed_; |
206 devices_changed_ = false; | 223 devices_changed_ = false; |
207 } | 224 } |
208 | 225 |
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 | |
230 { | 226 { |
231 base::AutoLock lock(shared_memory_lock_); | 227 base::AutoLock lock(shared_memory_lock_); |
232 | 228 |
233 // Acquire the SeqLock. There is only ever one writer to this data. | 229 // Acquire the SeqLock. There is only ever one writer to this data. |
234 // See gamepad_hardware_buffer.h. | 230 // See gamepad_hardware_buffer.h. |
235 hwbuf->sequence.WriteBegin(); | 231 hwbuf->sequence.WriteBegin(); |
236 hwbuf->buffer.length = 0; | 232 data_fetcher_->GetGamepadData(&hwbuf->buffer, changed); |
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 } | |
244 hwbuf->sequence.WriteEnd(); | 233 hwbuf->sequence.WriteEnd(); |
245 } | 234 } |
246 | 235 |
247 if (ever_had_user_gesture_) { | 236 if (ever_had_user_gesture_) { |
248 for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) { | 237 for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) { |
| 238 WebGamepad& pad = hwbuf->buffer.items[i]; |
249 PadState& state = pad_states_.get()[i]; | 239 PadState& state = pad_states_.get()[i]; |
250 | 240 if (pad.connected && !state.connected()) { |
251 if (state.active_state) { | 241 OnGamepadConnectionChange(true, i, pad); |
252 if (state.active_state == GAMEPAD_NEWLY_ACTIVE) | 242 } else if (!pad.connected && state.connected()) { |
253 OnGamepadConnectionChange(true, i, hwbuf->buffer.items[i]); | 243 OnGamepadConnectionChange(false, i, pad); |
254 state.active_state = GAMEPAD_INACTIVE; | 244 } else if (pad.connected && state.connected() && !state.Match(pad)) { |
| 245 WebGamepad old_pad; |
| 246 state.AsWebGamepad(&old_pad); |
| 247 OnGamepadConnectionChange(false, i, old_pad); |
| 248 OnGamepadConnectionChange(true, i, pad); |
255 } | 249 } |
256 } | 250 } |
257 } | 251 } |
258 | 252 |
259 CheckForUserGesture(); | 253 CheckForUserGesture(); |
260 | 254 |
261 // Schedule our next interval of polling. | 255 // Schedule our next interval of polling. |
262 ScheduleDoPoll(); | 256 ScheduleDoPoll(); |
263 } | 257 } |
264 | 258 |
265 void GamepadProvider::ScheduleDoPoll() { | 259 void GamepadProvider::ScheduleDoPoll() { |
266 DCHECK(base::MessageLoop::current() == polling_thread_->message_loop()); | 260 DCHECK(base::MessageLoop::current() == polling_thread_->message_loop()); |
267 if (have_scheduled_do_poll_) | 261 if (have_scheduled_do_poll_) |
268 return; | 262 return; |
269 | 263 |
270 { | 264 { |
271 base::AutoLock lock(is_paused_lock_); | 265 base::AutoLock lock(is_paused_lock_); |
272 if (is_paused_) | 266 if (is_paused_) |
273 return; | 267 return; |
274 } | 268 } |
275 | 269 |
276 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( | 270 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( |
277 FROM_HERE, base::Bind(&GamepadProvider::DoPoll, Unretained(this)), | 271 FROM_HERE, base::Bind(&GamepadProvider::DoPoll, Unretained(this)), |
278 base::TimeDelta::FromMilliseconds(kDesiredSamplingIntervalMs)); | 272 base::TimeDelta::FromMilliseconds(kDesiredSamplingIntervalMs)); |
279 have_scheduled_do_poll_ = true; | 273 have_scheduled_do_poll_ = true; |
280 } | 274 } |
281 | 275 |
282 void GamepadProvider::OnGamepadConnectionChange( | 276 void GamepadProvider::OnGamepadConnectionChange( |
283 bool connected, int index, const WebGamepad& pad) { | 277 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_; |
313 const WebGamepads& pads = SharedMemoryAsHardwareBuffer()->buffer; | 314 const WebGamepads& pads = SharedMemoryAsHardwareBuffer()->buffer; |
314 if (GamepadsHaveUserGesture(pads)) { | 315 if (GamepadsHaveUserGesture(pads)) { |
315 ever_had_user_gesture_ = true; | 316 ever_had_user_gesture_ = true; |
316 for (size_t i = 0; i < user_gesture_observers_.size(); i++) { | 317 for (size_t i = 0; i < user_gesture_observers_.size(); i++) { |
317 user_gesture_observers_[i].task_runner->PostTask( | 318 user_gesture_observers_[i].task_runner->PostTask( |
318 FROM_HERE, user_gesture_observers_[i].closure); | 319 FROM_HERE, user_gesture_observers_[i].closure); |
319 } | 320 } |
320 user_gesture_observers_.clear(); | 321 user_gesture_observers_.clear(); |
321 } | 322 } |
322 } | 323 if (!had_gesture_before && ever_had_user_gesture_) { |
323 | 324 // Initialize pad_states_ for the first time. |
324 void GamepadProvider::ClearPadState(PadState& state) { | 325 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) { |
325 memset(&state, 0, sizeof(PadState)); | 326 pad_states_.get()[i].SetPad(pads.items[i]); |
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 } | |
408 } | 327 } |
409 } | 328 } |
410 } | 329 } |
411 | 330 |
412 } // namespace content | 331 } // namespace content |
OLD | NEW |