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.
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
;
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()
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
)
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();
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_
);
80 void GamepadProvider::Pause() {
82 base::AutoLock
lock(is_paused_lock_
);
85 base::MessageLoop
* polling_loop
= polling_thread_
->message_loop();
86 polling_loop
->task_runner()->PostTask(
88 base::Bind(&GamepadProvider::SendPauseHint
, Unretained(this), true));
91 void GamepadProvider::Resume() {
93 base::AutoLock
lock(is_paused_lock_
);
99 base::MessageLoop
* polling_loop
= polling_thread_
->message_loop();
100 polling_loop
->task_runner()->PostTask(
102 base::Bind(&GamepadProvider::SendPauseHint
, Unretained(this), false));
103 polling_loop
->task_runner()->PostTask(
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();
123 monitor
->AddDevicesChangedObserver(this);
124 bool res
= gamepad_shared_memory_
.CreateAndMapAnonymous(data_size
);
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
;
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
;
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.
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());
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() {
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;
208 GamepadHardwareBuffer
* hwbuf
= SharedMemoryAsHardwareBuffer();
210 ANNOTATE_BENIGN_RACE_SIZED(
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
)) {
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.
254 void GamepadProvider::ScheduleDoPoll() {
255 DCHECK(base::MessageLoop::current() == polling_thread_
->message_loop());
256 if (have_scheduled_do_poll_
)
260 base::AutoLock
lock(is_paused_lock_
);
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
];
277 state
.SetDisconnected();
279 BrowserThread::PostTask(
282 base::Bind(&GamepadProvider::DispatchGamepadConnectionChange
,
283 base::Unretained(this),
289 void GamepadProvider::DispatchGamepadConnectionChange(
290 bool connected
, int index
, const WebGamepad
& pad
) {
292 GamepadService::GetInstance()->OnGamepadConnected(index
, pad
);
294 GamepadService::GetInstance()->OnGamepadDisconnected(index
, pad
);
297 GamepadHardwareBuffer
* GamepadProvider::SharedMemoryAsHardwareBuffer() {
298 void* mem
= gamepad_shared_memory_
.memory();
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_
)
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