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