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/logging.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/message_loop/message_loop_proxy.h"
13 #include "base/threading/thread.h"
14 #include "base/threading/thread_restrictions.h"
15 #include "content/browser/gamepad/gamepad_data_fetcher.h"
16 #include "content/browser/gamepad/gamepad_platform_data_fetcher.h"
17 #include "content/browser/gamepad/gamepad_provider.h"
18 #include "content/browser/gamepad/gamepad_service.h"
19 #include "content/common/gamepad_hardware_buffer.h"
20 #include "content/common/gamepad_messages.h"
21 #include "content/common/gamepad_user_gesture.h"
22 #include "content/public/browser/browser_thread.h"
24 using blink::WebGamepad
;
25 using blink::WebGamepads
;
29 GamepadProvider::ClosureAndThread::ClosureAndThread(
30 const base::Closure
& c
,
31 const scoped_refptr
<base::MessageLoopProxy
>& m
)
36 GamepadProvider::ClosureAndThread::~ClosureAndThread() {
39 GamepadProvider::GamepadProvider()
41 have_scheduled_do_poll_(false),
42 devices_changed_(true),
43 ever_had_user_gesture_(false) {
44 Initialize(scoped_ptr
<GamepadDataFetcher
>());
47 GamepadProvider::GamepadProvider(scoped_ptr
<GamepadDataFetcher
> fetcher
)
49 have_scheduled_do_poll_(false),
50 devices_changed_(true),
51 ever_had_user_gesture_(false) {
52 Initialize(fetcher
.Pass());
55 GamepadProvider::~GamepadProvider() {
56 base::SystemMonitor
* monitor
= base::SystemMonitor::Get();
58 monitor
->RemoveDevicesChangedObserver(this);
60 // Use Stop() to join the polling thread, as there may be pending callbacks
61 // which dereference |polling_thread_|.
62 polling_thread_
->Stop();
63 data_fetcher_
.reset();
66 base::SharedMemoryHandle
GamepadProvider::GetSharedMemoryHandleForProcess(
67 base::ProcessHandle process
) {
68 base::SharedMemoryHandle renderer_handle
;
69 gamepad_shared_memory_
.ShareToProcess(process
, &renderer_handle
);
70 return renderer_handle
;
73 void GamepadProvider::GetCurrentGamepadData(WebGamepads
* data
) {
74 const WebGamepads
& pads
= SharedMemoryAsHardwareBuffer()->buffer
;
75 base::AutoLock
lock(shared_memory_lock_
);
79 void GamepadProvider::Pause() {
81 base::AutoLock
lock(is_paused_lock_
);
84 base::MessageLoop
* polling_loop
= polling_thread_
->message_loop();
85 polling_loop
->PostTask(
87 base::Bind(&GamepadProvider::SendPauseHint
, Unretained(this), true));
90 void GamepadProvider::Resume() {
92 base::AutoLock
lock(is_paused_lock_
);
98 base::MessageLoop
* polling_loop
= polling_thread_
->message_loop();
99 polling_loop
->PostTask(
101 base::Bind(&GamepadProvider::SendPauseHint
, Unretained(this), false));
102 polling_loop
->PostTask(
104 base::Bind(&GamepadProvider::ScheduleDoPoll
, Unretained(this)));
107 void GamepadProvider::RegisterForUserGesture(const base::Closure
& closure
) {
108 base::AutoLock
lock(user_gesture_lock_
);
109 user_gesture_observers_
.push_back(ClosureAndThread(
110 closure
, base::MessageLoop::current()->message_loop_proxy()));
113 void GamepadProvider::OnDevicesChanged(base::SystemMonitor::DeviceType type
) {
114 base::AutoLock
lock(devices_changed_lock_
);
115 devices_changed_
= true;
118 void GamepadProvider::Initialize(scoped_ptr
<GamepadDataFetcher
> fetcher
) {
119 size_t data_size
= sizeof(GamepadHardwareBuffer
);
120 base::SystemMonitor
* monitor
= base::SystemMonitor::Get();
122 monitor
->AddDevicesChangedObserver(this);
123 bool res
= gamepad_shared_memory_
.CreateAndMapAnonymous(data_size
);
125 GamepadHardwareBuffer
* hwbuf
= SharedMemoryAsHardwareBuffer();
126 memset(hwbuf
, 0, sizeof(GamepadHardwareBuffer
));
127 pad_states_
.reset(new PadState
[WebGamepads::itemsLengthCap
]);
129 polling_thread_
.reset(new base::Thread("Gamepad polling thread"));
130 #if defined(OS_LINUX)
131 // On Linux, the data fetcher needs to watch file descriptors, so the message
132 // loop needs to be a libevent loop.
133 const base::MessageLoop::Type kMessageLoopType
= base::MessageLoop::TYPE_IO
;
134 #elif defined(OS_ANDROID)
135 // On Android, keeping a message loop of default type.
136 const base::MessageLoop::Type kMessageLoopType
=
137 base::MessageLoop::TYPE_DEFAULT
;
139 // On Mac, the data fetcher uses IOKit which depends on CFRunLoop, so the
140 // message loop needs to be a UI-type loop. On Windows it must be a UI loop
141 // to properly pump the MessageWindow that captures device state.
142 const base::MessageLoop::Type kMessageLoopType
= base::MessageLoop::TYPE_UI
;
144 polling_thread_
->StartWithOptions(base::Thread::Options(kMessageLoopType
, 0));
146 polling_thread_
->message_loop()->PostTask(
148 base::Bind(&GamepadProvider::DoInitializePollingThread
,
149 base::Unretained(this),
150 base::Passed(&fetcher
)));
153 void GamepadProvider::DoInitializePollingThread(
154 scoped_ptr
<GamepadDataFetcher
> fetcher
) {
155 DCHECK(base::MessageLoop::current() == polling_thread_
->message_loop());
156 DCHECK(!data_fetcher_
.get()); // Should only initialize once.
159 fetcher
.reset(new GamepadPlatformDataFetcher
);
160 data_fetcher_
= fetcher
.Pass();
163 void GamepadProvider::SendPauseHint(bool paused
) {
164 DCHECK(base::MessageLoop::current() == polling_thread_
->message_loop());
166 data_fetcher_
->PauseHint(paused
);
169 bool GamepadProvider::PadState::Match(const WebGamepad
& pad
) const {
170 return connected_
== pad
.connected
&&
171 axes_length_
== pad
.axesLength
&&
172 buttons_length_
== pad
.buttonsLength
&&
173 memcmp(id_
, pad
.id
, arraysize(id_
)) == 0 &&
174 memcmp(mapping_
, pad
.mapping
, arraysize(mapping_
)) == 0;
177 void GamepadProvider::PadState::SetPad(const WebGamepad
& pad
) {
178 connected_
= pad
.connected
;
179 axes_length_
= pad
.axesLength
;
180 buttons_length_
= pad
.buttonsLength
;
181 memcpy(id_
, pad
.id
, arraysize(id_
));
182 memcpy(mapping_
, pad
.mapping
, arraysize(mapping_
));
185 void GamepadProvider::PadState::SetDisconnected() {
189 memset(id_
, 0, arraysize(id_
));
190 memset(mapping_
, 0, arraysize(mapping_
));
193 void GamepadProvider::PadState::AsWebGamepad(WebGamepad
* pad
) {
194 pad
->connected
= connected_
;
195 pad
->axesLength
= axes_length_
;
196 pad
->buttonsLength
= buttons_length_
;
197 memcpy(pad
->id
, id_
, arraysize(id_
));
198 memcpy(pad
->mapping
, mapping_
, arraysize(mapping_
));
199 memset(pad
->axes
, 0, arraysize(pad
->axes
));
200 memset(pad
->buttons
, 0, arraysize(pad
->buttons
));
203 void GamepadProvider::DoPoll() {
204 DCHECK(base::MessageLoop::current() == polling_thread_
->message_loop());
205 DCHECK(have_scheduled_do_poll_
);
206 have_scheduled_do_poll_
= false;
209 GamepadHardwareBuffer
* hwbuf
= SharedMemoryAsHardwareBuffer();
211 ANNOTATE_BENIGN_RACE_SIZED(
214 "Racey reads are discarded");
217 base::AutoLock
lock(devices_changed_lock_
);
218 changed
= devices_changed_
;
219 devices_changed_
= false;
223 base::AutoLock
lock(shared_memory_lock_
);
225 // Acquire the SeqLock. There is only ever one writer to this data.
226 // See gamepad_hardware_buffer.h.
227 hwbuf
->sequence
.WriteBegin();
228 data_fetcher_
->GetGamepadData(&hwbuf
->buffer
, changed
);
229 hwbuf
->sequence
.WriteEnd();
232 if (ever_had_user_gesture_
) {
233 for (unsigned i
= 0; i
< WebGamepads::itemsLengthCap
; ++i
) {
234 WebGamepad
& pad
= hwbuf
->buffer
.items
[i
];
235 PadState
& state
= pad_states_
.get()[i
];
236 if (pad
.connected
&& !state
.connected()) {
237 OnGamepadConnectionChange(true, i
, pad
);
238 } else if (!pad
.connected
&& state
.connected()) {
239 OnGamepadConnectionChange(false, i
, pad
);
240 } else if (pad
.connected
&& state
.connected() && !state
.Match(pad
)) {
242 state
.AsWebGamepad(&old_pad
);
243 OnGamepadConnectionChange(false, i
, old_pad
);
244 OnGamepadConnectionChange(true, i
, pad
);
249 CheckForUserGesture();
251 // Schedule our next interval of polling.
255 void GamepadProvider::ScheduleDoPoll() {
256 DCHECK(base::MessageLoop::current() == polling_thread_
->message_loop());
257 if (have_scheduled_do_poll_
)
261 base::AutoLock
lock(is_paused_lock_
);
266 base::MessageLoop::current()->PostDelayedTask(
268 base::Bind(&GamepadProvider::DoPoll
, Unretained(this)),
269 base::TimeDelta::FromMilliseconds(kDesiredSamplingIntervalMs
));
270 have_scheduled_do_poll_
= true;
273 void GamepadProvider::OnGamepadConnectionChange(
274 bool connected
, int index
, const WebGamepad
& pad
) {
275 PadState
& state
= pad_states_
.get()[index
];
279 state
.SetDisconnected();
281 BrowserThread::PostTask(
284 base::Bind(&GamepadProvider::DispatchGamepadConnectionChange
,
285 base::Unretained(this),
291 void GamepadProvider::DispatchGamepadConnectionChange(
292 bool connected
, int index
, const WebGamepad
& pad
) {
294 GamepadService::GetInstance()->OnGamepadConnected(index
, pad
);
296 GamepadService::GetInstance()->OnGamepadDisconnected(index
, pad
);
299 GamepadHardwareBuffer
* GamepadProvider::SharedMemoryAsHardwareBuffer() {
300 void* mem
= gamepad_shared_memory_
.memory();
302 return static_cast<GamepadHardwareBuffer
*>(mem
);
305 void GamepadProvider::CheckForUserGesture() {
306 base::AutoLock
lock(user_gesture_lock_
);
307 if (user_gesture_observers_
.empty() && ever_had_user_gesture_
)
310 bool had_gesture_before
= ever_had_user_gesture_
;
311 const WebGamepads
& pads
= SharedMemoryAsHardwareBuffer()->buffer
;
312 if (GamepadsHaveUserGesture(pads
)) {
313 ever_had_user_gesture_
= true;
314 for (size_t i
= 0; i
< user_gesture_observers_
.size(); i
++) {
315 user_gesture_observers_
[i
].message_loop
->PostTask(FROM_HERE
,
316 user_gesture_observers_
[i
].closure
);
318 user_gesture_observers_
.clear();
320 if (!had_gesture_before
&& ever_had_user_gesture_
) {
321 // Initialize pad_states_ for the first time.
322 for (size_t i
= 0; i
< WebGamepads::itemsLengthCap
; ++i
) {
323 pad_states_
.get()[i
].SetPad(pads
.items
[i
]);
328 } // namespace content