content: Remove use of MessageLoopProxy and deprecated MessageLoop APIs
[chromium-blink-merge.git] / content / browser / gamepad / gamepad_provider.cc
blobeef900133b0f6e3bcca0ee4cf67e7697209cb2fe
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.
5 #include <cmath>
6 #include <set>
7 #include <vector>
9 #include "base/bind.h"
10 #include "base/location.h"
11 #include "base/logging.h"
12 #include "base/single_thread_task_runner.h"
13 #include "base/third_party/dynamic_annotations/dynamic_annotations.h"
14 #include "base/thread_task_runner_handle.h"
15 #include "base/threading/thread.h"
16 #include "base/threading/thread_restrictions.h"
17 #include "content/browser/gamepad/gamepad_data_fetcher.h"
18 #include "content/browser/gamepad/gamepad_platform_data_fetcher.h"
19 #include "content/browser/gamepad/gamepad_provider.h"
20 #include "content/browser/gamepad/gamepad_service.h"
21 #include "content/common/gamepad_hardware_buffer.h"
22 #include "content/common/gamepad_messages.h"
23 #include "content/common/gamepad_user_gesture.h"
24 #include "content/public/browser/browser_thread.h"
26 using blink::WebGamepad;
27 using blink::WebGamepads;
29 namespace content {
31 GamepadProvider::ClosureAndThread::ClosureAndThread(
32 const base::Closure& c,
33 const scoped_refptr<base::SingleThreadTaskRunner>& m)
34 : closure(c), task_runner(m) {
37 GamepadProvider::ClosureAndThread::~ClosureAndThread() {
40 GamepadProvider::GamepadProvider()
41 : is_paused_(true),
42 have_scheduled_do_poll_(false),
43 devices_changed_(true),
44 ever_had_user_gesture_(false) {
45 Initialize(scoped_ptr<GamepadDataFetcher>());
48 GamepadProvider::GamepadProvider(scoped_ptr<GamepadDataFetcher> fetcher)
49 : is_paused_(true),
50 have_scheduled_do_poll_(false),
51 devices_changed_(true),
52 ever_had_user_gesture_(false) {
53 Initialize(fetcher.Pass());
56 GamepadProvider::~GamepadProvider() {
57 base::SystemMonitor* monitor = base::SystemMonitor::Get();
58 if (monitor)
59 monitor->RemoveDevicesChangedObserver(this);
61 // Use Stop() to join the polling thread, as there may be pending callbacks
62 // which dereference |polling_thread_|.
63 polling_thread_->Stop();
64 data_fetcher_.reset();
67 base::SharedMemoryHandle GamepadProvider::GetSharedMemoryHandleForProcess(
68 base::ProcessHandle process) {
69 base::SharedMemoryHandle renderer_handle;
70 gamepad_shared_memory_.ShareToProcess(process, &renderer_handle);
71 return renderer_handle;
74 void GamepadProvider::GetCurrentGamepadData(WebGamepads* data) {
75 const WebGamepads& pads = SharedMemoryAsHardwareBuffer()->buffer;
76 base::AutoLock lock(shared_memory_lock_);
77 *data = pads;
80 void GamepadProvider::Pause() {
82 base::AutoLock lock(is_paused_lock_);
83 is_paused_ = true;
85 base::MessageLoop* polling_loop = polling_thread_->message_loop();
86 polling_loop->task_runner()->PostTask(
87 FROM_HERE,
88 base::Bind(&GamepadProvider::SendPauseHint, Unretained(this), true));
91 void GamepadProvider::Resume() {
93 base::AutoLock lock(is_paused_lock_);
94 if (!is_paused_)
95 return;
96 is_paused_ = false;
99 base::MessageLoop* polling_loop = polling_thread_->message_loop();
100 polling_loop->task_runner()->PostTask(
101 FROM_HERE,
102 base::Bind(&GamepadProvider::SendPauseHint, Unretained(this), false));
103 polling_loop->task_runner()->PostTask(
104 FROM_HERE,
105 base::Bind(&GamepadProvider::ScheduleDoPoll, Unretained(this)));
108 void GamepadProvider::RegisterForUserGesture(const base::Closure& closure) {
109 base::AutoLock lock(user_gesture_lock_);
110 user_gesture_observers_.push_back(
111 ClosureAndThread(closure, base::MessageLoop::current()->task_runner()));
114 void GamepadProvider::OnDevicesChanged(base::SystemMonitor::DeviceType type) {
115 base::AutoLock lock(devices_changed_lock_);
116 devices_changed_ = true;
119 void GamepadProvider::Initialize(scoped_ptr<GamepadDataFetcher> fetcher) {
120 size_t data_size = sizeof(GamepadHardwareBuffer);
121 base::SystemMonitor* monitor = base::SystemMonitor::Get();
122 if (monitor)
123 monitor->AddDevicesChangedObserver(this);
124 bool res = gamepad_shared_memory_.CreateAndMapAnonymous(data_size);
125 CHECK(res);
126 GamepadHardwareBuffer* hwbuf = SharedMemoryAsHardwareBuffer();
127 memset(hwbuf, 0, sizeof(GamepadHardwareBuffer));
128 pad_states_.reset(new PadState[WebGamepads::itemsLengthCap]);
130 polling_thread_.reset(new base::Thread("Gamepad polling thread"));
131 #if defined(OS_LINUX)
132 // On Linux, the data fetcher needs to watch file descriptors, so the message
133 // loop needs to be a libevent loop.
134 const base::MessageLoop::Type kMessageLoopType = base::MessageLoop::TYPE_IO;
135 #elif defined(OS_ANDROID)
136 // On Android, keeping a message loop of default type.
137 const base::MessageLoop::Type kMessageLoopType =
138 base::MessageLoop::TYPE_DEFAULT;
139 #else
140 // On Mac, the data fetcher uses IOKit which depends on CFRunLoop, so the
141 // message loop needs to be a UI-type loop. On Windows it must be a UI loop
142 // to properly pump the MessageWindow that captures device state.
143 const base::MessageLoop::Type kMessageLoopType = base::MessageLoop::TYPE_UI;
144 #endif
145 polling_thread_->StartWithOptions(base::Thread::Options(kMessageLoopType, 0));
147 polling_thread_->task_runner()->PostTask(
148 FROM_HERE, base::Bind(&GamepadProvider::DoInitializePollingThread,
149 base::Unretained(this), base::Passed(&fetcher)));
152 void GamepadProvider::DoInitializePollingThread(
153 scoped_ptr<GamepadDataFetcher> fetcher) {
154 DCHECK(base::MessageLoop::current() == polling_thread_->message_loop());
155 DCHECK(!data_fetcher_.get()); // Should only initialize once.
157 if (!fetcher)
158 fetcher.reset(new GamepadPlatformDataFetcher);
159 data_fetcher_ = fetcher.Pass();
162 void GamepadProvider::SendPauseHint(bool paused) {
163 DCHECK(base::MessageLoop::current() == polling_thread_->message_loop());
164 if (data_fetcher_)
165 data_fetcher_->PauseHint(paused);
168 bool GamepadProvider::PadState::Match(const WebGamepad& pad) const {
169 return connected_ == pad.connected &&
170 axes_length_ == pad.axesLength &&
171 buttons_length_ == pad.buttonsLength &&
172 memcmp(id_, pad.id, sizeof(id_)) == 0 &&
173 memcmp(mapping_, pad.mapping, sizeof(mapping_)) == 0;
176 void GamepadProvider::PadState::SetPad(const WebGamepad& pad) {
177 connected_ = pad.connected;
178 axes_length_ = pad.axesLength;
179 buttons_length_ = pad.buttonsLength;
180 memcpy(id_, pad.id, sizeof(id_));
181 memcpy(mapping_, pad.mapping, sizeof(mapping_));
184 void GamepadProvider::PadState::SetDisconnected() {
185 connected_ = false;
186 axes_length_ = 0;
187 buttons_length_ = 0;
188 memset(id_, 0, sizeof(id_));
189 memset(mapping_, 0, sizeof(mapping_));
192 void GamepadProvider::PadState::AsWebGamepad(WebGamepad* pad) {
193 pad->connected = connected_;
194 pad->axesLength = axes_length_;
195 pad->buttonsLength = buttons_length_;
196 memcpy(pad->id, id_, sizeof(id_));
197 memcpy(pad->mapping, mapping_, sizeof(mapping_));
198 memset(pad->axes, 0, sizeof(pad->axes));
199 memset(pad->buttons, 0, sizeof(pad->buttons));
202 void GamepadProvider::DoPoll() {
203 DCHECK(base::MessageLoop::current() == polling_thread_->message_loop());
204 DCHECK(have_scheduled_do_poll_);
205 have_scheduled_do_poll_ = false;
207 bool changed;
208 GamepadHardwareBuffer* hwbuf = SharedMemoryAsHardwareBuffer();
210 ANNOTATE_BENIGN_RACE_SIZED(
211 &hwbuf->buffer,
212 sizeof(WebGamepads),
213 "Racey reads are discarded");
216 base::AutoLock lock(devices_changed_lock_);
217 changed = devices_changed_;
218 devices_changed_ = false;
222 base::AutoLock lock(shared_memory_lock_);
224 // Acquire the SeqLock. There is only ever one writer to this data.
225 // See gamepad_hardware_buffer.h.
226 hwbuf->sequence.WriteBegin();
227 data_fetcher_->GetGamepadData(&hwbuf->buffer, changed);
228 hwbuf->sequence.WriteEnd();
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);
248 CheckForUserGesture();
250 // Schedule our next interval of polling.
251 ScheduleDoPoll();
254 void GamepadProvider::ScheduleDoPoll() {
255 DCHECK(base::MessageLoop::current() == polling_thread_->message_loop());
256 if (have_scheduled_do_poll_)
257 return;
260 base::AutoLock lock(is_paused_lock_);
261 if (is_paused_)
262 return;
265 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
266 FROM_HERE, base::Bind(&GamepadProvider::DoPoll, Unretained(this)),
267 base::TimeDelta::FromMilliseconds(kDesiredSamplingIntervalMs));
268 have_scheduled_do_poll_ = true;
271 void GamepadProvider::OnGamepadConnectionChange(
272 bool connected, int index, const WebGamepad& pad) {
273 PadState& state = pad_states_.get()[index];
274 if (connected)
275 state.SetPad(pad);
276 else
277 state.SetDisconnected();
279 BrowserThread::PostTask(
280 BrowserThread::IO,
281 FROM_HERE,
282 base::Bind(&GamepadProvider::DispatchGamepadConnectionChange,
283 base::Unretained(this),
284 connected,
285 index,
286 pad));
289 void GamepadProvider::DispatchGamepadConnectionChange(
290 bool connected, int index, const WebGamepad& pad) {
291 if (connected)
292 GamepadService::GetInstance()->OnGamepadConnected(index, pad);
293 else
294 GamepadService::GetInstance()->OnGamepadDisconnected(index, pad);
297 GamepadHardwareBuffer* GamepadProvider::SharedMemoryAsHardwareBuffer() {
298 void* mem = gamepad_shared_memory_.memory();
299 CHECK(mem);
300 return static_cast<GamepadHardwareBuffer*>(mem);
303 void GamepadProvider::CheckForUserGesture() {
304 base::AutoLock lock(user_gesture_lock_);
305 if (user_gesture_observers_.empty() && ever_had_user_gesture_)
306 return;
308 bool had_gesture_before = ever_had_user_gesture_;
309 const WebGamepads& pads = SharedMemoryAsHardwareBuffer()->buffer;
310 if (GamepadsHaveUserGesture(pads)) {
311 ever_had_user_gesture_ = true;
312 for (size_t i = 0; i < user_gesture_observers_.size(); i++) {
313 user_gesture_observers_[i].task_runner->PostTask(
314 FROM_HERE, user_gesture_observers_[i].closure);
316 user_gesture_observers_.clear();
318 if (!had_gesture_before && ever_had_user_gesture_) {
319 // Initialize pad_states_ for the first time.
320 for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) {
321 pad_states_.get()[i].SetPad(pads.items[i]);
326 } // namespace content