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 } |
| 197 |
151 void GamepadProvider::DoPoll() { | 198 void GamepadProvider::DoPoll() { |
152 DCHECK(base::MessageLoop::current() == polling_thread_->message_loop()); | 199 DCHECK(base::MessageLoop::current() == polling_thread_->message_loop()); |
153 DCHECK(have_scheduled_do_poll_); | 200 DCHECK(have_scheduled_do_poll_); |
154 have_scheduled_do_poll_ = false; | 201 have_scheduled_do_poll_ = false; |
155 | 202 |
156 bool changed; | 203 bool changed; |
157 GamepadHardwareBuffer* hwbuf = SharedMemoryAsHardwareBuffer(); | 204 GamepadHardwareBuffer* hwbuf = SharedMemoryAsHardwareBuffer(); |
158 | 205 |
159 ANNOTATE_BENIGN_RACE_SIZED( | 206 ANNOTATE_BENIGN_RACE_SIZED( |
160 &hwbuf->buffer, | 207 &hwbuf->buffer, |
161 sizeof(blink::WebGamepads), | 208 sizeof(WebGamepads), |
162 "Racey reads are discarded"); | 209 "Racey reads are discarded"); |
163 | 210 |
164 { | 211 { |
165 base::AutoLock lock(devices_changed_lock_); | 212 base::AutoLock lock(devices_changed_lock_); |
166 changed = devices_changed_; | 213 changed = devices_changed_; |
167 devices_changed_ = false; | 214 devices_changed_ = false; |
168 } | 215 } |
169 | 216 |
170 // Acquire the SeqLock. There is only ever one writer to this data. | 217 { |
171 // See gamepad_hardware_buffer.h. | 218 base::AutoLock lock(shared_memory_lock_); |
172 hwbuf->sequence.WriteBegin(); | 219 |
173 data_fetcher_->GetGamepadData(&hwbuf->buffer, changed); | 220 // Acquire the SeqLock. There is only ever one writer to this data. |
174 hwbuf->sequence.WriteEnd(); | 221 // See gamepad_hardware_buffer.h. |
| 222 hwbuf->sequence.WriteBegin(); |
| 223 data_fetcher_->GetGamepadData(&hwbuf->buffer, changed); |
| 224 hwbuf->sequence.WriteEnd(); |
| 225 } |
175 | 226 |
176 CheckForUserGesture(); | 227 CheckForUserGesture(); |
177 | 228 |
| 229 if (ever_had_user_gesture_) { |
| 230 for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) { |
| 231 WebGamepad& pad = hwbuf->buffer.items[i]; |
| 232 PadState& state = pad_states_.get()[i]; |
| 233 if (pad.connected && !state.connected) { |
| 234 OnGamepadConnectionChange(true, i, pad); |
| 235 } else if (!pad.connected && state.connected) { |
| 236 OnGamepadConnectionChange(false, i, pad); |
| 237 } else if (pad.connected && state.connected && !state.Match(pad)) { |
| 238 WebGamepad old_pad; |
| 239 state.AsWebGamepad(&old_pad); |
| 240 OnGamepadConnectionChange(false, i, old_pad); |
| 241 OnGamepadConnectionChange(true, i, pad); |
| 242 } |
| 243 } |
| 244 } |
| 245 |
178 // Schedule our next interval of polling. | 246 // Schedule our next interval of polling. |
179 ScheduleDoPoll(); | 247 ScheduleDoPoll(); |
180 } | 248 } |
181 | 249 |
182 void GamepadProvider::ScheduleDoPoll() { | 250 void GamepadProvider::ScheduleDoPoll() { |
183 DCHECK(base::MessageLoop::current() == polling_thread_->message_loop()); | 251 DCHECK(base::MessageLoop::current() == polling_thread_->message_loop()); |
184 if (have_scheduled_do_poll_) | 252 if (have_scheduled_do_poll_) |
185 return; | 253 return; |
186 | 254 |
187 { | 255 { |
188 base::AutoLock lock(is_paused_lock_); | 256 base::AutoLock lock(is_paused_lock_); |
189 if (is_paused_) | 257 if (is_paused_) |
190 return; | 258 return; |
191 } | 259 } |
192 | 260 |
193 base::MessageLoop::current()->PostDelayedTask( | 261 base::MessageLoop::current()->PostDelayedTask( |
194 FROM_HERE, | 262 FROM_HERE, |
195 base::Bind(&GamepadProvider::DoPoll, Unretained(this)), | 263 base::Bind(&GamepadProvider::DoPoll, Unretained(this)), |
196 base::TimeDelta::FromMilliseconds(kDesiredSamplingIntervalMs)); | 264 base::TimeDelta::FromMilliseconds(kDesiredSamplingIntervalMs)); |
197 have_scheduled_do_poll_ = true; | 265 have_scheduled_do_poll_ = true; |
198 } | 266 } |
199 | 267 |
| 268 void GamepadProvider::OnGamepadConnectionChange( |
| 269 bool connected, int index, const WebGamepad& pad) { |
| 270 PadState& state = pad_states_.get()[index]; |
| 271 if (connected) |
| 272 state.SetPad(pad); |
| 273 else |
| 274 state.SetDisconnected(); |
| 275 |
| 276 BrowserThread::PostTask( |
| 277 BrowserThread::IO, |
| 278 FROM_HERE, |
| 279 base::Bind(&GamepadProvider::DispatchGamepadConnectionChange, |
| 280 base::Unretained(this), |
| 281 connected, |
| 282 index, |
| 283 pad)); |
| 284 } |
| 285 |
| 286 void GamepadProvider::DispatchGamepadConnectionChange( |
| 287 bool connected, int index, const WebGamepad& pad) { |
| 288 if (connected) |
| 289 GamepadService::GetInstance()->OnGamepadConnected(index, pad); |
| 290 else |
| 291 GamepadService::GetInstance()->OnGamepadDisconnected(index, pad); |
| 292 } |
| 293 |
200 GamepadHardwareBuffer* GamepadProvider::SharedMemoryAsHardwareBuffer() { | 294 GamepadHardwareBuffer* GamepadProvider::SharedMemoryAsHardwareBuffer() { |
201 void* mem = gamepad_shared_memory_.memory(); | 295 void* mem = gamepad_shared_memory_.memory(); |
202 CHECK(mem); | 296 CHECK(mem); |
203 return static_cast<GamepadHardwareBuffer*>(mem); | 297 return static_cast<GamepadHardwareBuffer*>(mem); |
204 } | 298 } |
205 | 299 |
206 void GamepadProvider::CheckForUserGesture() { | 300 void GamepadProvider::CheckForUserGesture() { |
207 base::AutoLock lock(user_gesture_lock_); | 301 base::AutoLock lock(user_gesture_lock_); |
208 if (user_gesture_observers_.empty()) | 302 if (user_gesture_observers_.empty() && ever_had_user_gesture_) |
209 return; // Don't need to check if nobody is listening. | 303 return; |
210 | 304 |
211 if (GamepadsHaveUserGesture(SharedMemoryAsHardwareBuffer()->buffer)) { | 305 if (GamepadsHaveUserGesture(SharedMemoryAsHardwareBuffer()->buffer)) { |
| 306 ever_had_user_gesture_ = true; |
212 for (size_t i = 0; i < user_gesture_observers_.size(); i++) { | 307 for (size_t i = 0; i < user_gesture_observers_.size(); i++) { |
213 user_gesture_observers_[i].message_loop->PostTask(FROM_HERE, | 308 user_gesture_observers_[i].message_loop->PostTask(FROM_HERE, |
214 user_gesture_observers_[i].closure); | 309 user_gesture_observers_[i].closure); |
215 } | 310 } |
216 user_gesture_observers_.clear(); | 311 user_gesture_observers_.clear(); |
217 } | 312 } |
218 } | 313 } |
219 | 314 |
220 } // namespace content | 315 } // namespace content |
OLD | NEW |