blob: 836e9e7fd35776f33483b47b00ff4286242617da [file] [log] [blame]
// Copyright 2019 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/nintendo_controller.h"
#include "base/bind.h"
#include "device/gamepad/gamepad_data_fetcher.h"
namespace device {
namespace {
// Device IDs for known Switch devices.
const uint16_t kVendorNintendo = 0x057e;
const uint16_t kProductSwitchProController = 0x2009;
// Device name for a composite Joy-Con device.
const char kProductNameSwitchCompositeDevice[] = "Joy-Con L+R";
} // namespace
NintendoController::NintendoController(int source_id,
mojom::HidDeviceInfoPtr device_info,
mojom::HidManager* hid_manager)
: source_id_(source_id),
is_composite_(false),
device_info_(std::move(device_info)),
hid_manager_(hid_manager),
weak_factory_(this) {}
NintendoController::NintendoController(
int source_id,
std::unique_ptr<NintendoController> composite1,
std::unique_ptr<NintendoController> composite2,
mojom::HidManager* hid_manager)
: source_id_(source_id),
is_composite_(true),
hid_manager_(hid_manager),
weak_factory_(this) {
// Require exactly one left component and one right component, but allow them
// to be provided in either order.
DCHECK(composite1);
DCHECK(composite2);
composite_left_ = std::move(composite1);
composite_right_ = std::move(composite2);
if (composite_left_->GetGamepadHand() != GamepadHand::kLeft)
composite_left_.swap(composite_right_);
DCHECK_EQ(composite_left_->GetGamepadHand(), GamepadHand::kLeft);
DCHECK_EQ(composite_right_->GetGamepadHand(), GamepadHand::kRight);
}
NintendoController::~NintendoController() = default;
// static
std::unique_ptr<NintendoController> NintendoController::Create(
int source_id,
mojom::HidDeviceInfoPtr device_info,
mojom::HidManager* hid_manager) {
return std::make_unique<NintendoController>(source_id, std::move(device_info),
hid_manager);
}
// static
std::unique_ptr<NintendoController> NintendoController::CreateComposite(
int source_id,
std::unique_ptr<NintendoController> composite1,
std::unique_ptr<NintendoController> composite2,
mojom::HidManager* hid_manager) {
return std::make_unique<NintendoController>(
source_id, std::move(composite1), std::move(composite2), hid_manager);
}
// static
bool NintendoController::IsNintendoController(uint16_t vendor_id,
uint16_t product_id) {
// TODO(mattreynolds): Recognize Nintendo Switch devices.
return false;
}
std::vector<std::unique_ptr<NintendoController>>
NintendoController::Decompose() {
std::vector<std::unique_ptr<NintendoController>> decomposed_devices;
if (composite_left_)
decomposed_devices.push_back(std::move(composite_left_));
if (composite_right_)
decomposed_devices.push_back(std::move(composite_right_));
return decomposed_devices;
}
void NintendoController::Open(base::OnceClosure device_ready_closure) {
device_ready_closure_ = std::move(device_ready_closure);
if (is_composite_) {
StartInitSequence();
} else {
uint16_t vendor_id = device_info_->vendor_id;
uint16_t product_id = device_info_->product_id;
if (IsNintendoController(vendor_id, product_id)) {
Connect(base::BindOnce(&NintendoController::OnConnect,
weak_factory_.GetWeakPtr()));
}
}
}
GamepadHand NintendoController::GetGamepadHand() const {
// TODO(mattreynolds): Determine device handedness.
return GamepadHand::kNone;
}
bool NintendoController::IsUsable() const {
// TODO(mattreynolds): Determine if a device is usable.
NOTIMPLEMENTED();
return false;
}
bool NintendoController::HasGuid(const std::string& guid) const {
if (is_composite_)
return composite_left_->HasGuid(guid) || composite_right_->HasGuid(guid);
else
return device_info_->guid == guid;
}
GamepadStandardMappingFunction NintendoController::GetMappingFunction() const {
if (is_composite_) {
// In composite mode, we use the same mapping as a Pro Controller.
return GetGamepadStandardMappingFunction(
kVendorNintendo, kProductSwitchProController, 0, bus_type_);
} else {
// Version number is not available through the HID service.
return GetGamepadStandardMappingFunction(
device_info_->vendor_id, device_info_->product_id, 0, bus_type_);
}
}
void NintendoController::InitializeGamepadState(bool has_standard_mapping,
Gamepad& pad) const {
pad.buttons_length = device::BUTTON_INDEX_COUNT + 1;
pad.axes_length = device::AXIS_INDEX_COUNT;
pad.vibration_actuator.type = GamepadHapticActuatorType::kDualRumble;
pad.vibration_actuator.not_null = true;
pad.timestamp = GamepadDataFetcher::CurrentTimeInMicroseconds();
if (is_composite_) {
// Composite devices use the same product ID as the Switch Pro Controller,
// and expose the same buttons and axes.
GamepadDataFetcher::UpdateGamepadStrings(
kProductNameSwitchCompositeDevice, kVendorNintendo,
kProductSwitchProController, has_standard_mapping, pad);
} else {
GamepadDataFetcher::UpdateGamepadStrings(
device_info_->product_name, device_info_->vendor_id,
device_info_->product_id, has_standard_mapping, pad);
}
}
void NintendoController::UpdateGamepadState(Gamepad& pad) const {
// TODO(mattreynolds): Read gamepad state.
NOTIMPLEMENTED();
}
void NintendoController::Connect(mojom::HidManager::ConnectCallback callback) {
DCHECK(!is_composite_);
DCHECK(hid_manager_);
hid_manager_->Connect(device_info_->guid, std::move(callback));
}
void NintendoController::OnConnect(mojom::HidConnectionPtr connection) {
if (connection) {
connection_ = std::move(connection);
// TODO(mattreynolds): Start listening for input reports.
StartInitSequence();
}
}
void NintendoController::StartInitSequence() {
// TODO(mattreynolds): Implement initialization sequence.
NOTIMPLEMENTED();
}
void NintendoController::DoShutdown() {
if (composite_left_)
composite_left_->Shutdown();
composite_left_.reset();
if (composite_right_)
composite_right_->Shutdown();
composite_right_.reset();
connection_.reset();
device_info_.reset();
}
void NintendoController::SetVibration(double strong_magnitude,
double weak_magnitude) {
// TODO(mattreynolds): Set vibration.
NOTIMPLEMENTED();
}
} // namespace device