1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/browser/gamepad/gamepad_service.h"
6
7#include "base/bind.h"
8#include "base/logging.h"
9#include "base/memory/singleton.h"
10#include "content/browser/gamepad/gamepad_consumer.h"
11#include "content/browser/gamepad/gamepad_data_fetcher.h"
12#include "content/browser/gamepad/gamepad_provider.h"
13#include "content/public/browser/browser_thread.h"
14#include "content/public/browser/render_process_host.h"
15
16namespace content {
17
18namespace {
19GamepadService* g_gamepad_service = 0;
20}
21
22GamepadService::GamepadService()
23    : num_active_consumers_(0),
24      gesture_callback_pending_(false) {
25  SetInstance(this);
26}
27
28GamepadService::GamepadService(scoped_ptr<GamepadDataFetcher> fetcher)
29    : provider_(new GamepadProvider(fetcher.Pass())),
30      num_active_consumers_(0),
31      gesture_callback_pending_(false) {
32  SetInstance(this);
33  thread_checker_.DetachFromThread();
34}
35
36GamepadService::~GamepadService() {
37  SetInstance(NULL);
38}
39
40void GamepadService::SetInstance(GamepadService* instance) {
41  // Unit tests can create multiple instances but only one should exist at any
42  // given time so g_gamepad_service should only go from NULL to non-NULL and
43  // vica versa.
44  CHECK(!!instance != !!g_gamepad_service);
45  g_gamepad_service = instance;
46}
47
48GamepadService* GamepadService::GetInstance() {
49  if (!g_gamepad_service)
50    g_gamepad_service = new GamepadService;
51  return g_gamepad_service;
52}
53
54void GamepadService::ConsumerBecameActive(GamepadConsumer* consumer) {
55  DCHECK(thread_checker_.CalledOnValidThread());
56
57  if (!provider_)
58    provider_.reset(new GamepadProvider);
59
60  std::pair<ConsumerSet::iterator, bool> insert_result =
61      consumers_.insert(consumer);
62  insert_result.first->is_active = true;
63  if (!insert_result.first->did_observe_user_gesture &&
64      !gesture_callback_pending_) {
65    gesture_callback_pending_ = true;
66    provider_->RegisterForUserGesture(
67          base::Bind(&GamepadService::OnUserGesture,
68                     base::Unretained(this)));
69  }
70
71  if (num_active_consumers_++ == 0)
72    provider_->Resume();
73}
74
75void GamepadService::ConsumerBecameInactive(GamepadConsumer* consumer) {
76  DCHECK(provider_);
77  DCHECK(num_active_consumers_ > 0);
78  DCHECK(consumers_.count(consumer) > 0);
79  DCHECK(consumers_.find(consumer)->is_active);
80
81  consumers_.find(consumer)->is_active = false;
82  if (--num_active_consumers_ == 0)
83    provider_->Pause();
84}
85
86void GamepadService::RemoveConsumer(GamepadConsumer* consumer) {
87  DCHECK(thread_checker_.CalledOnValidThread());
88
89  ConsumerSet::iterator it = consumers_.find(consumer);
90  if (it->is_active && --num_active_consumers_ == 0)
91    provider_->Pause();
92  consumers_.erase(it);
93}
94
95void GamepadService::RegisterForUserGesture(const base::Closure& closure) {
96  DCHECK(consumers_.size() > 0);
97  DCHECK(thread_checker_.CalledOnValidThread());
98  provider_->RegisterForUserGesture(closure);
99}
100
101void GamepadService::Terminate() {
102  provider_.reset();
103}
104
105void GamepadService::OnGamepadConnected(
106    int index,
107    const blink::WebGamepad& pad) {
108  DCHECK(thread_checker_.CalledOnValidThread());
109
110  for (ConsumerSet::iterator it = consumers_.begin();
111       it != consumers_.end(); ++it) {
112    if (it->did_observe_user_gesture && it->is_active)
113      it->consumer->OnGamepadConnected(index, pad);
114  }
115}
116
117void GamepadService::OnGamepadDisconnected(
118    int index,
119    const blink::WebGamepad& pad) {
120  DCHECK(thread_checker_.CalledOnValidThread());
121
122  for (ConsumerSet::iterator it = consumers_.begin();
123       it != consumers_.end(); ++it) {
124    if (it->did_observe_user_gesture && it->is_active)
125      it->consumer->OnGamepadDisconnected(index, pad);
126  }
127}
128
129base::SharedMemoryHandle GamepadService::GetSharedMemoryHandleForProcess(
130    base::ProcessHandle handle) {
131  DCHECK(thread_checker_.CalledOnValidThread());
132  return provider_->GetSharedMemoryHandleForProcess(handle);
133}
134
135void GamepadService::OnUserGesture() {
136  DCHECK(thread_checker_.CalledOnValidThread());
137
138  gesture_callback_pending_ = false;
139
140  if (!provider_ ||
141      num_active_consumers_ == 0)
142    return;
143
144  for (ConsumerSet::iterator it = consumers_.begin();
145       it != consumers_.end(); ++it) {
146    if (!it->did_observe_user_gesture && it->is_active) {
147      const ConsumerInfo& info = *it;
148      info.did_observe_user_gesture = true;
149      blink::WebGamepads gamepads;
150      provider_->GetCurrentGamepadData(&gamepads);
151      for (unsigned i = 0; i < blink::WebGamepads::itemsLengthCap; ++i) {
152        const blink::WebGamepad& pad = gamepads.items[i];
153        if (pad.connected)
154          info.consumer->OnGamepadConnected(i, pad);
155      }
156    }
157  }
158}
159
160}  // namespace content
161