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

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

Issue 2076013002: exo: Implement wayland gamepad support (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@serv
Patch Set: fixed nits and smaller issues Created 4 years, 5 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
(Empty)
1 // Copyright 2016 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.
4
5 #include "components/exo/gamepad.h"
6
7 #include <cmath>
8
9 #include "ash/shell.h"
10 #include "base/bind.h"
11 #include "base/location.h"
12 #include "base/single_thread_task_runner.h"
13 #include "base/threading/thread.h"
14 #include "base/threading/thread_task_runner_handle.h"
15 #include "components/exo/gamepad_delegate.h"
16 #include "components/exo/shell_surface.h"
17 #include "components/exo/surface.h"
18 #include "device/gamepad/gamepad_data_fetcher.h"
19 #include "device/gamepad/gamepad_platform_data_fetcher.h"
20 #include "ui/aura/client/focus_client.h"
21 #include "ui/aura/window.h"
22
23 namespace exo {
24 namespace {
25
26 constexpr double kGamepadButtonValueEpsilon = 0.001;
27 bool GamepadButtonValuesAreEqual(double a, double b) {
28 return fabs(a - b) < kGamepadButtonValueEpsilon;
29 }
30
31 std::unique_ptr<device::GamepadDataFetcher> CreateGamepadPlatformDataFetcher() {
32 return std::unique_ptr<device::GamepadDataFetcher>(
33 new device::GamepadPlatformDataFetcher());
34 }
35
36 // Time between gamepad polls in milliseconds.
37 constexpr unsigned kPollingTimeIntervalMs = 16;
38
39 } // namespace
40
41 ////////////////////////////////////////////////////////////////////////////////
42 // Gamepad::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 Gamepad::ThreadSafeGamepadChangeFetcher
48 : public base::RefCountedThreadSafe<
49 Gamepad::ThreadSafeGamepadChangeFetcher> {
50 public:
51 using ProcessGamepadChangesCallback =
52 base::Callback<void(const blink::WebGamepad)>;
53
54 ThreadSafeGamepadChangeFetcher(
55 const ProcessGamepadChangesCallback& post_gamepad_changes,
56 const CreateGamepadDataFetcherCallback& create_fetcher_callback,
57 base::SingleThreadTaskRunner* task_runner)
58 : post_gamepad_changes_(post_gamepad_changes),
59 create_fetcher_callback_(create_fetcher_callback),
60 polling_task_runner_(task_runner),
61 origin_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
62 thread_checker_.DetachFromThread();
63 }
64
65 // Enable or disable gamepad polling. Can be called from any thread.
66 void EnablePolling(bool enabled) {
67 polling_task_runner_->PostTask(
68 FROM_HERE,
69 base::Bind(
70 &ThreadSafeGamepadChangeFetcher::EnablePollingOnPollingThread,
71 make_scoped_refptr(this), enabled));
72 }
73
74 private:
75 friend class base::RefCountedThreadSafe<ThreadSafeGamepadChangeFetcher>;
76
77 virtual ~ThreadSafeGamepadChangeFetcher() {}
78
79 // Enables or disables polling.
80 void EnablePollingOnPollingThread(bool enabled) {
81 DCHECK(thread_checker_.CalledOnValidThread());
82 is_enabled_ = enabled;
83
84 if (is_enabled_) {
85 if (!fetcher_) {
86 fetcher_ = create_fetcher_callback_.Run();
87 DCHECK(fetcher_);
88 }
89 SchedulePollOnPollingThread();
90 } else {
91 fetcher_.reset();
92 }
93 }
94
95 // Schedules the next poll on the polling thread.
96 void SchedulePollOnPollingThread() {
97 DCHECK(thread_checker_.CalledOnValidThread());
98 DCHECK(fetcher_);
99
100 if (!is_enabled_ || has_poll_scheduled_)
101 return;
102
103 has_poll_scheduled_ = true;
104 polling_task_runner_->PostDelayedTask(
105 FROM_HERE,
106 base::Bind(&ThreadSafeGamepadChangeFetcher::PollOnPollingThread,
107 make_scoped_refptr(this)),
108 base::TimeDelta::FromMilliseconds(kPollingTimeIntervalMs));
109 }
110
111 // Polls devices for new data and posts gamepad changes back to origin thread.
112 void PollOnPollingThread() {
113 DCHECK(thread_checker_.CalledOnValidThread());
114
115 if (!is_enabled_) {
116 has_poll_scheduled_ = false;
reveman 2016/07/11 18:45:01 nit: move this before if statement so l.133 can be
denniskempin 2016/07/12 19:54:12 Done.
117 return;
118 }
119 DCHECK(fetcher_);
120
121 blink::WebGamepads new_state = state_;
122 fetcher_->GetGamepadData(&new_state, false);
123
124 if (std::max(new_state.length, state_.length) > 0) {
125 if (new_state.items[0].connected != state_.items[0].connected ||
126 new_state.items[0].timestamp > state_.items[0].timestamp) {
127 origin_task_runner_->PostTask(
128 FROM_HERE, base::Bind(post_gamepad_changes_, new_state.items[0]));
129 }
130 }
131
132 state_ = new_state;
133 has_poll_scheduled_ = false;
134 SchedulePollOnPollingThread();
135 }
136
137 // Callback to ProcessGamepadChanges using weak reference to Gamepad.
138 ProcessGamepadChangesCallback post_gamepad_changes_;
reveman 2016/07/11 18:45:01 nit: process_gamepad_changes_callback_
denniskempin 2016/07/12 19:54:12 Done.
139
140 // Callback method to create a gamepad data fetcher.
141 CreateGamepadDataFetcherCallback create_fetcher_callback_;
reveman 2016/07/11 18:45:01 nit: const CreateGamepadDataFetcherCallback&
denniskempin 2016/07/12 19:54:12 Done.
denniskempin 2016/07/12 20:03:37 actually I have to change this back. the callback
142
143 // Reference to task runner on polling thread.
144 base::SingleThreadTaskRunner* polling_task_runner_;
145
146 // Reference to task runner on origin thread.
147 scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner_;
148
149 // Gamepad data fetcher used for querying gamepad devices.
150 std::unique_ptr<device::GamepadDataFetcher> fetcher_;
151
152 // The current state of all gamepads.
153 blink::WebGamepads state_;
154
155 // True if a poll has been scheduled.
156 bool has_poll_scheduled_ = false;
157
158 // True if the polling thread is paused.
159 bool is_enabled_ = false;
160
161 // ThreadChecker for the polling thread.
162 base::ThreadChecker thread_checker_;
163
164 DISALLOW_COPY_AND_ASSIGN(ThreadSafeGamepadChangeFetcher);
165 };
166
167 ////////////////////////////////////////////////////////////////////////////////
168 // Gamepad, public:
169
170 Gamepad::Gamepad(GamepadDelegate* delegate,
171 base::SingleThreadTaskRunner* polling_task_runner)
172 : Gamepad(delegate,
173 polling_task_runner,
174 base::Bind(CreateGamepadPlatformDataFetcher)) {}
175
176 Gamepad::Gamepad(GamepadDelegate* delegate,
177 base::SingleThreadTaskRunner* polling_task_runner,
178 CreateGamepadDataFetcherCallback create_fetcher_callback)
179 : delegate_(delegate), weak_factory_(this) {
180 gamepad_change_fetcher_ = new ThreadSafeGamepadChangeFetcher(
181 base::Bind(&Gamepad::ProcessGamepadChanges, weak_factory_.GetWeakPtr()),
182 create_fetcher_callback, polling_task_runner);
183
184 aura::client::FocusClient* focus_client =
185 aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow());
186 focus_client->AddObserver(this);
187 OnWindowFocused(focus_client->GetFocusedWindow(), nullptr);
188 }
189
190 Gamepad::~Gamepad() {
191 // Disable polling. Since ThreadSafeGamepadChangeFetcher are reference
192 // counted, we
193 // can safely have it shut down after Gamepad has been destroyed.
reveman 2016/07/11 18:45:01 nit: fix formatting of this comment
denniskempin 2016/07/12 19:54:12 Done.
194 gamepad_change_fetcher_->EnablePolling(false);
195
196 delegate_->OnGamepadDestroying(this);
197 aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow())
198 ->RemoveObserver(this);
199 }
200
201 ////////////////////////////////////////////////////////////////////////////////
202 // aura::client::FocusChangeObserver overrides:
203
204 void Gamepad::OnWindowFocused(aura::Window* gained_focus,
205 aura::Window* lost_focus) {
206 DCHECK(thread_checker_.CalledOnValidThread());
207 Surface* target = nullptr;
208 if (gained_focus) {
209 target = Surface::AsSurface(gained_focus);
210 if (!target) {
211 aura::Window* top_level_window = gained_focus->GetToplevelWindow();
212 if (top_level_window)
213 target = ShellSurface::GetMainSurface(top_level_window);
214 }
215 }
216
217 bool focused = target && delegate_->CanAcceptGamepadEventsForSurface(target);
218 gamepad_change_fetcher_->EnablePolling(focused);
219 }
220
221 ////////////////////////////////////////////////////////////////////////////////
222 // Gamepad, private:
223
224 void Gamepad::ProcessGamepadChanges(const blink::WebGamepad new_pad) {
225 DCHECK(thread_checker_.CalledOnValidThread());
226 bool send_frame = false;
227
228 // Update connection state.
229 if (new_pad.connected != pad_state_.connected) {
230 delegate_->OnStateChange(new_pad.connected);
231 }
232
233 if (!new_pad.connected || new_pad.timestamp <= pad_state_.timestamp) {
234 pad_state_ = new_pad;
235 return;
236 }
237
238 // Notify delegate of updated axes.
239 for (size_t axis = 0;
240 axis < std::max(pad_state_.axesLength, new_pad.axesLength); ++axis) {
241 if (!GamepadButtonValuesAreEqual(new_pad.axes[axis],
242 pad_state_.axes[axis])) {
243 send_frame = true;
244 delegate_->OnAxis(axis, new_pad.axes[axis]);
245 }
246 }
247
248 // Notify delegate of updated buttons.
249 for (size_t button_id = 0;
250 button_id < std::max(pad_state_.buttonsLength, new_pad.buttonsLength);
251 ++button_id) {
252 auto button = pad_state_.buttons[button_id];
253 auto new_button = new_pad.buttons[button_id];
254 if (button.pressed != new_button.pressed ||
255 !GamepadButtonValuesAreEqual(button.value, new_button.value)) {
256 send_frame = true;
257 delegate_->OnButton(button_id, new_button.pressed, new_button.value);
258 }
259 }
260 if (send_frame)
261 delegate_->OnFrame();
262
263 pad_state_ = new_pad;
264 }
265
266 } // namespace exo
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698