| 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 |