| OLD | NEW |
| 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 |
| OLD | NEW |