diff options
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); } //------------------------------------------------------------------------------ |