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 <cmath> | 5 #include <cmath> |
6 #include <set> | 6 #include <set> |
7 #include <vector> | 7 #include <vector> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/logging.h" | 10 #include "base/logging.h" |
11 #include "base/message_loop/message_loop.h" | 11 #include "base/message_loop/message_loop.h" |
12 #include "base/message_loop/message_loop_proxy.h" | 12 #include "base/message_loop/message_loop_proxy.h" |
13 #include "base/threading/thread.h" | 13 #include "base/threading/thread.h" |
14 #include "base/threading/thread_restrictions.h" | 14 #include "base/threading/thread_restrictions.h" |
15 #include "content/browser/gamepad/gamepad_data_fetcher.h" | 15 #include "content/browser/gamepad/gamepad_data_fetcher.h" |
16 #include "content/browser/gamepad/gamepad_platform_data_fetcher.h" | 16 #include "content/browser/gamepad/gamepad_platform_data_fetcher.h" |
17 #include "content/browser/gamepad/gamepad_provider.h" | 17 #include "content/browser/gamepad/gamepad_provider.h" |
| 18 #include "content/browser/gamepad/gamepad_service.h" |
18 #include "content/common/gamepad_hardware_buffer.h" | 19 #include "content/common/gamepad_hardware_buffer.h" |
19 #include "content/common/gamepad_messages.h" | 20 #include "content/common/gamepad_messages.h" |
20 #include "content/common/gamepad_user_gesture.h" | 21 #include "content/common/gamepad_user_gesture.h" |
| 22 #include "content/public/browser/browser_thread.h" |
| 23 |
| 24 using blink::WebGamepad; |
| 25 using blink::WebGamepads; |
21 | 26 |
22 namespace content { | 27 namespace content { |
23 | 28 |
24 GamepadProvider::ClosureAndThread::ClosureAndThread( | 29 GamepadProvider::ClosureAndThread::ClosureAndThread( |
25 const base::Closure& c, | 30 const base::Closure& c, |
26 const scoped_refptr<base::MessageLoopProxy>& m) | 31 const scoped_refptr<base::MessageLoopProxy>& m) |
27 : closure(c), | 32 : closure(c), |
28 message_loop(m) { | 33 message_loop(m) { |
29 } | 34 } |
30 | 35 |
31 GamepadProvider::ClosureAndThread::~ClosureAndThread() { | 36 GamepadProvider::ClosureAndThread::~ClosureAndThread() { |
32 } | 37 } |
33 | 38 |
34 GamepadProvider::GamepadProvider() | 39 GamepadProvider::GamepadProvider() |
35 : is_paused_(true), | 40 : is_paused_(true), |
36 have_scheduled_do_poll_(false), | 41 have_scheduled_do_poll_(false), |
37 devices_changed_(true) { | 42 devices_changed_(true), |
| 43 ever_had_user_gesture_(false) { |
38 Initialize(scoped_ptr<GamepadDataFetcher>()); | 44 Initialize(scoped_ptr<GamepadDataFetcher>()); |
39 } | 45 } |
40 | 46 |
41 GamepadProvider::GamepadProvider(scoped_ptr<GamepadDataFetcher> fetcher) | 47 GamepadProvider::GamepadProvider(scoped_ptr<GamepadDataFetcher> fetcher) |
42 : is_paused_(true), | 48 : is_paused_(true), |
43 have_scheduled_do_poll_(false), | 49 have_scheduled_do_poll_(false), |
44 devices_changed_(true) { | 50 devices_changed_(true), |
| 51 ever_had_user_gesture_(false) { |
45 Initialize(fetcher.Pass()); | 52 Initialize(fetcher.Pass()); |
46 } | 53 } |
47 | 54 |
48 GamepadProvider::~GamepadProvider() { | 55 GamepadProvider::~GamepadProvider() { |
49 base::SystemMonitor* monitor = base::SystemMonitor::Get(); | 56 base::SystemMonitor* monitor = base::SystemMonitor::Get(); |
50 if (monitor) | 57 if (monitor) |
51 monitor->RemoveDevicesChangedObserver(this); | 58 monitor->RemoveDevicesChangedObserver(this); |
52 | 59 |
53 // Use Stop() to join the polling thread, as there may be pending callbacks | 60 // Use Stop() to join the polling thread, as there may be pending callbacks |
54 // which dereference |polling_thread_|. | 61 // which dereference |polling_thread_|. |
55 polling_thread_->Stop(); | 62 polling_thread_->Stop(); |
56 data_fetcher_.reset(); | 63 data_fetcher_.reset(); |
57 } | 64 } |
58 | 65 |
59 base::SharedMemoryHandle GamepadProvider::GetSharedMemoryHandleForProcess( | 66 base::SharedMemoryHandle GamepadProvider::GetSharedMemoryHandleForProcess( |
60 base::ProcessHandle process) { | 67 base::ProcessHandle process) { |
61 base::SharedMemoryHandle renderer_handle; | 68 base::SharedMemoryHandle renderer_handle; |
62 gamepad_shared_memory_.ShareToProcess(process, &renderer_handle); | 69 gamepad_shared_memory_.ShareToProcess(process, &renderer_handle); |
63 return renderer_handle; | 70 return renderer_handle; |
64 } | 71 } |
65 | 72 |
| 73 void GamepadProvider::GetCurrentGamepadData(WebGamepads* data) { |
| 74 const WebGamepads& pads = SharedMemoryAsHardwareBuffer()->buffer; |
| 75 base::AutoLock lock(shared_memory_lock_); |
| 76 *data = pads; |
| 77 } |
| 78 |
66 void GamepadProvider::Pause() { | 79 void GamepadProvider::Pause() { |
67 { | 80 { |
68 base::AutoLock lock(is_paused_lock_); | 81 base::AutoLock lock(is_paused_lock_); |
69 is_paused_ = true; | 82 is_paused_ = true; |
70 } | 83 } |
71 base::MessageLoop* polling_loop = polling_thread_->message_loop(); | 84 base::MessageLoop* polling_loop = polling_thread_->message_loop(); |
72 polling_loop->PostTask( | 85 polling_loop->PostTask( |
73 FROM_HERE, | 86 FROM_HERE, |
74 base::Bind(&GamepadProvider::SendPauseHint, Unretained(this), true)); | 87 base::Bind(&GamepadProvider::SendPauseHint, Unretained(this), true)); |
75 } | 88 } |
(...skipping 28 matching lines...) Expand all Loading... |
104 | 117 |
105 void GamepadProvider::Initialize(scoped_ptr<GamepadDataFetcher> fetcher) { | 118 void GamepadProvider::Initialize(scoped_ptr<GamepadDataFetcher> fetcher) { |
106 size_t data_size = sizeof(GamepadHardwareBuffer); | 119 size_t data_size = sizeof(GamepadHardwareBuffer); |
107 base::SystemMonitor* monitor = base::SystemMonitor::Get(); | 120 base::SystemMonitor* monitor = base::SystemMonitor::Get(); |
108 if (monitor) | 121 if (monitor) |
109 monitor->AddDevicesChangedObserver(this); | 122 monitor->AddDevicesChangedObserver(this); |
110 bool res = gamepad_shared_memory_.CreateAndMapAnonymous(data_size); | 123 bool res = gamepad_shared_memory_.CreateAndMapAnonymous(data_size); |
111 CHECK(res); | 124 CHECK(res); |
112 GamepadHardwareBuffer* hwbuf = SharedMemoryAsHardwareBuffer(); | 125 GamepadHardwareBuffer* hwbuf = SharedMemoryAsHardwareBuffer(); |
113 memset(hwbuf, 0, sizeof(GamepadHardwareBuffer)); | 126 memset(hwbuf, 0, sizeof(GamepadHardwareBuffer)); |
| 127 pad_states_.reset(new PadState[WebGamepads::itemsLengthCap]); |
114 | 128 |
115 polling_thread_.reset(new base::Thread("Gamepad polling thread")); | 129 polling_thread_.reset(new base::Thread("Gamepad polling thread")); |
116 #if defined(OS_LINUX) | 130 #if defined(OS_LINUX) |
117 // On Linux, the data fetcher needs to watch file descriptors, so the message | 131 // On Linux, the data fetcher needs to watch file descriptors, so the message |
118 // loop needs to be a libevent loop. | 132 // loop needs to be a libevent loop. |
119 const base::MessageLoop::Type kMessageLoopType = base::MessageLoop::TYPE_IO; | 133 const base::MessageLoop::Type kMessageLoopType = base::MessageLoop::TYPE_IO; |
120 #else | 134 #else |
121 // On Mac, the data fetcher uses IOKit which depends on CFRunLoop, so the | 135 // On Mac, the data fetcher uses IOKit which depends on CFRunLoop, so the |
122 // message loop needs to be a UI-type loop. On Windows it must be a UI loop | 136 // message loop needs to be a UI-type loop. On Windows it must be a UI loop |
123 // to properly pump the MessageWindow that captures device state. | 137 // to properly pump the MessageWindow that captures device state. |
(...skipping 17 matching lines...) Expand all Loading... |
141 fetcher.reset(new GamepadPlatformDataFetcher); | 155 fetcher.reset(new GamepadPlatformDataFetcher); |
142 data_fetcher_ = fetcher.Pass(); | 156 data_fetcher_ = fetcher.Pass(); |
143 } | 157 } |
144 | 158 |
145 void GamepadProvider::SendPauseHint(bool paused) { | 159 void GamepadProvider::SendPauseHint(bool paused) { |
146 DCHECK(base::MessageLoop::current() == polling_thread_->message_loop()); | 160 DCHECK(base::MessageLoop::current() == polling_thread_->message_loop()); |
147 if (data_fetcher_) | 161 if (data_fetcher_) |
148 data_fetcher_->PauseHint(paused); | 162 data_fetcher_->PauseHint(paused); |
149 } | 163 } |
150 | 164 |
| 165 bool GamepadProvider::PadState::Match(const WebGamepad& pad) const { |
| 166 return connected_ == pad.connected && |
| 167 axes_length_ == pad.axesLength && |
| 168 buttons_length_ == pad.buttonsLength && |
| 169 memcmp(id_, pad.id, arraysize(id_)) == 0 && |
| 170 memcmp(mapping_, pad.mapping, arraysize(mapping_)) == 0; |
| 171 } |
| 172 |
| 173 void GamepadProvider::PadState::SetPad(const WebGamepad& pad) { |
| 174 DCHECK(pad.connected); |
| 175 connected_ = true; |
| 176 axes_length_ = pad.axesLength; |
| 177 buttons_length_ = pad.buttonsLength; |
| 178 memcpy(id_, pad.id, arraysize(id_)); |
| 179 memcpy(mapping_, pad.mapping, arraysize(mapping_)); |
| 180 } |
| 181 |
| 182 void GamepadProvider::PadState::SetDisconnected() { |
| 183 connected_ = false; |
| 184 axes_length_ = 0; |
| 185 buttons_length_ = 0; |
| 186 memset(id_, 0, arraysize(id_)); |
| 187 memset(mapping_, 0, arraysize(mapping_)); |
| 188 } |
| 189 |
| 190 void GamepadProvider::PadState::AsWebGamepad(WebGamepad* pad) { |
| 191 pad->connected = connected_; |
| 192 pad->axesLength = axes_length_; |
| 193 pad->buttonsLength = buttons_length_; |
| 194 memcpy(pad->id, id_, arraysize(id_)); |
| 195 memcpy(pad->mapping, mapping_, arraysize(mapping_)); |
| 196 memset(pad->axes, 0, arraysize(pad->axes)); |
| 197 memset(pad->buttons, 0, arraysize(pad->buttons)); |
| 198 } |
| 199 |
151 void GamepadProvider::DoPoll() { | 200 void GamepadProvider::DoPoll() { |
152 DCHECK(base::MessageLoop::current() == polling_thread_->message_loop()); | 201 DCHECK(base::MessageLoop::current() == polling_thread_->message_loop()); |
153 DCHECK(have_scheduled_do_poll_); | 202 DCHECK(have_scheduled_do_poll_); |
154 have_scheduled_do_poll_ = false; | 203 have_scheduled_do_poll_ = false; |
155 | 204 |
156 bool changed; | 205 bool changed; |
157 GamepadHardwareBuffer* hwbuf = SharedMemoryAsHardwareBuffer(); | 206 GamepadHardwareBuffer* hwbuf = SharedMemoryAsHardwareBuffer(); |
158 | 207 |
159 ANNOTATE_BENIGN_RACE_SIZED( | 208 ANNOTATE_BENIGN_RACE_SIZED( |
160 &hwbuf->buffer, | 209 &hwbuf->buffer, |
161 sizeof(blink::WebGamepads), | 210 sizeof(WebGamepads), |
162 "Racey reads are discarded"); | 211 "Racey reads are discarded"); |
163 | 212 |
164 { | 213 { |
165 base::AutoLock lock(devices_changed_lock_); | 214 base::AutoLock lock(devices_changed_lock_); |
166 changed = devices_changed_; | 215 changed = devices_changed_; |
167 devices_changed_ = false; | 216 devices_changed_ = false; |
168 } | 217 } |
169 | 218 |
170 // Acquire the SeqLock. There is only ever one writer to this data. | 219 { |
171 // See gamepad_hardware_buffer.h. | 220 base::AutoLock lock(shared_memory_lock_); |
172 hwbuf->sequence.WriteBegin(); | 221 |
173 data_fetcher_->GetGamepadData(&hwbuf->buffer, changed); | 222 // Acquire the SeqLock. There is only ever one writer to this data. |
174 hwbuf->sequence.WriteEnd(); | 223 // See gamepad_hardware_buffer.h. |
| 224 hwbuf->sequence.WriteBegin(); |
| 225 data_fetcher_->GetGamepadData(&hwbuf->buffer, changed); |
| 226 hwbuf->sequence.WriteEnd(); |
| 227 } |
175 | 228 |
176 CheckForUserGesture(); | 229 CheckForUserGesture(); |
177 | 230 |
| 231 if (ever_had_user_gesture_) { |
| 232 for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) { |
| 233 WebGamepad& pad = hwbuf->buffer.items[i]; |
| 234 PadState& state = pad_states_.get()[i]; |
| 235 if (pad.connected && !state.connected()) { |
| 236 OnGamepadConnectionChange(true, i, pad); |
| 237 } else if (!pad.connected && state.connected()) { |
| 238 OnGamepadConnectionChange(false, i, pad); |
| 239 } else if (pad.connected && state.connected() && !state.Match(pad)) { |
| 240 WebGamepad old_pad; |
| 241 state.AsWebGamepad(&old_pad); |
| 242 OnGamepadConnectionChange(false, i, old_pad); |
| 243 OnGamepadConnectionChange(true, i, pad); |
| 244 } |
| 245 } |
| 246 } |
| 247 |
178 // Schedule our next interval of polling. | 248 // Schedule our next interval of polling. |
179 ScheduleDoPoll(); | 249 ScheduleDoPoll(); |
180 } | 250 } |
181 | 251 |
182 void GamepadProvider::ScheduleDoPoll() { | 252 void GamepadProvider::ScheduleDoPoll() { |
183 DCHECK(base::MessageLoop::current() == polling_thread_->message_loop()); | 253 DCHECK(base::MessageLoop::current() == polling_thread_->message_loop()); |
184 if (have_scheduled_do_poll_) | 254 if (have_scheduled_do_poll_) |
185 return; | 255 return; |
186 | 256 |
187 { | 257 { |
188 base::AutoLock lock(is_paused_lock_); | 258 base::AutoLock lock(is_paused_lock_); |
189 if (is_paused_) | 259 if (is_paused_) |
190 return; | 260 return; |
191 } | 261 } |
192 | 262 |
193 base::MessageLoop::current()->PostDelayedTask( | 263 base::MessageLoop::current()->PostDelayedTask( |
194 FROM_HERE, | 264 FROM_HERE, |
195 base::Bind(&GamepadProvider::DoPoll, Unretained(this)), | 265 base::Bind(&GamepadProvider::DoPoll, Unretained(this)), |
196 base::TimeDelta::FromMilliseconds(kDesiredSamplingIntervalMs)); | 266 base::TimeDelta::FromMilliseconds(kDesiredSamplingIntervalMs)); |
197 have_scheduled_do_poll_ = true; | 267 have_scheduled_do_poll_ = true; |
198 } | 268 } |
199 | 269 |
| 270 void GamepadProvider::OnGamepadConnectionChange( |
| 271 bool connected, int index, const WebGamepad& pad) { |
| 272 PadState& state = pad_states_.get()[index]; |
| 273 if (connected) |
| 274 state.SetPad(pad); |
| 275 else |
| 276 state.SetDisconnected(); |
| 277 |
| 278 BrowserThread::PostTask( |
| 279 BrowserThread::IO, |
| 280 FROM_HERE, |
| 281 base::Bind(&GamepadProvider::DispatchGamepadConnectionChange, |
| 282 base::Unretained(this), |
| 283 connected, |
| 284 index, |
| 285 pad)); |
| 286 } |
| 287 |
| 288 void GamepadProvider::DispatchGamepadConnectionChange( |
| 289 bool connected, int index, const WebGamepad& pad) { |
| 290 if (connected) |
| 291 GamepadService::GetInstance()->OnGamepadConnected(index, pad); |
| 292 else |
| 293 GamepadService::GetInstance()->OnGamepadDisconnected(index, pad); |
| 294 } |
| 295 |
200 GamepadHardwareBuffer* GamepadProvider::SharedMemoryAsHardwareBuffer() { | 296 GamepadHardwareBuffer* GamepadProvider::SharedMemoryAsHardwareBuffer() { |
201 void* mem = gamepad_shared_memory_.memory(); | 297 void* mem = gamepad_shared_memory_.memory(); |
202 CHECK(mem); | 298 CHECK(mem); |
203 return static_cast<GamepadHardwareBuffer*>(mem); | 299 return static_cast<GamepadHardwareBuffer*>(mem); |
204 } | 300 } |
205 | 301 |
206 void GamepadProvider::CheckForUserGesture() { | 302 void GamepadProvider::CheckForUserGesture() { |
207 base::AutoLock lock(user_gesture_lock_); | 303 base::AutoLock lock(user_gesture_lock_); |
208 if (user_gesture_observers_.empty()) | 304 if (user_gesture_observers_.empty() && ever_had_user_gesture_) |
209 return; // Don't need to check if nobody is listening. | 305 return; |
210 | 306 |
211 if (GamepadsHaveUserGesture(SharedMemoryAsHardwareBuffer()->buffer)) { | 307 if (GamepadsHaveUserGesture(SharedMemoryAsHardwareBuffer()->buffer)) { |
| 308 ever_had_user_gesture_ = true; |
212 for (size_t i = 0; i < user_gesture_observers_.size(); i++) { | 309 for (size_t i = 0; i < user_gesture_observers_.size(); i++) { |
213 user_gesture_observers_[i].message_loop->PostTask(FROM_HERE, | 310 user_gesture_observers_[i].message_loop->PostTask(FROM_HERE, |
214 user_gesture_observers_[i].closure); | 311 user_gesture_observers_[i].closure); |
215 } | 312 } |
216 user_gesture_observers_.clear(); | 313 user_gesture_observers_.clear(); |
217 } | 314 } |
218 } | 315 } |
219 | 316 |
220 } // namespace content | 317 } // namespace content |
OLD | NEW |