OLD | NEW |
---|---|
(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 // Time between gamepad polls in milliseconds. | |
32 constexpr unsigned kPollingTimeIntervalMs = 16; | |
33 | |
34 } // namespace | |
35 | |
36 //////////////////////////////////////////////////////////////////////////////// | |
37 // Gamepad, public: | |
38 | |
39 Gamepad::Gamepad(GamepadDelegate* delegate) : Gamepad(delegate, nullptr) {} | |
40 | |
41 Gamepad::Gamepad(GamepadDelegate* delegate, device::GamepadDataFetcher* fetcher) | |
42 : delegate_(delegate), | |
43 origin_task_runner_(base::ThreadTaskRunnerHandle::Get()), | |
44 polling_thread_(new base::Thread("Exo gamepad polling thread")), | |
45 external_fetcher_(fetcher), | |
46 weak_factory_(this) { | |
47 polling_thread_checker_.DetachFromThread(); | |
48 post_gamepad_changes_callback_ = | |
49 base::Bind(&Gamepad::PostGamepadChanges, weak_factory_.GetWeakPtr()); | |
50 | |
51 polling_thread_->StartWithOptions( | |
52 base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); | |
53 | |
54 aura::client::FocusClient* focus_client = | |
55 aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow()); | |
56 focus_client->AddObserver(this); | |
57 OnWindowFocused(focus_client->GetFocusedWindow(), nullptr); | |
58 } | |
59 | |
60 Gamepad::~Gamepad() { | |
61 polling_thread_->Stop(); | |
62 delegate_->OnGamepadDestroying(this); | |
63 aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow()) | |
64 ->RemoveObserver(this); | |
65 } | |
66 | |
67 //////////////////////////////////////////////////////////////////////////////// | |
68 // aura::client::FocusChangeObserver overrides: | |
69 | |
70 void Gamepad::OnWindowFocused(aura::Window* gained_focus, | |
71 aura::Window* lost_focus) { | |
72 DCHECK(origin_thread_checker_.CalledOnValidThread()); | |
73 Surface* target = nullptr; | |
74 if (gained_focus) { | |
75 target = Surface::AsSurface(gained_focus); | |
76 if (!target) { | |
77 aura::Window* top_level_window = gained_focus->GetToplevelWindow(); | |
78 if (top_level_window) | |
79 target = ShellSurface::GetMainSurface(top_level_window); | |
80 } | |
81 } | |
82 | |
83 bool focused = target && delegate_->CanAcceptGamepadEventsForSurface(target); | |
84 if (focused) { | |
85 polling_thread_->task_runner()->PostTask( | |
86 FROM_HERE, base::Bind(&Gamepad::ResumePolling, base::Unretained(this))); | |
87 } else { | |
88 polling_thread_->task_runner()->PostTask( | |
89 FROM_HERE, base::Bind(&Gamepad::PausePolling, base::Unretained(this))); | |
90 } | |
91 } | |
92 | |
93 //////////////////////////////////////////////////////////////////////////////// | |
94 // Gamepad, private: | |
95 | |
96 void Gamepad::PausePolling() { | |
97 DCHECK(polling_thread_checker_.CalledOnValidThread()); | |
98 is_paused_ = true; | |
99 } | |
100 | |
101 void Gamepad::ResumePolling() { | |
102 DCHECK(polling_thread_checker_.CalledOnValidThread()); | |
103 if (!is_paused_) | |
104 return; | |
105 | |
106 is_paused_ = false; | |
107 ScheduleOnPoll(); | |
108 } | |
109 | |
110 void Gamepad::ScheduleOnPoll() { | |
111 DCHECK(polling_thread_checker_.CalledOnValidThread()); | |
112 if (is_paused_) { | |
113 internal_fetcher_.reset(); | |
114 return; | |
115 } | |
116 | |
117 if (has_poll_scheduled_) | |
118 return; | |
119 | |
120 has_poll_scheduled_ = true; | |
121 polling_thread_->task_runner()->PostDelayedTask( | |
122 FROM_HERE, base::Bind(&Gamepad::OnPoll, base::Unretained(this)), | |
123 base::TimeDelta::FromMilliseconds(kPollingTimeIntervalMs)); | |
124 } | |
125 | |
126 void Gamepad::OnPoll() { | |
127 DCHECK(polling_thread_checker_.CalledOnValidThread()); | |
128 blink::WebGamepads new_state = state_; | |
129 | |
130 if (external_fetcher_) { | |
reveman
2016/07/07 22:14:02
hm, when I suggested a refactor to avoid confusing
denniskempin
2016/07/08 23:49:40
Done.
| |
131 external_fetcher_->GetGamepadData(&new_state, false); | |
132 } else { | |
133 if (!internal_fetcher_) | |
134 internal_fetcher_.reset(new device::GamepadPlatformDataFetcher()); | |
135 internal_fetcher_->GetGamepadData(&new_state, false); | |
136 } | |
137 | |
138 if (std::max(new_state.length, state_.length) > 0) { | |
139 if (new_state.items[0].connected != state_.items[0].connected || | |
140 new_state.items[0].timestamp > state_.items[0].timestamp) { | |
141 origin_task_runner_->PostTask( | |
142 FROM_HERE, | |
143 base::Bind(post_gamepad_changes_callback_, new_state.items[0])); | |
144 } | |
145 } | |
146 | |
147 state_ = new_state; | |
148 has_poll_scheduled_ = false; | |
149 ScheduleOnPoll(); | |
150 } | |
151 | |
152 void Gamepad::PostGamepadChanges(const blink::WebGamepad new_pad) { | |
153 DCHECK(origin_thread_checker_.CalledOnValidThread()); | |
154 bool send_frame = false; | |
155 | |
156 // Update connection state. | |
157 if (new_pad.connected != pad_state_.connected) { | |
158 delegate_->OnStateChange(new_pad.connected); | |
159 } | |
160 | |
161 if (!new_pad.connected || new_pad.timestamp <= pad_state_.timestamp) { | |
162 pad_state_ = new_pad; | |
163 return; | |
164 } | |
165 | |
166 // Notify delegate of updated axes. | |
167 for (size_t axis = 0; | |
168 axis < std::max(pad_state_.axesLength, new_pad.axesLength); ++axis) { | |
169 if (!GamepadButtonValuesAreEqual(new_pad.axes[axis], | |
170 pad_state_.axes[axis])) { | |
171 send_frame = true; | |
172 delegate_->OnAxis(axis, new_pad.axes[axis]); | |
173 } | |
174 } | |
175 | |
176 // Notify delegate of updated buttons. | |
177 for (size_t button_id = 0; | |
178 button_id < std::max(pad_state_.buttonsLength, new_pad.buttonsLength); | |
179 ++button_id) { | |
180 auto button = pad_state_.buttons[button_id]; | |
181 auto new_button = new_pad.buttons[button_id]; | |
182 if (button.pressed != new_button.pressed || | |
183 !GamepadButtonValuesAreEqual(button.value, new_button.value)) { | |
184 send_frame = true; | |
185 delegate_->OnButton(button_id, new_button.pressed, new_button.value); | |
186 } | |
187 } | |
188 if (send_frame) | |
189 delegate_->OnFrame(); | |
190 | |
191 pad_state_ = new_pad; | |
192 } | |
193 | |
194 } // namespace exo | |
OLD | NEW |