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