input_common: Implement custom joycon driver
This commit is contained in:
@@ -183,17 +183,31 @@ enum class ButtonNames {
|
|||||||
Engine,
|
Engine,
|
||||||
// This will display the button by value instead of the button name
|
// This will display the button by value instead of the button name
|
||||||
Value,
|
Value,
|
||||||
|
|
||||||
|
// Joycon button names
|
||||||
ButtonLeft,
|
ButtonLeft,
|
||||||
ButtonRight,
|
ButtonRight,
|
||||||
ButtonDown,
|
ButtonDown,
|
||||||
ButtonUp,
|
ButtonUp,
|
||||||
TriggerZ,
|
|
||||||
TriggerR,
|
|
||||||
TriggerL,
|
|
||||||
ButtonA,
|
ButtonA,
|
||||||
ButtonB,
|
ButtonB,
|
||||||
ButtonX,
|
ButtonX,
|
||||||
ButtonY,
|
ButtonY,
|
||||||
|
ButtonPlus,
|
||||||
|
ButtonMinus,
|
||||||
|
ButtonHome,
|
||||||
|
ButtonCapture,
|
||||||
|
ButtonStickL,
|
||||||
|
ButtonStickR,
|
||||||
|
TriggerL,
|
||||||
|
TriggerZL,
|
||||||
|
TriggerSL,
|
||||||
|
TriggerR,
|
||||||
|
TriggerZR,
|
||||||
|
TriggerSR,
|
||||||
|
|
||||||
|
// GC button names
|
||||||
|
TriggerZ,
|
||||||
ButtonStart,
|
ButtonStart,
|
||||||
|
|
||||||
// DS4 button names
|
// DS4 button names
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ add_library(input_common STATIC
|
|||||||
drivers/keyboard.h
|
drivers/keyboard.h
|
||||||
drivers/mouse.cpp
|
drivers/mouse.cpp
|
||||||
drivers/mouse.h
|
drivers/mouse.h
|
||||||
drivers/sdl_driver.cpp
|
|
||||||
drivers/sdl_driver.h
|
|
||||||
drivers/tas_input.cpp
|
drivers/tas_input.cpp
|
||||||
drivers/tas_input.h
|
drivers/tas_input.h
|
||||||
drivers/touch_screen.cpp
|
drivers/touch_screen.cpp
|
||||||
@@ -53,11 +51,15 @@ endif()
|
|||||||
|
|
||||||
if (ENABLE_SDL2)
|
if (ENABLE_SDL2)
|
||||||
target_sources(input_common PRIVATE
|
target_sources(input_common PRIVATE
|
||||||
|
drivers/joycon.cpp
|
||||||
|
drivers/joycon.h
|
||||||
drivers/sdl_driver.cpp
|
drivers/sdl_driver.cpp
|
||||||
drivers/sdl_driver.h
|
drivers/sdl_driver.h
|
||||||
|
helpers/joycon_protocol.cpp
|
||||||
|
helpers/joycon_protocol.h
|
||||||
)
|
)
|
||||||
target_link_libraries(input_common PRIVATE SDL2)
|
|
||||||
target_compile_definitions(input_common PRIVATE HAVE_SDL2)
|
target_compile_definitions(input_common PRIVATE HAVE_SDL2)
|
||||||
|
target_link_libraries(input_common PRIVATE SDL2)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_link_libraries(input_common PRIVATE usb)
|
target_link_libraries(input_common PRIVATE usb)
|
||||||
|
|||||||
583
src/input_common/drivers/joycon.cpp
Normal file
583
src/input_common/drivers/joycon.cpp
Normal file
@@ -0,0 +1,583 @@
|
|||||||
|
// Copyright 2021 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
#include "common/param_package.h"
|
||||||
|
#include "common/settings_input.h"
|
||||||
|
#include "common/thread.h"
|
||||||
|
#include "input_common/drivers/joycon.h"
|
||||||
|
|
||||||
|
namespace InputCommon {
|
||||||
|
|
||||||
|
Joycons::Joycons(const std::string& input_engine_) : InputEngine(input_engine_) {
|
||||||
|
LOG_INFO(Input, "JC Adapter 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::UpdateJoyconData(JoyconData& jc, std::span<const u8> buffer) {
|
||||||
|
switch (jc.type) {
|
||||||
|
case Joycon::ControllerType::Left:
|
||||||
|
UpdateLeftPadInput(jc, buffer);
|
||||||
|
break;
|
||||||
|
case Joycon::ControllerType::Right:
|
||||||
|
UpdateRightPadInput(jc, buffer);
|
||||||
|
break;
|
||||||
|
case Joycon::ControllerType::Pro:
|
||||||
|
UpdateProPadInput(jc, buffer);
|
||||||
|
break;
|
||||||
|
case Joycon::ControllerType::None:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
jc.raw_battery = buffer[2] >> 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Joycons::JoyconName(std::size_t port) const {
|
||||||
|
switch (joycon_data[port].type) {
|
||||||
|
case Joycon::ControllerType::Left:
|
||||||
|
return "Left Joycon";
|
||||||
|
case Joycon::ControllerType::Right:
|
||||||
|
return "Right Joycon";
|
||||||
|
case Joycon::ControllerType::Pro:
|
||||||
|
return "Pro Controller";
|
||||||
|
default:
|
||||||
|
return "Unknow Joycon";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Joycons::ResetDeviceType(std::size_t port) {
|
||||||
|
if (port > joycon_data.size()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
joycon_data[port].type = Joycon::ControllerType::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Joycons::InputThread(std::stop_token stop_token) {
|
||||||
|
LOG_INFO(Input, "JC Adapter scan thread started");
|
||||||
|
Common::SetCurrentThreadName("yuzu:input:JoyconInputThread");
|
||||||
|
input_thread_running = true;
|
||||||
|
std::vector<u8> buffer(Joycon::max_resp_size);
|
||||||
|
|
||||||
|
while (!stop_token.stop_requested()) {
|
||||||
|
for (std::size_t port = 0; port < joycon_data.size(); ++port) {
|
||||||
|
if (joycon_data[port].type == Joycon::ControllerType::None) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const int status = SDL_hid_read_timeout(joycon_data[port].joycon_handle.handle,
|
||||||
|
buffer.data(), Joycon::max_resp_size, 15);
|
||||||
|
|
||||||
|
if (IsPayloadCorrect(status, buffer)) {
|
||||||
|
UpdateJoyconData(joycon_data[port], buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::this_thread::yield();
|
||||||
|
}
|
||||||
|
input_thread_running = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Joycons::IsPayloadCorrect(int status, std::span<const u8> buffer) {
|
||||||
|
if (status > 0) {
|
||||||
|
if (buffer[0] == 0x00) {
|
||||||
|
// Invalid response
|
||||||
|
LOG_DEBUG(Input, "Error reading payload");
|
||||||
|
if (input_error_counter++ > 20) {
|
||||||
|
LOG_ERROR(Input, "Timeout, Is the joycon connected?");
|
||||||
|
input_thread.request_stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
input_error_counter = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
void Joycons::Setup() {
|
||||||
|
// Initialize all controllers as unplugged
|
||||||
|
for (std::size_t port = 0; port < joycon_data.size(); ++port) {
|
||||||
|
PreSetController(GetIdentifier(port));
|
||||||
|
joycon_data[port].type = Joycon::ControllerType::None;
|
||||||
|
joycon_data[port].hd_rumble.high_amplitude = 0;
|
||||||
|
joycon_data[port].hd_rumble.low_amplitude = 0;
|
||||||
|
joycon_data[port].hd_rumble.high_frequency = 320.0f;
|
||||||
|
joycon_data[port].hd_rumble.low_frequency = 160.0f;
|
||||||
|
}
|
||||||
|
if (!scan_thread_running) {
|
||||||
|
scan_thread = std::jthread([this](std::stop_token stop_token) { ScanThread(stop_token); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Joycons::ScanThread(std::stop_token stop_token) {
|
||||||
|
Common::SetCurrentThreadName("yuzu:input:JoyconScanThread");
|
||||||
|
scan_thread_running = true;
|
||||||
|
std::size_t port = 0;
|
||||||
|
while (!stop_token.stop_requested()) {
|
||||||
|
LOG_INFO(Input, "Scanning for devices");
|
||||||
|
SDL_hid_device_info* devs = SDL_hid_enumerate(0x057e, 0x0);
|
||||||
|
SDL_hid_device_info* cur_dev = devs;
|
||||||
|
|
||||||
|
while (cur_dev) {
|
||||||
|
LOG_WARNING(Input, "Device Found");
|
||||||
|
LOG_WARNING(Input, "type : {:04X} {:04X}", cur_dev->vendor_id, cur_dev->product_id);
|
||||||
|
bool skip_device = false;
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < joycon_data.size(); ++i) {
|
||||||
|
if (Joycon::GetDeviceType(cur_dev) == joycon_data[i].type) {
|
||||||
|
LOG_WARNING(Input, "Device already exist");
|
||||||
|
skip_device = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!skip_device) {
|
||||||
|
if (Joycon::CheckDeviceAccess(joycon_data[port].joycon_handle, cur_dev)) {
|
||||||
|
LOG_WARNING(Input, "Device verified and accessible");
|
||||||
|
++port;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cur_dev = cur_dev->next;
|
||||||
|
}
|
||||||
|
GetJCEndpoint();
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||||
|
}
|
||||||
|
scan_thread_running = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Joycons::Reset() {
|
||||||
|
scan_thread = {};
|
||||||
|
input_thread = {};
|
||||||
|
|
||||||
|
for (std::size_t port = 0; port < joycon_data.size(); ++port) {
|
||||||
|
joycon_data[port].type = Joycon::ControllerType::None;
|
||||||
|
if (joycon_data[port].joycon_handle.handle) {
|
||||||
|
joycon_data[port].joycon_handle.handle = nullptr;
|
||||||
|
joycon_data[port].joycon_handle.packet_counter = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_hid_exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Joycons::GetJCEndpoint() {
|
||||||
|
// init joycons
|
||||||
|
for (std::size_t port = 0; port < joycon_data.size(); ++port) {
|
||||||
|
auto& joycon = joycon_data[port];
|
||||||
|
auto& handle = joycon.joycon_handle;
|
||||||
|
joycon.port = port;
|
||||||
|
if (!joycon.joycon_handle.handle) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
LOG_INFO(Input, "Initializing device {}", port);
|
||||||
|
|
||||||
|
joycon.type = Joycon::GetDeviceType(handle);
|
||||||
|
// joycon_data[port].mac= Joycon::GetMac();
|
||||||
|
// SetSerialNumber(joycon_data[port]);
|
||||||
|
// SetVersionNumber(joycon_data[port]);
|
||||||
|
// SetDeviceType(joycon_data[port]);
|
||||||
|
joycon.calibration = Joycon::GetFactoryCalibrationData(handle, joycon.type);
|
||||||
|
// GetColor(joycon_data[port]);
|
||||||
|
|
||||||
|
joycon.rumble_enabled = true;
|
||||||
|
joycon.imu_enabled = true;
|
||||||
|
joycon.gyro_sensitivity = Joycon::GyroSensitivity::DPS2000;
|
||||||
|
joycon.gyro_performance = Joycon::GyroPerformance::HZ833;
|
||||||
|
joycon.accelerometer_sensitivity = Joycon::AccelerometerSensitivity::G8;
|
||||||
|
joycon.accelerometer_performance = Joycon::AccelerometerPerformance::HZ100;
|
||||||
|
|
||||||
|
Joycon::SetLedConfig(handle, static_cast<u8>(port + 1));
|
||||||
|
Joycon::EnableRumble(handle, joycon.rumble_enabled);
|
||||||
|
Joycon::EnableImu(handle, joycon.imu_enabled);
|
||||||
|
Joycon::SetImuConfig(handle, joycon.gyro_sensitivity, joycon.gyro_performance,
|
||||||
|
joycon.accelerometer_sensitivity, joycon.accelerometer_performance);
|
||||||
|
Joycon::SetReportMode(handle, Joycon::ReportMode::STANDARD_FULL_60HZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!input_thread_running) {
|
||||||
|
input_thread =
|
||||||
|
std::jthread([this](std::stop_token stop_token) { InputThread(stop_token); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Joycons::DeviceConnected(std::size_t port) const {
|
||||||
|
if (port > joycon_data.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return joycon_data[port].type != Joycon::ControllerType::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
f32 Joycons::GetAxisValue(u16 raw_value, Joycon::JoyStickAxisCalibration calibration) const {
|
||||||
|
const f32 value = static_cast<f32>(raw_value - calibration.center);
|
||||||
|
if (value > 0.0f) {
|
||||||
|
return value / calibration.max;
|
||||||
|
}
|
||||||
|
return value / calibration.min;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Joycons::UpdateLeftPadInput(JoyconData& jc, std::span<const u8> buffer) {
|
||||||
|
static constexpr std::array<Joycon::PadButton, 11> left_buttons{
|
||||||
|
Joycon::PadButton::Down, Joycon::PadButton::Up, Joycon::PadButton::Right,
|
||||||
|
Joycon::PadButton::Left, Joycon::PadButton::LeftSL, Joycon::PadButton::LeftSR,
|
||||||
|
Joycon::PadButton::L, Joycon::PadButton::ZL, Joycon::PadButton::Minus,
|
||||||
|
Joycon::PadButton::Capture, Joycon::PadButton::StickL,
|
||||||
|
};
|
||||||
|
const auto identifier = GetIdentifier(jc.port);
|
||||||
|
|
||||||
|
const u32 raw_button = static_cast<u32>(buffer[5] | ((buffer[4] & 0b00101001) << 16));
|
||||||
|
for (std::size_t i = 0; i < left_buttons.size(); ++i) {
|
||||||
|
const bool button_status = (raw_button & static_cast<u32>(left_buttons[i])) != 0;
|
||||||
|
const int button = static_cast<int>(left_buttons[i]);
|
||||||
|
SetButton(identifier, button, button_status);
|
||||||
|
}
|
||||||
|
|
||||||
|
const u16 raw_left_axis_x = static_cast<u16>(buffer[6] | ((buffer[7] & 0xf) << 8));
|
||||||
|
const u16 raw_left_axis_y = static_cast<u16>((buffer[7] >> 4) | (buffer[8] << 4));
|
||||||
|
const f32 left_axis_x = GetAxisValue(raw_left_axis_x, jc.calibration.left_stick.x);
|
||||||
|
const f32 left_axis_y = GetAxisValue(raw_left_axis_y, jc.calibration.left_stick.y);
|
||||||
|
|
||||||
|
SetAxis(identifier, static_cast<int>(PadAxes::LeftStickX), left_axis_x);
|
||||||
|
SetAxis(identifier, static_cast<int>(PadAxes::LeftStickY), left_axis_y);
|
||||||
|
|
||||||
|
if (jc.imu_enabled) {
|
||||||
|
auto left_motion = GetMotionInput(buffer, jc.calibration.imu, jc.accelerometer_sensitivity,
|
||||||
|
jc.gyro_sensitivity);
|
||||||
|
// Rotate motion axis to the correct direction
|
||||||
|
left_motion.accel_y = -left_motion.accel_y;
|
||||||
|
left_motion.accel_z = -left_motion.accel_z;
|
||||||
|
left_motion.gyro_x = -left_motion.gyro_x;
|
||||||
|
SetMotion(identifier, static_cast<int>(PadMotion::LeftMotion), left_motion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Joycons::UpdateRightPadInput(JoyconData& jc, std::span<const u8> buffer) {
|
||||||
|
static constexpr std::array<Joycon::PadButton, 11> right_buttons{
|
||||||
|
Joycon::PadButton::Y, Joycon::PadButton::X, Joycon::PadButton::B,
|
||||||
|
Joycon::PadButton::A, Joycon::PadButton::RightSL, Joycon::PadButton::RightSR,
|
||||||
|
Joycon::PadButton::R, Joycon::PadButton::ZR, Joycon::PadButton::Plus,
|
||||||
|
Joycon::PadButton::Home, Joycon::PadButton::StickR,
|
||||||
|
};
|
||||||
|
const auto identifier = GetIdentifier(jc.port);
|
||||||
|
|
||||||
|
const u32 raw_button = static_cast<u32>((buffer[3] << 8) | (buffer[4] << 16));
|
||||||
|
for (std::size_t i = 0; i < right_buttons.size(); ++i) {
|
||||||
|
const bool button_status = (raw_button & static_cast<u32>(right_buttons[i])) != 0;
|
||||||
|
const int button = static_cast<int>(right_buttons[i]);
|
||||||
|
SetButton(identifier, button, button_status);
|
||||||
|
}
|
||||||
|
|
||||||
|
const u16 raw_right_axis_x = static_cast<u16>(buffer[9] | ((buffer[10] & 0xf) << 8));
|
||||||
|
const u16 raw_right_axis_y = static_cast<u16>((buffer[10] >> 4) | (buffer[11] << 4));
|
||||||
|
const f32 right_axis_x = GetAxisValue(raw_right_axis_x, jc.calibration.right_stick.x);
|
||||||
|
const f32 right_axis_y = GetAxisValue(raw_right_axis_y, jc.calibration.right_stick.y);
|
||||||
|
SetAxis(identifier, static_cast<int>(PadAxes::RightStickX), right_axis_x);
|
||||||
|
SetAxis(identifier, static_cast<int>(PadAxes::RightStickY), right_axis_y);
|
||||||
|
|
||||||
|
if (jc.imu_enabled) {
|
||||||
|
auto right_motion = GetMotionInput(buffer, jc.calibration.imu, jc.accelerometer_sensitivity,
|
||||||
|
jc.gyro_sensitivity);
|
||||||
|
// Rotate motion axis to the correct direction
|
||||||
|
right_motion.accel_x = -right_motion.accel_x;
|
||||||
|
right_motion.accel_y = -right_motion.accel_y;
|
||||||
|
right_motion.gyro_z = -right_motion.gyro_z;
|
||||||
|
SetMotion(identifier, static_cast<int>(PadMotion::RightMotion), right_motion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Joycons::UpdateProPadInput(JoyconData& jc, std::span<const u8> buffer) {
|
||||||
|
static constexpr std::array<Joycon::PadButton, 18> pro_buttons{
|
||||||
|
Joycon::PadButton::Down, Joycon::PadButton::Up, Joycon::PadButton::Right,
|
||||||
|
Joycon::PadButton::Left, Joycon::PadButton::L, Joycon::PadButton::ZL,
|
||||||
|
Joycon::PadButton::Minus, Joycon::PadButton::Capture, Joycon::PadButton::Y,
|
||||||
|
Joycon::PadButton::X, Joycon::PadButton::B, Joycon::PadButton::A,
|
||||||
|
Joycon::PadButton::R, Joycon::PadButton::ZR, Joycon::PadButton::Plus,
|
||||||
|
Joycon::PadButton::Home, Joycon::PadButton::StickL, Joycon::PadButton::StickR,
|
||||||
|
};
|
||||||
|
const auto identifier = GetIdentifier(jc.port);
|
||||||
|
|
||||||
|
const u32 raw_button = static_cast<u32>(buffer[5] | (buffer[3] << 8) | (buffer[4] << 16));
|
||||||
|
for (std::size_t i = 0; i < pro_buttons.size(); ++i) {
|
||||||
|
const bool button_status = (raw_button & static_cast<u32>(pro_buttons[i])) != 0;
|
||||||
|
const int button = static_cast<int>(pro_buttons[i]);
|
||||||
|
SetButton(identifier, button, button_status);
|
||||||
|
}
|
||||||
|
|
||||||
|
const u16 raw_left_axis_x = static_cast<u16>(buffer[6] | ((buffer[7] & 0xf) << 8));
|
||||||
|
const u16 raw_left_axis_y = static_cast<u16>((buffer[7] >> 4) | (buffer[8] << 4));
|
||||||
|
const u16 raw_right_axis_x = static_cast<u16>(buffer[9] | ((buffer[10] & 0xf) << 8));
|
||||||
|
const u16 raw_right_axis_y = static_cast<u16>((buffer[10] >> 4) | (buffer[11] << 4));
|
||||||
|
const f32 left_axis_x = GetAxisValue(raw_left_axis_x, jc.calibration.left_stick.x);
|
||||||
|
const f32 left_axis_y = GetAxisValue(raw_left_axis_y, jc.calibration.left_stick.y);
|
||||||
|
const f32 right_axis_x = GetAxisValue(raw_right_axis_x, jc.calibration.right_stick.x);
|
||||||
|
const f32 right_axis_y = GetAxisValue(raw_right_axis_y, jc.calibration.right_stick.y);
|
||||||
|
SetAxis(identifier, static_cast<int>(PadAxes::LeftStickX), left_axis_x);
|
||||||
|
SetAxis(identifier, static_cast<int>(PadAxes::LeftStickY), left_axis_y);
|
||||||
|
SetAxis(identifier, static_cast<int>(PadAxes::RightStickX), right_axis_x);
|
||||||
|
SetAxis(identifier, static_cast<int>(PadAxes::RightStickY), right_axis_y);
|
||||||
|
|
||||||
|
if (jc.imu_enabled) {
|
||||||
|
auto pro_motion = GetMotionInput(buffer, jc.calibration.imu, jc.accelerometer_sensitivity,
|
||||||
|
jc.gyro_sensitivity);
|
||||||
|
pro_motion.accel_y = -pro_motion.accel_y;
|
||||||
|
pro_motion.accel_z = -pro_motion.accel_z;
|
||||||
|
SetMotion(identifier, static_cast<int>(PadMotion::ProMotion), pro_motion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f32 Joycons::GetAccelerometerValue(s16 raw, Joycon::ImuSensorCalibration cal,
|
||||||
|
Joycon::AccelerometerSensitivity sen) const {
|
||||||
|
const f32 value = raw * (1.0f / (cal.scale - cal.offset)) * 4;
|
||||||
|
switch (sen) {
|
||||||
|
case Joycon::AccelerometerSensitivity::G2:
|
||||||
|
return value / 4.0f;
|
||||||
|
case Joycon::AccelerometerSensitivity::G4:
|
||||||
|
return value / 2.0f;
|
||||||
|
case Joycon::AccelerometerSensitivity::G8:
|
||||||
|
return value;
|
||||||
|
case Joycon::AccelerometerSensitivity::G16:
|
||||||
|
return value * 2.0f;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
f32 Joycons::GetGyroValue(s16 raw, Joycon::ImuSensorCalibration cal,
|
||||||
|
Joycon::GyroSensitivity sen) const {
|
||||||
|
const f32 value = (raw - cal.offset) * (936.0f / (cal.scale - cal.offset)) / 360.0f;
|
||||||
|
switch (sen) {
|
||||||
|
case Joycon::GyroSensitivity::DPS250:
|
||||||
|
return value / 8.0f;
|
||||||
|
case Joycon::GyroSensitivity::DPS500:
|
||||||
|
return value / 4.0f;
|
||||||
|
case Joycon::GyroSensitivity::DPS1000:
|
||||||
|
return value / 2.0f;
|
||||||
|
case Joycon::GyroSensitivity::DPS2000:
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
s16 Joycons::GetRawIMUValues(std::size_t sensor, size_t axis, std::span<const u8> buffer) const {
|
||||||
|
const size_t offset = (sensor * 6) + (axis * 2);
|
||||||
|
return static_cast<s16>(buffer[13 + offset] | (buffer[14 + offset] << 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
BasicMotion Joycons::GetMotionInput(std::span<const u8> buffer, Joycon::ImuCalibration calibration,
|
||||||
|
Joycon::AccelerometerSensitivity accel_sensitivity,
|
||||||
|
Joycon::GyroSensitivity gyro_sensitivity) const {
|
||||||
|
std::array<BasicMotion, 3> motion_data{};
|
||||||
|
for (std::size_t sample = 0; sample < motion_data.size(); ++sample) {
|
||||||
|
auto& motion = motion_data[sample];
|
||||||
|
const auto& accel_cal = calibration.accelerometer;
|
||||||
|
const auto& gyro_cal = calibration.gyro;
|
||||||
|
const std::size_t sample_index = sample * 2;
|
||||||
|
const s16 raw_accel_x = GetRawIMUValues(sample_index, 1, buffer);
|
||||||
|
const s16 raw_accel_y = GetRawIMUValues(sample_index, 0, buffer);
|
||||||
|
const s16 raw_accel_z = GetRawIMUValues(sample_index, 2, buffer);
|
||||||
|
const s16 raw_gyro_x = GetRawIMUValues(sample_index + 1, 1, buffer);
|
||||||
|
const s16 raw_gyro_y = GetRawIMUValues(sample_index + 1, 0, buffer);
|
||||||
|
const s16 raw_gyro_z = GetRawIMUValues(sample_index + 1, 2, buffer);
|
||||||
|
|
||||||
|
motion.delta_timestamp = 15000;
|
||||||
|
motion.accel_x = GetAccelerometerValue(raw_accel_x, accel_cal[1], accel_sensitivity);
|
||||||
|
motion.accel_y = GetAccelerometerValue(raw_accel_y, accel_cal[0], accel_sensitivity);
|
||||||
|
motion.accel_z = GetAccelerometerValue(raw_accel_z, accel_cal[2], accel_sensitivity);
|
||||||
|
motion.gyro_x = GetGyroValue(raw_gyro_x, gyro_cal[1], gyro_sensitivity);
|
||||||
|
motion.gyro_y = GetGyroValue(raw_gyro_y, gyro_cal[0], gyro_sensitivity);
|
||||||
|
motion.gyro_z = GetGyroValue(raw_gyro_z, gyro_cal[2], gyro_sensitivity);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(German77): Return all three samples data
|
||||||
|
return motion_data[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
Common::Input::VibrationError Joycons::SetRumble(const PadIdentifier& identifier,
|
||||||
|
const Common::Input::VibrationStatus vibration) {
|
||||||
|
const Joycon::VibrationValue native_vibration{
|
||||||
|
.low_amplitude = vibration.low_amplitude,
|
||||||
|
.low_frequency = vibration.low_frequency,
|
||||||
|
.high_amplitude = vibration.high_amplitude,
|
||||||
|
.high_frequency = vibration.high_amplitude,
|
||||||
|
};
|
||||||
|
Joycon::SetVibration(GetHandle(identifier), native_vibration);
|
||||||
|
return Common::Input::VibrationError::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Joycon::JoyconHandle& Joycons::GetHandle(PadIdentifier identifier) {
|
||||||
|
return joycon_data[identifier.port].joycon_handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
PadIdentifier Joycons::GetIdentifier(std::size_t port) const {
|
||||||
|
return {
|
||||||
|
.guid = Common::UUID{Common::INVALID_UUID},
|
||||||
|
.port = port,
|
||||||
|
.pad = 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Common::ParamPackage> Joycons::GetInputDevices() const {
|
||||||
|
std::vector<Common::ParamPackage> devices;
|
||||||
|
for (const auto& controller : joycon_data) {
|
||||||
|
if (DeviceConnected(controller.port)) {
|
||||||
|
std::string name = fmt::format("{} {}", JoyconName(controller.port), controller.port);
|
||||||
|
devices.emplace_back(Common::ParamPackage{
|
||||||
|
{"engine", GetEngineName()},
|
||||||
|
{"display", std::move(name)},
|
||||||
|
{"port", std::to_string(controller.port)},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
ButtonMapping Joycons::GetButtonMappingForDevice(const Common::ParamPackage& params) {
|
||||||
|
static constexpr std::array<std::pair<Settings::NativeButton::Values, Joycon::PadButton>, 20>
|
||||||
|
switch_to_joycon_button = {
|
||||||
|
std::pair{Settings::NativeButton::A, Joycon::PadButton::A},
|
||||||
|
{Settings::NativeButton::B, Joycon::PadButton::B},
|
||||||
|
{Settings::NativeButton::X, Joycon::PadButton::X},
|
||||||
|
{Settings::NativeButton::Y, Joycon::PadButton::Y},
|
||||||
|
{Settings::NativeButton::DLeft, Joycon::PadButton::Left},
|
||||||
|
{Settings::NativeButton::DUp, Joycon::PadButton::Up},
|
||||||
|
{Settings::NativeButton::DRight, Joycon::PadButton::Right},
|
||||||
|
{Settings::NativeButton::DDown, Joycon::PadButton::Down},
|
||||||
|
{Settings::NativeButton::SL, Joycon::PadButton::LeftSL},
|
||||||
|
{Settings::NativeButton::SR, Joycon::PadButton::LeftSR},
|
||||||
|
{Settings::NativeButton::L, Joycon::PadButton::L},
|
||||||
|
{Settings::NativeButton::R, Joycon::PadButton::R},
|
||||||
|
{Settings::NativeButton::ZL, Joycon::PadButton::ZL},
|
||||||
|
{Settings::NativeButton::ZR, Joycon::PadButton::ZR},
|
||||||
|
{Settings::NativeButton::Plus, Joycon::PadButton::Plus},
|
||||||
|
{Settings::NativeButton::Minus, Joycon::PadButton::Minus},
|
||||||
|
{Settings::NativeButton::Home, Joycon::PadButton::Home},
|
||||||
|
{Settings::NativeButton::Screenshot, Joycon::PadButton::Capture},
|
||||||
|
{Settings::NativeButton::LStick, Joycon::PadButton::StickL},
|
||||||
|
{Settings::NativeButton::RStick, Joycon::PadButton::StickR},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!params.Has("port")) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
ButtonMapping mapping{};
|
||||||
|
for (const auto& [switch_button, joycon_button] : switch_to_joycon_button) {
|
||||||
|
Common::ParamPackage button_params{};
|
||||||
|
button_params.Set("engine", GetEngineName());
|
||||||
|
button_params.Set("port", params.Get("port", 0));
|
||||||
|
button_params.Set("button", static_cast<int>(joycon_button));
|
||||||
|
mapping.insert_or_assign(switch_button, std::move(button_params));
|
||||||
|
}
|
||||||
|
|
||||||
|
return mapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
AnalogMapping Joycons::GetAnalogMappingForDevice(const Common::ParamPackage& params) {
|
||||||
|
if (!params.Has("port")) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
AnalogMapping mapping = {};
|
||||||
|
Common::ParamPackage left_analog_params;
|
||||||
|
left_analog_params.Set("engine", GetEngineName());
|
||||||
|
left_analog_params.Set("port", params.Get("port", 0));
|
||||||
|
left_analog_params.Set("axis_x", static_cast<int>(PadAxes::LeftStickX));
|
||||||
|
left_analog_params.Set("axis_y", static_cast<int>(PadAxes::LeftStickY));
|
||||||
|
mapping.insert_or_assign(Settings::NativeAnalog::LStick, std::move(left_analog_params));
|
||||||
|
Common::ParamPackage right_analog_params;
|
||||||
|
right_analog_params.Set("engine", GetEngineName());
|
||||||
|
right_analog_params.Set("port", params.Get("port", 0));
|
||||||
|
right_analog_params.Set("axis_x", static_cast<int>(PadAxes::RightStickX));
|
||||||
|
right_analog_params.Set("axis_y", static_cast<int>(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 {};
|
||||||
|
}
|
||||||
|
|
||||||
|
MotionMapping mapping = {};
|
||||||
|
Common::ParamPackage left_motion_params;
|
||||||
|
left_motion_params.Set("engine", GetEngineName());
|
||||||
|
left_motion_params.Set("port", params.Get("port", 0));
|
||||||
|
left_motion_params.Set("motion", 0);
|
||||||
|
mapping.insert_or_assign(Settings::NativeMotion::MotionLeft, std::move(left_motion_params));
|
||||||
|
Common::ParamPackage right_Motion_params;
|
||||||
|
right_Motion_params.Set("engine", GetEngineName());
|
||||||
|
right_Motion_params.Set("port", params.Get("port", 0));
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace InputCommon
|
||||||
126
src/input_common/drivers/joycon.h
Normal file
126
src/input_common/drivers/joycon.h
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
// Copyright 2021 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <span>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include "input_common/helpers/joycon_protocol.h"
|
||||||
|
#include "input_common/input_engine.h"
|
||||||
|
|
||||||
|
namespace InputCommon {
|
||||||
|
|
||||||
|
class Joycons final : public InputCommon::InputEngine {
|
||||||
|
public:
|
||||||
|
explicit Joycons(const std::string& input_engine_);
|
||||||
|
|
||||||
|
~Joycons();
|
||||||
|
|
||||||
|
/// Used for automapping features
|
||||||
|
std::vector<Common::ParamPackage> GetInputDevices() const override;
|
||||||
|
ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override;
|
||||||
|
AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override;
|
||||||
|
MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& params) override;
|
||||||
|
Common::Input::ButtonNames GetUIName(const Common::ParamPackage& params) const override;
|
||||||
|
|
||||||
|
Common::Input::VibrationError SetRumble(
|
||||||
|
const PadIdentifier& identifier, const Common::Input::VibrationStatus vibration) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
enum class PadAxes {
|
||||||
|
LeftStickX,
|
||||||
|
LeftStickY,
|
||||||
|
RightStickX,
|
||||||
|
RightStickY,
|
||||||
|
Undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class PadMotion {
|
||||||
|
LeftMotion,
|
||||||
|
RightMotion,
|
||||||
|
ProMotion,
|
||||||
|
Undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct JoyconData {
|
||||||
|
Joycon::JoyconHandle joycon_handle{nullptr, 0};
|
||||||
|
|
||||||
|
// Harware config
|
||||||
|
Joycon::GyroSensitivity gyro_sensitivity;
|
||||||
|
Joycon::GyroPerformance gyro_performance;
|
||||||
|
Joycon::AccelerometerSensitivity accelerometer_sensitivity;
|
||||||
|
Joycon::AccelerometerPerformance accelerometer_performance;
|
||||||
|
Joycon::ReportMode mode;
|
||||||
|
bool imu_enabled;
|
||||||
|
bool rumble_enabled;
|
||||||
|
u8 leds;
|
||||||
|
|
||||||
|
// Fixed value info
|
||||||
|
f32 version;
|
||||||
|
std::array<u8, 6> mac;
|
||||||
|
Joycon::CalibrationData calibration;
|
||||||
|
Joycon::ControllerType type;
|
||||||
|
std::array<u8, 15> serial_number;
|
||||||
|
Joycon::Color color;
|
||||||
|
std::size_t port;
|
||||||
|
|
||||||
|
// Polled data
|
||||||
|
u32 raw_temperature;
|
||||||
|
u8 raw_battery;
|
||||||
|
Common::Input::VibrationStatus hd_rumble;
|
||||||
|
};
|
||||||
|
void InputThread(std::stop_token stop_token);
|
||||||
|
void ScanThread(std::stop_token stop_token);
|
||||||
|
|
||||||
|
/// Resets status of device connected to port
|
||||||
|
void ResetDeviceType(std::size_t port);
|
||||||
|
|
||||||
|
/// Captures JC Adapter endpoint address,
|
||||||
|
void GetJCEndpoint();
|
||||||
|
|
||||||
|
/// For shutting down, clear all data, join all threads, release usb
|
||||||
|
void Reset();
|
||||||
|
|
||||||
|
bool IsPayloadCorrect(int status, std::span<const u8> buffer);
|
||||||
|
|
||||||
|
/// For use in initialization, querying devices to find the adapter
|
||||||
|
void Setup();
|
||||||
|
|
||||||
|
bool DeviceConnected(std::size_t port) const;
|
||||||
|
|
||||||
|
void UpdateJoyconData(JoyconData& jc, std::span<const u8> buffer);
|
||||||
|
void UpdateLeftPadInput(JoyconData& jc, std::span<const u8> buffer);
|
||||||
|
void UpdateRightPadInput(JoyconData& jc, std::span<const u8> buffer);
|
||||||
|
void UpdateProPadInput(JoyconData& jc, std::span<const u8> buffer);
|
||||||
|
|
||||||
|
// Reads gyro and accelerometer values
|
||||||
|
f32 GetAxisValue(u16 raw_value, Joycon::JoyStickAxisCalibration calibration) const;
|
||||||
|
f32 GetAccelerometerValue(s16 raw_value, Joycon::ImuSensorCalibration cal,
|
||||||
|
Joycon::AccelerometerSensitivity sen) const;
|
||||||
|
f32 GetGyroValue(s16 raw_value, Joycon::ImuSensorCalibration cal,
|
||||||
|
Joycon::GyroSensitivity sen) const;
|
||||||
|
|
||||||
|
s16 GetRawIMUValues(size_t sensor, size_t axis, std::span<const u8> buffer) const;
|
||||||
|
BasicMotion GetMotionInput(std::span<const u8> buffer, Joycon::ImuCalibration calibration,
|
||||||
|
Joycon::AccelerometerSensitivity accel_sensitivity,
|
||||||
|
Joycon::GyroSensitivity gyro_sensitivity) const;
|
||||||
|
|
||||||
|
Joycon::JoyconHandle& GetHandle(PadIdentifier identifier);
|
||||||
|
PadIdentifier GetIdentifier(std::size_t port) const;
|
||||||
|
|
||||||
|
std::string JoyconName(std::size_t port) const;
|
||||||
|
|
||||||
|
Common::Input::ButtonNames GetUIButtonName(const Common::ParamPackage& params) const;
|
||||||
|
|
||||||
|
std::jthread input_thread;
|
||||||
|
std::jthread scan_thread;
|
||||||
|
bool input_thread_running{};
|
||||||
|
bool scan_thread_running{};
|
||||||
|
u8 input_error_counter{};
|
||||||
|
|
||||||
|
std::array<JoyconData, 8> joycon_data{};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace InputCommon
|
||||||
357
src/input_common/helpers/joycon_protocol.cpp
Normal file
357
src/input_common/helpers/joycon_protocol.cpp
Normal file
@@ -0,0 +1,357 @@
|
|||||||
|
// Copyright 2021 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
#include "input_common/helpers/joycon_protocol.h"
|
||||||
|
|
||||||
|
namespace InputCommon::Joycon {
|
||||||
|
|
||||||
|
u8 GetCounter(JoyconHandle& joycon_handle) {
|
||||||
|
joycon_handle.packet_counter = (joycon_handle.packet_counter + 1) & 0x0F;
|
||||||
|
return joycon_handle.packet_counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CheckDeviceAccess(JoyconHandle& joycon_handle, SDL_hid_device_info* device_info) {
|
||||||
|
if (GetDeviceType(device_info) == ControllerType::None) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
joycon_handle.handle =
|
||||||
|
SDL_hid_open(device_info->vendor_id, device_info->product_id, device_info->serial_number);
|
||||||
|
if (!joycon_handle.handle) {
|
||||||
|
LOG_ERROR(Input, "Yuzu can't gain access to this device: ID {:04X}:{:04X}.",
|
||||||
|
device_info->vendor_id, device_info->product_id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
SDL_hid_set_nonblocking(joycon_handle.handle, 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
f32 EncodeRumbleAmplification(f32 amplification) {
|
||||||
|
if (amplification < 0.01182) {
|
||||||
|
return roundf(powf(amplification, 1.7f) * 7561);
|
||||||
|
} else if (amplification < 0.11249) {
|
||||||
|
return roundf((logf(amplification) * 11.556f) + 55.3f);
|
||||||
|
} else if (amplification < 0.22498) {
|
||||||
|
return roundf((log2f(amplification) * 32) + 131);
|
||||||
|
}
|
||||||
|
return roundf((log2f(amplification) * 64) + 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetVibration(JoyconHandle& joycon_handle, VibrationValue vibration) {
|
||||||
|
std::vector<u8> buffer(Joycon::max_resp_size);
|
||||||
|
|
||||||
|
buffer[0] = static_cast<u8>(Joycon::Output::RUMBLE_ONLY);
|
||||||
|
buffer[1] = GetCounter(joycon_handle);
|
||||||
|
|
||||||
|
if (vibration.high_amplitude == 0.0f && vibration.low_amplitude == 0.0f) {
|
||||||
|
for (std::size_t i = 0; i < Joycon::default_buffer.size(); ++i) {
|
||||||
|
buffer[2 + i] = Joycon::default_buffer[i];
|
||||||
|
}
|
||||||
|
SendData(joycon_handle, buffer, max_resp_size);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const u16 encoded_hf =
|
||||||
|
static_cast<u16>(roundf(128 * log2f(vibration.high_frequency * 0.1f)) - 0x180);
|
||||||
|
const u8 encoded_lf =
|
||||||
|
static_cast<u8>(roundf(32 * log2f(vibration.low_frequency * 0.1f)) - 0x40);
|
||||||
|
const u8 encoded_hamp = static_cast<u8>(EncodeRumbleAmplification(vibration.high_amplitude));
|
||||||
|
const u8 encoded_lamp = static_cast<u8>(EncodeRumbleAmplification(vibration.low_amplitude));
|
||||||
|
|
||||||
|
for (u8 i = 0; i < 2; ++i) {
|
||||||
|
const u8 amplitude = i == 0 ? encoded_lamp : encoded_hamp;
|
||||||
|
const u8 offset = i * 4;
|
||||||
|
u32 encoded_amp = amplitude >> 1;
|
||||||
|
const u32 parity = (encoded_amp % 2) * 0x80;
|
||||||
|
encoded_amp >>= 1;
|
||||||
|
encoded_amp += 0x40;
|
||||||
|
|
||||||
|
buffer[2 + offset] = static_cast<u8>(encoded_hf & 0xff);
|
||||||
|
buffer[3 + offset] = static_cast<u8>((encoded_hf >> 8) & 0xff);
|
||||||
|
buffer[4 + offset] = static_cast<u8>(encoded_lf & 0xff);
|
||||||
|
|
||||||
|
buffer[3 + offset] |= static_cast<u8>(amplitude);
|
||||||
|
buffer[4 + offset] |= static_cast<u8>(parity);
|
||||||
|
buffer[5 + offset] = static_cast<u8>(encoded_amp);
|
||||||
|
}
|
||||||
|
|
||||||
|
SendData(joycon_handle, buffer, max_resp_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetImuConfig(JoyconHandle& joycon_handle, GyroSensitivity gsen, GyroPerformance gfrec,
|
||||||
|
AccelerometerSensitivity asen, AccelerometerPerformance afrec) {
|
||||||
|
const std::vector<u8> buffer{static_cast<u8>(gsen), static_cast<u8>(asen),
|
||||||
|
static_cast<u8>(gfrec), static_cast<u8>(afrec)};
|
||||||
|
SDL_hid_set_nonblocking(joycon_handle.handle, 0);
|
||||||
|
SendSubCommand(joycon_handle, SubCommand::SET_IMU_SENSITIVITY, buffer, 4);
|
||||||
|
SDL_hid_set_nonblocking(joycon_handle.handle, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetLedConfig(JoyconHandle& joycon_handle, u8 leds) {
|
||||||
|
const std::vector<u8> buffer{leds};
|
||||||
|
SDL_hid_set_nonblocking(joycon_handle.handle, 0);
|
||||||
|
SendSubCommand(joycon_handle, SubCommand::SET_PLAYER_LIGHTS, buffer, 1);
|
||||||
|
SDL_hid_set_nonblocking(joycon_handle.handle, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetReportMode(JoyconHandle& joycon_handle, ReportMode report_mode) {
|
||||||
|
const std::vector<u8> buffer{static_cast<u8>(report_mode)};
|
||||||
|
SDL_hid_set_nonblocking(joycon_handle.handle, 0);
|
||||||
|
SendSubCommand(joycon_handle, SubCommand::SET_REPORT_MODE, buffer, 1);
|
||||||
|
SDL_hid_set_nonblocking(joycon_handle.handle, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Color GetColor(JoyconHandle& joycon_handle) {
|
||||||
|
Color joycon_color;
|
||||||
|
std::vector<u8> buffer;
|
||||||
|
SDL_hid_set_nonblocking(joycon_handle.handle, 0);
|
||||||
|
buffer = ReadSPI(joycon_handle, Joycon::CalAddr::COLOR_DATA, 12);
|
||||||
|
joycon_color.body = static_cast<u32>((buffer[2] << 16) | (buffer[1] << 8) | buffer[0]);
|
||||||
|
joycon_color.buttons = static_cast<u32>((buffer[5] << 16) | (buffer[4] << 8) | buffer[3]);
|
||||||
|
joycon_color.left_grip = static_cast<u32>((buffer[8] << 16) | (buffer[7] << 8) | buffer[6]);
|
||||||
|
joycon_color.right_grip = static_cast<u32>((buffer[11] << 16) | (buffer[10] << 8) | buffer[9]);
|
||||||
|
SDL_hid_set_nonblocking(joycon_handle.handle, 1);
|
||||||
|
return joycon_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<u8, 6> GetMacAddress(JoyconHandle& joycon_handle) {
|
||||||
|
std::array<u8, 6> mac_address{};
|
||||||
|
wchar_t buffer[255];
|
||||||
|
const auto result =
|
||||||
|
SDL_hid_get_serial_number_string(joycon_handle.handle, buffer, std::size(buffer));
|
||||||
|
if (result == -1) {
|
||||||
|
LOG_ERROR(Input, "Unable to get mac address");
|
||||||
|
return mac_address;
|
||||||
|
}
|
||||||
|
for (std::size_t i = 0; i < mac_address.size(); ++i) {
|
||||||
|
wchar_t value[3] = {buffer[i * 2], buffer[(i * 2) + 1]};
|
||||||
|
mac_address[i] = static_cast<u8>(std::stoi(value, 0, 16));
|
||||||
|
}
|
||||||
|
return mac_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<u8, 15> GetSerialNumber(JoyconHandle& joycon_handle) {
|
||||||
|
std::array<u8, 15> serial_number;
|
||||||
|
SDL_hid_set_nonblocking(joycon_handle.handle, 0);
|
||||||
|
const std::vector<u8> buffer = ReadSPI(joycon_handle, Joycon::CalAddr::SERIAL_NUMBER, 16);
|
||||||
|
SDL_hid_set_nonblocking(joycon_handle.handle, 1);
|
||||||
|
for (std::size_t i = 0; i < serial_number.size(); ++i) {
|
||||||
|
serial_number[i] = buffer[i + 1];
|
||||||
|
}
|
||||||
|
return serial_number;
|
||||||
|
}
|
||||||
|
|
||||||
|
ControllerType GetDeviceType(SDL_hid_device_info* device_info) {
|
||||||
|
if (device_info->vendor_id != 0x057e) {
|
||||||
|
return ControllerType::None;
|
||||||
|
}
|
||||||
|
switch (device_info->product_id) {
|
||||||
|
case 0x2006:
|
||||||
|
return ControllerType::Left;
|
||||||
|
case 0x2007:
|
||||||
|
return ControllerType::Right;
|
||||||
|
case 0x2009:
|
||||||
|
return ControllerType::Pro;
|
||||||
|
default:
|
||||||
|
return ControllerType::None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ControllerType GetDeviceType(JoyconHandle& joycon_handle) {
|
||||||
|
SDL_hid_set_nonblocking(joycon_handle.handle, 0);
|
||||||
|
const std::vector<u8> buffer = ReadSPI(joycon_handle, Joycon::CalAddr::DEVICE_TYPE, 1);
|
||||||
|
SDL_hid_set_nonblocking(joycon_handle.handle, 1);
|
||||||
|
return static_cast<Joycon::ControllerType>(buffer[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
u16 GetVersionNumber([[maybe_unused]] JoyconHandle& joycon_handle) {
|
||||||
|
// Not implemented
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
JoyStickCalibration GetLeftJoyStickCalibration(JoyconHandle& joycon_handle,
|
||||||
|
bool is_factory_calibration) {
|
||||||
|
JoyStickCalibration joystick{};
|
||||||
|
std::vector<u8> buffer;
|
||||||
|
SDL_hid_set_nonblocking(joycon_handle.handle, 0);
|
||||||
|
if (is_factory_calibration) {
|
||||||
|
buffer = ReadSPI(joycon_handle, CalAddr::FACT_LEFT_DATA, 9);
|
||||||
|
} else {
|
||||||
|
buffer = ReadSPI(joycon_handle, CalAddr::USER_LEFT_DATA, 9);
|
||||||
|
}
|
||||||
|
SDL_hid_set_nonblocking(joycon_handle.handle, 1);
|
||||||
|
|
||||||
|
joystick.x.max = static_cast<u16>(((buffer[1] & 0x0F) << 8) | buffer[0]);
|
||||||
|
joystick.y.max = static_cast<u16>((buffer[2] << 4) | (buffer[1] >> 4));
|
||||||
|
joystick.x.center = static_cast<u16>(((buffer[4] & 0x0F) << 8) | buffer[3]);
|
||||||
|
joystick.y.center = static_cast<u16>((buffer[5] << 4) | (buffer[4] >> 4));
|
||||||
|
joystick.x.min = static_cast<u16>(((buffer[7] & 0x0F) << 8) | buffer[6]);
|
||||||
|
joystick.y.min = static_cast<u16>((buffer[8] << 4) | (buffer[7] >> 4));
|
||||||
|
|
||||||
|
// Nintendo fix for drifting stick
|
||||||
|
// buffer = ReadSPI(0x60, 0x86 , 16);
|
||||||
|
// joystick.deadzone = (u16)((buffer[4] << 8) & 0xF00 | buffer[3]);
|
||||||
|
return joystick;
|
||||||
|
}
|
||||||
|
|
||||||
|
JoyStickCalibration GetRightJoyStickCalibration(JoyconHandle& joycon_handle,
|
||||||
|
bool is_factory_calibration) {
|
||||||
|
JoyStickCalibration joystick{};
|
||||||
|
std::vector<u8> buffer;
|
||||||
|
SDL_hid_set_nonblocking(joycon_handle.handle, 0);
|
||||||
|
if (is_factory_calibration) {
|
||||||
|
buffer = ReadSPI(joycon_handle, CalAddr::FACT_RIGHT_DATA, 9);
|
||||||
|
} else {
|
||||||
|
buffer = ReadSPI(joycon_handle, CalAddr::USER_RIGHT_DATA, 9);
|
||||||
|
}
|
||||||
|
SDL_hid_set_nonblocking(joycon_handle.handle, 1);
|
||||||
|
|
||||||
|
joystick.x.center = static_cast<u16>(((buffer[1] & 0x0F) << 8) | buffer[0]);
|
||||||
|
joystick.y.center = static_cast<u16>((buffer[2] << 4) | (buffer[1] >> 4));
|
||||||
|
joystick.x.min = static_cast<u16>(((buffer[4] & 0x0F) << 8) | buffer[3]);
|
||||||
|
joystick.y.min = static_cast<u16>((buffer[5] << 4) | (buffer[4] >> 4));
|
||||||
|
joystick.x.max = static_cast<u16>(((buffer[7] & 0x0F) << 8) | buffer[6]);
|
||||||
|
joystick.y.max = static_cast<u16>((buffer[8] << 4) | (buffer[7] >> 4));
|
||||||
|
|
||||||
|
// Nintendo fix for drifting stick
|
||||||
|
// buffer = ReadSPI(0x60, 0x98 , 16);
|
||||||
|
// joystick.deadzone = (u16)((buffer[4] << 8) & 0xF00 | buffer[3]);
|
||||||
|
return joystick;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImuCalibration GetImuCalibration(JoyconHandle& joycon_handle, bool is_factory_calibration) {
|
||||||
|
ImuCalibration imu{};
|
||||||
|
std::vector<u8> buffer;
|
||||||
|
SDL_hid_set_nonblocking(joycon_handle.handle, 0);
|
||||||
|
if (is_factory_calibration) {
|
||||||
|
buffer = ReadSPI(joycon_handle, CalAddr::FACT_IMU_DATA, 24);
|
||||||
|
} else {
|
||||||
|
buffer = ReadSPI(joycon_handle, CalAddr::USER_IMU_DATA, 24);
|
||||||
|
}
|
||||||
|
SDL_hid_set_nonblocking(joycon_handle.handle, 1);
|
||||||
|
for (std::size_t i = 0; i < imu.accelerometer.size(); ++i) {
|
||||||
|
const std::size_t index = i * 2;
|
||||||
|
imu.accelerometer[i].offset = static_cast<s16>(buffer[index] | (buffer[index + 1] << 8));
|
||||||
|
imu.accelerometer[i].scale = static_cast<s16>(buffer[index + 6] | (buffer[index + 7] << 8));
|
||||||
|
}
|
||||||
|
for (std::size_t i = 0; i < imu.gyro.size(); ++i) {
|
||||||
|
const std::size_t index = i * 2;
|
||||||
|
imu.gyro[i].offset = static_cast<s16>(buffer[index + 12] | (buffer[index + 13] << 8));
|
||||||
|
imu.gyro[i].scale = static_cast<s16>(buffer[index + 18] | (buffer[index + 19] << 8));
|
||||||
|
}
|
||||||
|
return imu;
|
||||||
|
}
|
||||||
|
|
||||||
|
CalibrationData GetUserCalibrationData(JoyconHandle& joycon_handle,
|
||||||
|
ControllerType controller_type) {
|
||||||
|
CalibrationData calibration{};
|
||||||
|
switch (controller_type) {
|
||||||
|
case ControllerType::Left:
|
||||||
|
calibration.left_stick = GetLeftJoyStickCalibration(joycon_handle, false);
|
||||||
|
break;
|
||||||
|
case ControllerType::Right:
|
||||||
|
calibration.right_stick = GetRightJoyStickCalibration(joycon_handle, false);
|
||||||
|
break;
|
||||||
|
case Joycon::ControllerType::Pro:
|
||||||
|
calibration.left_stick = GetLeftJoyStickCalibration(joycon_handle, false);
|
||||||
|
calibration.right_stick = GetRightJoyStickCalibration(joycon_handle, false);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
calibration.imu = GetImuCalibration(joycon_handle, false);
|
||||||
|
return calibration;
|
||||||
|
}
|
||||||
|
|
||||||
|
CalibrationData GetFactoryCalibrationData(JoyconHandle& joycon_handle,
|
||||||
|
ControllerType controller_type) {
|
||||||
|
CalibrationData calibration{};
|
||||||
|
switch (controller_type) {
|
||||||
|
case ControllerType::Left:
|
||||||
|
calibration.left_stick = GetLeftJoyStickCalibration(joycon_handle, true);
|
||||||
|
break;
|
||||||
|
case ControllerType::Right:
|
||||||
|
calibration.right_stick = GetRightJoyStickCalibration(joycon_handle, true);
|
||||||
|
break;
|
||||||
|
case Joycon::ControllerType::Pro:
|
||||||
|
calibration.left_stick = GetLeftJoyStickCalibration(joycon_handle, true);
|
||||||
|
calibration.right_stick = GetRightJoyStickCalibration(joycon_handle, true);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
calibration.imu = GetImuCalibration(joycon_handle, true);
|
||||||
|
return calibration;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnableImu(JoyconHandle& joycon_handle, bool enable) {
|
||||||
|
SDL_hid_set_nonblocking(joycon_handle.handle, 0);
|
||||||
|
const std::vector<u8> buffer{static_cast<u8>(enable ? 1 : 0)};
|
||||||
|
SendSubCommand(joycon_handle, SubCommand::ENABLE_IMU, buffer, 1);
|
||||||
|
SDL_hid_set_nonblocking(joycon_handle.handle, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnableRumble(JoyconHandle& joycon_handle, bool enable) {
|
||||||
|
SDL_hid_set_nonblocking(joycon_handle.handle, 0);
|
||||||
|
const std::vector<u8> buffer{static_cast<u8>(enable ? 1 : 0)};
|
||||||
|
SendSubCommand(joycon_handle, SubCommand::ENABLE_VIBRATION, buffer, 1);
|
||||||
|
SDL_hid_set_nonblocking(joycon_handle.handle, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SendData(JoyconHandle& joycon_handle, std::span<const u8> buffer, std::size_t size) {
|
||||||
|
SDL_hid_write(joycon_handle.handle, buffer.data(), size);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<u8> GetResponse(JoyconHandle& joycon_handle, SubCommand sc) {
|
||||||
|
int tries = 0;
|
||||||
|
std::vector<u8> buffer(max_resp_size);
|
||||||
|
do {
|
||||||
|
int result = SDL_hid_read_timeout(joycon_handle.handle, buffer.data(), max_resp_size, 100);
|
||||||
|
if (result < 1) {
|
||||||
|
LOG_ERROR(Input, "No response from joycon");
|
||||||
|
}
|
||||||
|
tries++;
|
||||||
|
} while (tries < 10 && buffer[0] != 0x21 && buffer[14] != static_cast<u8>(sc));
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<u8> SendSubCommand(JoyconHandle& joycon_handle, SubCommand sc,
|
||||||
|
std::span<const u8> buffer, std::size_t size) {
|
||||||
|
std::vector<u8> local_buffer(size + 11);
|
||||||
|
|
||||||
|
local_buffer[0] = static_cast<u8>(Output::RUMBLE_AND_SUBCMD);
|
||||||
|
local_buffer[1] = GetCounter(joycon_handle);
|
||||||
|
for (std::size_t i = 0; i < 8; ++i) {
|
||||||
|
local_buffer[i + 2] = default_buffer[i];
|
||||||
|
}
|
||||||
|
local_buffer[10] = static_cast<u8>(sc);
|
||||||
|
for (std::size_t i = 0; i < size; ++i) {
|
||||||
|
local_buffer[11 + i] = buffer[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
SendData(joycon_handle, local_buffer, size + 11);
|
||||||
|
return GetResponse(joycon_handle, sc);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<u8> ReadSPI(JoyconHandle& joycon_handle, CalAddr addr, u8 size) {
|
||||||
|
std::vector<u8> buffer = {0x00, 0x00, 0x00, 0x00, size};
|
||||||
|
std::vector<u8> local_buffer(size + 20);
|
||||||
|
|
||||||
|
buffer[0] = static_cast<u8>(static_cast<u16>(addr) & 0x00FF);
|
||||||
|
buffer[1] = static_cast<u8>((static_cast<u16>(addr) & 0xFF00) >> 8);
|
||||||
|
for (std::size_t i = 0; i < 100; ++i) {
|
||||||
|
local_buffer = SendSubCommand(joycon_handle, SubCommand::SPI_FLASH_READ, buffer, 5);
|
||||||
|
if (local_buffer[15] == buffer[0] && local_buffer[16] == buffer[1]) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::vector<u8>(local_buffer.begin() + 20, local_buffer.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace InputCommon::Joycon
|
||||||
366
src/input_common/helpers/joycon_protocol.h
Normal file
366
src/input_common/helpers/joycon_protocol.h
Normal file
@@ -0,0 +1,366 @@
|
|||||||
|
// Copyright 2021 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <span>
|
||||||
|
#include <vector>
|
||||||
|
#include <SDL_hidapi.h>
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
namespace InputCommon::Joycon {
|
||||||
|
constexpr u32 max_resp_size = 49;
|
||||||
|
constexpr std::array<u8, 8> default_buffer{0x0, 0x1, 0x40, 0x40, 0x0, 0x1, 0x40, 0x40};
|
||||||
|
|
||||||
|
enum class ControllerType {
|
||||||
|
None,
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Pro,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class PadButton : u32 {
|
||||||
|
Down = 0x000001,
|
||||||
|
Up = 0x000002,
|
||||||
|
Right = 0x000004,
|
||||||
|
Left = 0x000008,
|
||||||
|
LeftSR = 0x000010,
|
||||||
|
LeftSL = 0x000020,
|
||||||
|
L = 0x000040,
|
||||||
|
ZL = 0x000080,
|
||||||
|
Y = 0x000100,
|
||||||
|
X = 0x000200,
|
||||||
|
B = 0x000400,
|
||||||
|
A = 0x000800,
|
||||||
|
RightSL = 0x001000,
|
||||||
|
RightSR = 0x002000,
|
||||||
|
R = 0x004000,
|
||||||
|
ZR = 0x008000,
|
||||||
|
Minus = 0x010000,
|
||||||
|
Plus = 0x020000,
|
||||||
|
StickR = 0x040000,
|
||||||
|
StickL = 0x080000,
|
||||||
|
Home = 0x100000,
|
||||||
|
Capture = 0x200000,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Output {
|
||||||
|
RUMBLE_AND_SUBCMD = 0x01,
|
||||||
|
FW_UPDATE_PKT = 0x03,
|
||||||
|
RUMBLE_ONLY = 0x10,
|
||||||
|
MCU_DATA = 0x11,
|
||||||
|
USB_CMD = 0x80,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class SubCommand {
|
||||||
|
STATE = 0x00,
|
||||||
|
MANUAL_BT_PAIRING = 0x01,
|
||||||
|
REQ_DEV_INFO = 0x02,
|
||||||
|
SET_REPORT_MODE = 0x03,
|
||||||
|
TRIGGERS_ELAPSED = 0x04,
|
||||||
|
GET_PAGE_LIST_STATE = 0x05,
|
||||||
|
SET_HCI_STATE = 0x06,
|
||||||
|
RESET_PAIRING_INFO = 0x07,
|
||||||
|
LOW_POWER_MODE = 0x08,
|
||||||
|
SPI_FLASH_READ = 0x10,
|
||||||
|
SPI_FLASH_WRITE = 0x11,
|
||||||
|
RESET_MCU = 0x20,
|
||||||
|
SET_MCU_CONFIG = 0x21,
|
||||||
|
SET_MCU_STATE = 0x22,
|
||||||
|
SET_PLAYER_LIGHTS = 0x30,
|
||||||
|
GET_PLAYER_LIGHTS = 0x31,
|
||||||
|
SET_HOME_LIGHT = 0x38,
|
||||||
|
ENABLE_IMU = 0x40,
|
||||||
|
SET_IMU_SENSITIVITY = 0x41,
|
||||||
|
WRITE_IMU_REG = 0x42,
|
||||||
|
READ_IMU_REG = 0x43,
|
||||||
|
ENABLE_VIBRATION = 0x48,
|
||||||
|
GET_REGULATED_VOLTAGE = 0x50,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class CalAddr {
|
||||||
|
USER_LEFT_MAGIC = 0X8010,
|
||||||
|
USER_LEFT_DATA = 0X8012,
|
||||||
|
USER_RIGHT_MAGIC = 0X801B,
|
||||||
|
USER_RIGHT_DATA = 0X801D,
|
||||||
|
USER_IMU_MAGIC = 0X8026,
|
||||||
|
USER_IMU_DATA = 0X8028,
|
||||||
|
SERIAL_NUMBER = 0X6000,
|
||||||
|
DEVICE_TYPE = 0X6012,
|
||||||
|
COLOR_EXIST = 0X601B,
|
||||||
|
FACT_LEFT_DATA = 0X603d,
|
||||||
|
FACT_RIGHT_DATA = 0X6046,
|
||||||
|
COLOR_DATA = 0X6050,
|
||||||
|
FACT_IMU_DATA = 0X6020,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ReportMode {
|
||||||
|
ACTIVE_POLLING_NFC_IR_CAMERA_DATA = 0x00,
|
||||||
|
ACTIVE_POLLING_NFC_IR_CAMERA_CONFIGURATION = 0x01,
|
||||||
|
ACTIVE_POLLING_NFC_IR_CAMERA_DATA_CONFIGURATION = 0x02,
|
||||||
|
ACTIVE_POLLING_IR_CAMERA_DATA = 0x03,
|
||||||
|
MCU_UPDATE_STATE = 0x23,
|
||||||
|
STANDARD_FULL_60HZ = 0x30,
|
||||||
|
NFC_IR_MODE_60HZ = 0x31,
|
||||||
|
SIMPLE_HID_MODE = 0x3F,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class GyroSensitivity {
|
||||||
|
DPS250,
|
||||||
|
DPS500,
|
||||||
|
DPS1000,
|
||||||
|
DPS2000,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class AccelerometerSensitivity {
|
||||||
|
G8,
|
||||||
|
G4,
|
||||||
|
G2,
|
||||||
|
G16,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class GyroPerformance {
|
||||||
|
HZ833,
|
||||||
|
HZ208,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class AccelerometerPerformance {
|
||||||
|
HZ200,
|
||||||
|
HZ100,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ImuSensorCalibration {
|
||||||
|
s16 offset;
|
||||||
|
s16 scale;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ImuCalibration {
|
||||||
|
std::array<ImuSensorCalibration, 3> accelerometer;
|
||||||
|
std::array<ImuSensorCalibration, 3> gyro;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct JoyStickAxisCalibration {
|
||||||
|
u16 max;
|
||||||
|
u16 min;
|
||||||
|
u16 center;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct JoyStickCalibration {
|
||||||
|
JoyStickAxisCalibration x;
|
||||||
|
JoyStickAxisCalibration y;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CalibrationData {
|
||||||
|
JoyStickCalibration left_stick;
|
||||||
|
JoyStickCalibration right_stick;
|
||||||
|
ImuCalibration imu;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Color {
|
||||||
|
u32 body;
|
||||||
|
u32 buttons;
|
||||||
|
u32 left_grip;
|
||||||
|
u32 right_grip;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VibrationValue {
|
||||||
|
f32 low_amplitude;
|
||||||
|
f32 low_frequency;
|
||||||
|
f32 high_amplitude;
|
||||||
|
f32 high_frequency;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct JoyconHandle {
|
||||||
|
SDL_hid_device* handle;
|
||||||
|
u8 packet_counter;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increments and returns the packet counter of the handle
|
||||||
|
* @param joycon_handle device to send the data
|
||||||
|
* @returns packet counter value
|
||||||
|
*/
|
||||||
|
u8 GetCounter(JoyconHandle& joycon_handle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies and sets the joycon_handle if device is valid
|
||||||
|
* @param joycon_handle device to send the data
|
||||||
|
* @param device device info from the driver
|
||||||
|
* @returns true if the device is valid
|
||||||
|
*/
|
||||||
|
bool CheckDeviceAccess(JoyconHandle& joycon_handle, SDL_hid_device_info* device);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a request to set the configuration of the motion sensor
|
||||||
|
* @param joycon_handle device to send the data
|
||||||
|
* @param gsen gyro sensitivity
|
||||||
|
* @param gfrec gyro update frequency
|
||||||
|
* @param asen accelerometer sensitivity
|
||||||
|
* @param afrec accelerometer update frequency
|
||||||
|
*/
|
||||||
|
void SetImuConfig(JoyconHandle& joycon_handle, GyroSensitivity gsen, GyroPerformance gfrec,
|
||||||
|
AccelerometerSensitivity asen, AccelerometerPerformance afrec);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encondes the amplitude to be sended on a packet
|
||||||
|
* @param amplification original amplification value
|
||||||
|
* @returns encoded amplification value
|
||||||
|
*/
|
||||||
|
f32 EncodeRumbleAmplification(f32 amplification);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a request to set the vibration of the joycon
|
||||||
|
* @param joycon_handle device to send the data
|
||||||
|
* @param vibration amplitude and frequency of the vibration
|
||||||
|
*/
|
||||||
|
void SetVibration(JoyconHandle& joycon_handle, VibrationValue vibration);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a request to set the polling mode of the joycon
|
||||||
|
* @param joycon_handle device to send the data
|
||||||
|
* @param report_mode polling mode to be set
|
||||||
|
*/
|
||||||
|
void SetReportMode(JoyconHandle& joycon_handle, Joycon::ReportMode report_mode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a request to set a specific led pattern
|
||||||
|
* @param joycon_handle device to send the data
|
||||||
|
* @param leds led pattern to be set
|
||||||
|
*/
|
||||||
|
void SetLedConfig(JoyconHandle& joycon_handle, u8 leds);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a request to obtain the joycon colors from memory
|
||||||
|
* @param joycon_handle device to read the data
|
||||||
|
* @returns color object with the colors of the joycon
|
||||||
|
*/
|
||||||
|
Color GetColor(JoyconHandle& joycon_handle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a request to obtain the joycon mac address from handle
|
||||||
|
* @param joycon_handle device to read the data
|
||||||
|
* @returns array containing the mac address
|
||||||
|
*/
|
||||||
|
std::array<u8, 6> GetMacAddress(JoyconHandle& joycon_handle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a request to obtain the joycon serial number from memory
|
||||||
|
* @param joycon_handle device to read the data
|
||||||
|
* @returns array containing the serial number
|
||||||
|
*/
|
||||||
|
std::array<u8, 15> GetSerialNumber(JoyconHandle& joycon_handle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a request to obtain the joycon type from handle
|
||||||
|
* @param joycon_handle device to read the data
|
||||||
|
* @returns controller type of the joycon
|
||||||
|
*/
|
||||||
|
ControllerType GetDeviceType(JoyconHandle& joycon_handle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a request to obtain the joycon tyoe from memory
|
||||||
|
* @param joycon_handle device to read the data
|
||||||
|
* @returns controller type of the joycon
|
||||||
|
*/
|
||||||
|
ControllerType GetDeviceType(SDL_hid_device_info* device_info);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a request to obtain the joycon firmware version
|
||||||
|
* @param joycon_handle device to read the data
|
||||||
|
* @returns u16 with the version number
|
||||||
|
*/
|
||||||
|
u16 GetVersionNumber(JoyconHandle& joycon_handle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a request to obtain the left stick calibration from memory
|
||||||
|
* @param joycon_handle device to read the data
|
||||||
|
* @param is_factory_calibration if true factory values will be returned
|
||||||
|
* @returns JoyStickCalibration of the left joystick
|
||||||
|
*/
|
||||||
|
JoyStickCalibration GetLeftJoyStickCalibration(JoyconHandle& joycon_handle,
|
||||||
|
bool is_factory_calibration);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a request to obtain the right stick calibration from memory
|
||||||
|
* @param joycon_handle device to read the data
|
||||||
|
* @param is_factory_calibration if true factory values will be returned
|
||||||
|
* @returns JoyStickCalibration of the left joystick
|
||||||
|
*/
|
||||||
|
JoyStickCalibration GetRightJoyStickCalibration(JoyconHandle& joycon_handle,
|
||||||
|
bool is_factory_calibration);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a request to obtain the motion calibration from memory
|
||||||
|
* @param joycon_handle device to read the data
|
||||||
|
* @param is_factory_calibration if true factory values will be returned
|
||||||
|
* @returns ImuCalibration of the joystick motion
|
||||||
|
*/
|
||||||
|
ImuCalibration GetImuCalibration(JoyconHandle& joycon_handle, bool is_factory_calibration);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests user calibration from the joystick
|
||||||
|
* @param joycon_handle device to read the data
|
||||||
|
* @param controller_type type of calibration to be requested
|
||||||
|
* @returns User CalibrationData of the joystick
|
||||||
|
*/
|
||||||
|
CalibrationData GetUserCalibrationData(JoyconHandle& joycon_handle, ControllerType controller_type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests factory calibration from the joystick
|
||||||
|
* @param joycon_handle device to read the data
|
||||||
|
* @param controller_type type of calibration to be requested
|
||||||
|
* @returns Factory CalibrationData of the joystick
|
||||||
|
*/
|
||||||
|
CalibrationData GetFactoryCalibrationData(JoyconHandle& joycon_handle,
|
||||||
|
ControllerType controller_type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a request to enable motion
|
||||||
|
* @param joycon_handle device to read the data
|
||||||
|
* @param is_factory_calibration if true motion data will be enabled
|
||||||
|
*/
|
||||||
|
void EnableImu(JoyconHandle& joycon_handle, bool enable);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a request to enable vibrations
|
||||||
|
* @param joycon_handle device to read the data
|
||||||
|
* @param is_factory_calibration if true rumble will be enabled
|
||||||
|
*/
|
||||||
|
void EnableRumble(JoyconHandle& joycon_handle, bool enable);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends data to the joycon device
|
||||||
|
* @param joycon_handle device to send the data
|
||||||
|
* @param buffer data to be send
|
||||||
|
* @param size size in bytes of the buffer
|
||||||
|
*/
|
||||||
|
void SendData(JoyconHandle& joycon_handle, std::span<const u8> buffer, std::size_t size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waits for incoming data of the joycon device that matchs the subcommand
|
||||||
|
* @param joycon_handle device to read the data
|
||||||
|
* @param sub_command type of data to be returned
|
||||||
|
* @returns a buffer containing the responce
|
||||||
|
*/
|
||||||
|
std::vector<u8> GetResponse(JoyconHandle& joycon_handle, SubCommand sub_command);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends data to the joycon device
|
||||||
|
* @param joycon_handle device to send the data
|
||||||
|
* @param buffer data to be send
|
||||||
|
* @param size size in bytes of the buffer
|
||||||
|
*/
|
||||||
|
std::vector<u8> SendSubCommand(JoyconHandle& joycon_handle, SubCommand sc,
|
||||||
|
std::span<const u8> buffer, std::size_t size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends data to the joycon device
|
||||||
|
* @param joycon_handle device to send the data
|
||||||
|
* @param buffer data to be send
|
||||||
|
* @param size size in bytes of the buffer
|
||||||
|
*/
|
||||||
|
std::vector<u8> ReadSPI(JoyconHandle& joycon_handle, CalAddr addr, u8 size);
|
||||||
|
|
||||||
|
} // namespace InputCommon::Joycon
|
||||||
@@ -18,6 +18,7 @@
|
|||||||
#include "input_common/input_poller.h"
|
#include "input_common/input_poller.h"
|
||||||
#include "input_common/main.h"
|
#include "input_common/main.h"
|
||||||
#ifdef HAVE_SDL2
|
#ifdef HAVE_SDL2
|
||||||
|
#include "input_common/drivers/joycon.h"
|
||||||
#include "input_common/drivers/sdl_driver.h"
|
#include "input_common/drivers/sdl_driver.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -87,6 +88,15 @@ struct InputSubsystem::Impl {
|
|||||||
sdl_input_factory);
|
sdl_input_factory);
|
||||||
Common::Input::RegisterFactory<Common::Input::OutputDevice>(sdl->GetEngineName(),
|
Common::Input::RegisterFactory<Common::Input::OutputDevice>(sdl->GetEngineName(),
|
||||||
sdl_output_factory);
|
sdl_output_factory);
|
||||||
|
|
||||||
|
joycon = std::make_shared<Joycons>("joycon");
|
||||||
|
joycon->SetMappingCallback(mapping_callback);
|
||||||
|
joycon_input_factory = std::make_shared<InputFactory>(joycon);
|
||||||
|
joycon_output_factory = std::make_shared<OutputFactory>(joycon);
|
||||||
|
Common::Input::RegisterFactory<Common::Input::InputDevice>(joycon->GetEngineName(),
|
||||||
|
joycon_input_factory);
|
||||||
|
Common::Input::RegisterFactory<Common::Input::OutputDevice>(joycon->GetEngineName(),
|
||||||
|
joycon_output_factory);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Common::Input::RegisterFactory<Common::Input::InputDevice>(
|
Common::Input::RegisterFactory<Common::Input::InputDevice>(
|
||||||
@@ -123,6 +133,10 @@ struct InputSubsystem::Impl {
|
|||||||
Common::Input::UnregisterFactory<Common::Input::InputDevice>(sdl->GetEngineName());
|
Common::Input::UnregisterFactory<Common::Input::InputDevice>(sdl->GetEngineName());
|
||||||
Common::Input::UnregisterFactory<Common::Input::OutputDevice>(sdl->GetEngineName());
|
Common::Input::UnregisterFactory<Common::Input::OutputDevice>(sdl->GetEngineName());
|
||||||
sdl.reset();
|
sdl.reset();
|
||||||
|
|
||||||
|
Common::Input::UnregisterFactory<Common::Input::InputDevice>(joycon->GetEngineName());
|
||||||
|
Common::Input::UnregisterFactory<Common::Input::OutputDevice>(joycon->GetEngineName());
|
||||||
|
joycon.reset();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Common::Input::UnregisterFactory<Common::Input::InputDevice>("touch_from_button");
|
Common::Input::UnregisterFactory<Common::Input::InputDevice>("touch_from_button");
|
||||||
@@ -135,14 +149,17 @@ struct InputSubsystem::Impl {
|
|||||||
};
|
};
|
||||||
|
|
||||||
auto keyboard_devices = keyboard->GetInputDevices();
|
auto keyboard_devices = keyboard->GetInputDevices();
|
||||||
devices.insert(devices.end(), keyboard_devices.begin(), keyboard_devices.end());
|
|
||||||
auto mouse_devices = mouse->GetInputDevices();
|
auto mouse_devices = mouse->GetInputDevices();
|
||||||
devices.insert(devices.end(), mouse_devices.begin(), mouse_devices.end());
|
|
||||||
auto gcadapter_devices = gcadapter->GetInputDevices();
|
auto gcadapter_devices = gcadapter->GetInputDevices();
|
||||||
devices.insert(devices.end(), gcadapter_devices.begin(), gcadapter_devices.end());
|
|
||||||
auto udp_devices = udp_client->GetInputDevices();
|
auto udp_devices = udp_client->GetInputDevices();
|
||||||
|
|
||||||
|
devices.insert(devices.end(), keyboard_devices.begin(), keyboard_devices.end());
|
||||||
|
devices.insert(devices.end(), mouse_devices.begin(), mouse_devices.end());
|
||||||
|
devices.insert(devices.end(), gcadapter_devices.begin(), gcadapter_devices.end());
|
||||||
devices.insert(devices.end(), udp_devices.begin(), udp_devices.end());
|
devices.insert(devices.end(), udp_devices.begin(), udp_devices.end());
|
||||||
#ifdef HAVE_SDL2
|
#ifdef HAVE_SDL2
|
||||||
|
auto joycon_devices = joycon->GetInputDevices();
|
||||||
|
devices.insert(devices.end(), joycon_devices.begin(), joycon_devices.end());
|
||||||
auto sdl_devices = sdl->GetInputDevices();
|
auto sdl_devices = sdl->GetInputDevices();
|
||||||
devices.insert(devices.end(), sdl_devices.begin(), sdl_devices.end());
|
devices.insert(devices.end(), sdl_devices.begin(), sdl_devices.end());
|
||||||
#endif
|
#endif
|
||||||
@@ -172,6 +189,9 @@ struct InputSubsystem::Impl {
|
|||||||
if (engine == sdl->GetEngineName()) {
|
if (engine == sdl->GetEngineName()) {
|
||||||
return sdl->GetAnalogMappingForDevice(params);
|
return sdl->GetAnalogMappingForDevice(params);
|
||||||
}
|
}
|
||||||
|
if (engine == joycon->GetEngineName()) {
|
||||||
|
return joycon->GetAnalogMappingForDevice(params);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@@ -195,6 +215,9 @@ struct InputSubsystem::Impl {
|
|||||||
if (engine == sdl->GetEngineName()) {
|
if (engine == sdl->GetEngineName()) {
|
||||||
return sdl->GetButtonMappingForDevice(params);
|
return sdl->GetButtonMappingForDevice(params);
|
||||||
}
|
}
|
||||||
|
if (engine == joycon->GetEngineName()) {
|
||||||
|
return joycon->GetButtonMappingForDevice(params);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@@ -212,6 +235,9 @@ struct InputSubsystem::Impl {
|
|||||||
if (engine == sdl->GetEngineName()) {
|
if (engine == sdl->GetEngineName()) {
|
||||||
return sdl->GetMotionMappingForDevice(params);
|
return sdl->GetMotionMappingForDevice(params);
|
||||||
}
|
}
|
||||||
|
if (engine == joycon->GetEngineName()) {
|
||||||
|
return joycon->GetMotionMappingForDevice(params);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@@ -237,6 +263,9 @@ struct InputSubsystem::Impl {
|
|||||||
if (engine == sdl->GetEngineName()) {
|
if (engine == sdl->GetEngineName()) {
|
||||||
return sdl->GetUIName(params);
|
return sdl->GetUIName(params);
|
||||||
}
|
}
|
||||||
|
if (engine == joycon->GetEngineName()) {
|
||||||
|
return joycon->GetUIName(params);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
return Common::Input::ButtonNames::Invalid;
|
return Common::Input::ButtonNames::Invalid;
|
||||||
}
|
}
|
||||||
@@ -281,6 +310,9 @@ struct InputSubsystem::Impl {
|
|||||||
if (engine == sdl->GetEngineName()) {
|
if (engine == sdl->GetEngineName()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (engine == joycon->GetEngineName()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -292,6 +324,7 @@ struct InputSubsystem::Impl {
|
|||||||
udp_client->BeginConfiguration();
|
udp_client->BeginConfiguration();
|
||||||
#ifdef HAVE_SDL2
|
#ifdef HAVE_SDL2
|
||||||
sdl->BeginConfiguration();
|
sdl->BeginConfiguration();
|
||||||
|
joycon->BeginConfiguration();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,6 +335,7 @@ struct InputSubsystem::Impl {
|
|||||||
udp_client->EndConfiguration();
|
udp_client->EndConfiguration();
|
||||||
#ifdef HAVE_SDL2
|
#ifdef HAVE_SDL2
|
||||||
sdl->EndConfiguration();
|
sdl->EndConfiguration();
|
||||||
|
joycon->EndConfiguration();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -335,6 +369,9 @@ struct InputSubsystem::Impl {
|
|||||||
std::shared_ptr<SDLDriver> sdl;
|
std::shared_ptr<SDLDriver> sdl;
|
||||||
std::shared_ptr<InputFactory> sdl_input_factory;
|
std::shared_ptr<InputFactory> sdl_input_factory;
|
||||||
std::shared_ptr<OutputFactory> sdl_output_factory;
|
std::shared_ptr<OutputFactory> sdl_output_factory;
|
||||||
|
std::shared_ptr<Joycons> joycon;
|
||||||
|
std::shared_ptr<InputFactory> joycon_input_factory;
|
||||||
|
std::shared_ptr<OutputFactory> joycon_output_factory;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -67,6 +67,18 @@ QString GetButtonName(Common::Input::ButtonNames button_name) {
|
|||||||
return QObject::tr("R");
|
return QObject::tr("R");
|
||||||
case Common::Input::ButtonNames::TriggerL:
|
case Common::Input::ButtonNames::TriggerL:
|
||||||
return QObject::tr("L");
|
return QObject::tr("L");
|
||||||
|
case Common::Input::ButtonNames::TriggerZR:
|
||||||
|
return QObject::tr("ZR");
|
||||||
|
case Common::Input::ButtonNames::TriggerZL:
|
||||||
|
return QObject::tr("ZL");
|
||||||
|
case Common::Input::ButtonNames::TriggerSR:
|
||||||
|
return QObject::tr("SR");
|
||||||
|
case Common::Input::ButtonNames::TriggerSL:
|
||||||
|
return QObject::tr("SL");
|
||||||
|
case Common::Input::ButtonNames::ButtonStickL:
|
||||||
|
return QObject::tr("Stick L");
|
||||||
|
case Common::Input::ButtonNames::ButtonStickR:
|
||||||
|
return QObject::tr("Stick R");
|
||||||
case Common::Input::ButtonNames::ButtonA:
|
case Common::Input::ButtonNames::ButtonA:
|
||||||
return QObject::tr("A");
|
return QObject::tr("A");
|
||||||
case Common::Input::ButtonNames::ButtonB:
|
case Common::Input::ButtonNames::ButtonB:
|
||||||
@@ -77,6 +89,14 @@ QString GetButtonName(Common::Input::ButtonNames button_name) {
|
|||||||
return QObject::tr("Y");
|
return QObject::tr("Y");
|
||||||
case Common::Input::ButtonNames::ButtonStart:
|
case Common::Input::ButtonNames::ButtonStart:
|
||||||
return QObject::tr("Start");
|
return QObject::tr("Start");
|
||||||
|
case Common::Input::ButtonNames::ButtonPlus:
|
||||||
|
return QObject::tr("Plus");
|
||||||
|
case Common::Input::ButtonNames::ButtonMinus:
|
||||||
|
return QObject::tr("Minus");
|
||||||
|
case Common::Input::ButtonNames::ButtonHome:
|
||||||
|
return QObject::tr("Home");
|
||||||
|
case Common::Input::ButtonNames::ButtonCapture:
|
||||||
|
return QObject::tr("Capture");
|
||||||
case Common::Input::ButtonNames::L1:
|
case Common::Input::ButtonNames::L1:
|
||||||
return QObject::tr("L1");
|
return QObject::tr("L1");
|
||||||
case Common::Input::ButtonNames::L2:
|
case Common::Input::ButtonNames::L2:
|
||||||
|
|||||||
Reference in New Issue
Block a user