910e62b5创建于 1月15日历史提交
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "device/gamepad/nintendo_data_fetcher.h"

#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/task/sequenced_task_runner.h"
#include "device/gamepad/gamepad_service.h"
#include "device/gamepad/gamepad_uma.h"

namespace device {

NintendoDataFetcher::NintendoDataFetcher() = default;

NintendoDataFetcher::~NintendoDataFetcher() {
  for (auto& entry : controllers_) {
    auto& device = *entry.second;
    device.Shutdown();
  }
}

GamepadSource NintendoDataFetcher::source() {
  return Factory::static_source();
}

void NintendoDataFetcher::OnAddedToProvider() {
  // Open a connection to the HID service. On a successful connection,
  // OnGetDevices will be called with a list of connected HID devices.
  BindHidManager(hid_manager_.BindNewPipeAndPassReceiver());
  hid_manager_->GetDevicesAndSetClient(
      receiver_.BindNewEndpointAndPassRemote(),
      base::BindOnce(&NintendoDataFetcher::OnGetDevices,
                     weak_factory_.GetWeakPtr()));
}

void NintendoDataFetcher::OnGetDevices(
    std::vector<mojom::HidDeviceInfoPtr> device_infos) {
  for (auto& device_info : device_infos)
    DeviceAdded(std::move(device_info));
}

void NintendoDataFetcher::OnDeviceReady(int source_id) {
  auto find_it = controllers_.find(source_id);
  if (find_it == controllers_.end())
    return;

  const auto* ready_device_ptr = find_it->second.get();
  DCHECK(ready_device_ptr);
  if (ready_device_ptr->IsComposite())
    return;

  // Extract a connected device that can be associated with the newly-connected
  // device, if it exists.
  std::unique_ptr<NintendoController> associated_device =
      ExtractAssociatedDevice(ready_device_ptr);
  if (associated_device) {
    // Extract the newly-ready device from |controllers_|.
    auto ready_device = std::move(find_it->second);
    controllers_.erase(source_id);

    // Create a composite gamepad from the newly-ready device and the associated
    // device. Each device provides button and axis state for half of the
    // gamepad and vibration effects are split between them.
    int composite_source_id = next_source_id_++;
    auto emplace_result = controllers_.emplace(
        composite_source_id,
        NintendoController::CreateComposite(
            composite_source_id, std::move(associated_device),
            std::move(ready_device), hid_manager_.get()));
    if (emplace_result.second) {
      auto& composite_gamepad = emplace_result.first->second;
      DCHECK(composite_gamepad);
      composite_gamepad->Open(
          base::BindOnce(&NintendoDataFetcher::OnDeviceReady,
                         weak_factory_.GetWeakPtr(), composite_source_id));
    }
  }
}

void NintendoDataFetcher::DeviceAdded(mojom::HidDeviceInfoPtr device_info) {
  GamepadId gamepad_id = GamepadIdList::Get().GetGamepadId(
      device_info->product_name, device_info->vendor_id,
      device_info->product_id);
  if (NintendoController::IsNintendoController(gamepad_id)) {
    AddDevice(std::move(device_info));
  }
}

void NintendoDataFetcher::DeviceRemoved(mojom::HidDeviceInfoPtr device_info) {
  GamepadId gamepad_id = GamepadIdList::Get().GetGamepadId(
      device_info->product_name, device_info->vendor_id,
      device_info->product_id);
  if (NintendoController::IsNintendoController(gamepad_id)) {
    RemoveDevice(device_info->guid);
  }
}

void NintendoDataFetcher::DeviceChanged(mojom::HidDeviceInfoPtr device_info) {
  // Ignore updated device info. NintendoController will retain the old
  // HidDeviceInfo. This is fine since it does not rely on any HidDeviceInfo
  // members that could change.
}

bool NintendoDataFetcher::AddDevice(mojom::HidDeviceInfoPtr device_info) {
  DCHECK(hid_manager_);
  GamepadId gamepad_id = GamepadIdList::Get().GetGamepadId(
      device_info->product_name, device_info->vendor_id,
      device_info->product_id);
  RecordConnectedGamepad(gamepad_id);
  int source_id = next_source_id_++;

  std::unique_ptr<NintendoController> nintendo_controller =
      NintendoController::Create(source_id, std::move(device_info),
                                 hid_manager_.get());
  // If `nintendo_controller` is nullptr, the device is not a valid Nintendo
  // device.
  if (!nintendo_controller) {
    return false;
  }

  auto emplace_result =
      controllers_.emplace(source_id, std::move(nintendo_controller));
  if (emplace_result.second) {
    auto& new_device = emplace_result.first->second;
    DCHECK(new_device);
    new_device->Open(base::BindOnce(&NintendoDataFetcher::OnDeviceReady,
                                    weak_factory_.GetWeakPtr(), source_id));
    return true;
  }
  return false;
}

bool NintendoDataFetcher::RemoveDevice(const std::string& guid) {
  for (auto& entry : controllers_) {
    auto& match_device = entry.second;
    if (match_device->HasGuid(guid)) {
      if (match_device->IsComposite()) {
        // Decompose the composite device and return the remaining subdevice to
        // |controllers_|.
        auto decomposed_devices = match_device->Decompose();
        match_device->Shutdown();
        controllers_.erase(entry.first);
        for (auto& device : decomposed_devices) {
          if (device->HasGuid(guid)) {
            device->Shutdown();
          } else {
            int source_id = device->GetSourceId();
            controllers_.emplace(source_id, std::move(device));
          }
        }
      } else {
        match_device->Shutdown();
        controllers_.erase(entry.first);
      }
      return true;
    }
  }
  return false;
}

std::unique_ptr<NintendoController>
NintendoDataFetcher::ExtractAssociatedDevice(const NintendoController* device) {
  DCHECK(device);
  std::unique_ptr<NintendoController> associated_device;
  GamepadHand device_hand = device->GetGamepadHand();
  if (device_hand != GamepadHand::kNone) {
    GamepadHand target_hand = (device_hand == GamepadHand::kLeft)
                                  ? GamepadHand::kRight
                                  : GamepadHand::kLeft;
    for (auto& entry : controllers_) {
      auto& match_device = entry.second;
      if (match_device->IsOpen() &&
          match_device->GetGamepadHand() == target_hand &&
          match_device->GetBusType() == device->GetBusType()) {
        associated_device = std::move(match_device);
        controllers_.erase(entry.first);
        break;
      }
    }
  }

  // Set the PadState source back to the default to signal that the slot
  // occupied by the associated device is no longer in use.
  if (associated_device) {
    PadState* state = GetPadState(associated_device->GetSourceId());
    if (state)
      state->source = GamepadSource::kNone;
  }

  return associated_device;
}

void NintendoDataFetcher::GetGamepadData(bool) {
  for (auto& entry : controllers_) {
    auto& device = entry.second;
    if (device->IsOpen() && device->IsUsable()) {
      PadState* state = GetPadState(device->GetSourceId());
      if (!state)
        continue;

      if (!state->is_initialized) {
        state->mapper = device->GetMappingFunction();
        device->InitializeGamepadState(state->mapper != nullptr, state->data);
        state->is_initialized = true;
      }
      device->UpdateGamepadState(state->data);
    }
  }
}

NintendoController* NintendoDataFetcher::GetControllerFromGuid(
    const std::string& guid) {
  for (auto& entry : controllers_) {
    auto& device = entry.second;
    if (device->HasGuid(guid))
      return device.get();
  }
  return nullptr;
}

NintendoController* NintendoDataFetcher::GetControllerFromSourceId(
    int source_id) {
  auto find_it = controllers_.find(source_id);
  return find_it == controllers_.end() ? nullptr : find_it->second.get();
}

void NintendoDataFetcher::PlayEffect(
    int source_id,
    mojom::GamepadHapticEffectType type,
    mojom::GamepadEffectParametersPtr params,
    mojom::GamepadHapticsManager::PlayVibrationEffectOnceCallback callback,
    scoped_refptr<base::SequencedTaskRunner> callback_runner) {
  auto* controller = GetControllerFromSourceId(source_id);
  if (!controller || !controller->IsOpen()) {
    RunVibrationCallback(
        std::move(callback), std::move(callback_runner),
        mojom::GamepadHapticsResult::GamepadHapticsResultError);
    return;
  }
  controller->PlayEffect(type, std::move(params), std::move(callback),
                         std::move(callback_runner));
}

void NintendoDataFetcher::ResetVibration(
    int source_id,
    mojom::GamepadHapticsManager::ResetVibrationActuatorCallback callback,
    scoped_refptr<base::SequencedTaskRunner> callback_runner) {
  auto* controller = GetControllerFromSourceId(source_id);
  if (!controller || !controller->IsOpen()) {
    RunVibrationCallback(
        std::move(callback), std::move(callback_runner),
        mojom::GamepadHapticsResult::GamepadHapticsResultError);
    return;
  }
  controller->ResetVibration(std::move(callback), std::move(callback_runner));
}

}  // namespace device