diff options
author | b.kelemen@samsung.com <b.kelemen@samsung.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-05-09 09:09:47 +0000 |
---|---|---|
committer | b.kelemen@samsung.com <b.kelemen@samsung.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-05-09 09:09:47 +0000 |
commit | 0ff7365690e19be1f20fe83ab9001205989ae13f (patch) | |
tree | 5781167bec440aeaec1be61d65010b17f91bdc45 /content | |
parent | 32080c3798792f0f2a46b3da88971a4125690a08 (diff) | |
download | chromium_src-0ff7365690e19be1f20fe83ab9001205989ae13f.zip chromium_src-0ff7365690e19be1f20fe83ab9001205989ae13f.tar.gz chromium_src-0ff7365690e19be1f20fe83ab9001205989ae13f.tar.bz2 |
Gamepad API: add support for connection events
Co-authored with Brandon Jones.
This CL continues the work of updating the Gamepad API to latest spec.
IPC's added for connected and disconnected events. The connection state
is observed by GamepadProvider together with polling. The other option
would be to add logic to each platform fetcher. Doing it in GamepadProvider
avoids duplicated logic and I think it is a bit simpler and more consistent
with the polling base nature of the gamepad implementation.
Extra care has been taken to make it consistent with the policy of not
exposing gamepad data to the page before a user gesture is observed.
When a new page starts listening it will not get any events until
we see a user gesture. When we see it we notify it about all the connected
pads.
Now we stop polling as soon as blink is no more interested in gamepad data.
BUG=344556
Review URL: https://codereview.chromium.org/195873019
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@269189 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content')
21 files changed, 593 insertions, 58 deletions
diff --git a/content/browser/DEPS b/content/browser/DEPS index 41e19ab..bd7a602 100644 --- a/content/browser/DEPS +++ b/content/browser/DEPS @@ -32,6 +32,7 @@ include_rules = [ # header-only types, and some selected common code. "-third_party/WebKit", "+third_party/WebKit/public/platform/WebCursorInfo.h", + "+third_party/WebKit/public/platform/WebGamepad.h", "+third_party/WebKit/public/platform/WebGamepads.h", "+third_party/WebKit/public/platform/WebGraphicsContext3D.h", "+third_party/WebKit/public/platform/WebIDBDatabaseException.h", diff --git a/content/browser/gamepad/gamepad_consumer.h b/content/browser/gamepad/gamepad_consumer.h new file mode 100644 index 0000000..3d07452 --- /dev/null +++ b/content/browser/gamepad/gamepad_consumer.h @@ -0,0 +1,29 @@ +// Copyright 2014 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. + +#ifndef CONTENT_BROWSER_GAMEPAD_GAMEPAD_CONSUMER_H +#define CONTENT_BROWSER_GAMEPAD_GAMEPAD_CONSUMER_H + +#include "base/basictypes.h" +#include "content/common/content_export.h" +#include "third_party/WebKit/public/platform/WebGamepad.h" + +namespace content { + +class CONTENT_EXPORT GamepadConsumer { + public: + virtual void OnGamepadConnected( + unsigned index, + const blink::WebGamepad& gamepad) = 0; + virtual void OnGamepadDisconnected( + unsigned index, + const blink::WebGamepad& gamepad) = 0; + + protected: + virtual ~GamepadConsumer() {} +}; + +} // namespace content + +#endif // CONTENT_BROWSER_GAMEPAD_GAMEPAD_CONSUMER_H diff --git a/content/browser/gamepad/gamepad_provider.cc b/content/browser/gamepad/gamepad_provider.cc index 1cc7aab..8e7644e 100644 --- a/content/browser/gamepad/gamepad_provider.cc +++ b/content/browser/gamepad/gamepad_provider.cc @@ -15,9 +15,14 @@ #include "content/browser/gamepad/gamepad_data_fetcher.h" #include "content/browser/gamepad/gamepad_platform_data_fetcher.h" #include "content/browser/gamepad/gamepad_provider.h" +#include "content/browser/gamepad/gamepad_service.h" #include "content/common/gamepad_hardware_buffer.h" #include "content/common/gamepad_messages.h" #include "content/common/gamepad_user_gesture.h" +#include "content/public/browser/browser_thread.h" + +using blink::WebGamepad; +using blink::WebGamepads; namespace content { @@ -34,14 +39,16 @@ GamepadProvider::ClosureAndThread::~ClosureAndThread() { GamepadProvider::GamepadProvider() : is_paused_(true), have_scheduled_do_poll_(false), - devices_changed_(true) { + devices_changed_(true), + ever_had_user_gesture_(false) { Initialize(scoped_ptr<GamepadDataFetcher>()); } GamepadProvider::GamepadProvider(scoped_ptr<GamepadDataFetcher> fetcher) : is_paused_(true), have_scheduled_do_poll_(false), - devices_changed_(true) { + devices_changed_(true), + ever_had_user_gesture_(false) { Initialize(fetcher.Pass()); } @@ -63,6 +70,12 @@ base::SharedMemoryHandle GamepadProvider::GetSharedMemoryHandleForProcess( return renderer_handle; } +void GamepadProvider::GetCurrentGamepadData(WebGamepads* data) { + const WebGamepads& pads = SharedMemoryAsHardwareBuffer()->buffer; + base::AutoLock lock(shared_memory_lock_); + *data = pads; +} + void GamepadProvider::Pause() { { base::AutoLock lock(is_paused_lock_); @@ -111,6 +124,7 @@ void GamepadProvider::Initialize(scoped_ptr<GamepadDataFetcher> fetcher) { CHECK(res); GamepadHardwareBuffer* hwbuf = SharedMemoryAsHardwareBuffer(); memset(hwbuf, 0, sizeof(GamepadHardwareBuffer)); + pad_states_.reset(new PadState[WebGamepads::itemsLengthCap]); polling_thread_.reset(new base::Thread("Gamepad polling thread")); #if defined(OS_LINUX) @@ -148,6 +162,41 @@ void GamepadProvider::SendPauseHint(bool paused) { 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, arraysize(id_)) == 0 && + memcmp(mapping_, pad.mapping, arraysize(mapping_)) == 0; +} + +void GamepadProvider::PadState::SetPad(const WebGamepad& pad) { + DCHECK(pad.connected); + connected_ = true; + axes_length_ = pad.axesLength; + buttons_length_ = pad.buttonsLength; + memcpy(id_, pad.id, arraysize(id_)); + memcpy(mapping_, pad.mapping, arraysize(mapping_)); +} + +void GamepadProvider::PadState::SetDisconnected() { + connected_ = false; + axes_length_ = 0; + buttons_length_ = 0; + memset(id_, 0, arraysize(id_)); + memset(mapping_, 0, arraysize(mapping_)); +} + +void GamepadProvider::PadState::AsWebGamepad(WebGamepad* pad) { + pad->connected = connected_; + pad->axesLength = axes_length_; + pad->buttonsLength = buttons_length_; + memcpy(pad->id, id_, arraysize(id_)); + memcpy(pad->mapping, mapping_, arraysize(mapping_)); + memset(pad->axes, 0, arraysize(pad->axes)); + memset(pad->buttons, 0, arraysize(pad->buttons)); +} + void GamepadProvider::DoPoll() { DCHECK(base::MessageLoop::current() == polling_thread_->message_loop()); DCHECK(have_scheduled_do_poll_); @@ -158,7 +207,7 @@ void GamepadProvider::DoPoll() { ANNOTATE_BENIGN_RACE_SIZED( &hwbuf->buffer, - sizeof(blink::WebGamepads), + sizeof(WebGamepads), "Racey reads are discarded"); { @@ -167,14 +216,35 @@ void GamepadProvider::DoPoll() { devices_changed_ = false; } - // Acquire the SeqLock. There is only ever one writer to this data. - // See gamepad_hardware_buffer.h. - hwbuf->sequence.WriteBegin(); - data_fetcher_->GetGamepadData(&hwbuf->buffer, changed); - hwbuf->sequence.WriteEnd(); + { + 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(); + data_fetcher_->GetGamepadData(&hwbuf->buffer, changed); + hwbuf->sequence.WriteEnd(); + } CheckForUserGesture(); + 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 (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); + } + } + } + // Schedule our next interval of polling. ScheduleDoPoll(); } @@ -197,6 +267,32 @@ void GamepadProvider::ScheduleDoPoll() { have_scheduled_do_poll_ = true; } +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, + base::Bind(&GamepadProvider::DispatchGamepadConnectionChange, + base::Unretained(this), + connected, + index, + pad)); +} + +void GamepadProvider::DispatchGamepadConnectionChange( + bool connected, int index, const WebGamepad& pad) { + if (connected) + GamepadService::GetInstance()->OnGamepadConnected(index, pad); + else + GamepadService::GetInstance()->OnGamepadDisconnected(index, pad); +} + GamepadHardwareBuffer* GamepadProvider::SharedMemoryAsHardwareBuffer() { void* mem = gamepad_shared_memory_.memory(); CHECK(mem); @@ -205,10 +301,11 @@ GamepadHardwareBuffer* GamepadProvider::SharedMemoryAsHardwareBuffer() { void GamepadProvider::CheckForUserGesture() { base::AutoLock lock(user_gesture_lock_); - if (user_gesture_observers_.empty()) - return; // Don't need to check if nobody is listening. + if (user_gesture_observers_.empty() && ever_had_user_gesture_) + return; if (GamepadsHaveUserGesture(SharedMemoryAsHardwareBuffer()->buffer)) { + ever_had_user_gesture_ = true; for (size_t i = 0; i < user_gesture_observers_.size(); i++) { user_gesture_observers_[i].message_loop->PostTask(FROM_HERE, user_gesture_observers_[i].closure); diff --git a/content/browser/gamepad/gamepad_provider.h b/content/browser/gamepad/gamepad_provider.h index 5f10f13..86a4b17 100644 --- a/content/browser/gamepad/gamepad_provider.h +++ b/content/browser/gamepad/gamepad_provider.h @@ -17,6 +17,7 @@ #include "base/synchronization/lock.h" #include "base/system_monitor/system_monitor.h" #include "content/common/content_export.h" +#include "third_party/WebKit/public/platform/WebGamepads.h" namespace base { class MessageLoopProxy; @@ -43,6 +44,8 @@ class CONTENT_EXPORT GamepadProvider : base::SharedMemoryHandle GetSharedMemoryHandleForProcess( base::ProcessHandle renderer_process); + void GetCurrentGamepadData(blink::WebGamepads* data); + // Pause and resume the background polling thread. Can be called from any // thread. void Pause(); @@ -70,6 +73,13 @@ class CONTENT_EXPORT GamepadProvider : void DoPoll(); void ScheduleDoPoll(); + void OnGamepadConnectionChange(bool connected, + int index, + const blink::WebGamepad& pad); + void DispatchGamepadConnectionChange(bool connected, + int index, + const blink::WebGamepad& pad); + GamepadHardwareBuffer* SharedMemoryAsHardwareBuffer(); // Checks the gamepad state to see if the user has interacted with it. @@ -112,9 +122,36 @@ class CONTENT_EXPORT GamepadProvider : base::Lock devices_changed_lock_; bool devices_changed_; - // When polling_thread_ is running, members below are only to be used - // from that thread. + bool ever_had_user_gesture_; + + class PadState { + public: + PadState() { + SetDisconnected(); + } + + bool Match(const blink::WebGamepad& pad) const; + void SetPad(const blink::WebGamepad& pad); + void SetDisconnected(); + void AsWebGamepad(blink::WebGamepad* pad); + + bool connected() const { return connected_; } + + private: + bool connected_; + unsigned axes_length_; + unsigned buttons_length_; + blink::WebUChar id_[blink::WebGamepad::idLengthCap]; + blink::WebUChar mapping_[blink::WebGamepad::mappingLengthCap]; + }; + + // Used to detect connections and disconnections. + scoped_ptr<PadState[]> pad_states_; + + // Only used on the polling thread. scoped_ptr<GamepadDataFetcher> data_fetcher_; + + base::Lock shared_memory_lock_; base::SharedMemory gamepad_shared_memory_; // Polling is done on this background thread. diff --git a/content/browser/gamepad/gamepad_service.cc b/content/browser/gamepad/gamepad_service.cc index 11a6964..a2a83b3 100644 --- a/content/browser/gamepad/gamepad_service.cc +++ b/content/browser/gamepad/gamepad_service.cc @@ -7,18 +7,23 @@ #include "base/bind.h" #include "base/logging.h" #include "base/memory/singleton.h" +#include "content/browser/gamepad/gamepad_consumer.h" #include "content/browser/gamepad/gamepad_data_fetcher.h" #include "content/browser/gamepad/gamepad_provider.h" +#include "content/public/browser/browser_thread.h" #include "content/public/browser/render_process_host.h" namespace content { -GamepadService::GamepadService() : num_readers_(0) { +GamepadService::GamepadService() + : num_active_consumers_(0), + gesture_callback_pending_(false) { } GamepadService::GamepadService(scoped_ptr<GamepadDataFetcher> fetcher) - : num_readers_(0), - provider_(new GamepadProvider(fetcher.Pass())) { + : provider_(new GamepadProvider(fetcher.Pass())), + num_active_consumers_(0), + gesture_callback_pending_(false) { thread_checker_.DetachFromThread(); } @@ -30,28 +35,48 @@ GamepadService* GamepadService::GetInstance() { LeakySingletonTraits<GamepadService> >::get(); } -void GamepadService::AddConsumer() { +void GamepadService::ConsumerBecameActive(GamepadConsumer* consumer) { DCHECK(thread_checker_.CalledOnValidThread()); - num_readers_++; - DCHECK(num_readers_ > 0); if (!provider_) provider_.reset(new GamepadProvider); - provider_->Resume(); + + std::pair<ConsumerSet::iterator, bool> insert_result = + consumers_.insert(consumer); + insert_result.first->is_active = true; + if (!insert_result.first->did_observe_user_gesture && + !gesture_callback_pending_) { + provider_->RegisterForUserGesture( + base::Bind(&GamepadService::OnUserGesture, + base::Unretained(this))); + } + + if (num_active_consumers_++ == 0) + provider_->Resume(); } -void GamepadService::RemoveConsumer() { - DCHECK(thread_checker_.CalledOnValidThread()); +void GamepadService::ConsumerBecameInactive(GamepadConsumer* consumer) { + DCHECK(provider_); + DCHECK(num_active_consumers_ > 0); + DCHECK(consumers_.count(consumer) > 0); + DCHECK(consumers_.find(consumer)->is_active); - --num_readers_; - DCHECK(num_readers_ >= 0); + consumers_.find(consumer)->is_active = false; + if (--num_active_consumers_ == 0) + provider_->Pause(); +} - if (num_readers_ == 0) +void GamepadService::RemoveConsumer(GamepadConsumer* consumer) { + DCHECK(thread_checker_.CalledOnValidThread()); + + ConsumerSet::iterator it = consumers_.find(consumer); + if (it->is_active && --num_active_consumers_ == 0) provider_->Pause(); + consumers_.erase(it); } void GamepadService::RegisterForUserGesture(const base::Closure& closure) { - DCHECK(num_readers_ > 0); + DCHECK(consumers_.size() > 0); DCHECK(thread_checker_.CalledOnValidThread()); provider_->RegisterForUserGesture(closure); } @@ -60,10 +85,59 @@ void GamepadService::Terminate() { provider_.reset(); } +void GamepadService::OnGamepadConnected( + int index, + const blink::WebGamepad& pad) { + DCHECK(thread_checker_.CalledOnValidThread()); + + for (ConsumerSet::iterator it = consumers_.begin(); + it != consumers_.end(); ++it) { + if (it->did_observe_user_gesture && it->is_active) + it->consumer->OnGamepadConnected(index, pad); + } +} + +void GamepadService::OnGamepadDisconnected( + int index, + const blink::WebGamepad& pad) { + DCHECK(thread_checker_.CalledOnValidThread()); + + for (ConsumerSet::iterator it = consumers_.begin(); + it != consumers_.end(); ++it) { + if (it->did_observe_user_gesture && it->is_active) + it->consumer->OnGamepadDisconnected(index, pad); + } +} + base::SharedMemoryHandle GamepadService::GetSharedMemoryHandleForProcess( base::ProcessHandle handle) { DCHECK(thread_checker_.CalledOnValidThread()); return provider_->GetSharedMemoryHandleForProcess(handle); } +void GamepadService::OnUserGesture() { + DCHECK(thread_checker_.CalledOnValidThread()); + + gesture_callback_pending_ = false; + + if (!provider_ || + num_active_consumers_ == 0) + return; + + for (ConsumerSet::iterator it = consumers_.begin(); + it != consumers_.end(); ++it) { + if (!it->did_observe_user_gesture && it->is_active) { + const ConsumerInfo& info = *it; + info.did_observe_user_gesture = true; + blink::WebGamepads gamepads; + provider_->GetCurrentGamepadData(&gamepads); + for (unsigned i = 0; i < blink::WebGamepads::itemsLengthCap; ++i) { + const blink::WebGamepad& pad = gamepads.items[i]; + if (pad.connected) + info.consumer->OnGamepadConnected(i, pad); + } + } + } +} + } // namespace content diff --git a/content/browser/gamepad/gamepad_service.h b/content/browser/gamepad/gamepad_service.h index 94620b34..6008188 100644 --- a/content/browser/gamepad/gamepad_service.h +++ b/content/browser/gamepad/gamepad_service.h @@ -5,6 +5,8 @@ #ifndef CONTENT_BROWSER_GAMEPAD_GAMEPAD_SERVICE_H #define CONTENT_BROWSER_GAMEPAD_GAMEPAD_SERVICE_H +#include <set> + #include "base/basictypes.h" #include "base/callback_forward.h" #include "base/memory/scoped_ptr.h" @@ -13,8 +15,13 @@ #include "base/threading/thread_checker.h" #include "content/common/content_export.h" +namespace blink { +class WebGamepad; +} + namespace content { +class GamepadConsumer; class GamepadDataFetcher; class GamepadProvider; class GamepadServiceTestConstructor; @@ -30,14 +37,28 @@ class CONTENT_EXPORT GamepadService { // Increments the number of users of the provider. The Provider is running // when there's > 0 users, and is paused when the count drops to 0. + // consumer is registered to listen for gamepad connections. If this is the + // first time it is added to the set of consumers it will be treated + // specially: it will not be informed about connections before a new user + // gesture is observed at which point it will be notified for every connected + // gamepads. // // Must be called on the I/O thread. - void AddConsumer(); + void ConsumerBecameActive(GamepadConsumer* consumer); - // Removes a consumer. Should be matched with an AddConsumer call. + // Decrements the number of users of the provider. consumer will not be + // informed about connections until it's added back via ConsumerBecameActive. + // Must be matched with a ConsumerBecameActive call. // // Must be called on the I/O thread. - void RemoveConsumer(); + void ConsumerBecameInactive(GamepadConsumer* consumer); + + // Decrements the number of users of the provider and removes consumer from + // the set of consumers. Should be matched with a a ConsumerBecameActive + // call. + // + // Must be called on the I/O thread. + void RemoveConsumer(GamepadConsumer* consumer); // Registers the given closure for calling when the user has interacted with // the device. This callback will only be issued once. Should only be called @@ -52,6 +73,12 @@ class CONTENT_EXPORT GamepadService { // Stop/join with the background thread in GamepadProvider |provider_|. void Terminate(); + // Called on IO thread when a gamepad is connected. + void OnGamepadConnected(int index, const blink::WebGamepad& pad); + + // Called on IO thread when a gamepad is disconnected. + void OnGamepadDisconnected(int index, const blink::WebGamepad& pad); + private: friend struct DefaultSingletonTraits<GamepadService>; friend class GamepadServiceTestConstructor; @@ -64,11 +91,34 @@ class CONTENT_EXPORT GamepadService { virtual ~GamepadService(); - int num_readers_; + void OnUserGesture(); + + struct ConsumerInfo { + ConsumerInfo(GamepadConsumer* consumer) + : consumer(consumer), + did_observe_user_gesture(false) { + } + + bool operator<(const ConsumerInfo& other) const { + return consumer < other.consumer; + } + + GamepadConsumer* consumer; + mutable bool is_active; + mutable bool did_observe_user_gesture; + }; + scoped_ptr<GamepadProvider> provider_; base::ThreadChecker thread_checker_; + typedef std::set<ConsumerInfo> ConsumerSet; + ConsumerSet consumers_; + + int num_active_consumers_; + + bool gesture_callback_pending_; + DISALLOW_COPY_AND_ASSIGN(GamepadService); }; diff --git a/content/browser/renderer_host/gamepad_browser_message_filter.cc b/content/browser/renderer_host/gamepad_browser_message_filter.cc index e6e05f6..f616a74 100644 --- a/content/browser/renderer_host/gamepad_browser_message_filter.cc +++ b/content/browser/renderer_host/gamepad_browser_message_filter.cc @@ -17,7 +17,7 @@ GamepadBrowserMessageFilter::GamepadBrowserMessageFilter() GamepadBrowserMessageFilter::~GamepadBrowserMessageFilter() { DCHECK_CURRENTLY_ON(BrowserThread::IO); if (is_started_) - GamepadService::GetInstance()->RemoveConsumer(); + GamepadService::GetInstance()->RemoveConsumer(this); } bool GamepadBrowserMessageFilter::OnMessageReceived( @@ -34,28 +34,31 @@ bool GamepadBrowserMessageFilter::OnMessageReceived( return handled; } +void GamepadBrowserMessageFilter::OnGamepadConnected( + unsigned index, + const blink::WebGamepad& gamepad) { + Send(new GamepadMsg_GamepadConnected(index, gamepad)); +} + +void GamepadBrowserMessageFilter::OnGamepadDisconnected( + unsigned index, + const blink::WebGamepad& gamepad) { + Send(new GamepadMsg_GamepadDisconnected(index, gamepad)); +} + void GamepadBrowserMessageFilter::OnGamepadStartPolling( base::SharedMemoryHandle* renderer_handle) { GamepadService* service = GamepadService::GetInstance(); - if (!is_started_) { - is_started_ = true; - service->AddConsumer(); - *renderer_handle = service->GetSharedMemoryHandleForProcess(PeerHandle()); - } else { - // Currently we only expect the renderer to tell us once to start. - NOTREACHED(); - } + CHECK(!is_started_); + is_started_ = true; + service->ConsumerBecameActive(this); + *renderer_handle = service->GetSharedMemoryHandleForProcess(PeerHandle()); } void GamepadBrowserMessageFilter::OnGamepadStopPolling() { - // TODO(scottmg): Probably get rid of this message. We can't trust it will - // arrive anyway if the renderer crashes, etc. - if (is_started_) { - is_started_ = false; - GamepadService::GetInstance()->RemoveConsumer(); - } else { - NOTREACHED(); - } + CHECK(is_started_); + is_started_ = false; + GamepadService::GetInstance()->ConsumerBecameInactive(this); } } // namespace content diff --git a/content/browser/renderer_host/gamepad_browser_message_filter.h b/content/browser/renderer_host/gamepad_browser_message_filter.h index 8661e71..f328739 100644 --- a/content/browser/renderer_host/gamepad_browser_message_filter.h +++ b/content/browser/renderer_host/gamepad_browser_message_filter.h @@ -7,6 +7,7 @@ #include "base/compiler_specific.h" #include "base/memory/shared_memory.h" +#include "content/browser/gamepad/gamepad_consumer.h" #include "content/public/browser/browser_message_filter.h" namespace content { @@ -14,7 +15,9 @@ namespace content { class GamepadService; class RenderProcessHost; -class GamepadBrowserMessageFilter : public BrowserMessageFilter { +class GamepadBrowserMessageFilter : + public BrowserMessageFilter, + public GamepadConsumer { public: GamepadBrowserMessageFilter(); @@ -22,6 +25,14 @@ class GamepadBrowserMessageFilter : public BrowserMessageFilter { virtual bool OnMessageReceived(const IPC::Message& message, bool* message_was_ok) OVERRIDE; + // GamepadConsumer implementation. + virtual void OnGamepadConnected( + unsigned index, + const blink::WebGamepad& gamepad) OVERRIDE; + virtual void OnGamepadDisconnected( + unsigned index, + const blink::WebGamepad& gamepad) OVERRIDE; + private: virtual ~GamepadBrowserMessageFilter(); diff --git a/content/browser/renderer_host/pepper/pepper_gamepad_host.cc b/content/browser/renderer_host/pepper/pepper_gamepad_host.cc index 8783e81..bbb72b8 100644 --- a/content/browser/renderer_host/pepper/pepper_gamepad_host.cc +++ b/content/browser/renderer_host/pepper/pepper_gamepad_host.cc @@ -37,7 +37,7 @@ PepperGamepadHost::PepperGamepadHost(GamepadService* gamepad_service, PepperGamepadHost::~PepperGamepadHost() { if (is_started_) - gamepad_service_->RemoveConsumer(); + gamepad_service_->RemoveConsumer(this); } int32_t PepperGamepadHost::OnResourceMessageReceived( @@ -55,7 +55,7 @@ int32_t PepperGamepadHost::OnRequestMemory( if (is_started_) return PP_ERROR_FAILED; - gamepad_service_->AddConsumer(); + gamepad_service_->ConsumerBecameActive(this); is_started_ = true; // Don't send the shared memory back until the user has interacted with the diff --git a/content/browser/renderer_host/pepper/pepper_gamepad_host.h b/content/browser/renderer_host/pepper/pepper_gamepad_host.h index 1db73aa..c9f2c18 100644 --- a/content/browser/renderer_host/pepper/pepper_gamepad_host.h +++ b/content/browser/renderer_host/pepper/pepper_gamepad_host.h @@ -7,6 +7,7 @@ #include "base/compiler_specific.h" #include "base/memory/weak_ptr.h" +#include "content/browser/gamepad/gamepad_consumer.h" #include "content/common/content_export.h" #include "ppapi/host/resource_host.h" @@ -21,7 +22,9 @@ namespace content { class BrowserPpapiHost; class GamepadService; -class CONTENT_EXPORT PepperGamepadHost : public ppapi::host::ResourceHost { +class CONTENT_EXPORT PepperGamepadHost : + public ppapi::host::ResourceHost, + public GamepadConsumer { public: PepperGamepadHost(BrowserPpapiHost* host, PP_Instance instance, @@ -40,6 +43,14 @@ class CONTENT_EXPORT PepperGamepadHost : public ppapi::host::ResourceHost { const IPC::Message& msg, ppapi::host::HostMessageContext* context) OVERRIDE; + // GamepadConsumer implementation. + virtual void OnGamepadConnected( + unsigned index, + const blink::WebGamepad& gamepad) OVERRIDE {} + virtual void OnGamepadDisconnected( + unsigned index, + const blink::WebGamepad& gamepad) OVERRIDE {} + private: int32_t OnRequestMemory(ppapi::host::HostMessageContext* context); diff --git a/content/common/DEPS b/content/common/DEPS index 2e419da..645304d 100644 --- a/content/common/DEPS +++ b/content/common/DEPS @@ -13,6 +13,7 @@ include_rules = [ "+third_party/WebKit/public/platform/WebDeviceOrientationData.h", "+third_party/WebKit/public/platform/WebFloatPoint.h", "+third_party/WebKit/public/platform/WebFloatRect.h", + "+third_party/WebKit/public/platform/WebGamepad.h", "+third_party/WebKit/public/platform/WebGamepads.h", "+third_party/WebKit/public/platform/WebGraphicsContext3D.h", "+third_party/WebKit/public/platform/WebHTTPBody.h", diff --git a/content/common/gamepad_messages.h b/content/common/gamepad_messages.h index 0532b9c..63d6556 100644 --- a/content/common/gamepad_messages.h +++ b/content/common/gamepad_messages.h @@ -1,16 +1,26 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2014 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. // Multiply-included message file, no include guard. #include "base/memory/shared_memory.h" +#include "content/common/gamepad_param_traits.h" #include "ipc/ipc_message_macros.h" #include "ipc/ipc_param_traits.h" #include "ipc/ipc_platform_file.h" +#include "third_party/WebKit/public/platform/WebGamepad.h" #define IPC_MESSAGE_START GamepadMsgStart +IPC_MESSAGE_CONTROL2(GamepadMsg_GamepadConnected, + int /* index */, + blink::WebGamepad) + +IPC_MESSAGE_CONTROL2(GamepadMsg_GamepadDisconnected, + int /* index */, + blink::WebGamepad) + // Messages sent from the renderer to the browser. // Asks the browser process to start polling, and return a shared memory diff --git a/content/common/gamepad_param_traits.cc b/content/common/gamepad_param_traits.cc new file mode 100644 index 0000000..356d875 --- /dev/null +++ b/content/common/gamepad_param_traits.cc @@ -0,0 +1,79 @@ +// Copyright 2014 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 "content/common/gamepad_param_traits.h" + +#include "base/pickle.h" +#include "base/strings/string16.h" +#include "base/strings/utf_string_conversions.h" +#include "ipc/ipc_message_utils.h" +#include "third_party/WebKit/public/platform/WebGamepad.h" + +using blink::WebGamepad; + +namespace { + +void LogWebUCharString( + const blink::WebUChar web_string[], + const size_t array_size, + std::string* log) { + base::string16 utf16; + utf16.reserve(array_size); + for (size_t i = 0; i < array_size && web_string[i]; ++i) { + utf16[i] = web_string[i]; + } + log->append(base::UTF16ToUTF8(utf16)); +} + +} + +namespace IPC { + +void ParamTraits<WebGamepad>::Write( + Message* m, + const WebGamepad& p) { + m->WriteData(reinterpret_cast<const char*>(&p), sizeof(WebGamepad)); +} + +bool ParamTraits<WebGamepad>::Read( + const Message* m, + PickleIterator* iter, + WebGamepad* p) { + int length; + const char* data; + if (!m->ReadData(iter, &data, &length) || length != sizeof(WebGamepad)) + return false; + memcpy(p, data, sizeof(WebGamepad)); + + return true; +} + +void ParamTraits<WebGamepad>::Log( + const WebGamepad& p, + std::string* l) { + l->append("WebGamepad("); + LogParam(p.connected, l); + LogWebUCharString(p.id, WebGamepad::idLengthCap, l); + l->append(","); + LogWebUCharString(p.mapping, WebGamepad::mappingLengthCap, l); + l->append(","); + LogParam(p.timestamp, l); + l->append(","); + LogParam(p.axesLength, l); + l->append(", ["); + for (size_t i = 0; i < arraysize(p.axes); ++i) { + l->append(base::StringPrintf("%f%s", p.axes[i], + i < (arraysize(p.axes) - 1) ? ", " : "], ")); + } + LogParam(p.buttonsLength, l); + l->append(", ["); + for (size_t i = 0; i < arraysize(p.buttons); ++i) { + l->append(base::StringPrintf("(%u, %f)%s", + p.buttons[i].pressed, p.buttons[i].value, + i < (arraysize(p.buttons) - 1) ? ", " : "], ")); + } + l->append(")"); +} + +} // namespace IPC diff --git a/content/common/gamepad_param_traits.h b/content/common/gamepad_param_traits.h new file mode 100644 index 0000000..aa9e440 --- /dev/null +++ b/content/common/gamepad_param_traits.h @@ -0,0 +1,32 @@ +// Copyright 2014 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. + +#ifndef CONTENT_COMMON_GAMEPAD_PARAM_TRAITS_H_ +#define CONTENT_COMMON_GAMEPAD_PARAM_TRAITS_H_ + +#include <string> + +#include "ipc/ipc_param_traits.h" + +class PickleIterator; + +namespace blink { class WebGamepad; } + +namespace IPC { + +class Message; + +template <> +struct ParamTraits<blink::WebGamepad> { + typedef blink::WebGamepad param_type; + static void Write(Message* m, const blink::WebGamepad& p); + static bool Read(const Message* m, + PickleIterator* iter, + blink::WebGamepad* p); + static void Log(const blink::WebGamepad& p, std::string* l); +}; + +} // namespace IPC + +#endif diff --git a/content/content_browser.gypi b/content/content_browser.gypi index c073222..25ac91b 100644 --- a/content/content_browser.gypi +++ b/content/content_browser.gypi @@ -574,6 +574,7 @@ 'browser/frame_host/render_widget_host_view_child_frame.h', 'browser/frame_host/render_widget_host_view_guest.cc', 'browser/frame_host/render_widget_host_view_guest.h', + 'browser/gamepad/gamepad_consumer.h', 'browser/gamepad/gamepad_data_fetcher.h', 'browser/gamepad/gamepad_platform_data_fetcher.h', 'browser/gamepad/gamepad_platform_data_fetcher_linux.cc', diff --git a/content/content_common.gypi b/content/content_common.gypi index ff70a16..4fc97bd 100644 --- a/content/content_common.gypi +++ b/content/content_common.gypi @@ -212,6 +212,8 @@ 'common/frame_param_macros.h', 'common/gamepad_hardware_buffer.h', 'common/gamepad_messages.h', + 'common/gamepad_param_traits.cc', + 'common/gamepad_param_traits.h', 'common/gamepad_user_gesture.cc', 'common/gamepad_user_gesture.h', 'common/geolocation_messages.h', diff --git a/content/renderer/gamepad_shared_memory_reader.cc b/content/renderer/gamepad_shared_memory_reader.cc index 23edeb3..f1b1e83 100644 --- a/content/renderer/gamepad_shared_memory_reader.cc +++ b/content/renderer/gamepad_shared_memory_reader.cc @@ -6,19 +6,28 @@ #include "base/debug/trace_event.h" #include "base/metrics/histogram.h" -#include "content/common/gamepad_messages.h" #include "content/common/gamepad_user_gesture.h" #include "content/public/renderer/render_thread.h" #include "content/common/gamepad_hardware_buffer.h" #include "ipc/ipc_sync_message_filter.h" +#include "third_party/WebKit/public/platform/WebGamepadListener.h" namespace content { GamepadSharedMemoryReader::GamepadSharedMemoryReader() : gamepad_hardware_buffer_(NULL), + gamepad_listener_(NULL), + is_polling_(false), ever_interacted_with_(false) { +} + +void GamepadSharedMemoryReader::StartPollingIfNecessary() { + if (is_polling_) + return; + CHECK(RenderThread::Get()->Send(new GamepadHostMsg_StartPolling( &renderer_shared_memory_handle_))); + // If we don't get a valid handle from the browser, don't try to Map (we're // probably out of memory or file handles). bool valid_handle = base::SharedMemory::IsHandleValid( @@ -26,6 +35,7 @@ GamepadSharedMemoryReader::GamepadSharedMemoryReader() UMA_HISTOGRAM_BOOLEAN("Gamepad.ValidSharedMemoryHandle", valid_handle); if (!valid_handle) return; + renderer_shared_memory_.reset( new base::SharedMemory(renderer_shared_memory_handle_, true)); CHECK(renderer_shared_memory_->Map(sizeof(GamepadHardwareBuffer))); @@ -33,9 +43,25 @@ GamepadSharedMemoryReader::GamepadSharedMemoryReader() CHECK(memory); gamepad_hardware_buffer_ = static_cast<GamepadHardwareBuffer*>(memory); + + is_polling_ = true; +} + +void GamepadSharedMemoryReader::StopPollingIfNecessary() { + if (is_polling_) { + RenderThread::Get()->Send(new GamepadHostMsg_StopPolling()); + is_polling_ = false; + } } void GamepadSharedMemoryReader::SampleGamepads(blink::WebGamepads& gamepads) { + // Blink should set the listener before start sampling. + CHECK(gamepad_listener_); + + StartPollingIfNecessary(); + if (!is_polling_) + return; + // ========== // DANGER // ========== @@ -88,8 +114,45 @@ void GamepadSharedMemoryReader::SampleGamepads(blink::WebGamepads& gamepads) { } } +void GamepadSharedMemoryReader::SetGamepadListener( + blink::WebGamepadListener* listener) { + gamepad_listener_ = listener; + if (gamepad_listener_) { + // Polling has to be started rigth now and not just on the first sampling + // because want to get connection events from now. + StartPollingIfNecessary(); + } else { + StopPollingIfNecessary(); + } +} + GamepadSharedMemoryReader::~GamepadSharedMemoryReader() { - RenderThread::Get()->Send(new GamepadHostMsg_StopPolling()); + StopPollingIfNecessary(); +} + +bool GamepadSharedMemoryReader::OnControlMessageReceived( + const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(GamepadSharedMemoryReader, message) + IPC_MESSAGE_HANDLER(GamepadMsg_GamepadConnected, OnGamepadConnected) + IPC_MESSAGE_HANDLER(GamepadMsg_GamepadDisconnected, OnGamepadDisconnected) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void GamepadSharedMemoryReader::OnGamepadConnected( + int index, + const blink::WebGamepad& gamepad) { + if (gamepad_listener_) + gamepad_listener_->didConnectGamepad(index, gamepad); +} + +void GamepadSharedMemoryReader::OnGamepadDisconnected( + int index, + const blink::WebGamepad& gamepad) { + if (gamepad_listener_) + gamepad_listener_->didDisconnectGamepad(index, gamepad); } } // namespace content diff --git a/content/renderer/gamepad_shared_memory_reader.h b/content/renderer/gamepad_shared_memory_reader.h index 08dc41a..3846176 100644 --- a/content/renderer/gamepad_shared_memory_reader.h +++ b/content/renderer/gamepad_shared_memory_reader.h @@ -7,23 +7,40 @@ #include "base/memory/scoped_ptr.h" #include "base/memory/shared_memory.h" +#include "content/common/gamepad_messages.h" +#include "content/public/renderer/render_process_observer.h" #include "third_party/WebKit/public/platform/WebGamepads.h" +namespace blink { class WebGamepadListener; } + namespace content { struct GamepadHardwareBuffer; -class GamepadSharedMemoryReader { +class GamepadSharedMemoryReader : public RenderProcessObserver { public: GamepadSharedMemoryReader(); virtual ~GamepadSharedMemoryReader(); - void SampleGamepads(blink::WebGamepads&); + + void SampleGamepads(blink::WebGamepads& gamepads); + void SetGamepadListener(blink::WebGamepadListener* listener); + + // RenderProcessObserver implementation. + virtual bool OnControlMessageReceived(const IPC::Message& message) OVERRIDE; private: + void OnGamepadConnected(int index, const blink::WebGamepad& gamepad); + void OnGamepadDisconnected(int index, const blink::WebGamepad& gamepad); + + void StartPollingIfNecessary(); + void StopPollingIfNecessary(); + base::SharedMemoryHandle renderer_shared_memory_handle_; scoped_ptr<base::SharedMemory> renderer_shared_memory_; GamepadHardwareBuffer* gamepad_hardware_buffer_; + blink::WebGamepadListener* gamepad_listener_; + bool is_polling_; bool ever_interacted_with_; }; diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc index eabb5c2..9794185 100644 --- a/content/renderer/render_thread_impl.cc +++ b/content/renderer/render_thread_impl.cc @@ -396,6 +396,9 @@ void RenderThreadImpl::Init() { AddFilter((new EmbeddedWorkerContextMessageFilter())->GetFilter()); + gamepad_shared_memory_reader_.reset(new GamepadSharedMemoryReader()); + AddObserver(gamepad_shared_memory_reader_.get()); + GetContentClient()->renderer()->RenderThreadStarted(); InitSkiaEventTracer(); @@ -1463,11 +1466,13 @@ void RenderThreadImpl::SetFlingCurveParameters( } void RenderThreadImpl::SampleGamepads(blink::WebGamepads* data) { - if (!gamepad_shared_memory_reader_) - gamepad_shared_memory_reader_.reset(new GamepadSharedMemoryReader); gamepad_shared_memory_reader_->SampleGamepads(*data); } +void RenderThreadImpl::SetGamepadListener(blink::WebGamepadListener* listener) { + gamepad_shared_memory_reader_->SetGamepadListener(listener); +} + void RenderThreadImpl::WidgetCreated() { widget_count_++; } diff --git a/content/renderer/render_thread_impl.h b/content/renderer/render_thread_impl.h index ade85d4..f221bd4 100644 --- a/content/renderer/render_thread_impl.h +++ b/content/renderer/render_thread_impl.h @@ -33,6 +33,7 @@ struct WorkerProcessMsg_CreateWorker_Params; namespace blink { class WebGamepads; +class WebGamepadListener; class WebGraphicsContext3D; class WebMediaStreamCenter; class WebMediaStreamCenterClient; @@ -275,6 +276,10 @@ class CONTENT_EXPORT RenderThreadImpl : public RenderThread, return vc_manager_.get(); } + GamepadSharedMemoryReader* gamepad_shared_memory_reader() const { + return gamepad_shared_memory_reader_.get(); + } + // Get the GPU channel. Returns NULL if the channel is not established or // has been lost. GpuChannelHost* GetGpuChannel(); @@ -372,6 +377,10 @@ class CONTENT_EXPORT RenderThreadImpl : public RenderThread, // Retrieve current gamepad data. void SampleGamepads(blink::WebGamepads* data); + // Set a listener for gamepad connected/disconnected events. + // A non-null listener must be set first before calling SampleGamepads. + void SetGamepadListener(blink::WebGamepadListener* listener); + // Called by a RenderWidget when it is created or destroyed. This // allows the process to know when there are no visible widgets. void WidgetCreated(); diff --git a/content/renderer/renderer_webkitplatformsupport_impl.cc b/content/renderer/renderer_webkitplatformsupport_impl.cc index 8df6722..91906df 100644 --- a/content/renderer/renderer_webkitplatformsupport_impl.cc +++ b/content/renderer/renderer_webkitplatformsupport_impl.cc @@ -884,7 +884,8 @@ WebBlobRegistry* RendererWebKitPlatformSupportImpl::blobRegistry() { void RendererWebKitPlatformSupportImpl::sampleGamepads(WebGamepads& gamepads) { if (g_test_gamepads == 0) { - RenderThreadImpl::current()->SampleGamepads(&gamepads); + RenderThreadImpl::current()->gamepad_shared_memory_reader()-> + SampleGamepads(gamepads); } else { gamepads = g_test_gamepads.Get(); } @@ -893,6 +894,8 @@ void RendererWebKitPlatformSupportImpl::sampleGamepads(WebGamepads& gamepads) { void RendererWebKitPlatformSupportImpl::setGamepadListener( blink::WebGamepadListener* listener) { web_gamepad_listener = listener; + RenderThreadImpl::current()->gamepad_shared_memory_reader()-> + SetGamepadListener(listener); } //------------------------------------------------------------------------------ |