// Copyright (c) 2012 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 "device/gamepad/gamepad_service.h" #include #include "base/bind.h" #include "base/logging.h" #include "base/memory/ptr_util.h" #include "base/memory/singleton.h" #include "base/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" #include "device/gamepad/gamepad_consumer.h" #include "device/gamepad/gamepad_data_fetcher.h" #include "device/gamepad/gamepad_provider.h" namespace device { namespace { GamepadService* g_gamepad_service = 0; } GamepadService::GamepadService() : main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()), num_active_consumers_(0), gesture_callback_pending_(false) { SetInstance(this); } GamepadService::GamepadService( std::unique_ptr fetcher) : provider_(new device::GamepadProvider(this, std::move(fetcher))), main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()), num_active_consumers_(0), gesture_callback_pending_(false) { SetInstance(this); } GamepadService::~GamepadService() { SetInstance(NULL); } void GamepadService::SetInstance(GamepadService* instance) { // Unit tests can create multiple instances but only one should exist at any // given time so g_gamepad_service should only go from NULL to non-NULL and // vica versa. CHECK(!!instance != !!g_gamepad_service); g_gamepad_service = instance; } GamepadService* GamepadService::GetInstance() { if (!g_gamepad_service) g_gamepad_service = new GamepadService; return g_gamepad_service; } void GamepadService::ConsumerBecameActive(device::GamepadConsumer* consumer) { DCHECK(main_thread_task_runner_->BelongsToCurrentThread()); if (!provider_) provider_.reset(new device::GamepadProvider(this)); std::pair insert_result = consumers_.insert(consumer); const ConsumerInfo& info = *insert_result.first; info.is_active = true; if (info.did_observe_user_gesture) { auto consumer_state_it = inactive_consumer_state_.find(consumer); if (consumer_state_it != inactive_consumer_state_.end()) { const std::vector& old_connected_state = consumer_state_it->second; Gamepads gamepads; provider_->GetCurrentGamepadData(&gamepads); for (unsigned i = 0; i < Gamepads::kItemsLengthCap; ++i) { const Gamepad& gamepad = gamepads.items[i]; if (gamepad.connected) { info.consumer->OnGamepadConnected(i, gamepad); } else if (old_connected_state[i] && !gamepad.connected) { info.consumer->OnGamepadDisconnected(i, gamepad); } } inactive_consumer_state_.erase(consumer_state_it); } } else if (!gesture_callback_pending_) { gesture_callback_pending_ = true; provider_->RegisterForUserGesture( base::Bind(&GamepadService::OnUserGesture, base::Unretained(this))); } if (num_active_consumers_++ == 0) provider_->Resume(); } void GamepadService::ConsumerBecameInactive(device::GamepadConsumer* consumer) { DCHECK(provider_); DCHECK(num_active_consumers_ > 0); auto consumer_it = consumers_.find(consumer); DCHECK(consumer_it != consumers_.end()); const ConsumerInfo& info = *consumer_it; DCHECK(info.is_active); info.is_active = false; if (--num_active_consumers_ == 0) provider_->Pause(); // Save the current state of connected gamepads. if (info.did_observe_user_gesture) { Gamepads gamepads; provider_->GetCurrentGamepadData(&gamepads); std::vector connected_state(Gamepads::kItemsLengthCap); for (unsigned i = 0; i < Gamepads::kItemsLengthCap; ++i) connected_state[i] = gamepads.items[i].connected; inactive_consumer_state_[consumer] = connected_state; } } void GamepadService::RemoveConsumer(device::GamepadConsumer* consumer) { DCHECK(main_thread_task_runner_->BelongsToCurrentThread()); ConsumerSet::iterator it = consumers_.find(consumer); if (it->is_active && --num_active_consumers_ == 0) provider_->Pause(); consumers_.erase(it); inactive_consumer_state_.erase(consumer); } void GamepadService::RegisterForUserGesture(const base::Closure& closure) { DCHECK(consumers_.size() > 0); DCHECK(main_thread_task_runner_->BelongsToCurrentThread()); provider_->RegisterForUserGesture(closure); } void GamepadService::Terminate() { provider_.reset(); } void GamepadService::OnGamepadConnectionChange(bool connected, int index, const Gamepad& pad) { if (connected) { main_thread_task_runner_->PostTask( FROM_HERE, base::BindOnce(&GamepadService::OnGamepadConnected, base::Unretained(this), index, pad)); } else { main_thread_task_runner_->PostTask( FROM_HERE, base::BindOnce(&GamepadService::OnGamepadDisconnected, base::Unretained(this), index, pad)); } } void GamepadService::OnGamepadConnected(int index, const Gamepad& pad) { DCHECK(main_thread_task_runner_->BelongsToCurrentThread()); 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 Gamepad& pad) { DCHECK(main_thread_task_runner_->BelongsToCurrentThread()); for (ConsumerSet::iterator it = consumers_.begin(); it != consumers_.end(); ++it) { if (it->did_observe_user_gesture && it->is_active) it->consumer->OnGamepadDisconnected(index, pad); } } void GamepadService::PlayVibrationEffectOnce( int pad_index, mojom::GamepadHapticEffectType type, mojom::GamepadEffectParametersPtr params, mojom::GamepadHapticsManager::PlayVibrationEffectOnceCallback callback) { DCHECK(main_thread_task_runner_->BelongsToCurrentThread()); if (!provider_) { std::move(callback).Run( mojom::GamepadHapticsResult::GamepadHapticsResultError); return; } provider_->PlayVibrationEffectOnce(pad_index, type, std::move(params), std::move(callback)); } void GamepadService::ResetVibrationActuator( int pad_index, mojom::GamepadHapticsManager::ResetVibrationActuatorCallback callback) { DCHECK(main_thread_task_runner_->BelongsToCurrentThread()); if (!provider_) { std::move(callback).Run( mojom::GamepadHapticsResult::GamepadHapticsResultError); return; } provider_->ResetVibrationActuator(pad_index, std::move(callback)); } base::SharedMemoryHandle GamepadService::DuplicateSharedMemoryHandle() { DCHECK(main_thread_task_runner_->BelongsToCurrentThread()); return provider_->DuplicateSharedMemoryHandle(); } mojo::ScopedSharedBufferHandle GamepadService::GetSharedBufferHandle() { DCHECK(main_thread_task_runner_->BelongsToCurrentThread()); return provider_->GetSharedBufferHandle(); } void GamepadService::OnUserGesture() { DCHECK(main_thread_task_runner_->BelongsToCurrentThread()); 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; Gamepads gamepads; provider_->GetCurrentGamepadData(&gamepads); for (unsigned i = 0; i < Gamepads::kItemsLengthCap; ++i) { const Gamepad& pad = gamepads.items[i]; if (pad.connected) info.consumer->OnGamepadConnected(i, pad); } } } } void GamepadService::SetSanitizationEnabled(bool sanitize) { provider_->SetSanitizationEnabled(sanitize); } } // namespace device