Inform Renderer of Gamepad Button or Axis Changes
This CL makes the device service inform the renderer when it polls the gamepads. This will be used to check the difference and create GamepadButtonChange, GamepadButtonUp, GamepadButtonDown, and GamepadAxisMove Events. Change-Id: I903213cc925ab991a7cfec774af338868c7ef02e Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2855078 Reviewed-by: James Hollyer <jameshollyer@chromium.org> Reviewed-by: Matt Reynolds <mattreynolds@chromium.org> Reviewed-by: Bill Budge <bbudge@chromium.org> Reviewed-by: Ken Buchanan <kenrb@chromium.org> Commit-Queue: James Hollyer <jameshollyer@chromium.org> Cr-Commit-Position: refs/heads/master@{#893935}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
44e236e2f9
commit
24f30cb723
chrome/browser
content/browser/renderer_host/pepper
device
BUILD.gn
gamepad
third_party/blink/renderer/modules/gamepad
@ -5168,6 +5168,11 @@ const FeatureEntry kFeatureEntries[] = {
|
||||
FEATURE_VALUE_TYPE(chrome::android::kNewWindowAppMenu)},
|
||||
#endif // defined(OS_ANDROID)
|
||||
|
||||
{"enable-gamepad-button-axis-events",
|
||||
flag_descriptions::kEnableGamepadButtonAxisEventsName,
|
||||
flag_descriptions::kEnableGamepadButtonAxisEventsDescription, kOsAll,
|
||||
FEATURE_VALUE_TYPE(features::kEnableGamepadButtonAxisEvents)},
|
||||
|
||||
{"restrict-gamepad-access", flag_descriptions::kRestrictGamepadAccessName,
|
||||
flag_descriptions::kRestrictGamepadAccessDescription, kOsAll,
|
||||
FEATURE_VALUE_TYPE(features::kRestrictGamepadAccess)},
|
||||
|
@ -1849,6 +1849,11 @@
|
||||
// testing by JavaScript developers.
|
||||
"expiry_milestone": -1
|
||||
},
|
||||
{
|
||||
"name": "enable-gamepad-button-axis-events",
|
||||
"owners": [ "//device/gamepad/OWNERS", "jameshollyer@chromium.org" ],
|
||||
"expiry_milestone": 98
|
||||
},
|
||||
{
|
||||
"name": "enable-generated-webapks",
|
||||
"owners": [ "tsergeant@chromium.org" ],
|
||||
|
@ -865,6 +865,12 @@ const char kEnableFencedFramesDescription[] =
|
||||
"embedding an isolated top-level page. See "
|
||||
"https://github.com/shivanigithub/fenced-frame";
|
||||
|
||||
const char kEnableGamepadButtonAxisEventsName[] =
|
||||
"Gamepad Button and Axis Events";
|
||||
const char kEnableGamepadButtonAxisEventsDescription[] =
|
||||
"Enables the ability to subscribe to changes in buttons and/or axes "
|
||||
"on the gamepad object.";
|
||||
|
||||
const char kEnableGenericSensorExtraClassesName[] =
|
||||
"Generic Sensor Extra Classes";
|
||||
const char kEnableGenericSensorExtraClassesDescription[] =
|
||||
|
@ -599,6 +599,9 @@ extern const char kDownloadLaterDebugOnWifiNameDescription[];
|
||||
extern const char kEnableFencedFramesName[];
|
||||
extern const char kEnableFencedFramesDescription[];
|
||||
|
||||
extern const char kEnableGamepadButtonAxisEventsName[];
|
||||
extern const char kEnableGamepadButtonAxisEventsDescription[];
|
||||
|
||||
extern const char kEnableLoginDetectionName[];
|
||||
extern const char kEnableLoginDetectionDescription[];
|
||||
|
||||
|
@ -54,8 +54,6 @@ class CONTENT_EXPORT PepperGamepadHost :
|
||||
const device::Gamepad& gamepad) override {}
|
||||
void OnGamepadDisconnected(uint32_t index,
|
||||
const device::Gamepad& gamepad) override {}
|
||||
void OnGamepadButtonOrAxisChanged(uint32_t index,
|
||||
const device::Gamepad& gamepad) override {}
|
||||
|
||||
private:
|
||||
int32_t OnRequestMemory(ppapi::host::HostMessageContext* context);
|
||||
|
@ -129,6 +129,7 @@ test("device_unittests") {
|
||||
"//device/gamepad",
|
||||
"//device/gamepad:test_helpers",
|
||||
"//device/gamepad/public/cpp:shared_with_blink",
|
||||
"//device/gamepad/public/cpp:switches",
|
||||
"//device/gamepad/public/mojom",
|
||||
"//mojo/core/embedder",
|
||||
"//mojo/public/cpp/bindings",
|
||||
|
@ -3,6 +3,7 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "device/gamepad/gamepad_consumer.h"
|
||||
#include "device/gamepad/public/mojom/gamepad.mojom.h"
|
||||
|
||||
namespace device {
|
||||
|
||||
@ -10,4 +11,6 @@ GamepadConsumer::GamepadConsumer() = default;
|
||||
|
||||
GamepadConsumer::~GamepadConsumer() = default;
|
||||
|
||||
void GamepadConsumer::OnGamepadChanged(const mojom::GamepadChanges& change) {}
|
||||
|
||||
} // namespace device
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include "device/gamepad/gamepad_export.h"
|
||||
#include "device/gamepad/public/cpp/gamepad.h"
|
||||
#include "device/gamepad/public/mojom/gamepad.mojom-forward.h"
|
||||
|
||||
namespace device {
|
||||
|
||||
@ -18,8 +19,7 @@ class DEVICE_GAMEPAD_EXPORT GamepadConsumer {
|
||||
virtual void OnGamepadConnected(uint32_t index, const Gamepad& gamepad) = 0;
|
||||
virtual void OnGamepadDisconnected(uint32_t index,
|
||||
const Gamepad& gamepad) = 0;
|
||||
virtual void OnGamepadButtonOrAxisChanged(uint32_t index,
|
||||
const Gamepad& gamepad) = 0;
|
||||
virtual void OnGamepadChanged(const mojom::GamepadChanges& change);
|
||||
};
|
||||
|
||||
} // namespace device
|
||||
|
@ -40,10 +40,9 @@ void GamepadMonitor::OnGamepadDisconnected(uint32_t index,
|
||||
gamepad_observer_remote_->GamepadDisconnected(index, gamepad);
|
||||
}
|
||||
|
||||
void GamepadMonitor::OnGamepadButtonOrAxisChanged(uint32_t index,
|
||||
const Gamepad& gamepad) {
|
||||
void GamepadMonitor::OnGamepadChanged(const mojom::GamepadChanges& changes) {
|
||||
if (gamepad_observer_remote_)
|
||||
gamepad_observer_remote_->GamepadButtonOrAxisChanged(index, gamepad);
|
||||
gamepad_observer_remote_->GamepadChanged(changes.Clone());
|
||||
}
|
||||
|
||||
void GamepadMonitor::GamepadStartPolling(GamepadStartPollingCallback callback) {
|
||||
|
@ -26,8 +26,7 @@ class DEVICE_GAMEPAD_EXPORT GamepadMonitor : public GamepadConsumer,
|
||||
// GamepadConsumer implementation.
|
||||
void OnGamepadConnected(uint32_t index, const Gamepad& gamepad) override;
|
||||
void OnGamepadDisconnected(uint32_t index, const Gamepad& gamepad) override;
|
||||
void OnGamepadButtonOrAxisChanged(uint32_t index,
|
||||
const Gamepad& gamepad) override;
|
||||
void OnGamepadChanged(const mojom::GamepadChanges& change) override;
|
||||
|
||||
// mojom::GamepadMonitor implementation.
|
||||
void GamepadStartPolling(GamepadStartPollingCallback callback) override;
|
||||
|
@ -30,22 +30,89 @@
|
||||
|
||||
namespace device {
|
||||
|
||||
namespace {
|
||||
std::vector<mojom::ButtonChangePtr> CompareButtons(const Gamepad* old_gamepad,
|
||||
const Gamepad* new_gamepad) {
|
||||
if (!new_gamepad)
|
||||
return {};
|
||||
|
||||
std::vector<mojom::ButtonChangePtr> button_changes;
|
||||
const auto* new_buttons = new_gamepad->buttons;
|
||||
const auto* old_buttons = old_gamepad ? old_gamepad->buttons : nullptr;
|
||||
for (size_t i = 0; i < new_gamepad->buttons_length; ++i) {
|
||||
double new_value = new_buttons[i].value;
|
||||
bool new_pressed = new_buttons[i].pressed;
|
||||
if (old_buttons && i < old_gamepad->buttons_length) {
|
||||
double old_value = old_buttons[i].value;
|
||||
bool old_pressed = old_buttons[i].pressed;
|
||||
auto this_change = mojom::ButtonChange::New();
|
||||
this_change->button_index = i;
|
||||
this_change->button_snapshot = new_buttons[i];
|
||||
bool relevant_change = false;
|
||||
if (old_value != new_value) {
|
||||
relevant_change = true;
|
||||
this_change->value_changed = true;
|
||||
}
|
||||
if (old_pressed != new_pressed) {
|
||||
relevant_change = true;
|
||||
this_change->button_down = new_pressed;
|
||||
this_change->button_up = !new_pressed;
|
||||
}
|
||||
if (relevant_change)
|
||||
button_changes.push_back(std::move(this_change));
|
||||
}
|
||||
}
|
||||
return button_changes;
|
||||
}
|
||||
|
||||
std::vector<mojom::AxisChangePtr> CompareAxes(const Gamepad* old_gamepad,
|
||||
const Gamepad* new_gamepad) {
|
||||
if (!new_gamepad)
|
||||
return {};
|
||||
|
||||
std::vector<mojom::AxisChangePtr> axis_changes;
|
||||
const auto* new_axes = new_gamepad->axes;
|
||||
const auto* old_axes = old_gamepad ? old_gamepad->axes : nullptr;
|
||||
for (size_t i = 0; i < new_gamepad->axes_length; ++i) {
|
||||
const double new_value = new_axes[i];
|
||||
if (old_axes && i < old_gamepad->axes_length) {
|
||||
const double old_value = old_axes[i];
|
||||
if (old_value != new_value) {
|
||||
auto this_change = mojom::AxisChange::New();
|
||||
this_change->axis_index = i;
|
||||
this_change->axis_snapshot = new_value;
|
||||
axis_changes.push_back(std::move(this_change));
|
||||
}
|
||||
}
|
||||
}
|
||||
return axis_changes;
|
||||
}
|
||||
|
||||
mojom::GamepadChangesPtr CompareGamepadState(const Gamepad* old_gamepad,
|
||||
const Gamepad* new_gamepad,
|
||||
size_t index) {
|
||||
return mojom::GamepadChanges::New(index,
|
||||
CompareButtons(old_gamepad, new_gamepad),
|
||||
CompareAxes(old_gamepad, new_gamepad));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
constexpr int64_t kPollingIntervalMilliseconds = 4; // ~250 Hz
|
||||
|
||||
GamepadProvider::GamepadProvider(
|
||||
GamepadConnectionChangeClient* connection_change_client)
|
||||
GamepadProvider::GamepadProvider(GamepadChangeClient* gamepad_change_client)
|
||||
: gamepad_shared_buffer_(std::make_unique<GamepadSharedBuffer>()),
|
||||
connection_change_client_(connection_change_client) {
|
||||
main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
|
||||
gamepad_change_client_(gamepad_change_client) {
|
||||
Initialize(std::unique_ptr<GamepadDataFetcher>());
|
||||
}
|
||||
|
||||
GamepadProvider::GamepadProvider(
|
||||
GamepadConnectionChangeClient* connection_change_client,
|
||||
std::unique_ptr<GamepadDataFetcher> fetcher,
|
||||
std::unique_ptr<base::Thread> polling_thread)
|
||||
GamepadProvider::GamepadProvider(GamepadChangeClient* gamepad_change_client,
|
||||
std::unique_ptr<GamepadDataFetcher> fetcher,
|
||||
std::unique_ptr<base::Thread> polling_thread)
|
||||
: gamepad_shared_buffer_(std::make_unique<GamepadSharedBuffer>()),
|
||||
polling_thread_(std::move(polling_thread)),
|
||||
connection_change_client_(connection_change_client) {
|
||||
main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()),
|
||||
gamepad_change_client_(gamepad_change_client) {
|
||||
Initialize(std::move(fetcher));
|
||||
}
|
||||
|
||||
@ -307,21 +374,29 @@ void GamepadProvider::DoPoll() {
|
||||
it->GetGamepadData(changed);
|
||||
}
|
||||
|
||||
Gamepads* buffer = gamepad_shared_buffer_->buffer();
|
||||
Gamepads old_buffer;
|
||||
Gamepads new_buffer;
|
||||
GetCurrentGamepadData(&old_buffer);
|
||||
|
||||
// Send out disconnect events using the last polled data before we wipe it out
|
||||
// in the mapping step.
|
||||
if (ever_had_user_gesture_) {
|
||||
for (size_t i = 0; i < Gamepads::kItemsLengthCap; ++i) {
|
||||
PadState& state = pad_states_.get()[i];
|
||||
std::vector<mojom::GamepadChangesPtr> changes;
|
||||
changes.reserve(Gamepads::kItemsLengthCap);
|
||||
for (size_t i = 0; i < Gamepads::kItemsLengthCap; ++i) {
|
||||
PadState& state = pad_states_.get()[i];
|
||||
|
||||
if (!state.is_newly_active && !state.is_active &&
|
||||
state.source != GAMEPAD_SOURCE_NONE) {
|
||||
auto pad = buffer->items[i];
|
||||
pad.connected = false;
|
||||
OnGamepadConnectionChange(false, i, pad);
|
||||
ClearPadState(state);
|
||||
}
|
||||
// Send out disconnect events using the last polled data.
|
||||
if (ever_had_user_gesture_ && !state.is_newly_active && !state.is_active &&
|
||||
state.source != GAMEPAD_SOURCE_NONE) {
|
||||
auto pad = old_buffer.items[i];
|
||||
pad.connected = false;
|
||||
OnGamepadConnectionChange(false, i, pad);
|
||||
ClearPadState(state);
|
||||
}
|
||||
|
||||
MapAndSanitizeGamepadData(&state, &new_buffer.items[i], sanitize_);
|
||||
if (gamepad_change_client_ &&
|
||||
features::AreGamepadButtonAxisEventsEnabled()) {
|
||||
changes.push_back(
|
||||
CompareGamepadState(&old_buffer.items[i], &new_buffer.items[i], i));
|
||||
}
|
||||
}
|
||||
|
||||
@ -331,23 +406,21 @@ void GamepadProvider::DoPoll() {
|
||||
// Acquire the SeqLock. There is only ever one writer to this data.
|
||||
// See gamepad_shared_buffer.h.
|
||||
gamepad_shared_buffer_->WriteBegin();
|
||||
for (size_t i = 0; i < Gamepads::kItemsLengthCap; ++i) {
|
||||
PadState& state = pad_states_.get()[i];
|
||||
// Must run through the map+sanitize here or CheckForUserGesture may fail.
|
||||
MapAndSanitizeGamepadData(&state, &buffer->items[i], sanitize_);
|
||||
}
|
||||
*gamepad_shared_buffer_->buffer() = new_buffer;
|
||||
gamepad_shared_buffer_->WriteEnd();
|
||||
}
|
||||
|
||||
if (ever_had_user_gesture_) {
|
||||
for (size_t i = 0; i < Gamepads::kItemsLengthCap; ++i) {
|
||||
PadState& state = pad_states_.get()[i];
|
||||
|
||||
if (state.is_newly_active && buffer->items[i].connected) {
|
||||
if (state.is_newly_active && new_buffer.items[i].connected) {
|
||||
state.is_newly_active = false;
|
||||
OnGamepadConnectionChange(true, i, buffer->items[i]);
|
||||
OnGamepadConnectionChange(true, i, new_buffer.items[i]);
|
||||
}
|
||||
}
|
||||
for (auto& change : changes) {
|
||||
SendChangeEvents(std::move(change));
|
||||
}
|
||||
}
|
||||
|
||||
bool did_notify = CheckForUserGesture();
|
||||
@ -368,6 +441,19 @@ void GamepadProvider::DoPoll() {
|
||||
ScheduleDoPoll();
|
||||
}
|
||||
|
||||
void GamepadProvider::SendChangeEvents(
|
||||
mojom::GamepadChangesPtr gamepad_changes) {
|
||||
DCHECK(gamepad_changes);
|
||||
if (gamepad_changes->button_changes.empty() &&
|
||||
gamepad_changes->axis_changes.empty()) {
|
||||
return;
|
||||
}
|
||||
main_thread_task_runner_->PostTask(
|
||||
FROM_HERE, base::BindOnce(&GamepadChangeClient::OnGamepadChange,
|
||||
base::Unretained(gamepad_change_client_),
|
||||
std::move(gamepad_changes)));
|
||||
}
|
||||
|
||||
void GamepadProvider::DisconnectUnrecognizedGamepad(GamepadSource source,
|
||||
int source_id) {
|
||||
for (auto& fetcher : data_fetchers_) {
|
||||
@ -399,8 +485,13 @@ void GamepadProvider::ScheduleDoPoll() {
|
||||
void GamepadProvider::OnGamepadConnectionChange(bool connected,
|
||||
uint32_t index,
|
||||
const Gamepad& pad) {
|
||||
if (connection_change_client_)
|
||||
connection_change_client_->OnGamepadConnectionChange(connected, index, pad);
|
||||
if (gamepad_change_client_) {
|
||||
main_thread_task_runner_->PostTask(
|
||||
FROM_HERE,
|
||||
base::BindOnce(&GamepadChangeClient::OnGamepadConnectionChange,
|
||||
base::Unretained(gamepad_change_client_), connected,
|
||||
index, pad));
|
||||
}
|
||||
}
|
||||
|
||||
bool GamepadProvider::CheckForUserGesture() {
|
||||
|
@ -10,7 +10,6 @@
|
||||
#include <vector>
|
||||
|
||||
#include "base/callback_forward.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/memory/read_only_shared_memory_region.h"
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "base/synchronization/lock.h"
|
||||
@ -32,27 +31,27 @@ namespace device {
|
||||
|
||||
class GamepadDataFetcher;
|
||||
|
||||
class DEVICE_GAMEPAD_EXPORT GamepadConnectionChangeClient {
|
||||
class DEVICE_GAMEPAD_EXPORT GamepadChangeClient {
|
||||
public:
|
||||
virtual void OnGamepadConnectionChange(bool connected,
|
||||
uint32_t index,
|
||||
const Gamepad& pad) = 0;
|
||||
virtual void OnGamepadChange(mojom::GamepadChangesPtr changes) = 0;
|
||||
};
|
||||
|
||||
class DEVICE_GAMEPAD_EXPORT GamepadProvider
|
||||
: public GamepadPadStateProvider,
|
||||
public base::SystemMonitor::DevicesChangedObserver {
|
||||
public:
|
||||
explicit GamepadProvider(
|
||||
GamepadConnectionChangeClient* connection_change_client);
|
||||
explicit GamepadProvider(GamepadChangeClient* gamepad_change_client);
|
||||
|
||||
// Manually specifies the data fetcher and polling thread. The polling thread
|
||||
// will be created normally if |polling_thread| is nullptr. Used for testing.
|
||||
GamepadProvider(
|
||||
GamepadConnectionChangeClient* connection_change_client,
|
||||
std::unique_ptr<GamepadDataFetcher> fetcher,
|
||||
std::unique_ptr<base::Thread> polling_thread);
|
||||
|
||||
GamepadProvider(GamepadChangeClient* gamepad_change_client,
|
||||
std::unique_ptr<GamepadDataFetcher> fetcher,
|
||||
std::unique_ptr<base::Thread> polling_thread);
|
||||
GamepadProvider(const GamepadProvider&) = delete;
|
||||
GamepadProvider& operator=(const GamepadProvider&) = delete;
|
||||
~GamepadProvider() override;
|
||||
|
||||
// Returns a duplicate of the shared memory region of the gamepad data.
|
||||
@ -108,6 +107,8 @@ class DEVICE_GAMEPAD_EXPORT GamepadProvider
|
||||
void DoPoll();
|
||||
void ScheduleDoPoll();
|
||||
|
||||
void SendChangeEvents(mojom::GamepadChangesPtr changes);
|
||||
|
||||
void OnGamepadConnectionChange(bool connected,
|
||||
uint32_t index,
|
||||
const Gamepad& pad);
|
||||
@ -177,9 +178,9 @@ class DEVICE_GAMEPAD_EXPORT GamepadProvider
|
||||
// Polling is done on this background thread.
|
||||
std::unique_ptr<base::Thread> polling_thread_;
|
||||
|
||||
GamepadConnectionChangeClient* connection_change_client_;
|
||||
scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(GamepadProvider);
|
||||
GamepadChangeClient* gamepad_change_client_;
|
||||
};
|
||||
|
||||
} // namespace device
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <memory>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/command_line.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/run_loop.h"
|
||||
@ -15,6 +16,7 @@
|
||||
#include "build/build_config.h"
|
||||
#include "device/gamepad/gamepad_data_fetcher.h"
|
||||
#include "device/gamepad/gamepad_test_helpers.h"
|
||||
#include "device/gamepad/public/cpp/gamepad_switches.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
namespace device {
|
||||
@ -40,15 +42,59 @@ class UserGestureListener {
|
||||
base::WeakPtrFactory<UserGestureListener> weak_factory_{this};
|
||||
};
|
||||
|
||||
class TestChangeClient : public GamepadChangeClient {
|
||||
public:
|
||||
TestChangeClient() = default;
|
||||
TestChangeClient(const TestChangeClient&) = delete;
|
||||
TestChangeClient& operator=(const TestChangeClient&) = delete;
|
||||
~TestChangeClient() = default;
|
||||
|
||||
void OnGamepadConnectionChange(bool connected,
|
||||
uint32_t index,
|
||||
const Gamepad& pad) override {}
|
||||
|
||||
void OnGamepadChange(mojom::GamepadChangesPtr changes) override {
|
||||
all_changes_.push_back(std::move(changes));
|
||||
EXPECT_GT(num_changes_left_, 0);
|
||||
if (--num_changes_left_ == 0)
|
||||
run_loop_.Quit();
|
||||
}
|
||||
|
||||
void RunUntilChangeEvents(int num_changes) {
|
||||
// If we are explicitly not expecting any changes wait 20 milliseconds
|
||||
// to ensure no changes come in.
|
||||
if (num_changes == 0) {
|
||||
num_changes_left_ = 0;
|
||||
base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(20));
|
||||
base::RunLoop().RunUntilIdle();
|
||||
return;
|
||||
}
|
||||
num_changes_left_ = num_changes;
|
||||
run_loop_.Run();
|
||||
}
|
||||
|
||||
const std::vector<mojom::GamepadChangesPtr>& all_changes() const {
|
||||
return all_changes_;
|
||||
}
|
||||
|
||||
private:
|
||||
int num_changes_left_ = 0;
|
||||
base::RunLoop run_loop_;
|
||||
std::vector<mojom::GamepadChangesPtr> all_changes_;
|
||||
};
|
||||
|
||||
// Main test fixture
|
||||
class GamepadProviderTest : public testing::Test, public GamepadTestHelper {
|
||||
public:
|
||||
GamepadProviderTest(const GamepadProviderTest&) = delete;
|
||||
GamepadProviderTest& operator=(const GamepadProviderTest&) = delete;
|
||||
|
||||
GamepadProvider* CreateProvider(const Gamepads& test_data) {
|
||||
auto fetcher = std::make_unique<MockGamepadDataFetcher>(test_data);
|
||||
mock_data_fetcher_ = fetcher.get();
|
||||
provider_ = std::make_unique<GamepadProvider>(
|
||||
/*connection_change_client=*/nullptr, std::move(fetcher),
|
||||
/*polling_thread=*/nullptr);
|
||||
provider_ =
|
||||
std::make_unique<GamepadProvider>(&change_client_, std::move(fetcher),
|
||||
/*polling_thread=*/nullptr);
|
||||
return provider_.get();
|
||||
}
|
||||
|
||||
@ -92,7 +138,7 @@ class GamepadProviderTest : public testing::Test, public GamepadTestHelper {
|
||||
// Pointer owned by the provider.
|
||||
MockGamepadDataFetcher* mock_data_fetcher_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(GamepadProviderTest);
|
||||
TestChangeClient change_client_;
|
||||
};
|
||||
|
||||
TEST_F(GamepadProviderTest, PollingAccess) {
|
||||
@ -102,10 +148,10 @@ TEST_F(GamepadProviderTest, PollingAccess) {
|
||||
test_data.items[0].timestamp = 0;
|
||||
test_data.items[0].buttons_length = 1;
|
||||
test_data.items[0].axes_length = 2;
|
||||
test_data.items[0].buttons[0].value = 1.f;
|
||||
test_data.items[0].buttons[0].value = 1.0f;
|
||||
test_data.items[0].buttons[0].pressed = true;
|
||||
test_data.items[0].axes[0] = -1.f;
|
||||
test_data.items[0].axes[1] = .5f;
|
||||
test_data.items[0].axes[0] = -1.0f;
|
||||
test_data.items[0].axes[1] = 0.5f;
|
||||
|
||||
GamepadProvider* provider = CreateProvider(test_data);
|
||||
provider->SetSanitizationEnabled(false);
|
||||
@ -128,11 +174,11 @@ TEST_F(GamepadProviderTest, PollingAccess) {
|
||||
Gamepads output;
|
||||
ReadGamepadHardwareBuffer(buffer, &output);
|
||||
|
||||
EXPECT_EQ(1u, output.items[0].buttons_length);
|
||||
EXPECT_EQ(1.f, output.items[0].buttons[0].value);
|
||||
ASSERT_EQ(1u, output.items[0].buttons_length);
|
||||
EXPECT_EQ(1.0f, output.items[0].buttons[0].value);
|
||||
EXPECT_EQ(true, output.items[0].buttons[0].pressed);
|
||||
EXPECT_EQ(2u, output.items[0].axes_length);
|
||||
EXPECT_EQ(-1.f, output.items[0].axes[0]);
|
||||
ASSERT_EQ(2u, output.items[0].axes_length);
|
||||
EXPECT_EQ(-1.0f, output.items[0].axes[0]);
|
||||
EXPECT_EQ(0.5f, output.items[0].axes[1]);
|
||||
}
|
||||
|
||||
@ -141,21 +187,21 @@ TEST_F(GamepadProviderTest, ConnectDisconnectMultiple) {
|
||||
test_data.items[0].connected = true;
|
||||
test_data.items[0].timestamp = 0;
|
||||
test_data.items[0].axes_length = 2;
|
||||
test_data.items[0].axes[0] = -1.f;
|
||||
test_data.items[0].axes[1] = .5f;
|
||||
test_data.items[0].axes[0] = -1.0f;
|
||||
test_data.items[0].axes[1] = 0.5f;
|
||||
|
||||
test_data.items[1].connected = true;
|
||||
test_data.items[1].timestamp = 0;
|
||||
test_data.items[1].axes_length = 2;
|
||||
test_data.items[1].axes[0] = 1.f;
|
||||
test_data.items[1].axes[1] = -.5f;
|
||||
test_data.items[1].axes[0] = 1.0f;
|
||||
test_data.items[1].axes[1] = -0.5f;
|
||||
|
||||
Gamepads test_data_onedisconnected;
|
||||
test_data_onedisconnected.items[1].connected = true;
|
||||
test_data_onedisconnected.items[1].timestamp = 0;
|
||||
test_data_onedisconnected.items[1].axes_length = 2;
|
||||
test_data_onedisconnected.items[1].axes[0] = 1.f;
|
||||
test_data_onedisconnected.items[1].axes[1] = -.5f;
|
||||
test_data_onedisconnected.items[1].axes[0] = 1.0f;
|
||||
test_data_onedisconnected.items[1].axes[1] = -0.5f;
|
||||
|
||||
GamepadProvider* provider = CreateProvider(test_data);
|
||||
provider->SetSanitizationEnabled(false);
|
||||
@ -178,11 +224,11 @@ TEST_F(GamepadProviderTest, ConnectDisconnectMultiple) {
|
||||
Gamepads output;
|
||||
ReadGamepadHardwareBuffer(buffer, &output);
|
||||
|
||||
EXPECT_EQ(2u, output.items[0].axes_length);
|
||||
EXPECT_EQ(-1.f, output.items[0].axes[0]);
|
||||
ASSERT_EQ(2u, output.items[0].axes_length);
|
||||
EXPECT_EQ(-1.0f, output.items[0].axes[0]);
|
||||
EXPECT_EQ(0.5f, output.items[0].axes[1]);
|
||||
EXPECT_EQ(2u, output.items[1].axes_length);
|
||||
EXPECT_EQ(1.f, output.items[1].axes[0]);
|
||||
ASSERT_EQ(2u, output.items[1].axes_length);
|
||||
EXPECT_EQ(1.0f, output.items[1].axes[0]);
|
||||
EXPECT_EQ(-0.5f, output.items[1].axes[1]);
|
||||
|
||||
mock_data_fetcher_->SetTestData(test_data_onedisconnected);
|
||||
@ -192,8 +238,8 @@ TEST_F(GamepadProviderTest, ConnectDisconnectMultiple) {
|
||||
ReadGamepadHardwareBuffer(buffer, &output);
|
||||
|
||||
EXPECT_EQ(0u, output.items[0].axes_length);
|
||||
EXPECT_EQ(2u, output.items[1].axes_length);
|
||||
EXPECT_EQ(1.f, output.items[1].axes[0]);
|
||||
ASSERT_EQ(2u, output.items[1].axes_length);
|
||||
EXPECT_EQ(1.0f, output.items[1].axes[0]);
|
||||
EXPECT_EQ(-0.5f, output.items[1].axes[1]);
|
||||
}
|
||||
|
||||
@ -204,13 +250,13 @@ TEST_F(GamepadProviderTest, UserGesture) {
|
||||
no_button_data.items[0].timestamp = 0;
|
||||
no_button_data.items[0].buttons_length = 1;
|
||||
no_button_data.items[0].axes_length = 2;
|
||||
no_button_data.items[0].buttons[0].value = 0.f;
|
||||
no_button_data.items[0].buttons[0].value = 0.0f;
|
||||
no_button_data.items[0].buttons[0].pressed = false;
|
||||
no_button_data.items[0].axes[0] = 0.f;
|
||||
no_button_data.items[0].axes[1] = .4f;
|
||||
no_button_data.items[0].axes[0] = 0.0f;
|
||||
no_button_data.items[0].axes[1] = 0.4f;
|
||||
|
||||
Gamepads button_down_data = no_button_data;
|
||||
button_down_data.items[0].buttons[0].value = 1.f;
|
||||
button_down_data.items[0].buttons[0].value = 1.0f;
|
||||
button_down_data.items[0].buttons[0].pressed = true;
|
||||
|
||||
UserGestureListener listener;
|
||||
@ -256,18 +302,18 @@ TEST_F(GamepadProviderTest, Sanitization) {
|
||||
active_data.items[0].timestamp = 0;
|
||||
active_data.items[0].buttons_length = 1;
|
||||
active_data.items[0].axes_length = 1;
|
||||
active_data.items[0].buttons[0].value = 1.f;
|
||||
active_data.items[0].buttons[0].value = 1.0f;
|
||||
active_data.items[0].buttons[0].pressed = true;
|
||||
active_data.items[0].axes[0] = -1.f;
|
||||
active_data.items[0].axes[0] = -1.0f;
|
||||
|
||||
Gamepads zero_data;
|
||||
zero_data.items[0].connected = true;
|
||||
zero_data.items[0].timestamp = 0;
|
||||
zero_data.items[0].buttons_length = 1;
|
||||
zero_data.items[0].axes_length = 1;
|
||||
zero_data.items[0].buttons[0].value = 0.f;
|
||||
zero_data.items[0].buttons[0].value = 0.0f;
|
||||
zero_data.items[0].buttons[0].pressed = false;
|
||||
zero_data.items[0].axes[0] = 0.f;
|
||||
zero_data.items[0].axes[0] = 0.0f;
|
||||
|
||||
UserGestureListener listener;
|
||||
GamepadProvider* provider = CreateProvider(active_data);
|
||||
@ -276,6 +322,113 @@ TEST_F(GamepadProviderTest, Sanitization) {
|
||||
|
||||
base::RunLoop().RunUntilIdle();
|
||||
|
||||
// Renderer-side, pull data out of poll buffer.
|
||||
base::ReadOnlySharedMemoryRegion region =
|
||||
provider->DuplicateSharedMemoryRegion();
|
||||
base::ReadOnlySharedMemoryMapping mapping = region.Map();
|
||||
ASSERT_TRUE(mapping.IsValid());
|
||||
|
||||
const GamepadHardwareBuffer* buffer =
|
||||
static_cast<const GamepadHardwareBuffer*>(mapping.memory());
|
||||
|
||||
// Wait until the shared memory buffer has been written at least once.
|
||||
WaitForData(buffer);
|
||||
|
||||
Gamepads output;
|
||||
ReadGamepadHardwareBuffer(buffer, &output);
|
||||
|
||||
// Initial data should all be zeroed out due to sanitization, even though the
|
||||
// gamepad reported input
|
||||
ASSERT_EQ(1u, output.items[0].buttons_length);
|
||||
EXPECT_EQ(0.0f, output.items[0].buttons[0].value);
|
||||
EXPECT_FALSE(output.items[0].buttons[0].pressed);
|
||||
ASSERT_EQ(1u, output.items[0].axes_length);
|
||||
EXPECT_EQ(0.0f, output.items[0].axes[0]);
|
||||
|
||||
// Zero out the inputs
|
||||
mock_data_fetcher_->SetTestData(zero_data);
|
||||
|
||||
WaitForDataAndCallbacksIssued(buffer);
|
||||
|
||||
// Read updated data from shared memory
|
||||
ReadGamepadHardwareBuffer(buffer, &output);
|
||||
|
||||
// Should still read zero, which is now an accurate reflection of the data
|
||||
ASSERT_EQ(1u, output.items[0].buttons_length);
|
||||
EXPECT_EQ(0.0f, output.items[0].buttons[0].value);
|
||||
EXPECT_FALSE(output.items[0].buttons[0].pressed);
|
||||
ASSERT_EQ(1u, output.items[0].axes_length);
|
||||
EXPECT_EQ(0.0f, output.items[0].axes[0]);
|
||||
|
||||
// Re-set the active inputs
|
||||
mock_data_fetcher_->SetTestData(active_data);
|
||||
|
||||
WaitForDataAndCallbacksIssued(buffer);
|
||||
|
||||
// Read updated data from shared memory
|
||||
ReadGamepadHardwareBuffer(buffer, &output);
|
||||
|
||||
// Should now accurately reflect the reported data.
|
||||
ASSERT_EQ(1u, output.items[0].buttons_length);
|
||||
EXPECT_EQ(1.0f, output.items[0].buttons[0].value);
|
||||
EXPECT_TRUE(output.items[0].buttons[0].pressed);
|
||||
ASSERT_EQ(1u, output.items[0].axes_length);
|
||||
EXPECT_EQ(-1.0f, output.items[0].axes[0]);
|
||||
}
|
||||
|
||||
TEST_F(GamepadProviderTest, SendEvents) {
|
||||
// This is a test for the logic that is currently behind this flag.
|
||||
base::CommandLine::ForCurrentProcess()->AppendSwitch(
|
||||
switches::kEnableGamepadButtonAxisEvents);
|
||||
|
||||
Gamepads test_data;
|
||||
test_data.items[0].connected = true;
|
||||
test_data.items[0].timestamp = 0;
|
||||
test_data.items[0].axes_length = 2;
|
||||
test_data.items[0].axes[0] = -1.0f;
|
||||
test_data.items[0].axes[1] = 0.5f;
|
||||
test_data.items[0].buttons[0].value = 0.0f;
|
||||
test_data.items[0].buttons[0].pressed = false;
|
||||
test_data.items[0].buttons[1].value = 0.7f;
|
||||
test_data.items[0].buttons[1].pressed = true;
|
||||
test_data.items[0].buttons[2].value = 0.0f;
|
||||
test_data.items[0].buttons[2].pressed = false;
|
||||
test_data.items[0].buttons[3].value = 0.0f;
|
||||
test_data.items[0].buttons[3].pressed = false;
|
||||
test_data.items[0].buttons_length = 4;
|
||||
test_data.items[0].axes_length = 2;
|
||||
|
||||
test_data.items[1].connected = true;
|
||||
test_data.items[1].timestamp = 0;
|
||||
test_data.items[1].axes_length = 2;
|
||||
test_data.items[1].axes[0] = 1.0f;
|
||||
test_data.items[1].axes[1] = -0.5f;
|
||||
test_data.items[1].buttons[0].value = 0.0f;
|
||||
test_data.items[1].buttons[0].pressed = false;
|
||||
test_data.items[1].buttons[1].value = 1.0f;
|
||||
test_data.items[1].buttons[1].pressed = true;
|
||||
test_data.items[1].buttons[2].value = 1.0f;
|
||||
test_data.items[1].buttons[2].pressed = true;
|
||||
test_data.items[1].buttons_length = 3;
|
||||
test_data.items[1].axes_length = 2;
|
||||
|
||||
Gamepads test_data_changed = test_data;
|
||||
test_data_changed.items[0].axes[1] = -0.5f;
|
||||
test_data_changed.items[0].buttons[0].value = 0.4f;
|
||||
test_data_changed.items[0].buttons[1].value = 0.2f;
|
||||
test_data_changed.items[0].buttons[1].pressed = false;
|
||||
test_data_changed.items[0].buttons[3].value = 0.2f;
|
||||
|
||||
test_data_changed.items[1].axes[0] = 0.5f;
|
||||
test_data_changed.items[1].buttons[0].value = 1.0f;
|
||||
test_data_changed.items[1].buttons[0].pressed = true;
|
||||
test_data_changed.items[1].buttons[2].value = 0.9f;
|
||||
|
||||
GamepadProvider* provider = CreateProvider(test_data);
|
||||
provider->SetSanitizationEnabled(false);
|
||||
provider->Resume();
|
||||
base::RunLoop().RunUntilIdle();
|
||||
|
||||
// Renderer-side, pull data out of poll buffer.
|
||||
base::ReadOnlySharedMemoryRegion region =
|
||||
provider->DuplicateSharedMemoryRegion();
|
||||
@ -288,46 +441,219 @@ TEST_F(GamepadProviderTest, Sanitization) {
|
||||
// Wait until the shared memory buffer has been written at least once.
|
||||
WaitForData(buffer);
|
||||
|
||||
Gamepads output;
|
||||
ReadGamepadHardwareBuffer(buffer, &output);
|
||||
|
||||
// Initial data should all be zeroed out due to sanitization, even though the
|
||||
// gamepad reported input
|
||||
EXPECT_EQ(1u, output.items[0].buttons_length);
|
||||
EXPECT_EQ(0.f, output.items[0].buttons[0].value);
|
||||
EXPECT_FALSE(output.items[0].buttons[0].pressed);
|
||||
EXPECT_EQ(1u, output.items[0].axes_length);
|
||||
EXPECT_EQ(0.f, output.items[0].axes[0]);
|
||||
|
||||
// Zero out the inputs
|
||||
mock_data_fetcher_->SetTestData(zero_data);
|
||||
mock_data_fetcher_->SetTestData(test_data_changed);
|
||||
|
||||
// Wait for changes to take place and events to fire.
|
||||
WaitForDataAndCallbacksIssued(buffer);
|
||||
change_client_.RunUntilChangeEvents(2);
|
||||
|
||||
// Read updated data from shared memory
|
||||
ReadGamepadHardwareBuffer(buffer, &output);
|
||||
const auto& changes = change_client_.all_changes();
|
||||
|
||||
// Should still read zero, which is now an accurate reflection of the data
|
||||
EXPECT_EQ(1u, output.items[0].buttons_length);
|
||||
EXPECT_EQ(0.f, output.items[0].buttons[0].value);
|
||||
EXPECT_FALSE(output.items[0].buttons[0].pressed);
|
||||
EXPECT_EQ(1u, output.items[0].axes_length);
|
||||
EXPECT_EQ(0.f, output.items[0].axes[0]);
|
||||
// Ensure the |button_changes| and |axis_changes| objects have all the
|
||||
// expected values.
|
||||
ASSERT_EQ(2u, changes.size());
|
||||
ASSERT_EQ(1u, changes[1]->axis_changes.size());
|
||||
ASSERT_EQ(1u, changes[0]->axis_changes.size());
|
||||
ASSERT_EQ(2u, changes[1]->button_changes.size());
|
||||
ASSERT_EQ(3u, changes[0]->button_changes.size());
|
||||
|
||||
// Re-set the active inputs
|
||||
mock_data_fetcher_->SetTestData(active_data);
|
||||
EXPECT_EQ(1u, changes[0]->axis_changes[0]->axis_index);
|
||||
EXPECT_EQ(-0.5f, changes[0]->axis_changes[0]->axis_snapshot);
|
||||
|
||||
EXPECT_EQ(0u, changes[0]->button_changes[0]->button_index);
|
||||
EXPECT_FALSE(changes[0]->button_changes[0]->button_up);
|
||||
EXPECT_FALSE(changes[0]->button_changes[0]->button_down);
|
||||
EXPECT_TRUE(changes[0]->button_changes[0]->value_changed);
|
||||
EXPECT_EQ(changes[0]->button_changes[0]->button_snapshot,
|
||||
test_data_changed.items[0].buttons[0]);
|
||||
|
||||
EXPECT_EQ(1u, changes[0]->button_changes[1]->button_index);
|
||||
EXPECT_TRUE(changes[0]->button_changes[1]->button_up);
|
||||
EXPECT_FALSE(changes[0]->button_changes[1]->button_down);
|
||||
EXPECT_TRUE(changes[0]->button_changes[1]->value_changed);
|
||||
EXPECT_EQ(changes[0]->button_changes[1]->button_snapshot,
|
||||
test_data_changed.items[0].buttons[1]);
|
||||
|
||||
EXPECT_EQ(3u, changes[0]->button_changes[2]->button_index);
|
||||
EXPECT_FALSE(changes[0]->button_changes[2]->button_up);
|
||||
EXPECT_FALSE(changes[0]->button_changes[2]->button_down);
|
||||
EXPECT_TRUE(changes[0]->button_changes[2]->value_changed);
|
||||
EXPECT_EQ(changes[0]->button_changes[2]->button_snapshot,
|
||||
test_data_changed.items[0].buttons[3]);
|
||||
|
||||
EXPECT_EQ(0u, changes[1]->axis_changes[0]->axis_index);
|
||||
EXPECT_EQ(0.5f, changes[1]->axis_changes[0]->axis_snapshot);
|
||||
|
||||
EXPECT_EQ(0u, changes[1]->button_changes[0]->button_index);
|
||||
EXPECT_FALSE(changes[1]->button_changes[0]->button_up);
|
||||
EXPECT_TRUE(changes[1]->button_changes[0]->button_down);
|
||||
EXPECT_TRUE(changes[1]->button_changes[0]->value_changed);
|
||||
EXPECT_EQ(changes[1]->button_changes[0]->button_snapshot,
|
||||
test_data_changed.items[1].buttons[0]);
|
||||
|
||||
EXPECT_EQ(2u, changes[1]->button_changes[1]->button_index);
|
||||
EXPECT_FALSE(changes[1]->button_changes[1]->button_up);
|
||||
EXPECT_FALSE(changes[1]->button_changes[1]->button_down);
|
||||
EXPECT_TRUE(changes[1]->button_changes[1]->value_changed);
|
||||
EXPECT_EQ(changes[1]->button_changes[1]->button_snapshot,
|
||||
test_data_changed.items[1].buttons[2]);
|
||||
}
|
||||
|
||||
TEST_F(GamepadProviderTest, DontSendEventsBeforeUserGesture) {
|
||||
// This is a test for the logic that is currently behind this flag.
|
||||
base::CommandLine::ForCurrentProcess()->AppendSwitch(
|
||||
switches::kEnableGamepadButtonAxisEvents);
|
||||
|
||||
Gamepads test_data;
|
||||
test_data.items[0].connected = true;
|
||||
test_data.items[0].timestamp = 0;
|
||||
test_data.items[0].axes_length = 2;
|
||||
test_data.items[0].axes[0] = 0.0f;
|
||||
test_data.items[0].buttons[0].value = 0.0f;
|
||||
test_data.items[0].buttons[0].pressed = false;
|
||||
test_data.items[0].buttons_length = 1;
|
||||
test_data.items[0].axes_length = 1;
|
||||
|
||||
Gamepads test_data_changed = test_data;
|
||||
test_data_changed.items[0].axes[1] = 0.4f;
|
||||
test_data_changed.items[0].buttons[0].value = 0.3f;
|
||||
|
||||
GamepadProvider* provider = CreateProvider(test_data);
|
||||
provider->SetSanitizationEnabled(false);
|
||||
provider->Resume();
|
||||
base::RunLoop().RunUntilIdle();
|
||||
|
||||
// Renderer-side, pull data out of poll buffer.
|
||||
base::ReadOnlySharedMemoryRegion region =
|
||||
provider->DuplicateSharedMemoryRegion();
|
||||
base::ReadOnlySharedMemoryMapping mapping = region.Map();
|
||||
EXPECT_TRUE(mapping.IsValid());
|
||||
|
||||
const GamepadHardwareBuffer* buffer =
|
||||
static_cast<const GamepadHardwareBuffer*>(mapping.memory());
|
||||
|
||||
// Wait until the shared memory buffer has been written at least once.
|
||||
WaitForData(buffer);
|
||||
|
||||
mock_data_fetcher_->SetTestData(test_data_changed);
|
||||
|
||||
// Wait for changes to take place and allow potential events to fire.
|
||||
WaitForDataAndCallbacksIssued(buffer);
|
||||
change_client_.RunUntilChangeEvents(0);
|
||||
|
||||
// Read updated data from shared memory
|
||||
ReadGamepadHardwareBuffer(buffer, &output);
|
||||
EXPECT_TRUE(change_client_.all_changes().empty());
|
||||
}
|
||||
|
||||
// Should now accurately reflect the reported data.
|
||||
EXPECT_EQ(1u, output.items[0].buttons_length);
|
||||
EXPECT_EQ(1.f, output.items[0].buttons[0].value);
|
||||
EXPECT_TRUE(output.items[0].buttons[0].pressed);
|
||||
EXPECT_EQ(1u, output.items[0].axes_length);
|
||||
EXPECT_EQ(-1.f, output.items[0].axes[0]);
|
||||
TEST_F(GamepadProviderTest, DontSendEventsWhenDisconnected) {
|
||||
// This is a test for the logic that is currently behind this flag.
|
||||
base::CommandLine::ForCurrentProcess()->AppendSwitch(
|
||||
switches::kEnableGamepadButtonAxisEvents);
|
||||
|
||||
Gamepads test_data;
|
||||
test_data.items[0].connected = false;
|
||||
test_data.items[0].timestamp = 0;
|
||||
test_data.items[0].axes[0] = 0.0f;
|
||||
test_data.items[0].buttons[0].value = 1.0f;
|
||||
test_data.items[0].buttons[0].pressed = true;
|
||||
test_data.items[0].buttons_length = 1;
|
||||
test_data.items[0].axes_length = 1;
|
||||
|
||||
test_data.items[1].connected = true;
|
||||
test_data.items[1].timestamp = 0;
|
||||
test_data.items[1].axes[0] = 1.0f;
|
||||
test_data.items[1].buttons[0].value = 0.0f;
|
||||
test_data.items[1].buttons[0].pressed = false;
|
||||
test_data.items[1].buttons_length = 1;
|
||||
test_data.items[1].axes_length = 1;
|
||||
|
||||
Gamepads test_data_changed = test_data;
|
||||
test_data_changed.items[0].axes[0] = 1.0f;
|
||||
test_data_changed.items[0].buttons[0].value = 0.0f;
|
||||
test_data_changed.items[0].buttons[0].pressed = false;
|
||||
|
||||
test_data_changed.items[1].connected = false;
|
||||
test_data_changed.items[1].axes[0] = 0.0f;
|
||||
test_data_changed.items[1].buttons[0].value = 1.0f;
|
||||
test_data_changed.items[1].buttons[0].pressed = true;
|
||||
|
||||
GamepadProvider* provider = CreateProvider(test_data);
|
||||
provider->SetSanitizationEnabled(false);
|
||||
provider->Resume();
|
||||
base::RunLoop().RunUntilIdle();
|
||||
|
||||
// Renderer-side, pull data out of poll buffer.
|
||||
base::ReadOnlySharedMemoryRegion region =
|
||||
provider->DuplicateSharedMemoryRegion();
|
||||
base::ReadOnlySharedMemoryMapping mapping = region.Map();
|
||||
EXPECT_TRUE(mapping.IsValid());
|
||||
|
||||
const GamepadHardwareBuffer* buffer =
|
||||
static_cast<const GamepadHardwareBuffer*>(mapping.memory());
|
||||
|
||||
// Wait until the shared memory buffer has been written at least once.
|
||||
WaitForData(buffer);
|
||||
|
||||
mock_data_fetcher_->SetTestData(test_data_changed);
|
||||
|
||||
// Wait for changes to take place and allow potential events to fire.
|
||||
WaitForDataAndCallbacksIssued(buffer);
|
||||
change_client_.RunUntilChangeEvents(0);
|
||||
|
||||
EXPECT_TRUE(change_client_.all_changes().empty());
|
||||
}
|
||||
|
||||
TEST_F(GamepadProviderTest, DontSendEventsOnConnection) {
|
||||
// This is a test for the logic that is currently behind this flag.
|
||||
base::CommandLine::ForCurrentProcess()->AppendSwitch(
|
||||
switches::kEnableGamepadButtonAxisEvents);
|
||||
|
||||
Gamepads test_data;
|
||||
test_data.items[0].connected = true;
|
||||
test_data.items[0].timestamp = 0;
|
||||
test_data.items[0].axes[0] = 0.0f;
|
||||
test_data.items[0].buttons[0].value = 1.0f;
|
||||
test_data.items[0].buttons[0].pressed = true;
|
||||
test_data.items[0].buttons_length = 1;
|
||||
test_data.items[0].axes_length = 1;
|
||||
|
||||
test_data.items[1].connected = false;
|
||||
test_data.items[1].timestamp = 0;
|
||||
test_data.items[1].axes[0] = 0.0f;
|
||||
test_data.items[1].buttons[0].value = 1.0f;
|
||||
test_data.items[1].buttons[0].pressed = true;
|
||||
test_data.items[1].buttons_length = 1;
|
||||
test_data.items[1].axes_length = 1;
|
||||
|
||||
Gamepads test_data_changed = test_data;
|
||||
test_data_changed.items[1].connected = true;
|
||||
test_data_changed.items[1].axes[0] = 1.0f;
|
||||
test_data_changed.items[1].buttons[0].value = 0.0f;
|
||||
test_data_changed.items[1].buttons[0].pressed = false;
|
||||
|
||||
GamepadProvider* provider = CreateProvider(test_data);
|
||||
provider->SetSanitizationEnabled(false);
|
||||
provider->Resume();
|
||||
base::RunLoop().RunUntilIdle();
|
||||
|
||||
// Renderer-side, pull data out of poll buffer.
|
||||
base::ReadOnlySharedMemoryRegion region =
|
||||
provider->DuplicateSharedMemoryRegion();
|
||||
base::ReadOnlySharedMemoryMapping mapping = region.Map();
|
||||
EXPECT_TRUE(mapping.IsValid());
|
||||
|
||||
const GamepadHardwareBuffer* buffer =
|
||||
static_cast<const GamepadHardwareBuffer*>(mapping.memory());
|
||||
|
||||
// Wait until the shared memory buffer has been written at least once.
|
||||
WaitForData(buffer);
|
||||
|
||||
mock_data_fetcher_->SetTestData(test_data_changed);
|
||||
|
||||
// Wait for changes to take place and allow potential events to fire.
|
||||
WaitForDataAndCallbacksIssued(buffer);
|
||||
change_client_.RunUntilChangeEvents(0);
|
||||
|
||||
EXPECT_TRUE(change_client_.all_changes().empty());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -10,7 +10,6 @@
|
||||
#include "base/check_op.h"
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "base/memory/singleton.h"
|
||||
#include "base/single_thread_task_runner.h"
|
||||
#include "base/threading/thread.h"
|
||||
#include "base/threading/thread_task_runner_handle.h"
|
||||
#include "device/gamepad/gamepad_consumer.h"
|
||||
@ -24,8 +23,7 @@ namespace {
|
||||
GamepadService* g_gamepad_service = nullptr;
|
||||
} // namespace
|
||||
|
||||
GamepadService::GamepadService()
|
||||
: main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
|
||||
GamepadService::GamepadService() {
|
||||
SetInstance(this);
|
||||
}
|
||||
|
||||
@ -33,8 +31,7 @@ GamepadService::GamepadService(std::unique_ptr<GamepadDataFetcher> fetcher)
|
||||
: provider_(std::make_unique<GamepadProvider>(
|
||||
/*connection_change_client=*/this,
|
||||
std::move(fetcher),
|
||||
/*polling_thread=*/nullptr)),
|
||||
main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()) {
|
||||
/*polling_thread=*/nullptr)) {
|
||||
SetInstance(this);
|
||||
}
|
||||
|
||||
@ -67,7 +64,7 @@ void GamepadService::StartUp(
|
||||
}
|
||||
|
||||
bool GamepadService::ConsumerBecameActive(GamepadConsumer* consumer) {
|
||||
DCHECK(main_thread_task_runner_->BelongsToCurrentThread());
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
|
||||
if (!provider_) {
|
||||
provider_ = std::make_unique<GamepadProvider>(
|
||||
@ -134,7 +131,7 @@ bool GamepadService::ConsumerBecameInactive(GamepadConsumer* consumer) {
|
||||
}
|
||||
|
||||
bool GamepadService::RemoveConsumer(GamepadConsumer* consumer) {
|
||||
DCHECK(main_thread_task_runner_->BelongsToCurrentThread());
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
|
||||
auto it = consumers_.find(consumer);
|
||||
if (it == consumers_.end())
|
||||
@ -149,7 +146,7 @@ bool GamepadService::RemoveConsumer(GamepadConsumer* consumer) {
|
||||
|
||||
void GamepadService::RegisterForUserGesture(base::OnceClosure closure) {
|
||||
DCHECK(consumers_.size() > 0);
|
||||
DCHECK(main_thread_task_runner_->BelongsToCurrentThread());
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
provider_->RegisterForUserGesture(std::move(closure));
|
||||
}
|
||||
|
||||
@ -160,19 +157,25 @@ void GamepadService::Terminate() {
|
||||
void GamepadService::OnGamepadConnectionChange(bool connected,
|
||||
uint32_t index,
|
||||
const Gamepad& pad) {
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
if (connected) {
|
||||
main_thread_task_runner_->PostTask(
|
||||
FROM_HERE, base::BindOnce(&GamepadService::OnGamepadConnected,
|
||||
base::Unretained(this), index, pad));
|
||||
OnGamepadConnected(index, pad);
|
||||
} else {
|
||||
main_thread_task_runner_->PostTask(
|
||||
FROM_HERE, base::BindOnce(&GamepadService::OnGamepadDisconnected,
|
||||
base::Unretained(this), index, pad));
|
||||
OnGamepadDisconnected(index, pad);
|
||||
}
|
||||
}
|
||||
|
||||
void GamepadService::OnGamepadChange(mojom::GamepadChangesPtr changes) {
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
|
||||
for (auto& it : consumers_) {
|
||||
if (it.did_observe_user_gesture && it.is_active)
|
||||
it.consumer->OnGamepadChanged(*changes);
|
||||
}
|
||||
}
|
||||
|
||||
void GamepadService::OnGamepadConnected(uint32_t index, const Gamepad& pad) {
|
||||
DCHECK(main_thread_task_runner_->BelongsToCurrentThread());
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
|
||||
for (auto it = consumers_.begin(); it != consumers_.end(); ++it) {
|
||||
if (it->did_observe_user_gesture && it->is_active)
|
||||
@ -181,7 +184,7 @@ void GamepadService::OnGamepadConnected(uint32_t index, const Gamepad& pad) {
|
||||
}
|
||||
|
||||
void GamepadService::OnGamepadDisconnected(uint32_t index, const Gamepad& pad) {
|
||||
DCHECK(main_thread_task_runner_->BelongsToCurrentThread());
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
|
||||
for (auto it = consumers_.begin(); it != consumers_.end(); ++it) {
|
||||
if (it->did_observe_user_gesture && it->is_active)
|
||||
@ -217,12 +220,12 @@ void GamepadService::ResetVibrationActuator(
|
||||
}
|
||||
|
||||
base::ReadOnlySharedMemoryRegion GamepadService::DuplicateSharedMemoryRegion() {
|
||||
DCHECK(main_thread_task_runner_->BelongsToCurrentThread());
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
return provider_->DuplicateSharedMemoryRegion();
|
||||
}
|
||||
|
||||
void GamepadService::OnUserGesture() {
|
||||
DCHECK(main_thread_task_runner_->BelongsToCurrentThread());
|
||||
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
|
||||
|
||||
gesture_callback_pending_ = false;
|
||||
|
||||
|
@ -16,15 +16,12 @@
|
||||
#include "base/macros.h"
|
||||
#include "base/memory/read_only_shared_memory_region.h"
|
||||
#include "base/memory/singleton.h"
|
||||
#include "base/sequence_checker.h"
|
||||
#include "device/gamepad/gamepad_data_fetcher.h"
|
||||
#include "device/gamepad/gamepad_export.h"
|
||||
#include "device/gamepad/gamepad_provider.h"
|
||||
#include "device/gamepad/public/mojom/gamepad.mojom-forward.h"
|
||||
|
||||
namespace base {
|
||||
class SingleThreadTaskRunner;
|
||||
} // namespace base
|
||||
|
||||
namespace device {
|
||||
class GamepadConsumer;
|
||||
class GamepadProvider;
|
||||
@ -32,8 +29,7 @@ class GamepadProvider;
|
||||
// Owns the GamepadProvider (the background polling thread) and keeps track of
|
||||
// the number of consumers currently using the data (and pausing the provider
|
||||
// when not in use).
|
||||
class DEVICE_GAMEPAD_EXPORT GamepadService
|
||||
: public device::GamepadConnectionChangeClient {
|
||||
class DEVICE_GAMEPAD_EXPORT GamepadService : public GamepadChangeClient {
|
||||
public:
|
||||
// Returns the GamepadService singleton.
|
||||
static GamepadService* GetInstance();
|
||||
@ -126,6 +122,8 @@ class DEVICE_GAMEPAD_EXPORT GamepadService
|
||||
|
||||
void OnUserGesture();
|
||||
|
||||
// GamepadChangeClient implementation.
|
||||
void OnGamepadChange(mojom::GamepadChangesPtr change) override;
|
||||
void OnGamepadConnectionChange(bool connected,
|
||||
uint32_t index,
|
||||
const Gamepad& pad) override;
|
||||
@ -139,19 +137,19 @@ class DEVICE_GAMEPAD_EXPORT GamepadService
|
||||
return consumer < other.consumer;
|
||||
}
|
||||
|
||||
device::GamepadConsumer* consumer;
|
||||
GamepadConsumer* consumer;
|
||||
mutable bool is_active = false;
|
||||
mutable bool did_observe_user_gesture = false;
|
||||
};
|
||||
|
||||
std::unique_ptr<GamepadProvider> provider_;
|
||||
|
||||
scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
|
||||
SEQUENCE_CHECKER(sequence_checker_);
|
||||
|
||||
typedef std::set<ConsumerInfo> ConsumerSet;
|
||||
ConsumerSet consumers_;
|
||||
|
||||
typedef std::unordered_map<device::GamepadConsumer*, std::vector<bool>>
|
||||
typedef std::unordered_map<GamepadConsumer*, std::vector<bool>>
|
||||
ConsumerConnectedStateMap;
|
||||
|
||||
ConsumerConnectedStateMap inactive_consumer_state_;
|
||||
|
@ -8,7 +8,6 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "base/run_loop.h"
|
||||
#include "base/test/task_environment.h"
|
||||
#include "device/gamepad/gamepad_consumer.h"
|
||||
@ -32,8 +31,7 @@ class ConnectionListener : public GamepadConsumer {
|
||||
void OnGamepadDisconnected(uint32_t index, const Gamepad& gamepad) override {
|
||||
disconnected_counter_++;
|
||||
}
|
||||
void OnGamepadButtonOrAxisChanged(uint32_t index,
|
||||
const Gamepad& gamepad) override {}
|
||||
void OnGamepadChanged(const mojom::GamepadChanges& change) override {}
|
||||
|
||||
void ClearCounters() {
|
||||
connected_counter_ = 0;
|
||||
|
@ -47,12 +47,14 @@ component("shared_typemap_traits") {
|
||||
}
|
||||
|
||||
component("switches") {
|
||||
public = [ "gamepad_features.h" ]
|
||||
public = [
|
||||
"gamepad_features.h",
|
||||
"gamepad_switches.h",
|
||||
]
|
||||
sources = [
|
||||
"gamepad_features.cc",
|
||||
"gamepad_features_export.h",
|
||||
"gamepad_switches.cc",
|
||||
"gamepad_switches.h",
|
||||
]
|
||||
public_deps = [ "//base" ]
|
||||
defines = [ "GAMEPAD_FEATURES_IMPLEMENTATION" ]
|
||||
|
@ -25,11 +25,15 @@ class GamepadButton {
|
||||
GamepadButton() = default;
|
||||
GamepadButton(bool pressed, bool touched, double value)
|
||||
: used(true), pressed(pressed), touched(touched), value(value) {}
|
||||
bool operator==(const GamepadButton& other) const {
|
||||
return this->used == other.used && this->pressed == other.pressed &&
|
||||
this->touched == other.touched && this->value == other.value;
|
||||
}
|
||||
// Whether the button is actually reported by the gamepad at all.
|
||||
bool used{false};
|
||||
bool pressed{false};
|
||||
bool touched{false};
|
||||
double value{0.};
|
||||
double value{0.0};
|
||||
};
|
||||
|
||||
enum class GamepadHapticActuatorType { kVibration = 0, kDualRumble = 1 };
|
||||
|
@ -5,13 +5,15 @@
|
||||
#ifndef DEVICE_GAMEPAD_PUBLIC_CPP_GAMEPAD_SWITCHES_H_
|
||||
#define DEVICE_GAMEPAD_PUBLIC_CPP_GAMEPAD_SWITCHES_H_
|
||||
|
||||
#include "device/gamepad/public/cpp/gamepad_features_export.h"
|
||||
|
||||
namespace switches {
|
||||
|
||||
// All switches in alphabetical order. The switches should be documented
|
||||
// alongside the definition of their values in the .cc file.
|
||||
extern const char kEnableGamepadButtonAxisEvents[];
|
||||
extern const char kGamepadPollingInterval[];
|
||||
extern const char kRestrictGamepadAccess[];
|
||||
GAMEPAD_FEATURES_EXPORT extern const char kEnableGamepadButtonAxisEvents[];
|
||||
GAMEPAD_FEATURES_EXPORT extern const char kGamepadPollingInterval[];
|
||||
GAMEPAD_FEATURES_EXPORT extern const char kRestrictGamepadAccess[];
|
||||
|
||||
} // namespace switches
|
||||
|
||||
|
@ -25,6 +25,40 @@ struct GamepadButton {
|
||||
double value;
|
||||
};
|
||||
|
||||
// ButtonChange holds all necessary information for creating the correct
|
||||
// events when a button state changes on a gamepad.
|
||||
struct ButtonChange {
|
||||
// Index of the button in the |buttons| array.
|
||||
uint32 button_index;
|
||||
// True when the GamepadButton's |pressed| value just changed to true.
|
||||
bool button_down;
|
||||
// True when the GamepadButton's |pressed| value just changed to false.
|
||||
bool button_up;
|
||||
// True when the GamepadButton's |value| has changed.
|
||||
bool value_changed;
|
||||
// Snapshot of the GamepadButton's state when these events were triggered.
|
||||
GamepadButton button_snapshot;
|
||||
};
|
||||
|
||||
// AxisChange holds information about an axis value change on a gamepad.
|
||||
struct AxisChange {
|
||||
// Index of the axis in the |axes| array.
|
||||
uint32 axis_index;
|
||||
// Snapshot of the axis's value when these events were triggered.
|
||||
double axis_snapshot;
|
||||
};
|
||||
|
||||
// GamepadChanges holds all the simultaneous changes that occurred to a
|
||||
// gamepad at the same moment.
|
||||
struct GamepadChanges {
|
||||
// Index of the Gamepad in the array returned to the user from GetGamepads
|
||||
uint32 gamepad_index;
|
||||
// Array of all changes to the buttons.
|
||||
array<ButtonChange> button_changes;
|
||||
// Array of all changes to the axes.
|
||||
array<AxisChange> axis_changes;
|
||||
};
|
||||
|
||||
enum GamepadMapping {
|
||||
GamepadMappingNone = 0,
|
||||
GamepadMappingStandard = 1,
|
||||
@ -82,10 +116,10 @@ interface GamepadObserver {
|
||||
// connected gamepad.
|
||||
GamepadDisconnected(uint32 index, Gamepad gamepad);
|
||||
|
||||
// Called when a button or axis is changed on a connected gamepad. |index| is
|
||||
// the index of the gamepad in the gamepad array, and |gamepad| is a reference
|
||||
// to the gamepad.
|
||||
GamepadButtonOrAxisChanged(uint32 index, Gamepad gamepad);
|
||||
// Called when a button or axis is changed on a connected gamepad.
|
||||
// |changes| contains the necessary info to generate the proper
|
||||
// events.
|
||||
GamepadChanged(GamepadChanges changes);
|
||||
};
|
||||
|
||||
// Asks the browser process to start polling, and return a shared memory
|
||||
|
@ -151,11 +151,9 @@ void GamepadSharedMemoryReader::GamepadDisconnected(
|
||||
listener_->DidDisconnectGamepad(index, gamepad);
|
||||
}
|
||||
|
||||
void GamepadSharedMemoryReader::GamepadButtonOrAxisChanged(
|
||||
uint32_t index,
|
||||
const device::Gamepad& gamepad) {
|
||||
if (listener_)
|
||||
listener_->ButtonOrAxisDidChange(index, gamepad);
|
||||
void GamepadSharedMemoryReader::GamepadChanged(
|
||||
device::mojom::blink::GamepadChangesPtr change) {
|
||||
// TODO(crbug.com/856290): use these calls to Generate Button Event.
|
||||
}
|
||||
|
||||
} // namespace blink
|
||||
|
@ -54,8 +54,7 @@ class GamepadSharedMemoryReader
|
||||
const device::Gamepad& gamepad) override;
|
||||
void GamepadDisconnected(uint32_t index,
|
||||
const device::Gamepad& gamepad) override;
|
||||
void GamepadButtonOrAxisChanged(uint32_t index,
|
||||
const device::Gamepad& gamepad) override;
|
||||
void GamepadChanged(device::mojom::blink::GamepadChangesPtr changes) override;
|
||||
|
||||
base::ReadOnlySharedMemoryRegion renderer_shared_buffer_region_;
|
||||
base::ReadOnlySharedMemoryMapping renderer_shared_buffer_mapping_;
|
||||
|
Reference in New Issue
Block a user