| Index: content/browser/gamepad/gamepad_provider.cc
|
| diff --git a/content/browser/gamepad/gamepad_provider.cc b/content/browser/gamepad/gamepad_provider.cc
|
| index 430af56de642becf1a063d751ab8917f0e352a59..682afc51d353a73cc0759ec433c5f9e7ecae1772 100644
|
| --- a/content/browser/gamepad/gamepad_provider.cc
|
| +++ b/content/browser/gamepad/gamepad_provider.cc
|
| @@ -7,6 +7,7 @@
|
| #include <stddef.h>
|
| #include <string.h>
|
| #include <cmath>
|
| +#include <set>
|
| #include <utility>
|
| #include <vector>
|
|
|
| @@ -30,12 +31,6 @@
|
| using blink::WebGamepad;
|
| using blink::WebGamepads;
|
|
|
| -namespace {
|
| -
|
| -const float kMinAxisResetValue = 0.1f;
|
| -
|
| -} // namespace
|
| -
|
| namespace content {
|
|
|
| GamepadProvider::ClosureAndThread::ClosureAndThread(
|
| @@ -51,8 +46,7 @@
|
| : is_paused_(true),
|
| have_scheduled_do_poll_(false),
|
| devices_changed_(true),
|
| - ever_had_user_gesture_(false),
|
| - sanitize_(true) {
|
| + ever_had_user_gesture_(false) {
|
| Initialize(scoped_ptr<GamepadDataFetcher>());
|
| }
|
|
|
| @@ -72,6 +66,7 @@
|
| // 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(
|
| @@ -136,9 +131,6 @@
|
| GamepadHardwareBuffer* hwbuf = SharedMemoryAsHardwareBuffer();
|
| memset(hwbuf, 0, sizeof(GamepadHardwareBuffer));
|
| 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)
|
| @@ -157,34 +149,59 @@
|
| #endif
|
| polling_thread_->StartWithOptions(base::Thread::Options(kMessageLoopType, 0));
|
|
|
| - if (fetcher) {
|
| - AddGamepadDataFetcher(std::move(fetcher));
|
| - } else {
|
| - AddGamepadPlatformDataFetchers(this);
|
| - }
|
| -}
|
| -
|
| -void GamepadProvider::AddGamepadDataFetcher(
|
| - scoped_ptr<GamepadDataFetcher> fetcher) {
|
| polling_thread_->task_runner()->PostTask(
|
| - FROM_HERE, base::Bind(&GamepadProvider::DoAddGamepadDataFetcher,
|
| + FROM_HERE, base::Bind(&GamepadProvider::DoInitializePollingThread,
|
| base::Unretained(this), base::Passed(&fetcher)));
|
| }
|
|
|
| -void GamepadProvider::DoAddGamepadDataFetcher(
|
| +void GamepadProvider::DoInitializePollingThread(
|
| scoped_ptr<GamepadDataFetcher> fetcher) {
|
| DCHECK(base::MessageLoop::current() == polling_thread_->message_loop());
|
| -
|
| - fetcher->InitializeProvider(this);
|
| -
|
| - data_fetchers_.push_back(std::move(fetcher));
|
| + 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(base::MessageLoop::current() == polling_thread_->message_loop());
|
| - for (const auto& it : data_fetchers_) {
|
| - it->PauseHint(paused);
|
| - }
|
| + if (data_fetcher_)
|
| + data_fetcher_->PauseHint(paused);
|
| +}
|
| +
|
| +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;
|
| +}
|
| +
|
| +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::PadState::SetDisconnected() {
|
| + connected_ = false;
|
| + axes_length_ = 0;
|
| + buttons_length_ = 0;
|
| + memset(id_, 0, sizeof(id_));
|
| + memset(mapping_, 0, sizeof(mapping_));
|
| +}
|
| +
|
| +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::DoPoll() {
|
| @@ -206,52 +223,29 @@
|
| devices_changed_ = false;
|
| }
|
|
|
| - // Loop through each registered data fetcher and poll its 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);
|
| - }
|
| -
|
| - // 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, hwbuf->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.
|
| hwbuf->sequence.WriteBegin();
|
| - hwbuf->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, &hwbuf->buffer.items[i]);
|
| - if (state.active_state)
|
| - hwbuf->buffer.length++;
|
| - }
|
| + data_fetcher_->GetGamepadData(&hwbuf->buffer, changed);
|
| hwbuf->sequence.WriteEnd();
|
| }
|
|
|
| if (ever_had_user_gesture_) {
|
| for (unsigned i = 0; i < WebGamepads::itemsLengthCap; ++i) {
|
| + WebGamepad& pad = hwbuf->buffer.items[i];
|
| PadState& state = pad_states_.get()[i];
|
| -
|
| - if (state.active_state) {
|
| - if (state.active_state == GAMEPAD_NEWLY_ACTIVE)
|
| - OnGamepadConnectionChange(true, i, hwbuf->buffer.items[i]);
|
| - state.active_state = GAMEPAD_INACTIVE;
|
| + 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);
|
| }
|
| }
|
| }
|
| @@ -281,6 +275,12 @@
|
|
|
| void GamepadProvider::OnGamepadConnectionChange(
|
| bool connected, int index, const WebGamepad& pad) {
|
| + PadState& state = pad_states_.get()[index];
|
| + if (connected)
|
| + state.SetPad(pad);
|
| + else
|
| + state.SetDisconnected();
|
| +
|
| BrowserThread::PostTask(
|
| BrowserThread::IO,
|
| FROM_HERE,
|
| @@ -310,6 +310,7 @@
|
| if (user_gesture_observers_.empty() && ever_had_user_gesture_)
|
| return;
|
|
|
| + bool had_gesture_before = ever_had_user_gesture_;
|
| const WebGamepads& pads = SharedMemoryAsHardwareBuffer()->buffer;
|
| if (GamepadsHaveUserGesture(pads)) {
|
| ever_had_user_gesture_ = true;
|
| @@ -319,93 +320,11 @@
|
| }
|
| user_gesture_observers_.clear();
|
| }
|
| -}
|
| -
|
| -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 (!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]);
|
| }
|
| - 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->active_state) {
|
| - 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;
|
| - }
|
| - }
|
| - }
|
| }
|
| }
|
|
|
|
|