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

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

Issue 2129003002: Refactored gamepad polling to support dynamic sources (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Disabled sanitization test on Android. Suffers from same bug as PollingAccess test Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « device/gamepad/gamepad_provider.h ('k') | device/gamepad/gamepad_provider_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "device/gamepad/gamepad_provider.h" 5 #include "device/gamepad/gamepad_provider.h"
6 6
7 #include <stddef.h> 7 #include <stddef.h>
8 #include <string.h> 8 #include <string.h>
9 #include <cmath> 9 #include <cmath>
10 #include <set>
11 #include <utility> 10 #include <utility>
12 #include <vector> 11 #include <vector>
13 12
14 #include "base/bind.h" 13 #include "base/bind.h"
15 #include "base/location.h" 14 #include "base/location.h"
16 #include "base/logging.h" 15 #include "base/logging.h"
17 #include "base/single_thread_task_runner.h" 16 #include "base/single_thread_task_runner.h"
18 #include "base/third_party/dynamic_annotations/dynamic_annotations.h" 17 #include "base/third_party/dynamic_annotations/dynamic_annotations.h"
19 #include "base/threading/thread.h" 18 #include "base/threading/thread.h"
20 #include "base/threading/thread_restrictions.h" 19 #include "base/threading/thread_restrictions.h"
21 #include "base/threading/thread_task_runner_handle.h" 20 #include "base/threading/thread_task_runner_handle.h"
22 #include "build/build_config.h" 21 #include "build/build_config.h"
23 #include "device/gamepad/gamepad_data_fetcher.h" 22 #include "device/gamepad/gamepad_data_fetcher.h"
24 #include "device/gamepad/gamepad_platform_data_fetcher.h" 23 #include "device/gamepad/gamepad_data_fetcher_manager.h"
25 #include "device/gamepad/gamepad_user_gesture.h" 24 #include "device/gamepad/gamepad_user_gesture.h"
26 25
27 using blink::WebGamepad; 26 using blink::WebGamepad;
28 using blink::WebGamepads; 27 using blink::WebGamepads;
29 28
30 namespace device { 29 namespace device {
31 30
32 GamepadProvider::ClosureAndThread::ClosureAndThread( 31 GamepadProvider::ClosureAndThread::ClosureAndThread(
33 const base::Closure& c, 32 const base::Closure& c,
34 const scoped_refptr<base::SingleThreadTaskRunner>& m) 33 const scoped_refptr<base::SingleThreadTaskRunner>& m)
35 : closure(c), task_runner(m) {} 34 : closure(c), task_runner(m) {}
36 35
37 GamepadProvider::ClosureAndThread::ClosureAndThread( 36 GamepadProvider::ClosureAndThread::ClosureAndThread(
38 const ClosureAndThread& other) = default; 37 const ClosureAndThread& other) = default;
39 38
40 GamepadProvider::ClosureAndThread::~ClosureAndThread() {} 39 GamepadProvider::ClosureAndThread::~ClosureAndThread() {}
41 40
42 GamepadProvider::GamepadProvider( 41 GamepadProvider::GamepadProvider(
43 std::unique_ptr<GamepadSharedBuffer> buffer, 42 std::unique_ptr<GamepadSharedBuffer> buffer,
44 GamepadConnectionChangeClient* connection_change_client) 43 GamepadConnectionChangeClient* connection_change_client)
45 : is_paused_(true), 44 : is_paused_(true),
46 have_scheduled_do_poll_(false), 45 have_scheduled_do_poll_(false),
47 devices_changed_(true), 46 devices_changed_(true),
48 ever_had_user_gesture_(false), 47 ever_had_user_gesture_(false),
48 sanitize_(true),
49 gamepad_shared_buffer_(std::move(buffer)), 49 gamepad_shared_buffer_(std::move(buffer)),
50 connection_change_client_(connection_change_client) { 50 connection_change_client_(connection_change_client) {
51 Initialize(std::unique_ptr<GamepadDataFetcher>()); 51 Initialize(std::unique_ptr<GamepadDataFetcher>());
52 } 52 }
53 53
54 GamepadProvider::GamepadProvider( 54 GamepadProvider::GamepadProvider(
55 std::unique_ptr<GamepadSharedBuffer> buffer, 55 std::unique_ptr<GamepadSharedBuffer> buffer,
56 GamepadConnectionChangeClient* connection_change_client, 56 GamepadConnectionChangeClient* connection_change_client,
57 std::unique_ptr<GamepadDataFetcher> fetcher) 57 std::unique_ptr<GamepadDataFetcher> fetcher)
58 : is_paused_(true), 58 : is_paused_(true),
59 have_scheduled_do_poll_(false), 59 have_scheduled_do_poll_(false),
60 devices_changed_(true), 60 devices_changed_(true),
61 ever_had_user_gesture_(false), 61 ever_had_user_gesture_(false),
62 sanitize_(true),
62 gamepad_shared_buffer_(std::move(buffer)), 63 gamepad_shared_buffer_(std::move(buffer)),
63 connection_change_client_(connection_change_client) { 64 connection_change_client_(connection_change_client) {
64 Initialize(std::move(fetcher)); 65 Initialize(std::move(fetcher));
65 } 66 }
66 67
67 GamepadProvider::~GamepadProvider() { 68 GamepadProvider::~GamepadProvider() {
69 GamepadDataFetcherManager::GetInstance()->ClearProvider();
70
68 base::SystemMonitor* monitor = base::SystemMonitor::Get(); 71 base::SystemMonitor* monitor = base::SystemMonitor::Get();
69 if (monitor) 72 if (monitor)
70 monitor->RemoveDevicesChangedObserver(this); 73 monitor->RemoveDevicesChangedObserver(this);
71 74
72 // Use Stop() to join the polling thread, as there may be pending callbacks 75 // Use Stop() to join the polling thread, as there may be pending callbacks
73 // which dereference |polling_thread_|. 76 // which dereference |polling_thread_|.
74 polling_thread_->Stop(); 77 polling_thread_->Stop();
75 data_fetcher_.reset();
76 } 78 }
77 79
78 base::SharedMemoryHandle GamepadProvider::GetSharedMemoryHandleForProcess( 80 base::SharedMemoryHandle GamepadProvider::GetSharedMemoryHandleForProcess(
79 base::ProcessHandle process) { 81 base::ProcessHandle process) {
80 base::SharedMemoryHandle renderer_handle; 82 base::SharedMemoryHandle renderer_handle;
81 gamepad_shared_buffer_->shared_memory()->ShareToProcess(process, 83 gamepad_shared_buffer_->shared_memory()->ShareToProcess(process,
82 &renderer_handle); 84 &renderer_handle);
83 return renderer_handle; 85 return renderer_handle;
84 } 86 }
85 87
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
126 void GamepadProvider::OnDevicesChanged(base::SystemMonitor::DeviceType type) { 128 void GamepadProvider::OnDevicesChanged(base::SystemMonitor::DeviceType type) {
127 base::AutoLock lock(devices_changed_lock_); 129 base::AutoLock lock(devices_changed_lock_);
128 devices_changed_ = true; 130 devices_changed_ = true;
129 } 131 }
130 132
131 void GamepadProvider::Initialize(std::unique_ptr<GamepadDataFetcher> fetcher) { 133 void GamepadProvider::Initialize(std::unique_ptr<GamepadDataFetcher> fetcher) {
132 base::SystemMonitor* monitor = base::SystemMonitor::Get(); 134 base::SystemMonitor* monitor = base::SystemMonitor::Get();
133 if (monitor) 135 if (monitor)
134 monitor->AddDevicesChangedObserver(this); 136 monitor->AddDevicesChangedObserver(this);
135 137
136 pad_states_.reset(new PadState[WebGamepads::itemsLengthCap]);
137
138 polling_thread_.reset(new base::Thread("Gamepad polling thread")); 138 polling_thread_.reset(new base::Thread("Gamepad polling thread"));
139 #if defined(OS_LINUX) 139 #if defined(OS_LINUX)
140 // On Linux, the data fetcher needs to watch file descriptors, so the message 140 // On Linux, the data fetcher needs to watch file descriptors, so the message
141 // loop needs to be a libevent loop. 141 // loop needs to be a libevent loop.
142 const base::MessageLoop::Type kMessageLoopType = base::MessageLoop::TYPE_IO; 142 const base::MessageLoop::Type kMessageLoopType = base::MessageLoop::TYPE_IO;
143 #elif defined(OS_ANDROID) 143 #elif defined(OS_ANDROID)
144 // On Android, keeping a message loop of default type. 144 // On Android, keeping a message loop of default type.
145 const base::MessageLoop::Type kMessageLoopType = 145 const base::MessageLoop::Type kMessageLoopType =
146 base::MessageLoop::TYPE_DEFAULT; 146 base::MessageLoop::TYPE_DEFAULT;
147 #else 147 #else
148 // On Mac, the data fetcher uses IOKit which depends on CFRunLoop, so the 148 // 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 149 // 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. 150 // to properly pump the MessageWindow that captures device state.
151 const base::MessageLoop::Type kMessageLoopType = base::MessageLoop::TYPE_UI; 151 const base::MessageLoop::Type kMessageLoopType = base::MessageLoop::TYPE_UI;
152 #endif 152 #endif
153 polling_thread_->StartWithOptions(base::Thread::Options(kMessageLoopType, 0)); 153 polling_thread_->StartWithOptions(base::Thread::Options(kMessageLoopType, 0));
154 154
155 if (fetcher) {
156 AddGamepadDataFetcher(std::move(fetcher));
157 } else {
158 GamepadDataFetcherManager::GetInstance()->InitializeProvider(this);
159 }
160 }
161
162 void GamepadProvider::AddGamepadDataFetcher(
163 std::unique_ptr<GamepadDataFetcher> fetcher) {
155 polling_thread_->task_runner()->PostTask( 164 polling_thread_->task_runner()->PostTask(
156 FROM_HERE, base::Bind(&GamepadProvider::DoInitializePollingThread, 165 FROM_HERE, base::Bind(&GamepadProvider::DoAddGamepadDataFetcher,
157 base::Unretained(this), base::Passed(&fetcher))); 166 base::Unretained(this), base::Passed(&fetcher)));
158 } 167 }
159 168
160 void GamepadProvider::DoInitializePollingThread( 169 void GamepadProvider::RemoveSourceGamepadDataFetcher(GamepadSource source) {
170 polling_thread_->task_runner()->PostTask(
171 FROM_HERE, base::Bind(&GamepadProvider::DoRemoveSourceGamepadDataFetcher,
172 base::Unretained(this), source));
173 }
174
175 void GamepadProvider::DoAddGamepadDataFetcher(
161 std::unique_ptr<GamepadDataFetcher> fetcher) { 176 std::unique_ptr<GamepadDataFetcher> fetcher) {
162 DCHECK(polling_thread_->task_runner()->BelongsToCurrentThread()); 177 DCHECK(polling_thread_->task_runner()->BelongsToCurrentThread());
163 DCHECK(!data_fetcher_.get()); // Should only initialize once.
164 178
165 if (!fetcher) 179 if (!fetcher)
166 fetcher.reset(new GamepadPlatformDataFetcher); 180 return;
167 data_fetcher_ = std::move(fetcher); 181
182 InitializeDataFetcher(fetcher.get());
183 data_fetchers_.push_back(std::move(fetcher));
184 }
185
186 void GamepadProvider::DoRemoveSourceGamepadDataFetcher(GamepadSource source) {
187 DCHECK(polling_thread_->task_runner()->BelongsToCurrentThread());
188
189 for (GamepadFetcherVector::iterator it = data_fetchers_.begin();
190 it != data_fetchers_.end(); ++it) {
191 if ((*it)->source() == source) {
192 data_fetchers_.erase(it);
193 }
194 }
168 } 195 }
169 196
170 void GamepadProvider::SendPauseHint(bool paused) { 197 void GamepadProvider::SendPauseHint(bool paused) {
171 DCHECK(polling_thread_->task_runner()->BelongsToCurrentThread()); 198 DCHECK(polling_thread_->task_runner()->BelongsToCurrentThread());
172 if (data_fetcher_) 199 for (const auto& it : data_fetchers_) {
173 data_fetcher_->PauseHint(paused); 200 it->PauseHint(paused);
174 } 201 }
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 } 202 }
208 203
209 void GamepadProvider::DoPoll() { 204 void GamepadProvider::DoPoll() {
210 DCHECK(polling_thread_->task_runner()->BelongsToCurrentThread()); 205 DCHECK(polling_thread_->task_runner()->BelongsToCurrentThread());
211 DCHECK(have_scheduled_do_poll_); 206 DCHECK(have_scheduled_do_poll_);
212 have_scheduled_do_poll_ = false; 207 have_scheduled_do_poll_ = false;
213 208
214 bool changed; 209 bool changed;
215 210
216 ANNOTATE_BENIGN_RACE_SIZED(gamepad_shared_buffer_->buffer(), 211 ANNOTATE_BENIGN_RACE_SIZED(gamepad_shared_buffer_->buffer(),
217 sizeof(WebGamepads), "Racey reads are discarded"); 212 sizeof(WebGamepads), "Racey reads are discarded");
218 213
219 { 214 {
220 base::AutoLock lock(devices_changed_lock_); 215 base::AutoLock lock(devices_changed_lock_);
221 changed = devices_changed_; 216 changed = devices_changed_;
222 devices_changed_ = false; 217 devices_changed_ = false;
223 } 218 }
224 219
220 // Loop through each registered data fetcher and poll it's gamepad data.
221 // It's expected that GetGamepadData will mark each gamepad as active (via
222 // GetPadState). If a gamepad is not marked as active during the calls to
223 // GetGamepadData then it's assumed to be disconnected.
224 for (const auto& it : data_fetchers_) {
225 it->GetGamepadData(changed);
226 }
227
228 blink::WebGamepads* buffer = gamepad_shared_buffer_->buffer();
229
230 // Send out disconnect events using the last polled data before we wipe it out
231 // in the mapping step.
232 if (ever_had_user_gesture_) {
233 for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) {
234 PadState& state = pad_states_.get()[i];
235
236 if (!state.active_state && state.source != GAMEPAD_SOURCE_NONE) {
237 OnGamepadConnectionChange(false, i, buffer->items[i]);
238 ClearPadState(state);
239 }
240 }
241 }
242
225 { 243 {
226 base::AutoLock lock(shared_memory_lock_); 244 base::AutoLock lock(shared_memory_lock_);
227 245
228 // Acquire the SeqLock. There is only ever one writer to this data. 246 // Acquire the SeqLock. There is only ever one writer to this data.
229 // See gamepad_hardware_buffer.h. 247 // See gamepad_hardware_buffer.h.
230 gamepad_shared_buffer_->WriteBegin(); 248 gamepad_shared_buffer_->WriteBegin();
231 data_fetcher_->GetGamepadData(gamepad_shared_buffer_->buffer(), changed); 249 buffer->length = 0;
250 for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) {
251 PadState& state = pad_states_.get()[i];
252 // Must run through the map+sanitize here or CheckForUserGesture may fail.
253 MapAndSanitizeGamepadData(&state, &buffer->items[i], sanitize_);
254 if (state.active_state)
255 buffer->length++;
256 }
232 gamepad_shared_buffer_->WriteEnd(); 257 gamepad_shared_buffer_->WriteEnd();
233 } 258 }
234 259
235 if (ever_had_user_gesture_) { 260 if (ever_had_user_gesture_) {
236 for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) { 261 for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) {
237 WebGamepad& pad = gamepad_shared_buffer_->buffer()->items[i];
238 PadState& state = pad_states_.get()[i]; 262 PadState& state = pad_states_.get()[i];
239 if (pad.connected && !state.connected()) { 263
240 OnGamepadConnectionChange(true, i, pad); 264 if (state.active_state) {
241 } else if (!pad.connected && state.connected()) { 265 if (state.active_state == GAMEPAD_NEWLY_ACTIVE)
242 OnGamepadConnectionChange(false, i, pad); 266 OnGamepadConnectionChange(true, i, buffer->items[i]);
243 } else if (pad.connected && state.connected() && !state.Match(pad)) { 267 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 } 268 }
249 } 269 }
250 } 270 }
251 271
252 CheckForUserGesture(); 272 CheckForUserGesture();
253 273
254 // Schedule our next interval of polling. 274 // Schedule our next interval of polling.
255 ScheduleDoPoll(); 275 ScheduleDoPoll();
256 } 276 }
257 277
(...skipping 10 matching lines...) Expand all
268 288
269 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( 289 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
270 FROM_HERE, base::Bind(&GamepadProvider::DoPoll, Unretained(this)), 290 FROM_HERE, base::Bind(&GamepadProvider::DoPoll, Unretained(this)),
271 base::TimeDelta::FromMilliseconds(kDesiredSamplingIntervalMs)); 291 base::TimeDelta::FromMilliseconds(kDesiredSamplingIntervalMs));
272 have_scheduled_do_poll_ = true; 292 have_scheduled_do_poll_ = true;
273 } 293 }
274 294
275 void GamepadProvider::OnGamepadConnectionChange(bool connected, 295 void GamepadProvider::OnGamepadConnectionChange(bool connected,
276 int index, 296 int index,
277 const WebGamepad& pad) { 297 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_) 298 if (connection_change_client_)
285 connection_change_client_->OnGamepadConnectionChange(connected, index, pad); 299 connection_change_client_->OnGamepadConnectionChange(connected, index, pad);
286 } 300 }
287 301
288 void GamepadProvider::CheckForUserGesture() { 302 void GamepadProvider::CheckForUserGesture() {
289 base::AutoLock lock(user_gesture_lock_); 303 base::AutoLock lock(user_gesture_lock_);
290 if (user_gesture_observers_.empty() && ever_had_user_gesture_) 304 if (user_gesture_observers_.empty() && ever_had_user_gesture_)
291 return; 305 return;
292 306
293 bool had_gesture_before = ever_had_user_gesture_;
294 const WebGamepads* pads = gamepad_shared_buffer_->buffer(); 307 const WebGamepads* pads = gamepad_shared_buffer_->buffer();
295 if (GamepadsHaveUserGesture(*pads)) { 308 if (GamepadsHaveUserGesture(*pads)) {
296 ever_had_user_gesture_ = true; 309 ever_had_user_gesture_ = true;
297 for (size_t i = 0; i < user_gesture_observers_.size(); i++) { 310 for (size_t i = 0; i < user_gesture_observers_.size(); i++) {
298 user_gesture_observers_[i].task_runner->PostTask( 311 user_gesture_observers_[i].task_runner->PostTask(
299 FROM_HERE, user_gesture_observers_[i].closure); 312 FROM_HERE, user_gesture_observers_[i].closure);
300 } 313 }
301 user_gesture_observers_.clear(); 314 user_gesture_observers_.clear();
302 } 315 }
303 if (!had_gesture_before && ever_had_user_gesture_) {
304 // Initialize pad_states_ for the first time.
305 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) {
306 pad_states_.get()[i].SetPad(pads->items[i]);
307 }
308 }
309 } 316 }
310 317
311 } // namespace device 318 } // namespace device
OLDNEW
« no previous file with comments | « device/gamepad/gamepad_provider.h ('k') | device/gamepad/gamepad_provider_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698