2022-12-20 18:34:33 +01:00
|
|
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
|
|
|
|
#include <fmt/format.h>
|
|
|
|
|
|
|
|
#include "common/param_package.h"
|
2023-01-25 20:52:46 +01:00
|
|
|
#include "common/polyfill_ranges.h"
|
2023-01-25 22:03:54 +01:00
|
|
|
#include "common/polyfill_thread.h"
|
2022-12-20 18:34:33 +01:00
|
|
|
#include "common/settings.h"
|
|
|
|
#include "common/thread.h"
|
|
|
|
#include "input_common/drivers/joycon.h"
|
|
|
|
#include "input_common/helpers/joycon_driver.h"
|
|
|
|
#include "input_common/helpers/joycon_protocol/joycon_types.h"
|
|
|
|
|
|
|
|
namespace InputCommon {
|
|
|
|
|
|
|
|
Joycons::Joycons(const std::string& input_engine_) : InputEngine(input_engine_) {
|
2022-12-20 19:45:54 +01:00
|
|
|
// Avoid conflicting with SDL driver
|
2023-02-10 02:59:12 +01:00
|
|
|
if (!Settings::values.enable_joycon_driver && !Settings::values.enable_procon_driver) {
|
2022-12-20 19:45:54 +01:00
|
|
|
return;
|
|
|
|
}
|
2022-12-20 18:34:33 +01:00
|
|
|
LOG_INFO(Input, "Joycon driver Initialization started");
|
|
|
|
const int init_res = SDL_hid_init();
|
|
|
|
if (init_res == 0) {
|
|
|
|
Setup();
|
|
|
|
} else {
|
|
|
|
LOG_ERROR(Input, "Hidapi could not be initialized. failed with error = {}", init_res);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Joycons::~Joycons() {
|
|
|
|
Reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Joycons::Reset() {
|
|
|
|
scan_thread = {};
|
|
|
|
for (const auto& device : left_joycons) {
|
|
|
|
if (!device) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
device->Stop();
|
|
|
|
}
|
|
|
|
for (const auto& device : right_joycons) {
|
|
|
|
if (!device) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
device->Stop();
|
|
|
|
}
|
2023-02-10 02:59:12 +01:00
|
|
|
for (const auto& device : pro_controller) {
|
|
|
|
if (!device) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
device->Stop();
|
|
|
|
}
|
2022-12-20 18:34:33 +01:00
|
|
|
SDL_hid_exit();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Joycons::Setup() {
|
|
|
|
u32 port = 0;
|
2022-12-21 17:44:23 +01:00
|
|
|
PreSetController(GetIdentifier(0, Joycon::ControllerType::None));
|
2022-12-20 18:34:33 +01:00
|
|
|
for (auto& device : left_joycons) {
|
|
|
|
PreSetController(GetIdentifier(port, Joycon::ControllerType::Left));
|
|
|
|
device = std::make_shared<Joycon::JoyconDriver>(port++);
|
|
|
|
}
|
2022-12-21 17:44:23 +01:00
|
|
|
port = 0;
|
2022-12-20 18:34:33 +01:00
|
|
|
for (auto& device : right_joycons) {
|
|
|
|
PreSetController(GetIdentifier(port, Joycon::ControllerType::Right));
|
|
|
|
device = std::make_shared<Joycon::JoyconDriver>(port++);
|
|
|
|
}
|
2023-02-10 02:59:12 +01:00
|
|
|
port = 0;
|
|
|
|
for (auto& device : pro_controller) {
|
|
|
|
PreSetController(GetIdentifier(port, Joycon::ControllerType::Pro));
|
|
|
|
device = std::make_shared<Joycon::JoyconDriver>(port++);
|
|
|
|
}
|
2022-12-20 18:34:33 +01:00
|
|
|
|
2023-01-14 06:29:05 +01:00
|
|
|
scan_thread = std::jthread([this](std::stop_token stop_token) { ScanThread(stop_token); });
|
2022-12-20 18:34:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Joycons::ScanThread(std::stop_token stop_token) {
|
|
|
|
constexpr u16 nintendo_vendor_id = 0x057e;
|
2023-01-14 06:29:05 +01:00
|
|
|
Common::SetCurrentThreadName("JoyconScanThread");
|
2023-01-25 22:03:54 +01:00
|
|
|
|
|
|
|
do {
|
2022-12-20 18:34:33 +01:00
|
|
|
SDL_hid_device_info* devs = SDL_hid_enumerate(nintendo_vendor_id, 0x0);
|
|
|
|
SDL_hid_device_info* cur_dev = devs;
|
|
|
|
|
|
|
|
while (cur_dev) {
|
|
|
|
if (IsDeviceNew(cur_dev)) {
|
|
|
|
LOG_DEBUG(Input, "Device Found,type : {:04X} {:04X}", cur_dev->vendor_id,
|
|
|
|
cur_dev->product_id);
|
|
|
|
RegisterNewDevice(cur_dev);
|
|
|
|
}
|
|
|
|
cur_dev = cur_dev->next;
|
|
|
|
}
|
|
|
|
|
2023-01-14 06:29:05 +01:00
|
|
|
SDL_hid_free_enumeration(devs);
|
2023-01-25 22:03:54 +01:00
|
|
|
} while (Common::StoppableTimedWait(stop_token, std::chrono::seconds{5}));
|
2022-12-20 18:34:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Joycons::IsDeviceNew(SDL_hid_device_info* device_info) const {
|
|
|
|
Joycon::ControllerType type{};
|
|
|
|
Joycon::SerialNumber serial_number{};
|
|
|
|
|
|
|
|
const auto result = Joycon::JoyconDriver::GetDeviceType(device_info, type);
|
|
|
|
if (result != Joycon::DriverResult::Success) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto result2 = Joycon::JoyconDriver::GetSerialNumber(device_info, serial_number);
|
|
|
|
if (result2 != Joycon::DriverResult::Success) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-12-21 17:44:23 +01:00
|
|
|
auto is_handle_identical = [serial_number](std::shared_ptr<Joycon::JoyconDriver> device) {
|
2022-12-20 18:34:33 +01:00
|
|
|
if (!device) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!device->IsConnected()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (device->GetHandleSerialNumber() != serial_number) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Check if device already exist
|
|
|
|
switch (type) {
|
|
|
|
case Joycon::ControllerType::Left:
|
2023-02-10 02:59:12 +01:00
|
|
|
if (!Settings::values.enable_joycon_driver) {
|
|
|
|
return false;
|
|
|
|
}
|
2022-12-20 18:34:33 +01:00
|
|
|
for (const auto& device : left_joycons) {
|
|
|
|
if (is_handle_identical(device)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Joycon::ControllerType::Right:
|
2023-02-10 02:59:12 +01:00
|
|
|
if (!Settings::values.enable_joycon_driver) {
|
|
|
|
return false;
|
|
|
|
}
|
2022-12-20 18:34:33 +01:00
|
|
|
for (const auto& device : right_joycons) {
|
|
|
|
if (is_handle_identical(device)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2023-02-10 02:59:12 +01:00
|
|
|
case Joycon::ControllerType::Pro:
|
|
|
|
if (!Settings::values.enable_procon_driver) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
for (const auto& device : pro_controller) {
|
|
|
|
if (is_handle_identical(device)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2022-12-20 18:34:33 +01:00
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Joycons::RegisterNewDevice(SDL_hid_device_info* device_info) {
|
|
|
|
Joycon::ControllerType type{};
|
|
|
|
auto result = Joycon::JoyconDriver::GetDeviceType(device_info, type);
|
|
|
|
auto handle = GetNextFreeHandle(type);
|
|
|
|
if (handle == nullptr) {
|
|
|
|
LOG_WARNING(Input, "No free handles available");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (result == Joycon::DriverResult::Success) {
|
|
|
|
result = handle->RequestDeviceAccess(device_info);
|
|
|
|
}
|
|
|
|
if (result == Joycon::DriverResult::Success) {
|
|
|
|
LOG_WARNING(Input, "Initialize device");
|
|
|
|
|
|
|
|
const std::size_t port = handle->GetDevicePort();
|
2022-12-21 03:27:34 +01:00
|
|
|
const Joycon::JoyconCallbacks callbacks{
|
|
|
|
.on_battery_data = {[this, port, type](Joycon::Battery value) {
|
|
|
|
OnBatteryUpdate(port, type, value);
|
|
|
|
}},
|
|
|
|
.on_color_data = {[this, port, type](Joycon::Color value) {
|
|
|
|
OnColorUpdate(port, type, value);
|
|
|
|
}},
|
|
|
|
.on_button_data = {[this, port, type](int id, bool value) {
|
|
|
|
OnButtonUpdate(port, type, id, value);
|
|
|
|
}},
|
|
|
|
.on_stick_data = {[this, port, type](int id, f32 value) {
|
|
|
|
OnStickUpdate(port, type, id, value);
|
|
|
|
}},
|
|
|
|
.on_motion_data = {[this, port, type](int id, const Joycon::MotionData& value) {
|
|
|
|
OnMotionUpdate(port, type, id, value);
|
|
|
|
}},
|
|
|
|
.on_ring_data = {[this](f32 ring_data) { OnRingConUpdate(ring_data); }},
|
|
|
|
.on_amiibo_data = {[this, port](const std::vector<u8>& amiibo_data) {
|
|
|
|
OnAmiiboUpdate(port, amiibo_data);
|
|
|
|
}},
|
2022-12-28 22:21:12 +01:00
|
|
|
.on_camera_data = {[this, port](const std::vector<u8>& camera_data,
|
|
|
|
Joycon::IrsResolution format) {
|
|
|
|
OnCameraUpdate(port, camera_data, format);
|
|
|
|
}},
|
2022-12-21 03:27:34 +01:00
|
|
|
};
|
|
|
|
|
2022-12-20 18:34:33 +01:00
|
|
|
handle->InitializeDevice();
|
2022-12-21 03:27:34 +01:00
|
|
|
handle->SetCallbacks(callbacks);
|
2022-12-20 18:34:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<Joycon::JoyconDriver> Joycons::GetNextFreeHandle(
|
|
|
|
Joycon::ControllerType type) const {
|
|
|
|
if (type == Joycon::ControllerType::Left) {
|
2023-01-14 06:29:05 +01:00
|
|
|
const auto unconnected_device =
|
|
|
|
std::ranges::find_if(left_joycons, [](auto& device) { return !device->IsConnected(); });
|
|
|
|
if (unconnected_device != left_joycons.end()) {
|
|
|
|
return *unconnected_device;
|
2022-12-20 18:34:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (type == Joycon::ControllerType::Right) {
|
2023-01-14 06:29:05 +01:00
|
|
|
const auto unconnected_device = std::ranges::find_if(
|
|
|
|
right_joycons, [](auto& device) { return !device->IsConnected(); });
|
|
|
|
|
|
|
|
if (unconnected_device != right_joycons.end()) {
|
|
|
|
return *unconnected_device;
|
2022-12-20 18:34:33 +01:00
|
|
|
}
|
|
|
|
}
|
2023-02-10 02:59:12 +01:00
|
|
|
if (type == Joycon::ControllerType::Pro) {
|
|
|
|
const auto unconnected_device = std::ranges::find_if(
|
|
|
|
pro_controller, [](auto& device) { return !device->IsConnected(); });
|
|
|
|
|
|
|
|
if (unconnected_device != pro_controller.end()) {
|
|
|
|
return *unconnected_device;
|
|
|
|
}
|
|
|
|
}
|
2022-12-20 18:34:33 +01:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Joycons::IsVibrationEnabled(const PadIdentifier& identifier) {
|
|
|
|
const auto handle = GetHandle(identifier);
|
|
|
|
if (handle == nullptr) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return handle->IsVibrationEnabled();
|
|
|
|
}
|
|
|
|
|
2022-12-26 18:11:01 +01:00
|
|
|
Common::Input::DriverResult Joycons::SetVibration(const PadIdentifier& identifier,
|
|
|
|
const Common::Input::VibrationStatus& vibration) {
|
2022-12-20 18:34:33 +01:00
|
|
|
const Joycon::VibrationValue native_vibration{
|
|
|
|
.low_amplitude = vibration.low_amplitude,
|
|
|
|
.low_frequency = vibration.low_frequency,
|
|
|
|
.high_amplitude = vibration.high_amplitude,
|
2022-12-21 03:27:34 +01:00
|
|
|
.high_frequency = vibration.high_frequency,
|
2022-12-20 18:34:33 +01:00
|
|
|
};
|
|
|
|
auto handle = GetHandle(identifier);
|
|
|
|
if (handle == nullptr) {
|
2022-12-26 18:11:01 +01:00
|
|
|
return Common::Input::DriverResult::InvalidHandle;
|
2022-12-20 18:34:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
handle->SetVibration(native_vibration);
|
2022-12-26 18:11:01 +01:00
|
|
|
return Common::Input::DriverResult::Success;
|
2022-12-20 18:34:33 +01:00
|
|
|
}
|
|
|
|
|
2022-12-26 18:11:01 +01:00
|
|
|
Common::Input::DriverResult Joycons::SetLeds(const PadIdentifier& identifier,
|
|
|
|
const Common::Input::LedStatus& led_status) {
|
2022-12-20 18:34:33 +01:00
|
|
|
auto handle = GetHandle(identifier);
|
|
|
|
if (handle == nullptr) {
|
2022-12-26 18:11:01 +01:00
|
|
|
return Common::Input::DriverResult::InvalidHandle;
|
2022-12-20 18:34:33 +01:00
|
|
|
}
|
|
|
|
int led_config = led_status.led_1 ? 1 : 0;
|
|
|
|
led_config += led_status.led_2 ? 2 : 0;
|
|
|
|
led_config += led_status.led_3 ? 4 : 0;
|
|
|
|
led_config += led_status.led_4 ? 8 : 0;
|
|
|
|
|
2022-12-26 18:11:01 +01:00
|
|
|
return static_cast<Common::Input::DriverResult>(
|
|
|
|
handle->SetLedConfig(static_cast<u8>(led_config)));
|
2022-12-20 18:34:33 +01:00
|
|
|
}
|
|
|
|
|
2022-12-28 22:21:12 +01:00
|
|
|
Common::Input::DriverResult Joycons::SetCameraFormat(const PadIdentifier& identifier,
|
2022-12-26 18:11:01 +01:00
|
|
|
Common::Input::CameraFormat camera_format) {
|
2022-12-28 22:21:12 +01:00
|
|
|
auto handle = GetHandle(identifier);
|
|
|
|
if (handle == nullptr) {
|
|
|
|
return Common::Input::DriverResult::InvalidHandle;
|
|
|
|
}
|
|
|
|
return static_cast<Common::Input::DriverResult>(handle->SetIrsConfig(
|
|
|
|
Joycon::IrsMode::ImageTransfer, static_cast<Joycon::IrsResolution>(camera_format)));
|
2022-12-20 18:34:33 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
Common::Input::NfcState Joycons::SupportsNfc(const PadIdentifier& identifier_) const {
|
|
|
|
return Common::Input::NfcState::Success;
|
|
|
|
};
|
|
|
|
|
|
|
|
Common::Input::NfcState Joycons::WriteNfcData(const PadIdentifier& identifier_,
|
|
|
|
const std::vector<u8>& data) {
|
|
|
|
return Common::Input::NfcState::NotSupported;
|
|
|
|
};
|
|
|
|
|
2022-12-26 18:11:01 +01:00
|
|
|
Common::Input::DriverResult Joycons::SetPollingMode(const PadIdentifier& identifier,
|
2022-12-20 18:34:33 +01:00
|
|
|
const Common::Input::PollingMode polling_mode) {
|
|
|
|
auto handle = GetHandle(identifier);
|
|
|
|
if (handle == nullptr) {
|
|
|
|
LOG_ERROR(Input, "Invalid handle {}", identifier.port);
|
2022-12-26 18:11:01 +01:00
|
|
|
return Common::Input::DriverResult::InvalidHandle;
|
2022-12-20 18:34:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (polling_mode) {
|
|
|
|
case Common::Input::PollingMode::Active:
|
2022-12-26 18:11:01 +01:00
|
|
|
return static_cast<Common::Input::DriverResult>(handle->SetActiveMode());
|
2022-12-20 18:34:33 +01:00
|
|
|
case Common::Input::PollingMode::Pasive:
|
2022-12-26 18:11:01 +01:00
|
|
|
return static_cast<Common::Input::DriverResult>(handle->SetPasiveMode());
|
2022-12-28 22:21:12 +01:00
|
|
|
case Common::Input::PollingMode::IR:
|
|
|
|
return static_cast<Common::Input::DriverResult>(handle->SetIrMode());
|
|
|
|
case Common::Input::PollingMode::NFC:
|
|
|
|
return static_cast<Common::Input::DriverResult>(handle->SetNfcMode());
|
2022-12-20 18:34:33 +01:00
|
|
|
case Common::Input::PollingMode::Ring:
|
2022-12-26 18:11:01 +01:00
|
|
|
return static_cast<Common::Input::DriverResult>(handle->SetRingConMode());
|
2022-12-20 18:34:33 +01:00
|
|
|
default:
|
2022-12-26 18:11:01 +01:00
|
|
|
return Common::Input::DriverResult::NotSupported;
|
2022-12-20 18:34:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Joycons::OnBatteryUpdate(std::size_t port, Joycon::ControllerType type,
|
|
|
|
Joycon::Battery value) {
|
|
|
|
const auto identifier = GetIdentifier(port, type);
|
|
|
|
if (value.charging != 0) {
|
|
|
|
SetBattery(identifier, Common::Input::BatteryLevel::Charging);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-12-23 15:32:02 +01:00
|
|
|
Common::Input::BatteryLevel battery{};
|
2022-12-20 18:34:33 +01:00
|
|
|
switch (value.status) {
|
|
|
|
case 0:
|
|
|
|
battery = Common::Input::BatteryLevel::Empty;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
battery = Common::Input::BatteryLevel::Critical;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
battery = Common::Input::BatteryLevel::Low;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
battery = Common::Input::BatteryLevel::Medium;
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
default:
|
|
|
|
battery = Common::Input::BatteryLevel::Full;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
SetBattery(identifier, battery);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Joycons::OnColorUpdate(std::size_t port, Joycon::ControllerType type,
|
2022-12-20 20:23:31 +01:00
|
|
|
const Joycon::Color& value) {
|
|
|
|
const auto identifier = GetIdentifier(port, type);
|
|
|
|
Common::Input::BodyColorStatus color{
|
|
|
|
.body = value.body,
|
|
|
|
.buttons = value.buttons,
|
|
|
|
.left_grip = value.left_grip,
|
|
|
|
.right_grip = value.right_grip,
|
|
|
|
};
|
|
|
|
SetColor(identifier, color);
|
|
|
|
}
|
2022-12-20 18:34:33 +01:00
|
|
|
|
|
|
|
void Joycons::OnButtonUpdate(std::size_t port, Joycon::ControllerType type, int id, bool value) {
|
|
|
|
const auto identifier = GetIdentifier(port, type);
|
|
|
|
SetButton(identifier, id, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Joycons::OnStickUpdate(std::size_t port, Joycon::ControllerType type, int id, f32 value) {
|
|
|
|
const auto identifier = GetIdentifier(port, type);
|
|
|
|
SetAxis(identifier, id, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Joycons::OnMotionUpdate(std::size_t port, Joycon::ControllerType type, int id,
|
|
|
|
const Joycon::MotionData& value) {
|
|
|
|
const auto identifier = GetIdentifier(port, type);
|
|
|
|
BasicMotion motion_data{
|
|
|
|
.gyro_x = value.gyro_x,
|
|
|
|
.gyro_y = value.gyro_y,
|
|
|
|
.gyro_z = value.gyro_z,
|
|
|
|
.accel_x = value.accel_x,
|
|
|
|
.accel_y = value.accel_y,
|
|
|
|
.accel_z = value.accel_z,
|
|
|
|
.delta_timestamp = 15000,
|
|
|
|
};
|
|
|
|
SetMotion(identifier, id, motion_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Joycons::OnRingConUpdate(f32 ring_data) {
|
|
|
|
// To simplify ring detection it will always be mapped to an empty identifier for all
|
|
|
|
// controllers
|
|
|
|
constexpr PadIdentifier identifier = {
|
|
|
|
.guid = Common::UUID{},
|
|
|
|
.port = 0,
|
|
|
|
.pad = 0,
|
|
|
|
};
|
|
|
|
SetAxis(identifier, 100, ring_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Joycons::OnAmiiboUpdate(std::size_t port, const std::vector<u8>& amiibo_data) {
|
|
|
|
const auto identifier = GetIdentifier(port, Joycon::ControllerType::Right);
|
2022-12-22 08:07:46 +01:00
|
|
|
const auto nfc_state = amiibo_data.empty() ? Common::Input::NfcState::AmiiboRemoved
|
|
|
|
: Common::Input::NfcState::NewAmiibo;
|
|
|
|
SetNfc(identifier, {nfc_state, amiibo_data});
|
2022-12-20 18:34:33 +01:00
|
|
|
}
|
|
|
|
|
2022-12-28 22:21:12 +01:00
|
|
|
void Joycons::OnCameraUpdate(std::size_t port, const std::vector<u8>& camera_data,
|
|
|
|
Joycon::IrsResolution format) {
|
|
|
|
const auto identifier = GetIdentifier(port, Joycon::ControllerType::Right);
|
|
|
|
SetCamera(identifier, {static_cast<Common::Input::CameraFormat>(format), camera_data});
|
|
|
|
}
|
|
|
|
|
2022-12-20 18:34:33 +01:00
|
|
|
std::shared_ptr<Joycon::JoyconDriver> Joycons::GetHandle(PadIdentifier identifier) const {
|
|
|
|
auto is_handle_active = [&](std::shared_ptr<Joycon::JoyconDriver> device) {
|
|
|
|
if (!device) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!device->IsConnected()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (device->GetDevicePort() == identifier.port) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
const auto type = static_cast<Joycon::ControllerType>(identifier.pad);
|
2023-01-14 06:29:05 +01:00
|
|
|
|
2022-12-20 18:34:33 +01:00
|
|
|
if (type == Joycon::ControllerType::Left) {
|
2023-01-14 06:29:05 +01:00
|
|
|
const auto matching_device = std::ranges::find_if(
|
|
|
|
left_joycons, [is_handle_active](auto& device) { return is_handle_active(device); });
|
|
|
|
|
|
|
|
if (matching_device != left_joycons.end()) {
|
|
|
|
return *matching_device;
|
2022-12-20 18:34:33 +01:00
|
|
|
}
|
|
|
|
}
|
2023-01-14 06:29:05 +01:00
|
|
|
|
2022-12-20 18:34:33 +01:00
|
|
|
if (type == Joycon::ControllerType::Right) {
|
2023-01-14 06:29:05 +01:00
|
|
|
const auto matching_device = std::ranges::find_if(
|
|
|
|
right_joycons, [is_handle_active](auto& device) { return is_handle_active(device); });
|
|
|
|
|
|
|
|
if (matching_device != right_joycons.end()) {
|
|
|
|
return *matching_device;
|
2022-12-20 18:34:33 +01:00
|
|
|
}
|
|
|
|
}
|
2023-01-14 06:29:05 +01:00
|
|
|
|
2023-02-10 02:59:12 +01:00
|
|
|
if (type == Joycon::ControllerType::Pro) {
|
|
|
|
const auto matching_device = std::ranges::find_if(
|
|
|
|
pro_controller, [is_handle_active](auto& device) { return is_handle_active(device); });
|
|
|
|
|
|
|
|
if (matching_device != pro_controller.end()) {
|
|
|
|
return *matching_device;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-20 18:34:33 +01:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
PadIdentifier Joycons::GetIdentifier(std::size_t port, Joycon::ControllerType type) const {
|
2023-01-14 07:36:03 +01:00
|
|
|
const std::array<u8, 16> guid{0, 0, 0, 0, 0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, static_cast<u8>(type)};
|
2022-12-20 18:34:33 +01:00
|
|
|
return {
|
2023-01-14 07:36:03 +01:00
|
|
|
.guid = Common::UUID{guid},
|
2022-12-20 18:34:33 +01:00
|
|
|
.port = port,
|
|
|
|
.pad = static_cast<std::size_t>(type),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-01-14 07:36:03 +01:00
|
|
|
Common::ParamPackage Joycons::GetParamPackage(std::size_t port, Joycon::ControllerType type) const {
|
|
|
|
const auto identifier = GetIdentifier(port, type);
|
|
|
|
return {
|
|
|
|
{"engine", GetEngineName()},
|
|
|
|
{"guid", identifier.guid.RawString()},
|
|
|
|
{"port", std::to_string(identifier.port)},
|
|
|
|
{"pad", std::to_string(identifier.pad)},
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-12-20 18:34:33 +01:00
|
|
|
std::vector<Common::ParamPackage> Joycons::GetInputDevices() const {
|
|
|
|
std::vector<Common::ParamPackage> devices{};
|
|
|
|
|
|
|
|
auto add_entry = [&](std::shared_ptr<Joycon::JoyconDriver> device) {
|
|
|
|
if (!device) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!device->IsConnected()) {
|
|
|
|
return;
|
|
|
|
}
|
2023-01-14 07:36:03 +01:00
|
|
|
auto param = GetParamPackage(device->GetDevicePort(), device->GetHandleDeviceType());
|
2022-12-20 18:34:33 +01:00
|
|
|
std::string name = fmt::format("{} {}", JoyconName(device->GetHandleDeviceType()),
|
2022-12-21 17:44:23 +01:00
|
|
|
device->GetDevicePort() + 1);
|
2023-01-14 07:36:03 +01:00
|
|
|
param.Set("display", std::move(name));
|
|
|
|
devices.emplace_back(param);
|
2022-12-20 18:34:33 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
for (const auto& controller : left_joycons) {
|
|
|
|
add_entry(controller);
|
|
|
|
}
|
|
|
|
for (const auto& controller : right_joycons) {
|
|
|
|
add_entry(controller);
|
|
|
|
}
|
2023-02-10 02:59:12 +01:00
|
|
|
for (const auto& controller : pro_controller) {
|
|
|
|
add_entry(controller);
|
|
|
|
}
|
2022-12-20 18:34:33 +01:00
|
|
|
|
2022-12-21 17:44:23 +01:00
|
|
|
// List dual joycon pairs
|
|
|
|
for (std::size_t i = 0; i < MaxSupportedControllers; i++) {
|
|
|
|
if (!left_joycons[i] || !right_joycons[i]) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!left_joycons[i]->IsConnected() || !right_joycons[i]->IsConnected()) {
|
|
|
|
continue;
|
|
|
|
}
|
2023-01-14 07:36:03 +01:00
|
|
|
auto main_param = GetParamPackage(i, left_joycons[i]->GetHandleDeviceType());
|
|
|
|
const auto second_param = GetParamPackage(i, right_joycons[i]->GetHandleDeviceType());
|
|
|
|
const auto type = Joycon::ControllerType::Dual;
|
2022-12-21 17:44:23 +01:00
|
|
|
std::string name = fmt::format("{} {}", JoyconName(type), i + 1);
|
2023-01-14 07:36:03 +01:00
|
|
|
|
|
|
|
main_param.Set("display", std::move(name));
|
|
|
|
main_param.Set("guid2", second_param.Get("guid", ""));
|
|
|
|
main_param.Set("pad", std::to_string(static_cast<size_t>(type)));
|
|
|
|
devices.emplace_back(main_param);
|
2022-12-21 17:44:23 +01:00
|
|
|
}
|
|
|
|
|
2022-12-20 18:34:33 +01:00
|
|
|
return devices;
|
|
|
|
}
|
|
|
|
|
|
|
|
ButtonMapping Joycons::GetButtonMappingForDevice(const Common::ParamPackage& params) {
|
2022-12-21 17:44:23 +01:00
|
|
|
static constexpr std::array<std::tuple<Settings::NativeButton::Values, Joycon::PadButton, bool>,
|
|
|
|
18>
|
2022-12-20 18:34:33 +01:00
|
|
|
switch_to_joycon_button = {
|
2022-12-21 17:44:23 +01:00
|
|
|
std::tuple{Settings::NativeButton::A, Joycon::PadButton::A, true},
|
|
|
|
{Settings::NativeButton::B, Joycon::PadButton::B, true},
|
|
|
|
{Settings::NativeButton::X, Joycon::PadButton::X, true},
|
|
|
|
{Settings::NativeButton::Y, Joycon::PadButton::Y, true},
|
|
|
|
{Settings::NativeButton::DLeft, Joycon::PadButton::Left, false},
|
|
|
|
{Settings::NativeButton::DUp, Joycon::PadButton::Up, false},
|
|
|
|
{Settings::NativeButton::DRight, Joycon::PadButton::Right, false},
|
|
|
|
{Settings::NativeButton::DDown, Joycon::PadButton::Down, false},
|
|
|
|
{Settings::NativeButton::L, Joycon::PadButton::L, false},
|
|
|
|
{Settings::NativeButton::R, Joycon::PadButton::R, true},
|
|
|
|
{Settings::NativeButton::ZL, Joycon::PadButton::ZL, false},
|
|
|
|
{Settings::NativeButton::ZR, Joycon::PadButton::ZR, true},
|
|
|
|
{Settings::NativeButton::Plus, Joycon::PadButton::Plus, true},
|
|
|
|
{Settings::NativeButton::Minus, Joycon::PadButton::Minus, false},
|
|
|
|
{Settings::NativeButton::Home, Joycon::PadButton::Home, true},
|
|
|
|
{Settings::NativeButton::Screenshot, Joycon::PadButton::Capture, false},
|
|
|
|
{Settings::NativeButton::LStick, Joycon::PadButton::StickL, false},
|
|
|
|
{Settings::NativeButton::RStick, Joycon::PadButton::StickR, true},
|
2022-12-20 18:34:33 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
if (!params.Has("port")) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
ButtonMapping mapping{};
|
2022-12-21 17:44:23 +01:00
|
|
|
for (const auto& [switch_button, joycon_button, side] : switch_to_joycon_button) {
|
2023-01-14 07:36:03 +01:00
|
|
|
const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
|
|
|
|
auto pad = static_cast<Joycon::ControllerType>(params.Get("pad", 0));
|
|
|
|
if (pad == Joycon::ControllerType::Dual) {
|
|
|
|
pad = side ? Joycon::ControllerType::Right : Joycon::ControllerType::Left;
|
2022-12-21 17:44:23 +01:00
|
|
|
}
|
|
|
|
|
2023-01-14 07:36:03 +01:00
|
|
|
Common::ParamPackage button_params = GetParamPackage(port, pad);
|
2022-12-20 18:34:33 +01:00
|
|
|
button_params.Set("button", static_cast<int>(joycon_button));
|
|
|
|
mapping.insert_or_assign(switch_button, std::move(button_params));
|
|
|
|
}
|
|
|
|
|
2022-12-21 17:44:23 +01:00
|
|
|
// Map SL and SR buttons for left joycons
|
|
|
|
if (params.Get("pad", 0) == static_cast<int>(Joycon::ControllerType::Left)) {
|
2023-01-14 07:36:03 +01:00
|
|
|
const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
|
|
|
|
Common::ParamPackage button_params = GetParamPackage(port, Joycon::ControllerType::Left);
|
2022-12-21 17:44:23 +01:00
|
|
|
|
|
|
|
Common::ParamPackage sl_button_params = button_params;
|
|
|
|
Common::ParamPackage sr_button_params = button_params;
|
|
|
|
sl_button_params.Set("button", static_cast<int>(Joycon::PadButton::LeftSL));
|
|
|
|
sr_button_params.Set("button", static_cast<int>(Joycon::PadButton::LeftSR));
|
|
|
|
mapping.insert_or_assign(Settings::NativeButton::SL, std::move(sl_button_params));
|
|
|
|
mapping.insert_or_assign(Settings::NativeButton::SR, std::move(sr_button_params));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Map SL and SR buttons for right joycons
|
|
|
|
if (params.Get("pad", 0) == static_cast<int>(Joycon::ControllerType::Right)) {
|
2023-01-14 07:36:03 +01:00
|
|
|
const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
|
|
|
|
Common::ParamPackage button_params = GetParamPackage(port, Joycon::ControllerType::Right);
|
2022-12-21 17:44:23 +01:00
|
|
|
|
|
|
|
Common::ParamPackage sl_button_params = button_params;
|
|
|
|
Common::ParamPackage sr_button_params = button_params;
|
|
|
|
sl_button_params.Set("button", static_cast<int>(Joycon::PadButton::RightSL));
|
|
|
|
sr_button_params.Set("button", static_cast<int>(Joycon::PadButton::RightSR));
|
|
|
|
mapping.insert_or_assign(Settings::NativeButton::SL, std::move(sl_button_params));
|
|
|
|
mapping.insert_or_assign(Settings::NativeButton::SR, std::move(sr_button_params));
|
|
|
|
}
|
|
|
|
|
2022-12-20 18:34:33 +01:00
|
|
|
return mapping;
|
|
|
|
}
|
|
|
|
|
|
|
|
AnalogMapping Joycons::GetAnalogMappingForDevice(const Common::ParamPackage& params) {
|
|
|
|
if (!params.Has("port")) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2023-01-14 07:36:03 +01:00
|
|
|
const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
|
|
|
|
auto pad_left = static_cast<Joycon::ControllerType>(params.Get("pad", 0));
|
|
|
|
auto pad_right = pad_left;
|
|
|
|
if (pad_left == Joycon::ControllerType::Dual) {
|
|
|
|
pad_left = Joycon::ControllerType::Left;
|
|
|
|
pad_right = Joycon::ControllerType::Right;
|
2022-12-21 17:44:23 +01:00
|
|
|
}
|
|
|
|
|
2022-12-20 18:34:33 +01:00
|
|
|
AnalogMapping mapping = {};
|
2023-01-14 07:36:03 +01:00
|
|
|
Common::ParamPackage left_analog_params = GetParamPackage(port, pad_left);
|
2022-12-20 18:34:33 +01:00
|
|
|
left_analog_params.Set("axis_x", static_cast<int>(Joycon::PadAxes::LeftStickX));
|
|
|
|
left_analog_params.Set("axis_y", static_cast<int>(Joycon::PadAxes::LeftStickY));
|
|
|
|
mapping.insert_or_assign(Settings::NativeAnalog::LStick, std::move(left_analog_params));
|
2023-01-14 07:36:03 +01:00
|
|
|
Common::ParamPackage right_analog_params = GetParamPackage(port, pad_right);
|
2022-12-20 18:34:33 +01:00
|
|
|
right_analog_params.Set("axis_x", static_cast<int>(Joycon::PadAxes::RightStickX));
|
|
|
|
right_analog_params.Set("axis_y", static_cast<int>(Joycon::PadAxes::RightStickY));
|
|
|
|
mapping.insert_or_assign(Settings::NativeAnalog::RStick, std::move(right_analog_params));
|
|
|
|
return mapping;
|
|
|
|
}
|
|
|
|
|
|
|
|
MotionMapping Joycons::GetMotionMappingForDevice(const Common::ParamPackage& params) {
|
|
|
|
if (!params.Has("port")) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2023-01-14 07:36:03 +01:00
|
|
|
const std::size_t port = static_cast<std::size_t>(params.Get("port", 0));
|
|
|
|
auto pad_left = static_cast<Joycon::ControllerType>(params.Get("pad", 0));
|
|
|
|
auto pad_right = pad_left;
|
|
|
|
if (pad_left == Joycon::ControllerType::Dual) {
|
|
|
|
pad_left = Joycon::ControllerType::Left;
|
|
|
|
pad_right = Joycon::ControllerType::Right;
|
2022-12-21 17:44:23 +01:00
|
|
|
}
|
|
|
|
|
2022-12-20 18:34:33 +01:00
|
|
|
MotionMapping mapping = {};
|
2023-01-14 07:36:03 +01:00
|
|
|
Common::ParamPackage left_motion_params = GetParamPackage(port, pad_left);
|
2022-12-20 18:34:33 +01:00
|
|
|
left_motion_params.Set("motion", 0);
|
|
|
|
mapping.insert_or_assign(Settings::NativeMotion::MotionLeft, std::move(left_motion_params));
|
2023-01-14 07:36:03 +01:00
|
|
|
Common::ParamPackage right_Motion_params = GetParamPackage(port, pad_right);
|
2022-12-20 18:34:33 +01:00
|
|
|
right_Motion_params.Set("motion", 1);
|
|
|
|
mapping.insert_or_assign(Settings::NativeMotion::MotionRight, std::move(right_Motion_params));
|
|
|
|
return mapping;
|
|
|
|
}
|
|
|
|
|
|
|
|
Common::Input::ButtonNames Joycons::GetUIButtonName(const Common::ParamPackage& params) const {
|
|
|
|
const auto button = static_cast<Joycon::PadButton>(params.Get("button", 0));
|
|
|
|
switch (button) {
|
|
|
|
case Joycon::PadButton::Left:
|
|
|
|
return Common::Input::ButtonNames::ButtonLeft;
|
|
|
|
case Joycon::PadButton::Right:
|
|
|
|
return Common::Input::ButtonNames::ButtonRight;
|
|
|
|
case Joycon::PadButton::Down:
|
|
|
|
return Common::Input::ButtonNames::ButtonDown;
|
|
|
|
case Joycon::PadButton::Up:
|
|
|
|
return Common::Input::ButtonNames::ButtonUp;
|
|
|
|
case Joycon::PadButton::LeftSL:
|
|
|
|
case Joycon::PadButton::RightSL:
|
|
|
|
return Common::Input::ButtonNames::TriggerSL;
|
|
|
|
case Joycon::PadButton::LeftSR:
|
|
|
|
case Joycon::PadButton::RightSR:
|
|
|
|
return Common::Input::ButtonNames::TriggerSR;
|
|
|
|
case Joycon::PadButton::L:
|
|
|
|
return Common::Input::ButtonNames::TriggerL;
|
|
|
|
case Joycon::PadButton::R:
|
|
|
|
return Common::Input::ButtonNames::TriggerR;
|
|
|
|
case Joycon::PadButton::ZL:
|
|
|
|
return Common::Input::ButtonNames::TriggerZL;
|
|
|
|
case Joycon::PadButton::ZR:
|
|
|
|
return Common::Input::ButtonNames::TriggerZR;
|
|
|
|
case Joycon::PadButton::A:
|
|
|
|
return Common::Input::ButtonNames::ButtonA;
|
|
|
|
case Joycon::PadButton::B:
|
|
|
|
return Common::Input::ButtonNames::ButtonB;
|
|
|
|
case Joycon::PadButton::X:
|
|
|
|
return Common::Input::ButtonNames::ButtonX;
|
|
|
|
case Joycon::PadButton::Y:
|
|
|
|
return Common::Input::ButtonNames::ButtonY;
|
|
|
|
case Joycon::PadButton::Plus:
|
|
|
|
return Common::Input::ButtonNames::ButtonPlus;
|
|
|
|
case Joycon::PadButton::Minus:
|
|
|
|
return Common::Input::ButtonNames::ButtonMinus;
|
|
|
|
case Joycon::PadButton::Home:
|
|
|
|
return Common::Input::ButtonNames::ButtonHome;
|
|
|
|
case Joycon::PadButton::Capture:
|
|
|
|
return Common::Input::ButtonNames::ButtonCapture;
|
|
|
|
case Joycon::PadButton::StickL:
|
|
|
|
return Common::Input::ButtonNames::ButtonStickL;
|
|
|
|
case Joycon::PadButton::StickR:
|
|
|
|
return Common::Input::ButtonNames::ButtonStickR;
|
|
|
|
default:
|
|
|
|
return Common::Input::ButtonNames::Undefined;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Common::Input::ButtonNames Joycons::GetUIName(const Common::ParamPackage& params) const {
|
|
|
|
if (params.Has("button")) {
|
|
|
|
return GetUIButtonName(params);
|
|
|
|
}
|
|
|
|
if (params.Has("axis")) {
|
|
|
|
return Common::Input::ButtonNames::Value;
|
|
|
|
}
|
|
|
|
if (params.Has("motion")) {
|
|
|
|
return Common::Input::ButtonNames::Engine;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Common::Input::ButtonNames::Invalid;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string Joycons::JoyconName(Joycon::ControllerType type) const {
|
|
|
|
switch (type) {
|
|
|
|
case Joycon::ControllerType::Left:
|
|
|
|
return "Left Joycon";
|
|
|
|
case Joycon::ControllerType::Right:
|
|
|
|
return "Right Joycon";
|
|
|
|
case Joycon::ControllerType::Pro:
|
|
|
|
return "Pro Controller";
|
2022-12-21 17:44:23 +01:00
|
|
|
case Joycon::ControllerType::Dual:
|
|
|
|
return "Dual Joycon";
|
2022-12-20 18:34:33 +01:00
|
|
|
default:
|
2023-01-28 03:35:51 +01:00
|
|
|
return "Unknown Switch Controller";
|
2022-12-20 18:34:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} // namespace InputCommon
|