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

Side by Side Diff: components/exo/gaming_seat.cc

Issue 2900773003: Allow gaming_seat to use ozone gamepad as back-end (Closed)
Patch Set: Add gaming_seat_ozone to exo Created 3 years, 6 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
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/exo/gaming_seat.h" 5 #include <memory>
6 6
7 #include <cmath> 7 #include "base/macros.h"
8 8 #include "base/sequenced_task_runner.h"
9 #include "base/bind.h"
10 #include "base/location.h"
11 #include "base/single_thread_task_runner.h"
12 #include "base/threading/thread.h" 9 #include "base/threading/thread.h"
13 #include "base/threading/thread_task_runner_handle.h" 10 #include "base/threading/thread_task_runner_handle.h"
14 #include "components/exo/gamepad_delegate.h" 11 #include "components/exo/gaming_seat.h"
15 #include "components/exo/gaming_seat_delegate.h" 12 #include "components/exo/gaming_seat_joydev.h"
16 #include "components/exo/shell_surface.h" 13
17 #include "components/exo/surface.h" 14 #if defined(USE_OZONE)
18 #include "device/gamepad/gamepad_data_fetcher.h" 15 #include "components/exo/gaming_seat_ozone.h"
19 #include "device/gamepad/gamepad_pad_state_provider.h" 16 #endif
20 #include "device/gamepad/gamepad_platform_data_fetcher_linux.h"
21 #include "ui/aura/window.h"
22 17
23 namespace exo { 18 namespace exo {
24 namespace {
25 19
26 constexpr double kGamepadButtonValueEpsilon = 0.001; 20 base::Thread* GamingSeat::CreatePollingThreadIfNeeded() {
27 bool GamepadButtonValuesAreEqual(double a, double b) { 21 #if defined(USE_OZONE) && defined(USE_OZONE_GAMEPAD)
28 return fabs(a - b) < kGamepadButtonValueEpsilon; 22 return nullptr;
23 #else
24 base::Thread* polling_thread =
25 new base::Thread("Exo gaming input polling thread.");
26 polling_thread->StartWithOptions(
27 base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
28 return polling_thread;
29 #endif
29 } 30 }
30 31
31 std::unique_ptr<device::GamepadDataFetcher> CreateGamepadPlatformDataFetcher() { 32 GamingSeat* GamingSeat::CreateGamingSeat(
32 return std::unique_ptr<device::GamepadDataFetcher>( 33 GamingSeatDelegate* gaming_seat_delegate,
33 new device::GamepadPlatformDataFetcherLinux()); 34 base::Thread* polling_thread) {
34 } 35 #if defined(USE_OZONE) && defined(USE_OZONE_GAMEPAD)
35 36 return new GamingSeatOzone(gaming_seat_delegate);
36 // Time between gamepad polls in milliseconds. 37 #else
37 constexpr unsigned kPollingTimeIntervalMs = 16; 38 DCHECK(polling_thread);
38 39 return new GamingSeatJoydev(gaming_seat_delegate,
39 } // namespace 40 polling_thread->task_runner().get());
40 41 #endif
41 ////////////////////////////////////////////////////////////////////////////////
42 // GamingSeat::ThreadSafeGamepadChangeFetcher
43
44 // Implements all methods and resources running on the polling thread.
45 // This class is reference counted to allow it to shut down safely on the
46 // polling thread even if the Gamepad has been destroyed on the origin thread.
47 class GamingSeat::ThreadSafeGamepadChangeFetcher
48 : public device::GamepadPadStateProvider,
49 public base::RefCountedThreadSafe<
50 GamingSeat::ThreadSafeGamepadChangeFetcher> {
51 public:
52 using ProcessGamepadChangesCallback =
53 base::Callback<void(int index, const device::Gamepad)>;
54
55 ThreadSafeGamepadChangeFetcher(
56 const ProcessGamepadChangesCallback& post_gamepad_changes,
57 const CreateGamepadDataFetcherCallback& create_fetcher_callback,
58 base::SingleThreadTaskRunner* task_runner)
59 : process_gamepad_changes_(post_gamepad_changes),
60 create_fetcher_callback_(create_fetcher_callback),
61 polling_task_runner_(task_runner),
62 origin_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
63 thread_checker_.DetachFromThread();
64 }
65
66 // Enable or disable gamepad polling. Can be called from any thread.
67 void EnablePolling(bool enabled) {
68 polling_task_runner_->PostTask(
69 FROM_HERE,
70 base::Bind(
71 &ThreadSafeGamepadChangeFetcher::EnablePollingOnPollingThread,
72 make_scoped_refptr(this), enabled));
73 }
74
75 private:
76 friend class base::RefCountedThreadSafe<ThreadSafeGamepadChangeFetcher>;
77
78 ~ThreadSafeGamepadChangeFetcher() override {}
79
80 // Enables or disables polling.
81 void EnablePollingOnPollingThread(bool enabled) {
82 DCHECK(thread_checker_.CalledOnValidThread());
83 is_enabled_ = enabled;
84
85 if (is_enabled_) {
86 if (!fetcher_) {
87 fetcher_ = create_fetcher_callback_.Run();
88 InitializeDataFetcher(fetcher_.get());
89 DCHECK(fetcher_);
90 }
91 SchedulePollOnPollingThread();
92 } else {
93 fetcher_.reset();
94 }
95 }
96
97 // Schedules the next poll on the polling thread.
98 void SchedulePollOnPollingThread() {
99 DCHECK(thread_checker_.CalledOnValidThread());
100 DCHECK(fetcher_);
101
102 if (!is_enabled_ || has_poll_scheduled_)
103 return;
104
105 has_poll_scheduled_ = true;
106 polling_task_runner_->PostDelayedTask(
107 FROM_HERE,
108 base::Bind(&ThreadSafeGamepadChangeFetcher::PollOnPollingThread,
109 make_scoped_refptr(this)),
110 base::TimeDelta::FromMilliseconds(kPollingTimeIntervalMs));
111 }
112
113 // Polls devices for new data and posts gamepad changes back to origin thread.
114 void PollOnPollingThread() {
115 DCHECK(thread_checker_.CalledOnValidThread());
116
117 has_poll_scheduled_ = false;
118 if (!is_enabled_)
119 return;
120
121 DCHECK(fetcher_);
122
123 device::Gamepads new_state = state_;
124 fetcher_->GetGamepadData(
125 false /* No hardware changed notification from the system */);
126
127 for (size_t i = 0; i < device::Gamepads::kItemsLengthCap; ++i) {
128 device::PadState& pad_state = pad_states_.get()[i];
129
130 // After querying the gamepad clear the state if it did not have it's
131 // active
132 // state updated but is still listed as being associated with a specific
133 // source. This indicates the gamepad is disconnected.
134 if (!pad_state.active_state &&
135 pad_state.source != device::GAMEPAD_SOURCE_NONE) {
136 ClearPadState(pad_state);
137 }
138
139 MapAndSanitizeGamepadData(&pad_state, &new_state.items[i],
140 false /* Don't sanitize gamepad data */);
141
142 // If the gamepad is still actively reporting the next call to
143 // GetGamepadData will set the active state to active again.
144 if (pad_state.active_state)
145 pad_state.active_state = device::GAMEPAD_INACTIVE;
146
147 if (new_state.items[i].connected != state_.items[i].connected ||
148 new_state.items[i].timestamp > state_.items[i].timestamp) {
149 origin_task_runner_->PostTask(
150 FROM_HERE,
151 base::Bind(process_gamepad_changes_, i, new_state.items[i]));
152 }
153 }
154 state_ = new_state;
155 SchedulePollOnPollingThread();
156 }
157
158 // Callback to ProcessGamepadChanges using weak reference to Gamepad.
159 ProcessGamepadChangesCallback process_gamepad_changes_;
160
161 // Callback method to create a gamepad data fetcher.
162 CreateGamepadDataFetcherCallback create_fetcher_callback_;
163
164 // Reference to task runner on polling thread.
165 scoped_refptr<base::SingleThreadTaskRunner> polling_task_runner_;
166
167 // Reference to task runner on origin thread.
168 scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner_;
169
170 // Gamepad data fetcher used for querying gamepad devices.
171 std::unique_ptr<device::GamepadDataFetcher> fetcher_;
172
173 // The current state of all gamepads.
174 device::Gamepads state_;
175
176 // True if a poll has been scheduled.
177 bool has_poll_scheduled_ = false;
178
179 // True if the polling thread is paused.
180 bool is_enabled_ = false;
181
182 // ThreadChecker for the polling thread.
183 base::ThreadChecker thread_checker_;
184
185 DISALLOW_COPY_AND_ASSIGN(ThreadSafeGamepadChangeFetcher);
186 };
187
188 ////////////////////////////////////////////////////////////////////////////////
189 // GamingSeat, public:
190
191 GamingSeat::GamingSeat(GamingSeatDelegate* gaming_seat_delegate,
192 base::SingleThreadTaskRunner* polling_task_runner)
193 : GamingSeat(gaming_seat_delegate,
194 polling_task_runner,
195 base::Bind(CreateGamepadPlatformDataFetcher)) {}
196
197 GamingSeat::GamingSeat(GamingSeatDelegate* gaming_seat_delegate,
198 base::SingleThreadTaskRunner* polling_task_runner,
199 CreateGamepadDataFetcherCallback create_fetcher_callback)
200 : delegate_(gaming_seat_delegate),
201 gamepad_delegates_{nullptr},
202 weak_ptr_factory_(this) {
203 gamepad_change_fetcher_ = new ThreadSafeGamepadChangeFetcher(
204 base::Bind(&GamingSeat::ProcessGamepadChanges,
205 weak_ptr_factory_.GetWeakPtr()),
206 create_fetcher_callback, polling_task_runner);
207
208 auto* helper = WMHelper::GetInstance();
209 helper->AddFocusObserver(this);
210 OnWindowFocused(helper->GetFocusedWindow(), nullptr);
211 }
212
213 GamingSeat::~GamingSeat() {
214 // Disable polling. Since ThreadSafeGamepadChangeFetcher are reference
215 // counted, we can safely have it shut down after Gamepad has been destroyed.
216 gamepad_change_fetcher_->EnablePolling(false);
217
218 delegate_->OnGamingSeatDestroying(this);
219 for (size_t i = 0; i < device::Gamepads::kItemsLengthCap; ++i) {
220 if (gamepad_delegates_[i]) {
221 gamepad_delegates_[i]->OnRemoved();
222 }
223 }
224 WMHelper::GetInstance()->RemoveFocusObserver(this);
225 }
226
227 ////////////////////////////////////////////////////////////////////////////////
228 // aura::client::FocusChangeObserver overrides:
229
230 void GamingSeat::OnWindowFocused(aura::Window* gained_focus,
231 aura::Window* lost_focus) {
232 DCHECK(thread_checker_.CalledOnValidThread());
233 Surface* target = nullptr;
234 if (gained_focus) {
235 target = Surface::AsSurface(gained_focus);
236 if (!target) {
237 aura::Window* top_level_window = gained_focus->GetToplevelWindow();
238 if (top_level_window)
239 target = ShellSurface::GetMainSurface(top_level_window);
240 }
241 }
242
243 bool focused = target && delegate_->CanAcceptGamepadEventsForSurface(target);
244
245 gamepad_change_fetcher_->EnablePolling(focused);
246 }
247
248 ////////////////////////////////////////////////////////////////////////////////
249 // GamingSeat, private:
250
251 void GamingSeat::ProcessGamepadChanges(int index,
252 const device::Gamepad new_pad) {
253 DCHECK(thread_checker_.CalledOnValidThread());
254 bool send_frame = false;
255
256 device::Gamepad& pad_state = pad_state_.items[index];
257 // Update connection state.
258 GamepadDelegate* delegate = gamepad_delegates_[index];
259 if (new_pad.connected != pad_state.connected) {
260 // New pad is disconnected.
261 if (!new_pad.connected) {
262 // If gamepad is disconnected now, it should be connected before, then
263 // gamepad_delegate should not be null.
264 DCHECK(delegate);
265 delegate->OnRemoved();
266 gamepad_delegates_[index] = nullptr;
267 pad_state = new_pad;
268 return;
269 } else if (new_pad.connected) {
270 gamepad_delegates_[index] = delegate_->GamepadAdded();
271 }
272 }
273
274 if (!delegate || !new_pad.connected ||
275 new_pad.timestamp <= pad_state.timestamp) {
276 pad_state = new_pad;
277 return;
278 }
279
280 // Notify delegate of updated axes.
281 for (size_t axis = 0;
282 axis < std::max(pad_state.axes_length, new_pad.axes_length); ++axis) {
283 if (!GamepadButtonValuesAreEqual(new_pad.axes[axis],
284 pad_state.axes[axis])) {
285 send_frame = true;
286 delegate->OnAxis(axis, new_pad.axes[axis]);
287 }
288 }
289
290 // Notify delegate of updated buttons.
291 for (size_t button_id = 0;
292 button_id < std::max(pad_state.buttons_length, new_pad.buttons_length);
293 ++button_id) {
294 auto button = pad_state.buttons[button_id];
295 auto new_button = new_pad.buttons[button_id];
296 if (button.pressed != new_button.pressed ||
297 !GamepadButtonValuesAreEqual(button.value, new_button.value)) {
298 send_frame = true;
299 delegate->OnButton(button_id, new_button.pressed, new_button.value);
300 }
301 }
302 if (send_frame)
303 delegate->OnFrame();
304
305 pad_state = new_pad;
306 } 42 }
307 43
308 } // namespace exo 44 } // namespace exo
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698