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

Side by Side Diff: content/browser/gamepad/gamepad_provider.cc

Issue 2081583002: Migrating majority of gamepad from content/browser/ to device/ (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Final tweaks Created 4 years, 5 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
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "content/browser/gamepad/gamepad_provider.h"
6
7 #include <stddef.h>
8 #include <string.h>
9 #include <cmath>
10 #include <set>
11 #include <utility>
12 #include <vector>
13
14 #include "base/bind.h"
15 #include "base/location.h"
16 #include "base/logging.h"
17 #include "base/single_thread_task_runner.h"
18 #include "base/third_party/dynamic_annotations/dynamic_annotations.h"
19 #include "base/threading/thread.h"
20 #include "base/threading/thread_restrictions.h"
21 #include "base/threading/thread_task_runner_handle.h"
22 #include "build/build_config.h"
23 #include "content/browser/gamepad/gamepad_data_fetcher.h"
24 #include "content/browser/gamepad/gamepad_platform_data_fetcher.h"
25 #include "content/browser/gamepad/gamepad_service.h"
26 #include "content/common/gamepad_hardware_buffer.h"
27 #include "content/common/gamepad_messages.h"
28 #include "content/common/gamepad_user_gesture.h"
29 #include "content/public/browser/browser_thread.h"
30
31 using blink::WebGamepad;
32 using blink::WebGamepads;
33
34 namespace content {
35
36 GamepadProvider::ClosureAndThread::ClosureAndThread(
37 const base::Closure& c,
38 const scoped_refptr<base::SingleThreadTaskRunner>& m)
39 : closure(c), task_runner(m) {
40 }
41
42 GamepadProvider::ClosureAndThread::ClosureAndThread(
43 const ClosureAndThread& other) = default;
44
45 GamepadProvider::ClosureAndThread::~ClosureAndThread() {
46 }
47
48 GamepadProvider::GamepadProvider()
49 : is_paused_(true),
50 have_scheduled_do_poll_(false),
51 devices_changed_(true),
52 ever_had_user_gesture_(false) {
53 Initialize(std::unique_ptr<GamepadDataFetcher>());
54 }
55
56 GamepadProvider::GamepadProvider(std::unique_ptr<GamepadDataFetcher> fetcher)
57 : is_paused_(true),
58 have_scheduled_do_poll_(false),
59 devices_changed_(true),
60 ever_had_user_gesture_(false) {
61 Initialize(std::move(fetcher));
62 }
63
64 GamepadProvider::~GamepadProvider() {
65 base::SystemMonitor* monitor = base::SystemMonitor::Get();
66 if (monitor)
67 monitor->RemoveDevicesChangedObserver(this);
68
69 // Use Stop() to join the polling thread, as there may be pending callbacks
70 // which dereference |polling_thread_|.
71 polling_thread_->Stop();
72 data_fetcher_.reset();
73 }
74
75 base::SharedMemoryHandle GamepadProvider::GetSharedMemoryHandleForProcess(
76 base::ProcessHandle process) {
77 base::SharedMemoryHandle renderer_handle;
78 gamepad_shared_memory_.ShareToProcess(process, &renderer_handle);
79 return renderer_handle;
80 }
81
82 void GamepadProvider::GetCurrentGamepadData(WebGamepads* data) {
83 const WebGamepads& pads = SharedMemoryAsHardwareBuffer()->buffer;
84 base::AutoLock lock(shared_memory_lock_);
85 *data = pads;
86 }
87
88 void GamepadProvider::Pause() {
89 {
90 base::AutoLock lock(is_paused_lock_);
91 is_paused_ = true;
92 }
93 base::MessageLoop* polling_loop = polling_thread_->message_loop();
94 polling_loop->task_runner()->PostTask(
95 FROM_HERE,
96 base::Bind(&GamepadProvider::SendPauseHint, Unretained(this), true));
97 }
98
99 void GamepadProvider::Resume() {
100 {
101 base::AutoLock lock(is_paused_lock_);
102 if (!is_paused_)
103 return;
104 is_paused_ = false;
105 }
106
107 base::MessageLoop* polling_loop = polling_thread_->message_loop();
108 polling_loop->task_runner()->PostTask(
109 FROM_HERE,
110 base::Bind(&GamepadProvider::SendPauseHint, Unretained(this), false));
111 polling_loop->task_runner()->PostTask(
112 FROM_HERE,
113 base::Bind(&GamepadProvider::ScheduleDoPoll, Unretained(this)));
114 }
115
116 void GamepadProvider::RegisterForUserGesture(const base::Closure& closure) {
117 base::AutoLock lock(user_gesture_lock_);
118 user_gesture_observers_.push_back(
119 ClosureAndThread(closure, base::ThreadTaskRunnerHandle::Get()));
120 }
121
122 void GamepadProvider::OnDevicesChanged(base::SystemMonitor::DeviceType type) {
123 base::AutoLock lock(devices_changed_lock_);
124 devices_changed_ = true;
125 }
126
127 void GamepadProvider::Initialize(std::unique_ptr<GamepadDataFetcher> fetcher) {
128 size_t data_size = sizeof(GamepadHardwareBuffer);
129 base::SystemMonitor* monitor = base::SystemMonitor::Get();
130 if (monitor)
131 monitor->AddDevicesChangedObserver(this);
132 bool res = gamepad_shared_memory_.CreateAndMapAnonymous(data_size);
133 CHECK(res);
134 GamepadHardwareBuffer* hwbuf = SharedMemoryAsHardwareBuffer();
135 memset(hwbuf, 0, sizeof(GamepadHardwareBuffer));
136 pad_states_.reset(new PadState[WebGamepads::itemsLengthCap]);
137
138 polling_thread_.reset(new base::Thread("Gamepad polling thread"));
139 #if defined(OS_LINUX)
140 // On Linux, the data fetcher needs to watch file descriptors, so the message
141 // loop needs to be a libevent loop.
142 const base::MessageLoop::Type kMessageLoopType = base::MessageLoop::TYPE_IO;
143 #elif defined(OS_ANDROID)
144 // On Android, keeping a message loop of default type.
145 const base::MessageLoop::Type kMessageLoopType =
146 base::MessageLoop::TYPE_DEFAULT;
147 #else
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
150 // to properly pump the MessageWindow that captures device state.
151 const base::MessageLoop::Type kMessageLoopType = base::MessageLoop::TYPE_UI;
152 #endif
153 polling_thread_->StartWithOptions(base::Thread::Options(kMessageLoopType, 0));
154
155 polling_thread_->task_runner()->PostTask(
156 FROM_HERE, base::Bind(&GamepadProvider::DoInitializePollingThread,
157 base::Unretained(this), base::Passed(&fetcher)));
158 }
159
160 void GamepadProvider::DoInitializePollingThread(
161 std::unique_ptr<GamepadDataFetcher> fetcher) {
162 DCHECK(base::MessageLoop::current() == polling_thread_->message_loop());
163 DCHECK(!data_fetcher_.get()); // Should only initialize once.
164
165 if (!fetcher)
166 fetcher.reset(new GamepadPlatformDataFetcher);
167 data_fetcher_ = std::move(fetcher);
168 }
169
170 void GamepadProvider::SendPauseHint(bool paused) {
171 DCHECK(base::MessageLoop::current() == polling_thread_->message_loop());
172 if (data_fetcher_)
173 data_fetcher_->PauseHint(paused);
174 }
175
176 bool GamepadProvider::PadState::Match(const WebGamepad& pad) const {
177 return connected_ == pad.connected &&
178 axes_length_ == pad.axesLength &&
179 buttons_length_ == pad.buttonsLength &&
180 memcmp(id_, pad.id, sizeof(id_)) == 0 &&
181 memcmp(mapping_, pad.mapping, sizeof(mapping_)) == 0;
182 }
183
184 void GamepadProvider::PadState::SetPad(const WebGamepad& pad) {
185 connected_ = pad.connected;
186 axes_length_ = pad.axesLength;
187 buttons_length_ = pad.buttonsLength;
188 memcpy(id_, pad.id, sizeof(id_));
189 memcpy(mapping_, pad.mapping, sizeof(mapping_));
190 }
191
192 void GamepadProvider::PadState::SetDisconnected() {
193 connected_ = false;
194 axes_length_ = 0;
195 buttons_length_ = 0;
196 memset(id_, 0, sizeof(id_));
197 memset(mapping_, 0, sizeof(mapping_));
198 }
199
200 void GamepadProvider::PadState::AsWebGamepad(WebGamepad* pad) {
201 pad->connected = connected_;
202 pad->axesLength = axes_length_;
203 pad->buttonsLength = buttons_length_;
204 memcpy(pad->id, id_, sizeof(id_));
205 memcpy(pad->mapping, mapping_, sizeof(mapping_));
206 memset(pad->axes, 0, sizeof(pad->axes));
207 memset(pad->buttons, 0, sizeof(pad->buttons));
208 }
209
210 void GamepadProvider::DoPoll() {
211 DCHECK(base::MessageLoop::current() == polling_thread_->message_loop());
212 DCHECK(have_scheduled_do_poll_);
213 have_scheduled_do_poll_ = false;
214
215 bool changed;
216 GamepadHardwareBuffer* hwbuf = SharedMemoryAsHardwareBuffer();
217
218 ANNOTATE_BENIGN_RACE_SIZED(
219 &hwbuf->buffer,
220 sizeof(WebGamepads),
221 "Racey reads are discarded");
222
223 {
224 base::AutoLock lock(devices_changed_lock_);
225 changed = devices_changed_;
226 devices_changed_ = false;
227 }
228
229 {
230 base::AutoLock lock(shared_memory_lock_);
231
232 // Acquire the SeqLock. There is only ever one writer to this data.
233 // See gamepad_hardware_buffer.h.
234 hwbuf->sequence.WriteBegin();
235 data_fetcher_->GetGamepadData(&hwbuf->buffer, changed);
236 hwbuf->sequence.WriteEnd();
237 }
238
239 if (ever_had_user_gesture_) {
240 for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) {
241 WebGamepad& pad = hwbuf->buffer.items[i];
242 PadState& state = pad_states_.get()[i];
243 if (pad.connected && !state.connected()) {
244 OnGamepadConnectionChange(true, i, pad);
245 } else if (!pad.connected && state.connected()) {
246 OnGamepadConnectionChange(false, i, pad);
247 } else if (pad.connected && state.connected() && !state.Match(pad)) {
248 WebGamepad old_pad;
249 state.AsWebGamepad(&old_pad);
250 OnGamepadConnectionChange(false, i, old_pad);
251 OnGamepadConnectionChange(true, i, pad);
252 }
253 }
254 }
255
256 CheckForUserGesture();
257
258 // Schedule our next interval of polling.
259 ScheduleDoPoll();
260 }
261
262 void GamepadProvider::ScheduleDoPoll() {
263 DCHECK(base::MessageLoop::current() == polling_thread_->message_loop());
264 if (have_scheduled_do_poll_)
265 return;
266
267 {
268 base::AutoLock lock(is_paused_lock_);
269 if (is_paused_)
270 return;
271 }
272
273 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
274 FROM_HERE, base::Bind(&GamepadProvider::DoPoll, Unretained(this)),
275 base::TimeDelta::FromMilliseconds(kDesiredSamplingIntervalMs));
276 have_scheduled_do_poll_ = true;
277 }
278
279 void GamepadProvider::OnGamepadConnectionChange(
280 bool connected, int index, const WebGamepad& pad) {
281 PadState& state = pad_states_.get()[index];
282 if (connected)
283 state.SetPad(pad);
284 else
285 state.SetDisconnected();
286
287 BrowserThread::PostTask(
288 BrowserThread::IO,
289 FROM_HERE,
290 base::Bind(&GamepadProvider::DispatchGamepadConnectionChange,
291 base::Unretained(this),
292 connected,
293 index,
294 pad));
295 }
296
297 void GamepadProvider::DispatchGamepadConnectionChange(
298 bool connected, int index, const WebGamepad& pad) {
299 if (connected)
300 GamepadService::GetInstance()->OnGamepadConnected(index, pad);
301 else
302 GamepadService::GetInstance()->OnGamepadDisconnected(index, pad);
303 }
304
305 GamepadHardwareBuffer* GamepadProvider::SharedMemoryAsHardwareBuffer() {
306 void* mem = gamepad_shared_memory_.memory();
307 CHECK(mem);
308 return static_cast<GamepadHardwareBuffer*>(mem);
309 }
310
311 void GamepadProvider::CheckForUserGesture() {
312 base::AutoLock lock(user_gesture_lock_);
313 if (user_gesture_observers_.empty() && ever_had_user_gesture_)
314 return;
315
316 bool had_gesture_before = ever_had_user_gesture_;
317 const WebGamepads& pads = SharedMemoryAsHardwareBuffer()->buffer;
318 if (GamepadsHaveUserGesture(pads)) {
319 ever_had_user_gesture_ = true;
320 for (size_t i = 0; i < user_gesture_observers_.size(); i++) {
321 user_gesture_observers_[i].task_runner->PostTask(
322 FROM_HERE, user_gesture_observers_[i].closure);
323 }
324 user_gesture_observers_.clear();
325 }
326 if (!had_gesture_before && ever_had_user_gesture_) {
327 // Initialize pad_states_ for the first time.
328 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) {
329 pad_states_.get()[i].SetPad(pads.items[i]);
330 }
331 }
332 }
333
334 } // namespace content
OLDNEW
« no previous file with comments | « content/browser/gamepad/gamepad_provider.h ('k') | content/browser/gamepad/gamepad_provider_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698