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

Unified 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: moved polling thread resources into separate class. pass task_runner into Gamepad. 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 side-by-side diff with in-line comments
Download patch
Index: components/exo/gamepad.cc
diff --git a/components/exo/gamepad.cc b/components/exo/gamepad.cc
new file mode 100644
index 0000000000000000000000000000000000000000..714ba2e279e077cd42ec05d2168974c6e4af28b3
--- /dev/null
+++ b/components/exo/gamepad.cc
@@ -0,0 +1,269 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/exo/gamepad.h"
+
+#include <cmath>
+
+#include "ash/shell.h"
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "components/exo/gamepad_delegate.h"
+#include "components/exo/shell_surface.h"
+#include "components/exo/surface.h"
+#include "device/gamepad/gamepad_data_fetcher.h"
+#include "device/gamepad/gamepad_platform_data_fetcher.h"
+#include "ui/aura/client/focus_client.h"
+#include "ui/aura/window.h"
+
+namespace exo {
+namespace {
+
+constexpr double kGamepadButtonValueEpsilon = 0.001;
+bool GamepadButtonValuesAreEqual(double a, double b) {
+ return fabs(a - b) < kGamepadButtonValueEpsilon;
+}
+
+// Time between gamepad polls in milliseconds.
+constexpr unsigned kPollingTimeIntervalMs = 16;
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// Gamepad::PollingThreadResources
+
+// Implements all methods and resources running on the polling thread.
+// This class is reference counted to allow it to shut down safely on the
+// polling thread even if the Gamepad has been destroyed on the origin thread.
+class Gamepad::PollingThreadResources
reveman 2016/07/11 10:50:44 nit: this class contains more than some resources.
denniskempin 2016/07/11 17:43:28 Done.
+ : public base::RefCounted<Gamepad::PollingThreadResources> {
reveman 2016/07/11 10:50:44 Does this need to be base::ThreadSafeRefCounted?
denniskempin 2016/07/11 17:43:28 Done.
+ public:
+ typedef base::Callback<void(const blink::WebGamepad)>
+ PostGamepadChangesCallback;
reveman 2016/07/11 10:50:44 nit: using HandleGamepadChangesCallback = ..
denniskempin 2016/07/11 17:43:28 Done.
+
+ PollingThreadResources(
+ const PostGamepadChangesCallback& post_gamepad_changes,
+ const GamepadDataFetcherFactory& fetcher_factory,
+ const scoped_refptr<base::SingleThreadTaskRunner>& task_runner)
reveman 2016/07/11 10:50:44 nit: different parts of chrome does this different
denniskempin 2016/07/11 17:43:28 Done.
+ : post_gamepad_changes_(post_gamepad_changes),
+ fetcher_factory_(fetcher_factory),
+ polling_task_runner_(task_runner),
+ origin_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
+ thread_checker_.DetachFromThread();
+ }
+
+ // Enable or disable gamepad polling. Can be called from any thread.
+ void EnablePolling(bool enabled) {
+ polling_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&PollingThreadResources::EnablePollingOnPollingThread,
+ make_scoped_refptr(this), enabled));
+ }
+
+ private:
+ friend class base::RefCounted<PollingThreadResources>;
+ virtual ~PollingThreadResources() {}
reveman 2016/07/11 10:50:44 nit: blank line before this
denniskempin 2016/07/11 17:43:28 Done.
+
+ // Enables or disables polling.
+ void EnablePollingOnPollingThread(bool enabled) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ if (enabled == is_enabled_)
reveman 2016/07/11 10:50:44 nit: is this check needed?
denniskempin 2016/07/11 17:43:28 Done.
+ return;
+ is_enabled_ = enabled;
+
+ if (is_enabled_)
+ SchedulePollOnPollingThread();
+ }
+
+ // Schedules the next poll on the polling thread.
+ void SchedulePollOnPollingThread() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (!is_enabled_) {
reveman 2016/07/11 10:50:44 is this needed? if it is, then shouldn't the condi
denniskempin 2016/07/11 17:43:28 Done. I've moved the creation/destruction of the f
+ fetcher_.reset();
+ return;
+ }
+
+ if (has_poll_scheduled_)
+ return;
+
+ if (!fetcher_) {
+ fetcher_ = fetcher_factory_.Run();
+ DCHECK(fetcher_);
+ }
+
+ has_poll_scheduled_ = true;
+ polling_task_runner_->PostDelayedTask(
+ FROM_HERE, base::Bind(&PollingThreadResources::PollOnPollingThread,
+ make_scoped_refptr(this)),
+ base::TimeDelta::FromMilliseconds(kPollingTimeIntervalMs));
+ }
+
+ // Polls devices for new data and posts gamepad changes back to origin thread.
+ void PollOnPollingThread() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(fetcher_);
+ blink::WebGamepads new_state = state_;
+
reveman 2016/07/11 10:50:44 nit: move this blank line above l.110 instead
denniskempin 2016/07/11 17:43:28 Done.
+ fetcher_->GetGamepadData(&new_state, false);
+
+ if (std::max(new_state.length, state_.length) > 0) {
+ if (new_state.items[0].connected != state_.items[0].connected ||
+ new_state.items[0].timestamp > state_.items[0].timestamp) {
+ origin_task_runner_->PostTask(
+ FROM_HERE, base::Bind(post_gamepad_changes_, new_state.items[0]));
+ }
+ }
+
+ state_ = new_state;
+ has_poll_scheduled_ = false;
+ SchedulePollOnPollingThread();
+ }
+
+ // Callback to PostGamepadChanges using weak reference to Gamepad.
+ PostGamepadChangesCallback post_gamepad_changes_;
+
+ // Factory method to create a gamepad data fetcher. Defaults to creating
+ // a PlatformGamepadDataFetcher instance, but can be overwritten for
+ // testing purposes.
+ GamepadDataFetcherFactory fetcher_factory_;
reveman 2016/07/11 10:50:44 nit: create_fetcher_callback_
denniskempin 2016/07/11 17:43:28 Done.
+
+ // Reference to task runner on polling thread.
+ scoped_refptr<base::SingleThreadTaskRunner> polling_task_runner_;
+
+ // Reference to task runner on polling thread.
+ scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner_;
+
+ // Gamepad data fetcher used for querying gamepad devices.
+ std::unique_ptr<device::GamepadDataFetcher> fetcher_;
+
+ // The current state of all gamepads.
+ blink::WebGamepads state_;
+
+ // True if a poll has been scheduled.
+ bool has_poll_scheduled_{false};
reveman 2016/07/11 10:50:44 nit: "has_poll_scheduled_ = false" is preferred in
denniskempin 2016/07/11 17:43:28 Done.
+
+ // True if the polling thread is paused.
+ bool is_enabled_{true};
reveman 2016/07/11 10:50:44 nit: is_enabled_ = true
denniskempin 2016/07/11 17:43:28 Done.
+
+ // ThreadChecker for the polling thread.
+ base::ThreadChecker thread_checker_;
+
+ DISALLOW_COPY_AND_ASSIGN(PollingThreadResources);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// Gamepad, public:
+
+Gamepad::Gamepad(
+ GamepadDelegate* delegate,
+ scoped_refptr<base::SingleThreadTaskRunner> polling_task_runner)
+ : Gamepad(delegate,
+ polling_task_runner,
+ base::Bind(Gamepad::GamepadPlatformDataFetcherFactory)) {}
+
+Gamepad::Gamepad(
+ GamepadDelegate* delegate,
+ scoped_refptr<base::SingleThreadTaskRunner> polling_task_runner,
+ GamepadDataFetcherFactory fetcher_factory)
+ : delegate_(delegate), weak_factory_(this) {
+ polling_thread_resources_ = new PollingThreadResources(
+ base::Bind(&Gamepad::PostGamepadChanges, weak_factory_.GetWeakPtr()),
+ fetcher_factory, polling_task_runner);
+
+ aura::client::FocusClient* focus_client =
+ aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow());
+ focus_client->AddObserver(this);
+ OnWindowFocused(focus_client->GetFocusedWindow(), nullptr);
+}
+
+Gamepad::~Gamepad() {
+ // Disable polling. Since PollingThreadResources are reference counted, we
+ // can safely have it shut down after Gamepad has been destroyed.
+ polling_thread_resources_->EnablePolling(false);
+
+ delegate_->OnGamepadDestroying(this);
+ aura::client::GetFocusClient(ash::Shell::GetPrimaryRootWindow())
+ ->RemoveObserver(this);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// aura::client::FocusChangeObserver overrides:
+
+void Gamepad::OnWindowFocused(aura::Window* gained_focus,
+ aura::Window* lost_focus) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ Surface* target = nullptr;
+ if (gained_focus) {
+ target = Surface::AsSurface(gained_focus);
+ if (!target) {
+ aura::Window* top_level_window = gained_focus->GetToplevelWindow();
+ if (top_level_window)
+ target = ShellSurface::GetMainSurface(top_level_window);
+ }
+ }
+
+ bool focused = target && delegate_->CanAcceptGamepadEventsForSurface(target);
+ if (focused) {
reveman 2016/07/11 10:50:44 nit: polling_thread_resources_->EnablePolling(focu
denniskempin 2016/07/11 17:43:28 Done. Yes.. well this was just dumb ;)
+ polling_thread_resources_->EnablePolling(true);
+ } else {
+ polling_thread_resources_->EnablePolling(false);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Gamepad, private:
+
+void Gamepad::PostGamepadChanges(const blink::WebGamepad new_pad) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ bool send_frame = false;
+
+ // Update connection state.
+ if (new_pad.connected != pad_state_.connected) {
+ delegate_->OnStateChange(new_pad.connected);
+ }
+
+ if (!new_pad.connected || new_pad.timestamp <= pad_state_.timestamp) {
+ pad_state_ = new_pad;
+ return;
+ }
+
+ // Notify delegate of updated axes.
+ for (size_t axis = 0;
+ axis < std::max(pad_state_.axesLength, new_pad.axesLength); ++axis) {
+ if (!GamepadButtonValuesAreEqual(new_pad.axes[axis],
+ pad_state_.axes[axis])) {
+ send_frame = true;
+ delegate_->OnAxis(axis, new_pad.axes[axis]);
+ }
+ }
+
+ // Notify delegate of updated buttons.
+ for (size_t button_id = 0;
+ button_id < std::max(pad_state_.buttonsLength, new_pad.buttonsLength);
+ ++button_id) {
+ auto button = pad_state_.buttons[button_id];
+ auto new_button = new_pad.buttons[button_id];
+ if (button.pressed != new_button.pressed ||
+ !GamepadButtonValuesAreEqual(button.value, new_button.value)) {
+ send_frame = true;
+ delegate_->OnButton(button_id, new_button.pressed, new_button.value);
+ }
+ }
+ if (send_frame)
+ delegate_->OnFrame();
+
+ pad_state_ = new_pad;
+}
+
+std::unique_ptr<device::GamepadDataFetcher>
+Gamepad::GamepadPlatformDataFetcherFactory() {
+ return std::unique_ptr<device::GamepadDataFetcher>(
+ new device::GamepadPlatformDataFetcher());
+}
+
+} // namespace exo

Powered by Google App Engine
This is Rietveld 408576698