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/common/gamepad_hardware_buffer.h"
19 #include "content/common/gamepad_messages.h"
20 #include "content/common/gamepad_user_gesture.h"
24 GamepadProvider::ClosureAndThread::ClosureAndThread(
25 const base::Closure
& c
,
26 const scoped_refptr
<base::MessageLoopProxy
>& m
)
31 GamepadProvider::ClosureAndThread::~ClosureAndThread() {
34 GamepadProvider::GamepadProvider()
36 have_scheduled_do_poll_(false),
37 devices_changed_(true) {
38 Initialize(scoped_ptr
<GamepadDataFetcher
>());
41 GamepadProvider::GamepadProvider(scoped_ptr
<GamepadDataFetcher
> fetcher
)
43 have_scheduled_do_poll_(false),
44 devices_changed_(true) {
45 Initialize(fetcher
.Pass());
48 GamepadProvider::~GamepadProvider() {
49 base::SystemMonitor
* monitor
= base::SystemMonitor::Get();
51 monitor
->RemoveDevicesChangedObserver(this);
53 // Use Stop() to join the polling thread, as there may be pending callbacks
54 // which dereference |polling_thread_|.
55 polling_thread_
->Stop();
56 data_fetcher_
.reset();
59 base::SharedMemoryHandle
GamepadProvider::GetSharedMemoryHandleForProcess(
60 base::ProcessHandle process
) {
61 base::SharedMemoryHandle renderer_handle
;
62 gamepad_shared_memory_
.ShareToProcess(process
, &renderer_handle
);
63 return renderer_handle
;
66 void GamepadProvider::Pause() {
68 base::AutoLock
lock(is_paused_lock_
);
71 base::MessageLoop
* polling_loop
= polling_thread_
->message_loop();
72 polling_loop
->PostTask(
74 base::Bind(&GamepadProvider::SendPauseHint
, Unretained(this), true));
77 void GamepadProvider::Resume() {
79 base::AutoLock
lock(is_paused_lock_
);
85 base::MessageLoop
* polling_loop
= polling_thread_
->message_loop();
86 polling_loop
->PostTask(
88 base::Bind(&GamepadProvider::SendPauseHint
, Unretained(this), false));
89 polling_loop
->PostTask(
91 base::Bind(&GamepadProvider::ScheduleDoPoll
, Unretained(this)));
94 void GamepadProvider::RegisterForUserGesture(const base::Closure
& closure
) {
95 base::AutoLock
lock(user_gesture_lock_
);
96 user_gesture_observers_
.push_back(ClosureAndThread(
97 closure
, base::MessageLoop::current()->message_loop_proxy()));
100 void GamepadProvider::OnDevicesChanged(base::SystemMonitor::DeviceType type
) {
101 base::AutoLock
lock(devices_changed_lock_
);
102 devices_changed_
= true;
105 void GamepadProvider::Initialize(scoped_ptr
<GamepadDataFetcher
> fetcher
) {
106 size_t data_size
= sizeof(GamepadHardwareBuffer
);
107 base::SystemMonitor
* monitor
= base::SystemMonitor::Get();
109 monitor
->AddDevicesChangedObserver(this);
110 bool res
= gamepad_shared_memory_
.CreateAndMapAnonymous(data_size
);
112 GamepadHardwareBuffer
* hwbuf
= SharedMemoryAsHardwareBuffer();
113 memset(hwbuf
, 0, sizeof(GamepadHardwareBuffer
));
115 polling_thread_
.reset(new base::Thread("Gamepad polling thread"));
116 #if defined(OS_LINUX)
117 // On Linux, the data fetcher needs to watch file descriptors, so the message
118 // loop needs to be a libevent loop.
119 const base::MessageLoop::Type kMessageLoopType
= base::MessageLoop::TYPE_IO
;
121 // 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
123 // to properly pump the MessageWindow that captures device state.
124 const base::MessageLoop::Type kMessageLoopType
= base::MessageLoop::TYPE_UI
;
126 polling_thread_
->StartWithOptions(base::Thread::Options(kMessageLoopType
, 0));
128 polling_thread_
->message_loop()->PostTask(
130 base::Bind(&GamepadProvider::DoInitializePollingThread
,
131 base::Unretained(this),
132 base::Passed(&fetcher
)));
135 void GamepadProvider::DoInitializePollingThread(
136 scoped_ptr
<GamepadDataFetcher
> fetcher
) {
137 DCHECK(base::MessageLoop::current() == polling_thread_
->message_loop());
138 DCHECK(!data_fetcher_
.get()); // Should only initialize once.
141 fetcher
.reset(new GamepadPlatformDataFetcher
);
142 data_fetcher_
= fetcher
.Pass();
145 void GamepadProvider::SendPauseHint(bool paused
) {
146 DCHECK(base::MessageLoop::current() == polling_thread_
->message_loop());
148 data_fetcher_
->PauseHint(paused
);
151 void GamepadProvider::DoPoll() {
152 DCHECK(base::MessageLoop::current() == polling_thread_
->message_loop());
153 DCHECK(have_scheduled_do_poll_
);
154 have_scheduled_do_poll_
= false;
157 GamepadHardwareBuffer
* hwbuf
= SharedMemoryAsHardwareBuffer();
159 ANNOTATE_BENIGN_RACE_SIZED(
161 sizeof(blink::WebGamepads
),
162 "Racey reads are discarded");
165 base::AutoLock
lock(devices_changed_lock_
);
166 changed
= devices_changed_
;
167 devices_changed_
= false;
170 // Acquire the SeqLock. There is only ever one writer to this data.
171 // See gamepad_hardware_buffer.h.
172 hwbuf
->sequence
.WriteBegin();
173 data_fetcher_
->GetGamepadData(&hwbuf
->buffer
, changed
);
174 hwbuf
->sequence
.WriteEnd();
176 CheckForUserGesture();
178 // Schedule our next interval of polling.
182 void GamepadProvider::ScheduleDoPoll() {
183 DCHECK(base::MessageLoop::current() == polling_thread_
->message_loop());
184 if (have_scheduled_do_poll_
)
188 base::AutoLock
lock(is_paused_lock_
);
193 base::MessageLoop::current()->PostDelayedTask(
195 base::Bind(&GamepadProvider::DoPoll
, Unretained(this)),
196 base::TimeDelta::FromMilliseconds(kDesiredSamplingIntervalMs
));
197 have_scheduled_do_poll_
= true;
200 GamepadHardwareBuffer
* GamepadProvider::SharedMemoryAsHardwareBuffer() {
201 void* mem
= gamepad_shared_memory_
.memory();
203 return static_cast<GamepadHardwareBuffer
*>(mem
);
206 void GamepadProvider::CheckForUserGesture() {
207 base::AutoLock
lock(user_gesture_lock_
);
208 if (user_gesture_observers_
.empty())
209 return; // Don't need to check if nobody is listening.
211 if (GamepadsHaveUserGesture(SharedMemoryAsHardwareBuffer()->buffer
)) {
212 for (size_t i
= 0; i
< user_gesture_observers_
.size(); i
++) {
213 user_gesture_observers_
[i
].message_loop
->PostTask(FROM_HERE
,
214 user_gesture_observers_
[i
].closure
);
216 user_gesture_observers_
.clear();
220 } // namespace content