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

Unified Diff: device/gamepad/gamepad_provider.cc

Issue 2129003002: Refactored gamepad polling to support dynamic sources (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fixed unit test issue and Mac XBoxDataFetcher constructor 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: device/gamepad/gamepad_provider.cc
diff --git a/device/gamepad/gamepad_provider.cc b/device/gamepad/gamepad_provider.cc
index c37c38a7dbc78ccaca92b5df5c3fa2ccfbbc87b0..23fa47b72858ed6b25b724a7392773368f17c999 100644
--- a/device/gamepad/gamepad_provider.cc
+++ b/device/gamepad/gamepad_provider.cc
@@ -7,7 +7,6 @@
#include <stddef.h>
#include <string.h>
#include <cmath>
-#include <set>
#include <utility>
#include <vector>
@@ -21,12 +20,18 @@
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "device/gamepad/gamepad_data_fetcher.h"
-#include "device/gamepad/gamepad_platform_data_fetcher.h"
+#include "device/gamepad/gamepad_data_fetcher_manager.h"
#include "device/gamepad/gamepad_user_gesture.h"
using blink::WebGamepad;
using blink::WebGamepads;
+namespace {
+
+const float kMinAxisResetValue = 0.1f;
+
+} // namespace
+
namespace device {
GamepadProvider::ClosureAndThread::ClosureAndThread(
@@ -46,6 +51,7 @@ GamepadProvider::GamepadProvider(
have_scheduled_do_poll_(false),
devices_changed_(true),
ever_had_user_gesture_(false),
+ sanitize_(true),
gamepad_shared_buffer_(std::move(buffer)),
connection_change_client_(connection_change_client) {
Initialize(std::unique_ptr<GamepadDataFetcher>());
@@ -59,12 +65,15 @@ GamepadProvider::GamepadProvider(
have_scheduled_do_poll_(false),
devices_changed_(true),
ever_had_user_gesture_(false),
+ sanitize_(true),
gamepad_shared_buffer_(std::move(buffer)),
connection_change_client_(connection_change_client) {
Initialize(std::move(fetcher));
}
GamepadProvider::~GamepadProvider() {
+ GamepadDataFetcherManager::GetInstance()->ClearProvider();
+
base::SystemMonitor* monitor = base::SystemMonitor::Get();
if (monitor)
monitor->RemoveDevicesChangedObserver(this);
@@ -72,7 +81,6 @@ GamepadProvider::~GamepadProvider() {
// Use Stop() to join the polling thread, as there may be pending callbacks
// which dereference |polling_thread_|.
polling_thread_->Stop();
- data_fetcher_.reset();
}
base::SharedMemoryHandle GamepadProvider::GetSharedMemoryHandleForProcess(
@@ -135,6 +143,9 @@ void GamepadProvider::Initialize(std::unique_ptr<GamepadDataFetcher> fetcher) {
pad_states_.reset(new PadState[WebGamepads::itemsLengthCap]);
+ for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i)
+ ClearPadState(pad_states_.get()[i]);
+
polling_thread_.reset(new base::Thread("Gamepad polling thread"));
#if defined(OS_LINUX)
// On Linux, the data fetcher needs to watch file descriptors, so the message
@@ -152,58 +163,53 @@ void GamepadProvider::Initialize(std::unique_ptr<GamepadDataFetcher> fetcher) {
#endif
polling_thread_->StartWithOptions(base::Thread::Options(kMessageLoopType, 0));
+ if (fetcher) {
+ AddGamepadDataFetcher(std::move(fetcher));
+ } else {
+ GamepadDataFetcherManager::GetInstance()->InitializeProvider(this);
+ }
+}
+
+void GamepadProvider::AddGamepadDataFetcher(
+ std::unique_ptr<GamepadDataFetcher> fetcher) {
polling_thread_->task_runner()->PostTask(
- FROM_HERE, base::Bind(&GamepadProvider::DoInitializePollingThread,
+ FROM_HERE, base::Bind(&GamepadProvider::DoAddGamepadDataFetcher,
base::Unretained(this), base::Passed(&fetcher)));
}
-void GamepadProvider::DoInitializePollingThread(
+void GamepadProvider::RemoveSourceGamepadDataFetcher(GamepadSource source) {
+ polling_thread_->task_runner()->PostTask(
+ FROM_HERE, base::Bind(&GamepadProvider::DoRemoveSourceGamepadDataFetcher,
+ base::Unretained(this), source));
+}
+
+void GamepadProvider::DoAddGamepadDataFetcher(
std::unique_ptr<GamepadDataFetcher> fetcher) {
DCHECK(polling_thread_->task_runner()->BelongsToCurrentThread());
- DCHECK(!data_fetcher_.get()); // Should only initialize once.
if (!fetcher)
- fetcher.reset(new GamepadPlatformDataFetcher);
- data_fetcher_ = std::move(fetcher);
-}
-
-void GamepadProvider::SendPauseHint(bool paused) {
- DCHECK(polling_thread_->task_runner()->BelongsToCurrentThread());
- if (data_fetcher_)
- data_fetcher_->PauseHint(paused);
-}
+ return;
-bool GamepadProvider::PadState::Match(const WebGamepad& pad) const {
- return connected_ == pad.connected && axes_length_ == pad.axesLength &&
- buttons_length_ == pad.buttonsLength &&
- memcmp(id_, pad.id, sizeof(id_)) == 0 &&
- memcmp(mapping_, pad.mapping, sizeof(mapping_)) == 0;
+ fetcher->InitializeProvider(this);
+ data_fetchers_.push_back(std::move(fetcher));
}
-void GamepadProvider::PadState::SetPad(const WebGamepad& pad) {
- connected_ = pad.connected;
- axes_length_ = pad.axesLength;
- buttons_length_ = pad.buttonsLength;
- memcpy(id_, pad.id, sizeof(id_));
- memcpy(mapping_, pad.mapping, sizeof(mapping_));
-}
+void GamepadProvider::DoRemoveSourceGamepadDataFetcher(GamepadSource source) {
+ DCHECK(polling_thread_->task_runner()->BelongsToCurrentThread());
-void GamepadProvider::PadState::SetDisconnected() {
- connected_ = false;
- axes_length_ = 0;
- buttons_length_ = 0;
- memset(id_, 0, sizeof(id_));
- memset(mapping_, 0, sizeof(mapping_));
+ for (GamepadFetcherVector::iterator it = data_fetchers_.begin();
+ it != data_fetchers_.end(); ++it) {
+ if ((*it)->source() == source) {
+ data_fetchers_.erase(it);
+ }
+ }
}
-void GamepadProvider::PadState::AsWebGamepad(WebGamepad* pad) {
- pad->connected = connected_;
- pad->axesLength = axes_length_;
- pad->buttonsLength = buttons_length_;
- memcpy(pad->id, id_, sizeof(id_));
- memcpy(pad->mapping, mapping_, sizeof(mapping_));
- memset(pad->axes, 0, sizeof(pad->axes));
- memset(pad->buttons, 0, sizeof(pad->buttons));
+void GamepadProvider::SendPauseHint(bool paused) {
+ DCHECK(polling_thread_->task_runner()->BelongsToCurrentThread());
+ for (const auto& it : data_fetchers_) {
+ it->PauseHint(paused);
+ }
}
void GamepadProvider::DoPoll() {
@@ -222,29 +228,54 @@ void GamepadProvider::DoPoll() {
devices_changed_ = false;
}
+ // Loop through each registered data fetcher and poll it's gamepad data.
+ // It's expected that GetGamepadData will mark each gamepad as active (via
+ // GetPadState). If a gamepad is not marked as active during the calls to
+ // GetGamepadData then it's assumed to be disconnected.
+ for (const auto& it : data_fetchers_) {
+ it->GetGamepadData(changed);
+ }
+
+ blink::WebGamepads* buffer = gamepad_shared_buffer_->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 (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) {
+ PadState& state = pad_states_.get()[i];
+
+ if (!state.active_state && state.source != GAMEPAD_SOURCE_NONE) {
+ OnGamepadConnectionChange(false, i, buffer->items[i]);
+ ClearPadState(state);
+ }
+ }
+ }
+
{
base::AutoLock lock(shared_memory_lock_);
// Acquire the SeqLock. There is only ever one writer to this data.
// See gamepad_hardware_buffer.h.
gamepad_shared_buffer_->WriteBegin();
- data_fetcher_->GetGamepadData(gamepad_shared_buffer_->buffer(), changed);
+ buffer->length = 0;
+ for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) {
+ PadState& state = pad_states_.get()[i];
+ // Must run through the map+sanitize here or CheckForUserGesture may fail.
+ MapAndSanitizeGamepadData(&state, &buffer->items[i]);
+ if (state.active_state)
+ buffer->length++;
+ }
gamepad_shared_buffer_->WriteEnd();
}
if (ever_had_user_gesture_) {
for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) {
- WebGamepad& pad = gamepad_shared_buffer_->buffer()->items[i];
PadState& state = pad_states_.get()[i];
- if (pad.connected && !state.connected()) {
- OnGamepadConnectionChange(true, i, pad);
- } else if (!pad.connected && state.connected()) {
- OnGamepadConnectionChange(false, i, pad);
- } else if (pad.connected && state.connected() && !state.Match(pad)) {
- WebGamepad old_pad;
- state.AsWebGamepad(&old_pad);
- OnGamepadConnectionChange(false, i, old_pad);
- OnGamepadConnectionChange(true, i, pad);
+
+ if (state.active_state) {
+ if (state.active_state == GAMEPAD_NEWLY_ACTIVE)
+ OnGamepadConnectionChange(true, i, buffer->items[i]);
+ state.active_state = GAMEPAD_INACTIVE;
}
}
}
@@ -275,12 +306,6 @@ void GamepadProvider::ScheduleDoPoll() {
void GamepadProvider::OnGamepadConnectionChange(bool connected,
int index,
const WebGamepad& pad) {
- PadState& state = pad_states_.get()[index];
- if (connected)
- state.SetPad(pad);
- else
- state.SetDisconnected();
-
if (connection_change_client_)
connection_change_client_->OnGamepadConnectionChange(connected, index, pad);
}
@@ -290,7 +315,6 @@ void GamepadProvider::CheckForUserGesture() {
if (user_gesture_observers_.empty() && ever_had_user_gesture_)
return;
- bool had_gesture_before = ever_had_user_gesture_;
const WebGamepads* pads = gamepad_shared_buffer_->buffer();
if (GamepadsHaveUserGesture(*pads)) {
ever_had_user_gesture_ = true;
@@ -300,10 +324,91 @@ void GamepadProvider::CheckForUserGesture() {
}
user_gesture_observers_.clear();
}
- if (!had_gesture_before && ever_had_user_gesture_) {
- // Initialize pad_states_ for the first time.
- for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) {
- pad_states_.get()[i].SetPad(pads->items[i]);
+}
+
+void GamepadProvider::ClearPadState(PadState& state) {
+ memset(&state, 0, sizeof(PadState));
+}
+
+PadState* GamepadProvider::GetPadState(GamepadSource source, int source_id) {
+ // Check to see if the device already has a reserved slot
+ PadState* empty_slot = nullptr;
+ for (size_t i = 0; i < WebGamepads::itemsLengthCap; ++i) {
+ PadState& state = pad_states_.get()[i];
+ if (state.source == source && state.source_id == source_id) {
+ // Retrieving the pad state marks this gamepad as active.
+ state.active_state = GAMEPAD_ACTIVE;
+ return &state;
+ }
+ if (!empty_slot && state.source == GAMEPAD_SOURCE_NONE)
+ empty_slot = &state;
+ }
+ if (empty_slot) {
+ empty_slot->source = source;
+ empty_slot->source_id = source_id;
+ empty_slot->active_state = GAMEPAD_NEWLY_ACTIVE;
+ }
+ return empty_slot;
+}
+
+void GamepadProvider::MapAndSanitizeGamepadData(PadState* pad_state,
+ WebGamepad* pad) {
+ DCHECK(pad_state);
+ DCHECK(pad);
+
+ if (!pad_state->data.connected) {
+ memset(pad, 0, sizeof(WebGamepad));
+ return;
+ }
+
+ // Copy the current state to the output buffer, using the mapping
+ // function, if there is one available.
+ if (pad_state->mapper)
+ pad_state->mapper(pad_state->data, pad);
+ else
+ *pad = pad_state->data;
+
+ pad->connected = true;
+
+ if (!sanitize_)
+ return;
+
+ // About sanitization: Gamepads may report input event if the user is not
+ // interacting with it, due to hardware problems or environmental ones (pad
+ // has something heavy leaning against an axis.) This may cause user gestures
+ // to be detected erroniously, exposing gamepad information when the user had
+ // no intention of doing so. To avoid this we require that each button or axis
+ // report being at rest (zero) at least once before exposing its value to the
+ // Gamepad API. This state is tracked by the axis_mask and button_mask
+ // bitfields. If the bit for an axis or button is 0 it means the axis has
+ // never reported being at rest, and the value will be forced to zero.
+
+ // We can skip axis sanitation if all available axes have been masked.
+ uint32_t full_axis_mask = (1 << pad->axesLength) - 1;
+ if (pad_state->axis_mask != full_axis_mask) {
+ for (size_t axis = 0; axis < pad->axesLength; ++axis) {
+ if (!(pad_state->axis_mask & 1 << axis)) {
+ if (fabs(pad->axes[axis]) < kMinAxisResetValue) {
+ pad_state->axis_mask |= 1 << axis;
+ } else {
+ pad->axes[axis] = 0.0f;
+ }
+ }
+ }
+ }
+
+ // We can skip button sanitation if all available buttons have been masked.
+ uint32_t full_button_mask = (1 << pad->buttonsLength) - 1;
+ if (pad_state->button_mask != full_button_mask) {
+ for (size_t button = 0; button < pad->buttonsLength; ++button) {
+ if (!(pad_state->button_mask & 1 << button)) {
+ if (!pad->buttons[button].pressed) {
+ pad_state->button_mask |= 1 << button;
+ } else {
+ pad->buttons[button].pressed = false;
+ pad->buttons[button].value = 0.0f;
+ }
+ }
}
}
}

Powered by Google App Engine
This is Rietveld 408576698