Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / content / browser / gamepad / gamepad_provider.cc
index 1cc7aab..8e7644e 100644 (file)
 #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);