Compare commits
3 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d4a28bbb74 | ||
|
|
e2523c3828 | ||
|
|
693a1fe1e8 |
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -37,3 +37,6 @@
|
||||
[submodule "opus"]
|
||||
path = externals/opus/opus
|
||||
url = https://github.com/xiph/opus.git
|
||||
[submodule "externals/hidapi/hidapi"]
|
||||
path = externals/hidapi/hidapi
|
||||
url = https://github.com/libusb/hidapi.git
|
||||
|
||||
@@ -384,6 +384,10 @@ if (NOT LIBUSB_FOUND)
|
||||
set(LIBUSB_LIBRARIES usb)
|
||||
endif()
|
||||
|
||||
find_package(HIDAPI)
|
||||
add_subdirectory(externals/hidapi)
|
||||
set(HIDAPI_LIBRARIES hidapi)
|
||||
|
||||
# Use system installed ffmpeg.
|
||||
if (NOT MSVC)
|
||||
find_package(FFmpeg REQUIRED)
|
||||
|
||||
20
externals/hidapi/CMakeLists.txt
vendored
Normal file
20
externals/hidapi/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
project(hidapi)
|
||||
|
||||
add_library(hidapi STATIC hidapi/hidapi/hidapi.h)
|
||||
target_include_directories(hidapi PUBLIC hidapi/hidapi)
|
||||
|
||||
if(APPLE)
|
||||
target_sources(hidapi PRIVATE hidapi/mac/hid.c)
|
||||
elseif(MSVC)
|
||||
target_sources(hidapi PRIVATE hidapi/windows/hid.c)
|
||||
else()
|
||||
find_package(Libudev)
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND LIBUDEV_FOUND)
|
||||
target_sources(hidapi PRIVATE hidapi/linux/hid.c)
|
||||
target_link_libraries(hidapi PRIVATE hidapi/udev)
|
||||
else()
|
||||
target_sources(hidapi PRIVATE hidapi/libusb/hid.c)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_library(Hidapi::Hidapi ALIAS hidapi)
|
||||
1
externals/hidapi/hidapi
vendored
Submodule
1
externals/hidapi/hidapi
vendored
Submodule
Submodule externals/hidapi/hidapi added at ad27b46170
@@ -21,6 +21,10 @@ add_library(input_common STATIC
|
||||
mouse/mouse_input.h
|
||||
mouse/mouse_poller.cpp
|
||||
mouse/mouse_poller.h
|
||||
joycon/jc_adapter.cpp
|
||||
joycon/jc_adapter.h
|
||||
joycon/jc_poller.cpp
|
||||
joycon/jc_poller.h
|
||||
sdl/sdl.cpp
|
||||
sdl/sdl.h
|
||||
udp/client.cpp
|
||||
@@ -76,5 +80,7 @@ endif()
|
||||
target_include_directories(input_common SYSTEM PRIVATE ${LIBUSB_INCLUDE_DIR})
|
||||
target_link_libraries(input_common PRIVATE ${LIBUSB_LIBRARIES})
|
||||
|
||||
target_link_libraries(input_common PUBLIC ${HIDAPI_LIBRARIES})
|
||||
|
||||
create_target_directory_groups(input_common)
|
||||
target_link_libraries(input_common PUBLIC core PRIVATE common Boost::boost)
|
||||
|
||||
986
src/input_common/joycon/jc_adapter.cpp
Normal file
986
src/input_common/joycon/jc_adapter.cpp
Normal file
@@ -0,0 +1,986 @@
|
||||
// Copyright 2020 Yuzu Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <span>
|
||||
#include <thread>
|
||||
#include "common/logging/log.h"
|
||||
#include "input_common/joycon/jc_adapter.h"
|
||||
|
||||
namespace JCAdapter {
|
||||
|
||||
/// Used to loop through and assign button in poller
|
||||
constexpr std::array<PadButton, 22> PadButtonArray{
|
||||
PadButton::BUTTON_DOWN, PadButton::BUTTON_UP, PadButton::BUTTON_RIGHT,
|
||||
PadButton::BUTTON_LEFT, PadButton::BUTTON_L_SR, PadButton::BUTTON_L_SL,
|
||||
PadButton::TRIGGER_L, PadButton::TRIGGER_ZL, PadButton::BUTTON_Y,
|
||||
PadButton::BUTTON_X, PadButton::BUTTON_B, PadButton::BUTTON_A,
|
||||
PadButton::BUTTON_R_SL, PadButton::BUTTON_R_SR, PadButton::TRIGGER_R,
|
||||
PadButton::TRIGGER_ZR, PadButton::BUTTON_MINUS, PadButton::BUTTON_PLUS,
|
||||
PadButton::STICK_R, PadButton::STICK_L, PadButton::BUTTON_HOME,
|
||||
PadButton::BUTTON_CAPTURE,
|
||||
};
|
||||
|
||||
Joycons::Joycons() {
|
||||
LOG_INFO(Input, "JC Adapter Initialization started");
|
||||
global_counter = 0;
|
||||
adapter_thread_running = false;
|
||||
const int init_res = hid_init();
|
||||
if (init_res == 0) {
|
||||
Setup();
|
||||
} else {
|
||||
LOG_ERROR(Input, "HIDapi could not be initialized. failed with error = {}", init_res);
|
||||
}
|
||||
}
|
||||
|
||||
void Joycons::SendWrite(hid_device* handle, std::vector<u8> buffer, int size) {
|
||||
hid_write(handle, buffer.data(), size);
|
||||
}
|
||||
|
||||
u8 Joycons::GetCounter() {
|
||||
const u8 counter = global_counter;
|
||||
global_counter = (global_counter + 1) & 0x0F;
|
||||
return counter;
|
||||
}
|
||||
|
||||
std::vector<u8> Joycons::SubCommand(hid_device* handle, SubComamnd sc, std::vector<u8> buffer,
|
||||
int size) {
|
||||
std::vector<u8> local_buffer(size + 11);
|
||||
|
||||
local_buffer[0] = static_cast<u8>(Output::RUMBLE_AND_SUBCMD);
|
||||
local_buffer[1] = GetCounter();
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
local_buffer[i + 2] = default_buffer[i];
|
||||
}
|
||||
local_buffer[10] = static_cast<u8>(sc);
|
||||
for (int i = 0; i < size; ++i) {
|
||||
local_buffer[11 + i] = buffer[i];
|
||||
}
|
||||
|
||||
SendWrite(handle, local_buffer, size + 11);
|
||||
return GetResponse(handle, sc);
|
||||
}
|
||||
|
||||
std::vector<u8> Joycons::GetResponse(hid_device* handle, SubComamnd sc) {
|
||||
int tries = 0;
|
||||
std::vector<u8> buffer(max_resp_size);
|
||||
do {
|
||||
int result = hid_read_timeout(handle, buffer.data(), max_resp_size, 100);
|
||||
if (result < 1) {
|
||||
LOG_ERROR(Input, "No response from joystick");
|
||||
}
|
||||
tries++;
|
||||
} while (tries < 10 && buffer[0] != 0x21 && buffer[14] != static_cast<u8>(sc));
|
||||
return buffer;
|
||||
}
|
||||
|
||||
std::vector<u8> Joycons::ReadSPI(hid_device* 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 (int i = 0; i < 100; ++i) {
|
||||
local_buffer = SubCommand(handle, SubComamnd::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());
|
||||
}
|
||||
|
||||
void Joycons::SetJoyStickCal(std::vector<u8> buffer, JoyStick& axis1, JoyStick& axis2, bool left) {
|
||||
if (left) {
|
||||
axis1.max = (u16)(((buffer[1] & 0x0F) << 8) | buffer[0]);
|
||||
axis2.max = (u16)((buffer[2] << 4) | (buffer[1] >> 4));
|
||||
axis1.center = (u16)(((buffer[4] & 0x0F) << 8) | buffer[3]);
|
||||
axis2.center = (u16)((buffer[5] << 4) | (buffer[4] >> 4));
|
||||
axis1.min = (u16)(((buffer[7] & 0x0F) << 8) | buffer[6]);
|
||||
axis2.min = (u16)((buffer[8] << 4) | (buffer[7] >> 4));
|
||||
} else {
|
||||
axis1.center = (u16)(((buffer[1] & 0x0F) << 8) | buffer[0]);
|
||||
axis2.center = (u16)((buffer[2] << 4) | (buffer[1] >> 4));
|
||||
axis1.min = (u16)(((buffer[4] & 0x0F) << 8) | buffer[3]);
|
||||
axis2.min = (u16)((buffer[5] << 4) | (buffer[4] >> 4));
|
||||
axis1.max = (u16)(((buffer[7] & 0x0F) << 8) | buffer[6]);
|
||||
axis2.max = (u16)((buffer[8] << 4) | (buffer[7] >> 4));
|
||||
}
|
||||
|
||||
/* Nintendo fix for drifting stick
|
||||
buffer = ReadSPI(0x60, (isLeft ? 0x86 : 0x98), 16);
|
||||
joycon[0].joystick.deadzone = (u16)((buffer[4] << 8) & 0xF00 | buffer[3]);*/
|
||||
}
|
||||
|
||||
void Joycons::SetImuCal(Joycon& jc, std::vector<u8> buffer) {
|
||||
for (std::size_t i = 0; i < jc.imu.size(); ++i) {
|
||||
jc.imu[i].acc.offset = (u16)(buffer[0 + (i * 2)] | (buffer[1 + (i * 2)] << 8));
|
||||
jc.imu[i].acc.scale = (u16)(buffer[0 + 6 + (i * 2)] | (buffer[1 + 6 + (i * 2)] << 8));
|
||||
jc.imu[i].gyr.offset = (u16)(buffer[0 + 12 + (i * 2)] | (buffer[1 + 12 + (i * 2)] << 8));
|
||||
jc.imu[i].gyr.scale = (u16)(buffer[0 + 18 + (i * 2)] | (buffer[1 + 18 + (i * 2)] << 8));
|
||||
}
|
||||
}
|
||||
|
||||
void Joycons::GetUserCalibrationData(Joycon& jc) {
|
||||
std::vector<u8> buffer;
|
||||
hid_set_nonblocking(jc.handle, 0);
|
||||
|
||||
switch (jc.type) {
|
||||
case JoyControllerTypes::Left:
|
||||
buffer = ReadSPI(jc.handle, CalAddr::USER_LEFT_DATA, 9);
|
||||
SetJoyStickCal(buffer, jc.axis[0], jc.axis[1], true);
|
||||
break;
|
||||
case JoyControllerTypes::Right:
|
||||
buffer = ReadSPI(jc.handle, CalAddr::USER_RIGHT_DATA, 9);
|
||||
SetJoyStickCal(buffer, jc.axis[2], jc.axis[3], false);
|
||||
break;
|
||||
case JoyControllerTypes::Pro:
|
||||
buffer = ReadSPI(jc.handle, CalAddr::USER_LEFT_DATA, 9);
|
||||
SetJoyStickCal(buffer, jc.axis[0], jc.axis[1], true);
|
||||
buffer = ReadSPI(jc.handle, CalAddr::USER_RIGHT_DATA, 9);
|
||||
SetJoyStickCal(buffer, jc.axis[2], jc.axis[3], false);
|
||||
break;
|
||||
case JoyControllerTypes::None:
|
||||
break;
|
||||
}
|
||||
|
||||
buffer = ReadSPI(jc.handle, CalAddr::USER_IMU_DATA, 24);
|
||||
SetImuCal(jc, buffer);
|
||||
hid_set_nonblocking(jc.handle, 1);
|
||||
}
|
||||
|
||||
void Joycons::GetFactoryCalibrationData(Joycon& jc) {
|
||||
std::vector<u8> buffer;
|
||||
hid_set_nonblocking(jc.handle, 0);
|
||||
|
||||
switch (jc.type) {
|
||||
case JoyControllerTypes::Left:
|
||||
buffer = ReadSPI(jc.handle, CalAddr::FACT_LEFT_DATA, 9);
|
||||
SetJoyStickCal(buffer, jc.axis[0], jc.axis[1], true);
|
||||
break;
|
||||
case JoyControllerTypes::Right:
|
||||
buffer = ReadSPI(jc.handle, CalAddr::FACT_RIGHT_DATA, 9);
|
||||
SetJoyStickCal(buffer, jc.axis[2], jc.axis[3], false);
|
||||
break;
|
||||
case JoyControllerTypes::Pro:
|
||||
buffer = ReadSPI(jc.handle, CalAddr::FACT_LEFT_DATA, 9);
|
||||
SetJoyStickCal(buffer, jc.axis[0], jc.axis[1], true);
|
||||
buffer = ReadSPI(jc.handle, CalAddr::FACT_RIGHT_DATA, 9);
|
||||
SetJoyStickCal(buffer, jc.axis[2], jc.axis[3], false);
|
||||
break;
|
||||
case JoyControllerTypes::None:
|
||||
break;
|
||||
}
|
||||
|
||||
buffer = ReadSPI(jc.handle, CalAddr::FACT_IMU_DATA, 24);
|
||||
SetImuCal(jc, buffer);
|
||||
hid_set_nonblocking(jc.handle, 1);
|
||||
}
|
||||
|
||||
s16 Joycons::GetRawIMUValues(std::size_t sensor, size_t axis, std::vector<u8> buffer) {
|
||||
const size_t offset = (sensor * 6) + (axis * 2);
|
||||
return static_cast<s16>(buffer[13 + offset] | (buffer[14 + offset] << 8));
|
||||
}
|
||||
|
||||
f32 Joycons::TransformAccValue(s16 raw, ImuData cal, AccSensitivity sen) {
|
||||
// const f32 value = (raw - cal.offset) * cal.scale / 65535.0f / 1000.0f;
|
||||
const f32 value = raw * (1.0f / (cal.scale - cal.offset)) * 4;
|
||||
switch (sen) {
|
||||
case AccSensitivity::G2:
|
||||
return value / 4.0f;
|
||||
case AccSensitivity::G4:
|
||||
return value / 2.0f;
|
||||
case AccSensitivity::G8:
|
||||
return value;
|
||||
case AccSensitivity::G16:
|
||||
return value * 2.0f;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
f32 Joycons::TransformGyrValue(s16 raw, ImuData cal, GyrSensitivity sen) {
|
||||
// const f32 value = (raw - cal.offset) * cal.scale / 65535.0f / 360.0f / 3.8f;
|
||||
const f32 value = (raw - cal.offset) * (936.0f / (cal.scale - cal.offset)) / 360.0f;
|
||||
switch (sen) {
|
||||
case GyrSensitivity::DPS250:
|
||||
return value / 8.0f;
|
||||
case GyrSensitivity::DPS500:
|
||||
return value / 4.0f;
|
||||
case GyrSensitivity::DPS1000:
|
||||
return value / 2.0f;
|
||||
case GyrSensitivity::DPS2000:
|
||||
return value;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
void Joycons::GetIMUValues(Joycon& jc, std::vector<u8> buffer) {
|
||||
for (std::size_t i = 0; i < jc.imu.size(); ++i) {
|
||||
jc.imu[i].gyr.value = 0;
|
||||
jc.imu[i].acc.value = 0;
|
||||
}
|
||||
for (std::size_t i = 0; i < jc.imu.size(); ++i) {
|
||||
for (std::size_t sample = 0; sample < 3; ++sample) {
|
||||
const s16 raw_gyr = GetRawIMUValues((sample * 2) + 1, i, buffer);
|
||||
const s16 raw_acc = GetRawIMUValues(sample * 2, i, buffer);
|
||||
switch (i) {
|
||||
case 0:
|
||||
jc.gyro[sample].y = TransformGyrValue(raw_gyr, jc.imu[i].gyr, jc.gsen);
|
||||
jc.imu[1].gyr.value += TransformGyrValue(raw_gyr, jc.imu[i].gyr, jc.gsen) * 0.3333f;
|
||||
jc.imu[1].acc.value += TransformAccValue(raw_acc, jc.imu[i].acc, jc.asen) * 0.3333f;
|
||||
break;
|
||||
case 1:
|
||||
jc.gyro[sample].x = TransformGyrValue(raw_gyr, jc.imu[i].gyr, jc.gsen);
|
||||
jc.imu[0].gyr.value += TransformGyrValue(raw_gyr, jc.imu[i].gyr, jc.gsen) * 0.3333f;
|
||||
jc.imu[0].acc.value += TransformAccValue(raw_acc, jc.imu[i].acc, jc.asen) * 0.3333f;
|
||||
break;
|
||||
case 2:
|
||||
jc.gyro[sample].z = TransformGyrValue(raw_gyr, jc.imu[i].gyr, jc.gsen);
|
||||
jc.imu[2].gyr.value += TransformGyrValue(raw_gyr, jc.imu[i].gyr, jc.gsen) * 0.3333f;
|
||||
jc.imu[2].acc.value += TransformAccValue(raw_acc, jc.imu[i].acc, jc.asen) * 0.3333f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Joycons::SetImuConfig(Joycon& jc, GyrSensitivity gsen, GyrPerformance gfrec,
|
||||
AccSensitivity asen, AccPerformance afrec) {
|
||||
hid_set_nonblocking(jc.handle, 0);
|
||||
jc.gsen = gsen;
|
||||
jc.gfrec = gfrec;
|
||||
jc.asen = asen;
|
||||
jc.afrec = afrec;
|
||||
const std::vector<u8> buffer{static_cast<u8>(gsen), static_cast<u8>(asen),
|
||||
static_cast<u8>(gfrec), static_cast<u8>(afrec)};
|
||||
SubCommand(jc.handle, SubComamnd::SET_IMU_SENSITIVITY, buffer, 4);
|
||||
hid_set_nonblocking(jc.handle, 1);
|
||||
}
|
||||
|
||||
void Joycons::GetLeftPadInput(Joycon& jc, std::vector<u8> buffer) {
|
||||
jc.button = static_cast<u32>(buffer[5]);
|
||||
jc.button |= static_cast<u32>((buffer[4] & 0b00101001) << 16);
|
||||
jc.axis[0].value = static_cast<u16>(buffer[6] | ((buffer[7] & 0xf) << 8));
|
||||
jc.axis[1].value = static_cast<u16>((buffer[7] >> 4) | (buffer[8] << 4));
|
||||
}
|
||||
|
||||
void Joycons::GetRightPadInput(Joycon& jc, std::vector<u8> buffer) {
|
||||
jc.button = static_cast<u32>(buffer[3] << 8);
|
||||
jc.button |= static_cast<u32>((buffer[4] & 0b00010110) << 16);
|
||||
jc.axis[2].value = static_cast<u16>(buffer[9] | ((buffer[10] & 0xf) << 8));
|
||||
jc.axis[3].value = static_cast<u16>((buffer[10] >> 4) | (buffer[11] << 4));
|
||||
}
|
||||
|
||||
void Joycons::GetProPadInput(Joycon& jc, std::vector<u8> buffer) {
|
||||
jc.button = static_cast<u32>(buffer[5] & 0b11001111);
|
||||
jc.button |= static_cast<u32>((buffer[3] & 0b11001111) << 8);
|
||||
jc.button |= static_cast<u32>((buffer[4] & 0b00111111) << 16);
|
||||
jc.axis[0].value = static_cast<u16>(buffer[6] | ((buffer[7] & 0xf) << 8));
|
||||
jc.axis[1].value = static_cast<u16>((buffer[7] >> 4) | (buffer[8] << 4));
|
||||
jc.axis[2].value = static_cast<u16>(buffer[9] | ((buffer[10] & 0xf) << 8));
|
||||
jc.axis[3].value = static_cast<u16>((buffer[10] >> 4) | (buffer[11] << 4));
|
||||
}
|
||||
|
||||
void Joycons::SetRumble(std::size_t port, f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) {
|
||||
if (!DeviceConnected(port) || !joycon[port].rumble_enabled) {
|
||||
return;
|
||||
}
|
||||
freq_high = std::clamp(freq_high, 81.75177f, 1252.572266f);
|
||||
freq_low = std::clamp(freq_low, 40.875885f, 626.286133f);
|
||||
amp_high = std::clamp(amp_high, 0.0f, 1.0f);
|
||||
amp_low = std::clamp(amp_low, 0.0f, 1.0f);
|
||||
if (freq_high != joycon[port].hd_rumble.freq_high ||
|
||||
freq_low != joycon[port].hd_rumble.freq_low ||
|
||||
amp_high != joycon[port].hd_rumble.amp_high || amp_low != joycon[port].hd_rumble.amp_low) {
|
||||
joycon[port].hd_rumble.freq_high = freq_high;
|
||||
joycon[port].hd_rumble.freq_low = freq_low;
|
||||
joycon[port].hd_rumble.amp_high = amp_high;
|
||||
joycon[port].hd_rumble.amp_low = amp_low;
|
||||
SendRumble(port);
|
||||
}
|
||||
}
|
||||
|
||||
void Joycons::SendRumble(std::size_t port) {
|
||||
std::vector<u8> buffer(max_resp_size);
|
||||
|
||||
buffer[port] = static_cast<u8>(Output::RUMBLE_ONLY);
|
||||
buffer[1] = GetCounter();
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
buffer[2 + i] = default_buffer[i];
|
||||
}
|
||||
|
||||
if (joycon[port].hd_rumble.amp_low > 0 || joycon[port].hd_rumble.amp_high > 0) {
|
||||
u16 encoded_hf =
|
||||
static_cast<u16>(roundf(128 * log2f(joycon[port].hd_rumble.freq_high * 0.1f)) - 0x180);
|
||||
u8 encoded_lf =
|
||||
static_cast<u8>(roundf(32 * log2f(joycon[port].hd_rumble.freq_low * 0.1f)) - 0x40);
|
||||
u8 encoded_hamp =
|
||||
static_cast<u8>(EncodeRumbleAmplification(joycon[port].hd_rumble.amp_high));
|
||||
u8 encoded_lamp =
|
||||
static_cast<u8>(EncodeRumbleAmplification(joycon[port].hd_rumble.amp_low));
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
SendWrite(joycon[port].handle, buffer, max_resp_size);
|
||||
}
|
||||
|
||||
const f32 Joycons::EncodeRumbleAmplification(f32 amplification) {
|
||||
if (amplification < 0.01182) {
|
||||
return roundf(pow(amplification, 1.7f) * 7561);
|
||||
} else if (amplification < 0.11249) {
|
||||
return roundf((log(amplification) * 11.556f) + 55.3f);
|
||||
} else if (amplification < 0.22498) {
|
||||
return roundf((log2f(amplification) * 32) + 131);
|
||||
}
|
||||
|
||||
return roundf((log2f(amplification) * 64) + 200);
|
||||
}
|
||||
|
||||
const f32 Joycons::GetTemperatureCelcius(std::size_t port) {
|
||||
if (!DeviceConnected(port)) {
|
||||
return 0.0f;
|
||||
}
|
||||
return 25.0f + joycon[port].temperature * 0.0625f;
|
||||
}
|
||||
|
||||
const f32 Joycons::GetTemperatureFahrenheit(std::size_t port) {
|
||||
if (!DeviceConnected(port)) {
|
||||
return 0.0f;
|
||||
}
|
||||
return GetTemperatureCelcius(port) * 1.8f + 32;
|
||||
}
|
||||
|
||||
const u8 Joycons::GetBatteryLevel(std::size_t port) {
|
||||
if (!DeviceConnected(port)) {
|
||||
return 0x0;
|
||||
}
|
||||
return joycon[port].battery;
|
||||
}
|
||||
|
||||
const std::array<u8, 15> Joycons::GetSerialNumber(std::size_t port) {
|
||||
if (!DeviceConnected(port)) {
|
||||
return {};
|
||||
}
|
||||
return joycon[port].serial_number;
|
||||
}
|
||||
|
||||
const f32 Joycons::GetVersion(std::size_t port) {
|
||||
if (!DeviceConnected(port)) {
|
||||
return 0x0;
|
||||
}
|
||||
return joycon[port].version;
|
||||
}
|
||||
|
||||
const JoyControllerTypes Joycons::GetDeviceType(std::size_t port) {
|
||||
if (!DeviceConnected(port)) {
|
||||
return JoyControllerTypes::None;
|
||||
}
|
||||
return joycon[port].type;
|
||||
}
|
||||
|
||||
const std::array<u8, 6> Joycons::GetMac(std::size_t port) {
|
||||
if (!DeviceConnected(port)) {
|
||||
return {};
|
||||
}
|
||||
return joycon[port].mac;
|
||||
}
|
||||
|
||||
const u32 Joycons::GetBodyColor(std::size_t port) {
|
||||
if (!DeviceConnected(port)) {
|
||||
return 0x0;
|
||||
}
|
||||
return joycon[port].color.body;
|
||||
}
|
||||
|
||||
const u32 Joycons::GetButtonColor(std::size_t port) {
|
||||
if (!DeviceConnected(port)) {
|
||||
return 0x0;
|
||||
}
|
||||
return joycon[port].color.buttons;
|
||||
}
|
||||
|
||||
const u32 Joycons::GetLeftGripColor(std::size_t port) {
|
||||
if (!DeviceConnected(port)) {
|
||||
return 0x0;
|
||||
}
|
||||
return joycon[port].color.left_grip;
|
||||
}
|
||||
const u32 Joycons::GetRightGripColor(std::size_t port) {
|
||||
if (!DeviceConnected(port)) {
|
||||
return 0x0;
|
||||
}
|
||||
return joycon[port].color.right_grip;
|
||||
}
|
||||
|
||||
void Joycons::SetSerialNumber(Joycon& jc) {
|
||||
std::vector<u8> buffer;
|
||||
hid_set_nonblocking(jc.handle, 0);
|
||||
buffer = ReadSPI(jc.handle, CalAddr::SERIAL_NUMBER, 16);
|
||||
for (int i = 0; i < 15; ++i) {
|
||||
jc.serial_number[i] = buffer[i + 1];
|
||||
}
|
||||
hid_set_nonblocking(jc.handle, 1);
|
||||
}
|
||||
|
||||
void Joycons::SetDeviceType(Joycon& jc) {
|
||||
std::vector<u8> buffer;
|
||||
hid_set_nonblocking(jc.handle, 0);
|
||||
buffer = ReadSPI(jc.handle, CalAddr::DEVICE_TYPE, 1);
|
||||
jc.type = static_cast<JoyControllerTypes>(buffer[0]);
|
||||
hid_set_nonblocking(jc.handle, 1);
|
||||
}
|
||||
|
||||
void Joycons::GetColor(Joycon& jc) {
|
||||
std::vector<u8> buffer;
|
||||
hid_set_nonblocking(jc.handle, 0);
|
||||
buffer = ReadSPI(jc.handle, CalAddr::COLOR_DATA, 12);
|
||||
jc.color.body = (buffer[2] << 16) | (buffer[1] << 8) | buffer[0];
|
||||
jc.color.buttons = (buffer[5] << 16) | (buffer[4] << 8) | buffer[3];
|
||||
jc.color.left_grip = (buffer[8] << 16) | (buffer[7] << 8) | buffer[6];
|
||||
jc.color.right_grip = (buffer[11] << 16) | (buffer[10] << 8) | buffer[9];
|
||||
hid_set_nonblocking(jc.handle, 1);
|
||||
}
|
||||
|
||||
void Joycons::SetMac(Joycon& jc) {
|
||||
wchar_t mac[255];
|
||||
hid_get_serial_number_string(jc.handle, mac, std::size(mac));
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
wchar_t value[3] = {mac[i * 2], mac[(i * 2) + 1]};
|
||||
jc.mac[i] = static_cast<u8>(std::stoi(value, 0, 16));
|
||||
}
|
||||
}
|
||||
|
||||
void Joycons::SetVersionNumber(Joycon& jc) {
|
||||
jc.version = 0.0f;
|
||||
}
|
||||
|
||||
void Joycons::SetLedConfig(Joycon& jc, u8 leds) {
|
||||
hid_set_nonblocking(jc.handle, 0);
|
||||
jc.leds = leds;
|
||||
const std::vector<u8> buffer{leds};
|
||||
SubCommand(jc.handle, SubComamnd::SET_PLAYER_LIGHTS, buffer, 1);
|
||||
hid_set_nonblocking(jc.handle, 1);
|
||||
}
|
||||
|
||||
void Joycons::EnableImu(Joycon& jc, bool enable) {
|
||||
hid_set_nonblocking(jc.handle, 0);
|
||||
jc.imu_enabled = enable;
|
||||
const std::vector<u8> buffer{static_cast<u8>(enable ? 1 : 0)};
|
||||
SubCommand(jc.handle, SubComamnd::ENABLE_IMU, buffer, 1);
|
||||
hid_set_nonblocking(jc.handle, 1);
|
||||
}
|
||||
|
||||
void Joycons::EnableRumble(Joycon& jc, bool enable) {
|
||||
hid_set_nonblocking(jc.handle, 0);
|
||||
jc.rumble_enabled = enable;
|
||||
const std::vector<u8> buffer{static_cast<u8>(enable ? 1 : 0)};
|
||||
SubCommand(jc.handle, SubComamnd::ENABLE_VIBRATION, buffer, 1);
|
||||
hid_set_nonblocking(jc.handle, 1);
|
||||
}
|
||||
|
||||
void Joycons::SetReportMode(Joycon& jc, ReportMode mode) {
|
||||
hid_set_nonblocking(jc.handle, 0);
|
||||
jc.mode = mode;
|
||||
const std::vector<u8> buffer{static_cast<u8>(mode)};
|
||||
SubCommand(jc.handle, SubComamnd::SET_REPORT_MODE, buffer, 1);
|
||||
hid_set_nonblocking(jc.handle, 1);
|
||||
}
|
||||
|
||||
void Joycons::UpdateJoyconData(Joycon& jc, std::vector<u8> buffer) {
|
||||
switch (jc.type) {
|
||||
case JoyControllerTypes::Left:
|
||||
GetLeftPadInput(jc, buffer);
|
||||
if (jc.imu_enabled) {
|
||||
GetIMUValues(jc, buffer);
|
||||
jc.imu[1].acc.value = -jc.imu[1].acc.value;
|
||||
jc.imu[0].gyr.value = -jc.imu[0].gyr.value;
|
||||
jc.gyro[0].x = -jc.gyro[0].x;
|
||||
jc.gyro[1].x = -jc.gyro[1].x;
|
||||
jc.gyro[2].x = -jc.gyro[2].x;
|
||||
|
||||
jc.imu[2].acc.value = -jc.imu[2].acc.value;
|
||||
}
|
||||
break;
|
||||
case JoyControllerTypes::Right:
|
||||
GetRightPadInput(jc, buffer);
|
||||
if (jc.imu_enabled) {
|
||||
GetIMUValues(jc, buffer);
|
||||
jc.imu[0].acc.value = -jc.imu[0].acc.value;
|
||||
jc.imu[1].acc.value = -jc.imu[1].acc.value;
|
||||
jc.imu[2].gyr.value = -jc.imu[2].gyr.value;
|
||||
jc.gyro[0].z = -jc.gyro[0].z;
|
||||
jc.gyro[1].z = -jc.gyro[1].z;
|
||||
jc.gyro[2].z = -jc.gyro[2].z;
|
||||
}
|
||||
break;
|
||||
case JoyControllerTypes::Pro:
|
||||
GetProPadInput(jc, buffer);
|
||||
if (jc.imu_enabled) {
|
||||
GetIMUValues(jc, buffer);
|
||||
jc.imu[1].acc.value = -jc.imu[1].acc.value;
|
||||
jc.imu[2].acc.value = -jc.imu[2].acc.value;
|
||||
}
|
||||
break;
|
||||
case JoyControllerTypes::None:
|
||||
break;
|
||||
}
|
||||
const auto now = std::chrono::system_clock::now();
|
||||
u64 difference =
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(now - jc.last_motion_update).count();
|
||||
jc.last_motion_update = now;
|
||||
Common::Vec3f acceleration =
|
||||
Common::Vec3f(jc.imu[0].acc.value, jc.imu[1].acc.value, jc.imu[2].acc.value);
|
||||
jc.motion->SetAcceleration(acceleration);
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
jc.motion->SetGyroscope(jc.gyro[i]);
|
||||
jc.motion->UpdateRotation(difference / 3);
|
||||
jc.motion->UpdateOrientation(difference / 3);
|
||||
}
|
||||
jc.battery = buffer[2] >> 4;
|
||||
}
|
||||
|
||||
void Joycons::UpdateYuzuSettings(Joycon& jc, std::size_t port) {
|
||||
if (DeviceConnected(port) && configuring) {
|
||||
JCPadStatus pad;
|
||||
if (jc.button != 0) {
|
||||
pad.button = jc.button;
|
||||
pad_queue[port].Push(pad);
|
||||
}
|
||||
for (std::size_t i = 0; i < jc.axis.size(); ++i) {
|
||||
const u16 value = jc.axis[i].value;
|
||||
const u16 origin = jc.axis[i].center;
|
||||
if (value != 0) {
|
||||
if (value > origin + 500 || value < origin - 500) {
|
||||
pad.axis = static_cast<PadAxes>(i);
|
||||
pad.axis_value = value - origin;
|
||||
pad_queue[port].Push(pad);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (std::size_t i = 0; i < jc.imu.size(); ++i) {
|
||||
const f32 value = jc.imu[i].gyr.value;
|
||||
const f32 value2 = jc.imu[i].acc.value;
|
||||
if (value > 6.0f || value < -6.0f) {
|
||||
pad.motion = static_cast<PadMotion>(i);
|
||||
pad.motion_value = value;
|
||||
pad_queue[port].Push(pad);
|
||||
}
|
||||
if (value2 > 2.0f || value2 < -2.0f) {
|
||||
pad.motion = static_cast<PadMotion>(i + 3);
|
||||
pad.motion_value = value;
|
||||
pad_queue[port].Push(pad);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Joycons::JoyconToState(Joycon& jc, JCState& state) {
|
||||
for (const auto& button : PadButtonArray) {
|
||||
const u32 button_value = static_cast<u32>(button);
|
||||
state.buttons.insert_or_assign(button_value, jc.button & button_value);
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < jc.axis.size(); ++i) {
|
||||
f32 axis_value = static_cast<f32>(jc.axis[i].value - jc.axis[i].center);
|
||||
if (axis_value > 0) {
|
||||
axis_value = axis_value / jc.axis[i].max;
|
||||
} else {
|
||||
axis_value = axis_value / jc.axis[i].min;
|
||||
}
|
||||
axis_value = std::clamp(axis_value, -1.0f, 1.0f);
|
||||
state.axes.insert_or_assign(static_cast<u16>(i), axis_value);
|
||||
}
|
||||
|
||||
Common::Vec3f gyroscope = jc.motion->GetGyroscope();
|
||||
Common::Vec3f accelerometer = jc.motion->GetAcceleration();
|
||||
Common::Vec3f rotation = jc.motion->GetRotations();
|
||||
std::array<Common::Vec3f, 3> orientation = jc.motion->GetOrientation();
|
||||
|
||||
state.motion.insert_or_assign(static_cast<u16>(0), gyroscope.x);
|
||||
state.motion.insert_or_assign(static_cast<u16>(1), gyroscope.y);
|
||||
state.motion.insert_or_assign(static_cast<u16>(2), gyroscope.z);
|
||||
|
||||
state.motion.insert_or_assign(static_cast<u16>(3), accelerometer.x);
|
||||
state.motion.insert_or_assign(static_cast<u16>(4), accelerometer.y);
|
||||
state.motion.insert_or_assign(static_cast<u16>(5), accelerometer.z);
|
||||
|
||||
state.motion.insert_or_assign(static_cast<u16>(6), rotation.x);
|
||||
state.motion.insert_or_assign(static_cast<u16>(7), rotation.y);
|
||||
state.motion.insert_or_assign(static_cast<u16>(8), rotation.z);
|
||||
|
||||
state.motion.insert_or_assign(static_cast<u16>(9), orientation[0].x);
|
||||
state.motion.insert_or_assign(static_cast<u16>(10), orientation[0].y);
|
||||
state.motion.insert_or_assign(static_cast<u16>(11), orientation[0].z);
|
||||
state.motion.insert_or_assign(static_cast<u16>(12), orientation[1].x);
|
||||
state.motion.insert_or_assign(static_cast<u16>(13), orientation[1].y);
|
||||
state.motion.insert_or_assign(static_cast<u16>(14), orientation[1].z);
|
||||
state.motion.insert_or_assign(static_cast<u16>(15), orientation[2].x);
|
||||
state.motion.insert_or_assign(static_cast<u16>(16), orientation[2].y);
|
||||
state.motion.insert_or_assign(static_cast<u16>(17), orientation[2].z);
|
||||
|
||||
if (jc.button & 0x1 || jc.button & 0x100) {
|
||||
jc.motion->ResetRotations();
|
||||
jc.motion->SetQuaternion({{0, 0, -1}, 0});
|
||||
}
|
||||
}
|
||||
|
||||
void Joycons::ReadLoop() {
|
||||
LOG_DEBUG(Input, "JC Adapter Read() thread started");
|
||||
|
||||
std::vector<u8> buffer(max_resp_size);
|
||||
|
||||
while (adapter_thread_running) {
|
||||
for (std::size_t port = 0; port < joycon.size(); ++port) {
|
||||
if (joycon[port].type != JoyControllerTypes::None) {
|
||||
|
||||
const int status =
|
||||
hid_read_timeout(joycon[port].handle, buffer.data(), max_resp_size, 15);
|
||||
if (status > 0) {
|
||||
if (buffer[0] == 0x00) {
|
||||
// Invalid response
|
||||
LOG_ERROR(Input, "error reading buffer");
|
||||
adapter_thread_running = false;
|
||||
joycon[port].type = JoyControllerTypes::None;
|
||||
break;
|
||||
}
|
||||
UpdateJoyconData(joycon[port], buffer);
|
||||
UpdateYuzuSettings(joycon[port], port);
|
||||
JoyconToState(joycon[port], joycon[port].state);
|
||||
/*
|
||||
printf("ctrl:%d\tbtn:%d\tx1:%d\ty1:%d\tx2:%d\ty2:%d\tax:%.3f\tay:%.3f\taz:%.3f\tgx:%.3f\tgy:%.3f,
|
||||
" "gz:%.3f\tbuffer:",port, joycon[port].button, joycon[port].axis[0].value -
|
||||
joycon[port].axis[0].center, joycon[port].axis[1].value -
|
||||
joycon[port].axis[1].center, joycon[port].axis[2].value -
|
||||
joycon[port].axis[2].center, joycon[port].axis[3].value -
|
||||
joycon[port].axis[3].center, joycon[port].imu[0].acc.value,
|
||||
joycon[port].imu[1].acc.value, joycon[port].imu[2].acc.value,
|
||||
joycon[port].imu[0].gyr.value, joycon[port].imu[1].gyr.value,
|
||||
joycon[port].imu[2].gyr.value);
|
||||
for (int i = 0; i < 7; ++i) {
|
||||
printf("%02hx ", buffer[i]);
|
||||
}
|
||||
printf("\n");*/
|
||||
}
|
||||
}
|
||||
}
|
||||
std::this_thread::yield();
|
||||
}
|
||||
}
|
||||
|
||||
void Joycons::Setup() {
|
||||
// Initialize all controllers as unplugged
|
||||
for (std::size_t port = 0; port < joycon.size(); ++port) {
|
||||
joycon[port].type = JoyControllerTypes::None;
|
||||
for (std::size_t i = 0; i < joycon[port].axis.size(); ++i) {
|
||||
joycon[port].axis[i].value = 0;
|
||||
}
|
||||
for (std::size_t i = 0; i < joycon[port].imu.size(); ++i) {
|
||||
joycon[port].imu[i].acc.value = 0;
|
||||
joycon[port].imu[i].gyr.value = 0;
|
||||
}
|
||||
joycon[port].hd_rumble.amp_high = 0;
|
||||
joycon[port].hd_rumble.amp_low = 0;
|
||||
joycon[port].hd_rumble.freq_high = 160.0f;
|
||||
joycon[port].hd_rumble.freq_low = 80.0f;
|
||||
}
|
||||
std::size_t port = 0;
|
||||
|
||||
hid_device_info* devs = hid_enumerate(0x057e, 0x2006);
|
||||
hid_device_info* cur_dev = devs;
|
||||
|
||||
while (cur_dev) {
|
||||
printf("Device Found\n type: %04hx %04hx\n path: %s\n serial_number: %ls",
|
||||
cur_dev->vendor_id, cur_dev->product_id, cur_dev->path, cur_dev->serial_number);
|
||||
printf("\n");
|
||||
printf(" Manufacturer: %ls\n", cur_dev->manufacturer_string);
|
||||
printf(" Product: %ls\n", cur_dev->product_string);
|
||||
printf(" Release: %hx\n", cur_dev->release_number);
|
||||
printf(" Interface: %d\n", cur_dev->interface_number);
|
||||
printf(" Usage (page): 0x%hx (0x%hx)\n", cur_dev->usage, cur_dev->usage_page);
|
||||
printf("\n");
|
||||
|
||||
if (CheckDeviceAccess(port, cur_dev)) {
|
||||
// JC Adapter found and accessible, registering it
|
||||
++port;
|
||||
}
|
||||
cur_dev = cur_dev->next;
|
||||
}
|
||||
|
||||
devs = hid_enumerate(0x057e, 0x2007);
|
||||
cur_dev = devs;
|
||||
|
||||
while (cur_dev) {
|
||||
printf("Device Found\n type: %04hx %04hx\n path: %s\n serial_number: %ls",
|
||||
cur_dev->vendor_id, cur_dev->product_id, cur_dev->path, cur_dev->serial_number);
|
||||
printf("\n");
|
||||
printf(" Manufacturer: %ls\n", cur_dev->manufacturer_string);
|
||||
printf(" Product: %ls\n", cur_dev->product_string);
|
||||
printf(" Release: %hx\n", cur_dev->release_number);
|
||||
printf(" Interface: %d\n", cur_dev->interface_number);
|
||||
printf(" Usage (page): 0x%hx (0x%hx)\n", cur_dev->usage, cur_dev->usage_page);
|
||||
printf("\n");
|
||||
|
||||
if (CheckDeviceAccess(port, cur_dev)) {
|
||||
// JC Adapter found and accessible, registering it
|
||||
++port;
|
||||
}
|
||||
cur_dev = cur_dev->next;
|
||||
} //*/
|
||||
/*
|
||||
devs = hid_enumerate(0x057e, 0x2009);
|
||||
cur_dev = devs;
|
||||
|
||||
while (cur_dev) {
|
||||
printf("Device Found\n type: %04hx %04hx\n path: %s\n serial_number: %ls",
|
||||
cur_dev->vendor_id, cur_dev->product_id, cur_dev->path, cur_dev->serial_number);
|
||||
printf("\n");
|
||||
printf(" Manufacturer: %ls\n", cur_dev->manufacturer_string);
|
||||
printf(" Product: %ls\n", cur_dev->product_string);
|
||||
printf(" Release: %hx\n", cur_dev->release_number);
|
||||
printf(" Interface: %d\n", cur_dev->interface_number);
|
||||
printf(" Usage (page): 0x%hx (0x%hx)\n", cur_dev->usage, cur_dev->usage_page);
|
||||
printf("\n");
|
||||
|
||||
if (CheckDeviceAccess(port, cur_dev)) {
|
||||
// JC Adapter found and accessible, registering it
|
||||
++port;
|
||||
}
|
||||
cur_dev = cur_dev->next;
|
||||
}
|
||||
*/
|
||||
GetJCEndpoint();
|
||||
}
|
||||
|
||||
bool Joycons::CheckDeviceAccess(std::size_t port, hid_device_info* device) {
|
||||
if (device->vendor_id != 0x057e ||
|
||||
!(device->product_id == 0x2006 || device->product_id == 0x2007 ||
|
||||
device->product_id == 0x2009)) {
|
||||
// This isn't the device we are looking for.
|
||||
return false;
|
||||
}
|
||||
joycon[port].handle = hid_open(device->vendor_id, device->product_id, device->serial_number);
|
||||
if (!joycon[port].handle) {
|
||||
LOG_ERROR(Input, "Yuzu can not gain access to this device: ID {:04X}:{:04X}.",
|
||||
device->vendor_id, device->product_id);
|
||||
return false;
|
||||
}
|
||||
hid_set_nonblocking(joycon[port].handle, 1);
|
||||
switch (device->product_id) {
|
||||
case 0x2006:
|
||||
joycon[port].type = JoyControllerTypes::Left;
|
||||
break;
|
||||
case 0x2007:
|
||||
joycon[port].type = JoyControllerTypes::Right;
|
||||
break;
|
||||
case 0x2009:
|
||||
joycon[port].type = JoyControllerTypes::Pro;
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Joycons::GetJCEndpoint() {
|
||||
// init joycons
|
||||
for (u8 port = 0; port < joycon.size(); ++port) {
|
||||
joycon[port].port = port;
|
||||
if (joycon[port].type != JoyControllerTypes::None) {
|
||||
SetMac(joycon[port]);
|
||||
SetSerialNumber(joycon[port]);
|
||||
SetVersionNumber(joycon[port]);
|
||||
SetDeviceType(joycon[port]);
|
||||
GetFactoryCalibrationData(joycon[port]);
|
||||
GetColor(joycon[port]);
|
||||
|
||||
SetLedConfig(joycon[port], port + 1);
|
||||
EnableImu(joycon[port], true);
|
||||
SetImuConfig(joycon[port], GyrSensitivity::DPS2000, GyrPerformance::HZ833,
|
||||
AccSensitivity::G8, AccPerformance::HZ100);
|
||||
EnableRumble(joycon[port], true);
|
||||
SetReportMode(joycon[port], ReportMode::STANDARD_FULL_60HZ);
|
||||
joycon[port].motion = new InputCommon::MotionInput(0.3f, 0.005f, 0.0f);
|
||||
joycon[port].motion->SetGyroThreshold(0.001f);
|
||||
// joycon[port].motion->EnableReset(false);
|
||||
}
|
||||
}
|
||||
|
||||
adapter_thread_running = true;
|
||||
adapter_input_thread = std::thread(&Joycons::ReadLoop, this); // Read input
|
||||
}
|
||||
|
||||
Joycons::~Joycons() {
|
||||
Reset();
|
||||
}
|
||||
|
||||
void Joycons::Reset() {
|
||||
if (adapter_thread_running) {
|
||||
adapter_thread_running = false;
|
||||
}
|
||||
if (adapter_input_thread.joinable()) {
|
||||
adapter_input_thread.join();
|
||||
}
|
||||
|
||||
for (u8 port = 0; port < joycon.size(); ++port) {
|
||||
joycon[port].type = JoyControllerTypes::None;
|
||||
if (joycon[port].handle) {
|
||||
joycon[port].handle = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
hid_exit();
|
||||
}
|
||||
std::string Joycons::JoyconName(std::size_t port) const {
|
||||
switch (joycon[port].type) {
|
||||
case JoyControllerTypes::Left:
|
||||
return "Left Joycon";
|
||||
break;
|
||||
case JoyControllerTypes::Right:
|
||||
return "Right Joycon";
|
||||
break;
|
||||
case JoyControllerTypes::Pro:
|
||||
return "Pro Controller";
|
||||
break;
|
||||
case JoyControllerTypes::None:
|
||||
return "Unknow Joycon";
|
||||
break;
|
||||
}
|
||||
return "Unknow Joycon";
|
||||
}
|
||||
|
||||
std::vector<Common::ParamPackage> Joycons::GetInputDevices() const {
|
||||
// std::scoped_lock lock(joystick_map_mutex);
|
||||
std::vector<Common::ParamPackage> devices;
|
||||
for (const auto& controller : joycon) {
|
||||
if (DeviceConnected(controller.port)) {
|
||||
std::string name = fmt::format("{} {}", JoyconName(controller.port), controller.port);
|
||||
devices.emplace_back(Common::ParamPackage{
|
||||
{"class", "jcpad"},
|
||||
{"display", std::move(name)},
|
||||
{"port", std::to_string(controller.port)},
|
||||
});
|
||||
}
|
||||
}
|
||||
return devices;
|
||||
}
|
||||
|
||||
InputCommon::ButtonMapping Joycons::GetButtonMappingForDevice(const Common::ParamPackage& params) {
|
||||
// This list is missing ZL/ZR since those are not considered buttons in SDL GameController.
|
||||
// We will add those afterwards
|
||||
// This list also excludes Screenshot since theres not really a mapping for that
|
||||
static constexpr std::array<std::pair<Settings::NativeButton::Values, PadButton>, 20>
|
||||
switch_to_jcadapter_button = {
|
||||
std::pair{Settings::NativeButton::A, PadButton::BUTTON_B},
|
||||
{Settings::NativeButton::B, PadButton::BUTTON_A},
|
||||
{Settings::NativeButton::X, PadButton::BUTTON_Y},
|
||||
{Settings::NativeButton::Y, PadButton::BUTTON_X},
|
||||
{Settings::NativeButton::LStick, PadButton::STICK_L},
|
||||
{Settings::NativeButton::RStick, PadButton::STICK_R},
|
||||
{Settings::NativeButton::L, PadButton::TRIGGER_L},
|
||||
{Settings::NativeButton::R, PadButton::TRIGGER_R},
|
||||
{Settings::NativeButton::Plus, PadButton::BUTTON_PLUS},
|
||||
{Settings::NativeButton::Minus, PadButton::BUTTON_MINUS},
|
||||
{Settings::NativeButton::DLeft, PadButton::BUTTON_LEFT},
|
||||
{Settings::NativeButton::DUp, PadButton::BUTTON_UP},
|
||||
{Settings::NativeButton::DRight, PadButton::BUTTON_RIGHT},
|
||||
{Settings::NativeButton::DDown, PadButton::BUTTON_DOWN},
|
||||
{Settings::NativeButton::SL, PadButton::BUTTON_L_SL},
|
||||
{Settings::NativeButton::SR, PadButton::BUTTON_L_SR},
|
||||
{Settings::NativeButton::Screenshot, PadButton::BUTTON_CAPTURE},
|
||||
{Settings::NativeButton::Home, PadButton::BUTTON_HOME},
|
||||
{Settings::NativeButton::ZL, PadButton::TRIGGER_ZL},
|
||||
{Settings::NativeButton::ZR, PadButton::TRIGGER_ZR},
|
||||
};
|
||||
if (!params.Has("port")) {
|
||||
return {};
|
||||
}
|
||||
|
||||
InputCommon::ButtonMapping mapping{};
|
||||
for (const auto& [switch_button, jcadapter_button] : switch_to_jcadapter_button) {
|
||||
Common::ParamPackage button_params({{"engine", "jcpad"}});
|
||||
button_params.Set("port", params.Get("port", 0));
|
||||
button_params.Set("button", static_cast<int>(jcadapter_button));
|
||||
mapping[switch_button] = button_params;
|
||||
}
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
InputCommon::AnalogMapping Joycons::GetAnalogMappingForDevice(const Common::ParamPackage& params) {
|
||||
if (!params.Has("port")) {
|
||||
return {};
|
||||
}
|
||||
|
||||
InputCommon::AnalogMapping mapping = {};
|
||||
Common::ParamPackage left_analog_params;
|
||||
left_analog_params.Set("engine", "jcpad");
|
||||
left_analog_params.Set("port", params.Get("port", 0));
|
||||
left_analog_params.Set("axis_x", 0);
|
||||
left_analog_params.Set("axis_y", 1);
|
||||
mapping[Settings::NativeAnalog::LStick] = left_analog_params;
|
||||
Common::ParamPackage right_analog_params;
|
||||
right_analog_params.Set("engine", "jcpad");
|
||||
right_analog_params.Set("port", params.Get("port", 0));
|
||||
right_analog_params.Set("axis_x", 2);
|
||||
right_analog_params.Set("axis_y", 3);
|
||||
mapping[Settings::NativeAnalog::RStick] = right_analog_params;
|
||||
return mapping;
|
||||
}
|
||||
|
||||
bool Joycons::DeviceConnected(std::size_t port) const {
|
||||
if (port > 4) {
|
||||
return false;
|
||||
}
|
||||
return joycon[port].type != JoyControllerTypes::None;
|
||||
}
|
||||
|
||||
void Joycons::ResetDeviceType(std::size_t port) {
|
||||
if (port > 4) {
|
||||
return;
|
||||
}
|
||||
joycon[port].type = JoyControllerTypes::None;
|
||||
}
|
||||
|
||||
void Joycons::BeginConfiguration() {
|
||||
for (auto& pq : pad_queue) {
|
||||
pq.Clear();
|
||||
}
|
||||
configuring = true;
|
||||
}
|
||||
|
||||
void Joycons::EndConfiguration() {
|
||||
for (auto& pq : pad_queue) {
|
||||
pq.Clear();
|
||||
}
|
||||
configuring = false;
|
||||
}
|
||||
|
||||
JCState& Joycons::GetPadState(std::size_t port) {
|
||||
return joycon[port].state;
|
||||
}
|
||||
|
||||
const JCState& Joycons::GetPadState(std::size_t port) const {
|
||||
return joycon[port].state;
|
||||
}
|
||||
|
||||
std::array<Common::SPSCQueue<JCPadStatus>, 4>& Joycons::GetPadQueue() {
|
||||
return pad_queue;
|
||||
}
|
||||
|
||||
const std::array<Common::SPSCQueue<JCPadStatus>, 4>& Joycons::GetPadQueue() const {
|
||||
return pad_queue;
|
||||
}
|
||||
} // namespace JCAdapter
|
||||
371
src/input_common/joycon/jc_adapter.h
Normal file
371
src/input_common/joycon/jc_adapter.h
Normal file
@@ -0,0 +1,371 @@
|
||||
// Copyright 2020 Yuzu Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <span>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <hidapi.h>
|
||||
#include "common/param_package.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
#include "common/vector_math.h"
|
||||
#include "input_common/main.h"
|
||||
#include "input_common/motion_input.h"
|
||||
#include "input_common/settings.h"
|
||||
|
||||
namespace JCAdapter {
|
||||
|
||||
enum class PadButton {
|
||||
BUTTON_DOWN = 0x000001,
|
||||
BUTTON_UP = 0x000002,
|
||||
BUTTON_RIGHT = 0x000004,
|
||||
BUTTON_LEFT = 0x000008,
|
||||
BUTTON_L_SR = 0x000010,
|
||||
BUTTON_L_SL = 0x000020,
|
||||
TRIGGER_L = 0x000040,
|
||||
TRIGGER_ZL = 0x000080,
|
||||
BUTTON_Y = 0x000100,
|
||||
BUTTON_X = 0x000200,
|
||||
BUTTON_B = 0x000400,
|
||||
BUTTON_A = 0x000800,
|
||||
BUTTON_R_SL = 0x001000,
|
||||
BUTTON_R_SR = 0x002000,
|
||||
TRIGGER_R = 0x004000,
|
||||
TRIGGER_ZR = 0x008000,
|
||||
BUTTON_MINUS = 0x010000,
|
||||
BUTTON_PLUS = 0x020000,
|
||||
STICK_R = 0x040000,
|
||||
STICK_L = 0x080000,
|
||||
BUTTON_HOME = 0x100000,
|
||||
BUTTON_CAPTURE = 0x200000,
|
||||
// Below is for compatibility with "AxisButton" and "MotionButton" type
|
||||
STICK = 0x400000,
|
||||
MOTION = 0x800000,
|
||||
};
|
||||
|
||||
enum class Output {
|
||||
RUMBLE_AND_SUBCMD = 0x01,
|
||||
FW_UPDATE_PKT = 0x03,
|
||||
RUMBLE_ONLY = 0x10,
|
||||
MCU_DATA = 0x11,
|
||||
USB_CMD = 0x80,
|
||||
};
|
||||
|
||||
enum class SubComamnd {
|
||||
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 GyrSensitivity {
|
||||
DPS250,
|
||||
DPS500,
|
||||
DPS1000,
|
||||
DPS2000,
|
||||
};
|
||||
|
||||
enum class AccSensitivity {
|
||||
G8,
|
||||
G4,
|
||||
G2,
|
||||
G16,
|
||||
};
|
||||
|
||||
enum class GyrPerformance {
|
||||
HZ833,
|
||||
HZ208,
|
||||
};
|
||||
|
||||
enum class AccPerformance {
|
||||
HZ200,
|
||||
HZ100,
|
||||
};
|
||||
|
||||
enum class PadAxes {
|
||||
StickX,
|
||||
StickY,
|
||||
SubstickX,
|
||||
SubstickY,
|
||||
Undefined,
|
||||
};
|
||||
|
||||
enum class PadMotion {
|
||||
GyrX,
|
||||
GyrY,
|
||||
GyrZ,
|
||||
AccX,
|
||||
AccY,
|
||||
AccZ,
|
||||
Undefined,
|
||||
};
|
||||
|
||||
enum class JoyControllerTypes {
|
||||
None,
|
||||
Left,
|
||||
Right,
|
||||
Pro,
|
||||
};
|
||||
|
||||
extern const std::array<PadButton, 22> PadButtonArray;
|
||||
|
||||
struct JCPadStatus {
|
||||
u32 button{};
|
||||
|
||||
std::array<u16, 4> axis_values{};
|
||||
std::array<f32, 6> imu_values{};
|
||||
std::array<f32, 9> orientation{};
|
||||
|
||||
u8 port{};
|
||||
PadAxes axis{PadAxes::Undefined};
|
||||
s16 axis_value{0};
|
||||
PadMotion motion{PadMotion::Undefined};
|
||||
f32 motion_value{0.0f};
|
||||
};
|
||||
|
||||
struct JCState {
|
||||
std::unordered_map<int, bool> buttons;
|
||||
std::unordered_map<int, float> axes;
|
||||
std::unordered_map<int, float> motion;
|
||||
};
|
||||
|
||||
class Joycons {
|
||||
public:
|
||||
/// Initialize the JC Adapter capture and read sequence
|
||||
Joycons();
|
||||
|
||||
/// Close the adapter read thread and release the adapter
|
||||
~Joycons();
|
||||
/// Used for polling
|
||||
void BeginConfiguration();
|
||||
void EndConfiguration();
|
||||
|
||||
std::vector<Common::ParamPackage> GetInputDevices() const;
|
||||
InputCommon::ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params);
|
||||
InputCommon::AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params);
|
||||
|
||||
bool DeviceConnected(std::size_t port) const;
|
||||
|
||||
void SetRumble(std::size_t port, f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low);
|
||||
|
||||
const f32 GetTemperatureCelcius(std::size_t port);
|
||||
const f32 GetTemperatureFahrenheit(std::size_t port);
|
||||
const u8 GetBatteryLevel(std::size_t port);
|
||||
const std::array<u8, 15> GetSerialNumber(std::size_t port);
|
||||
const f32 GetVersion(std::size_t port);
|
||||
const JoyControllerTypes GetDeviceType(std::size_t port);
|
||||
const std::array<u8, 6> GetMac(std::size_t port);
|
||||
const u32 GetBodyColor(std::size_t port);
|
||||
const u32 GetButtonColor(std::size_t port);
|
||||
const u32 GetLeftGripColor(std::size_t port);
|
||||
const u32 GetRightGripColor(std::size_t port);
|
||||
|
||||
std::array<Common::SPSCQueue<JCPadStatus>, 4>& GetPadQueue();
|
||||
const std::array<Common::SPSCQueue<JCPadStatus>, 4>& GetPadQueue() const;
|
||||
|
||||
JCState& GetPadState(std::size_t port);
|
||||
const JCState& GetPadState(std::size_t port) const;
|
||||
|
||||
private:
|
||||
struct HDRumble {
|
||||
f32 freq_high;
|
||||
f32 freq_low;
|
||||
f32 amp_high;
|
||||
f32 amp_low;
|
||||
};
|
||||
|
||||
struct ImuData {
|
||||
s16 offset;
|
||||
s16 scale;
|
||||
f32 value;
|
||||
};
|
||||
|
||||
struct Imu {
|
||||
ImuData acc;
|
||||
ImuData gyr;
|
||||
};
|
||||
|
||||
struct JoyStick {
|
||||
u16 max;
|
||||
u16 min;
|
||||
u16 center;
|
||||
u16 deadzone;
|
||||
u16 value;
|
||||
};
|
||||
|
||||
struct Color {
|
||||
u32 body;
|
||||
u32 buttons;
|
||||
u32 left_grip;
|
||||
u32 right_grip;
|
||||
};
|
||||
|
||||
struct Joycon {
|
||||
hid_device* handle = nullptr;
|
||||
JCState state;
|
||||
|
||||
// Harware config
|
||||
GyrSensitivity gsen;
|
||||
GyrPerformance gfrec;
|
||||
AccSensitivity asen;
|
||||
AccPerformance afrec;
|
||||
ReportMode mode;
|
||||
bool imu_enabled;
|
||||
bool rumble_enabled;
|
||||
u8 leds;
|
||||
|
||||
// Fixed value info
|
||||
f32 version;
|
||||
std::array<u8, 6> mac;
|
||||
JoyControllerTypes type;
|
||||
std::array<u8, 15> serial_number;
|
||||
Color color;
|
||||
u8 port;
|
||||
|
||||
// Realtime values
|
||||
InputCommon::MotionInput* motion = nullptr;
|
||||
|
||||
std::array<Common::Vec3f, 3> gyro;
|
||||
std::array<JoyStick, 4> axis;
|
||||
std::array<Imu, 3> imu;
|
||||
HDRumble hd_rumble;
|
||||
u32 button;
|
||||
u32 temperature;
|
||||
u8 battery;
|
||||
|
||||
std::chrono::time_point<std::chrono::system_clock> last_motion_update;
|
||||
};
|
||||
|
||||
// Low level commands for communication
|
||||
void SendWrite(hid_device* handle, std::vector<u8> buffer, int size);
|
||||
u8 GetCounter();
|
||||
std::vector<u8> SubCommand(hid_device* handle, SubComamnd sc, std::vector<u8> buffer, int size);
|
||||
std::vector<u8> GetResponse(hid_device* handle, SubComamnd sc);
|
||||
std::vector<u8> ReadSPI(hid_device* handle, CalAddr addr, u8 size);
|
||||
|
||||
// Reads calibration values
|
||||
void SetJoyStickCal(std::vector<u8> buffer, JoyStick& axis1, JoyStick& axis2, bool left);
|
||||
void SetImuCal(Joycon& jc, std::vector<u8> buffer);
|
||||
void GetUserCalibrationData(Joycon& jc);
|
||||
void GetFactoryCalibrationData(Joycon& jc);
|
||||
|
||||
// Reads buttons and stick values
|
||||
void GetLeftPadInput(Joycon& jc, std::vector<u8> buffer);
|
||||
void GetRightPadInput(Joycon& jc, std::vector<u8> buffer);
|
||||
void GetProPadInput(Joycon& jc, std::vector<u8> buffer);
|
||||
|
||||
// Reads gyro and accelerometer values
|
||||
f32 TransformAccValue(s16 raw, ImuData cal, AccSensitivity sen);
|
||||
f32 TransformGyrValue(s16 raw, ImuData cal, GyrSensitivity sen);
|
||||
s16 GetRawIMUValues(size_t sensor, size_t axis, std::vector<u8> buffer);
|
||||
void GetIMUValues(Joycon& jc, std::vector<u8> buffer);
|
||||
void SetImuConfig(Joycon& jc, GyrSensitivity gsen, GyrPerformance gfrec, AccSensitivity asen,
|
||||
AccPerformance afrec);
|
||||
|
||||
// Sends rumble state
|
||||
void SendRumble(std::size_t port);
|
||||
const f32 EncodeRumbleAmplification(f32 amplification);
|
||||
|
||||
// Reads color values
|
||||
void GetColor(Joycon& jc);
|
||||
|
||||
// Reads device hardware values
|
||||
void SetMac(Joycon& jc);
|
||||
void SetSerialNumber(Joycon& jc);
|
||||
void SetDeviceType(Joycon& jc);
|
||||
void SetVersionNumber(Joycon& jc);
|
||||
|
||||
// Write device hardware configuration
|
||||
void SetLedConfig(Joycon& jc, u8 leds);
|
||||
void EnableImu(Joycon& jc, bool enable);
|
||||
void EnableRumble(Joycon& jc, bool enable);
|
||||
void SetReportMode(Joycon& jc, ReportMode mode);
|
||||
|
||||
void UpdateJoyconData(Joycon& jc, std::vector<u8> buffer);
|
||||
void UpdateYuzuSettings(Joycon& jc, std::size_t port);
|
||||
void JoyconToState(Joycon& jc, JCState& state);
|
||||
std::string JoyconName(std::size_t port) const;
|
||||
void ReadLoop();
|
||||
|
||||
/// Resets status of device connected to port
|
||||
void ResetDeviceType(std::size_t port);
|
||||
|
||||
/// Returns true if we successfully gain access to JC Adapter
|
||||
bool CheckDeviceAccess(std::size_t port, hid_device_info* device);
|
||||
|
||||
/// Captures JC Adapter endpoint address,
|
||||
void GetJCEndpoint();
|
||||
|
||||
/// For shutting down, clear all data, join all threads, release usb
|
||||
void Reset();
|
||||
|
||||
/// For use in initialization, querying devices to find the adapter
|
||||
void Setup();
|
||||
|
||||
// void UpdateOrientation(Joycon& jc, u64 time, std::size_t iteration);
|
||||
|
||||
std::thread adapter_input_thread;
|
||||
bool adapter_thread_running;
|
||||
|
||||
bool configuring = false;
|
||||
|
||||
std::array<Joycon, 4> joycon;
|
||||
std::array<Common::SPSCQueue<JCPadStatus>, 4> pad_queue;
|
||||
|
||||
u8 global_counter;
|
||||
static constexpr u32 max_resp_size = 49;
|
||||
static constexpr std::array<u8, 8> default_buffer{0x0, 0x1, 0x40, 0x40, 0x0, 0x1, 0x40, 0x40};
|
||||
};
|
||||
|
||||
} // namespace JCAdapter
|
||||
459
src/input_common/joycon/jc_poller.cpp
Normal file
459
src/input_common/joycon/jc_poller.cpp
Normal file
@@ -0,0 +1,459 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <atomic>
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
#include "common/assert.h"
|
||||
#include "common/threadsafe_queue.h"
|
||||
#include "input_common/joycon/jc_adapter.h"
|
||||
#include "input_common/joycon/jc_poller.h"
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
class JCButton final : public Input::ButtonDevice {
|
||||
public:
|
||||
explicit JCButton(int port_, int button_, JCAdapter::Joycons* adapter)
|
||||
: port(port_), button(button_), jcadapter(adapter) {}
|
||||
|
||||
~JCButton() override;
|
||||
|
||||
bool GetStatus() const override {
|
||||
if (jcadapter->DeviceConnected(port)) {
|
||||
return jcadapter->GetPadState(port).buttons.at(button);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const override {
|
||||
if (jcadapter->DeviceConnected(port)) {
|
||||
jcadapter->SetRumble(port, amp_high, amp_low, freq_high, freq_low);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
const int port;
|
||||
const int button;
|
||||
JCAdapter::Joycons* jcadapter;
|
||||
};
|
||||
|
||||
class JCAxisButton final : public Input::ButtonDevice {
|
||||
public:
|
||||
explicit JCAxisButton(int port_, int axis_, float threshold_, bool trigger_if_greater_,
|
||||
JCAdapter::Joycons* adapter)
|
||||
: port(port_), axis(axis_), threshold(threshold_), trigger_if_greater(trigger_if_greater_),
|
||||
jcadapter(adapter) {}
|
||||
|
||||
bool GetStatus() const override {
|
||||
if (jcadapter->DeviceConnected(port)) {
|
||||
const float axis_value = jcadapter->GetPadState(port).axes.at(axis);
|
||||
if (trigger_if_greater) {
|
||||
// TODO: Might be worthwile to set a slider for the trigger threshold. It is
|
||||
// currently always set to 0.5 in configure_input_player.cpp ZL/ZR HandleClick
|
||||
return axis_value > threshold;
|
||||
}
|
||||
return axis_value < -threshold;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const override {
|
||||
if (jcadapter->DeviceConnected(port)) {
|
||||
jcadapter->SetRumble(port, amp_high, amp_low, freq_high, freq_low);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
const int port;
|
||||
const int axis;
|
||||
float threshold;
|
||||
bool trigger_if_greater;
|
||||
JCAdapter::Joycons* jcadapter;
|
||||
};
|
||||
// Motion buttons are a temporary way to test motion
|
||||
class JCMotionButton final : public Input::ButtonDevice {
|
||||
public:
|
||||
explicit JCMotionButton(int port_, int axis_, float threshold_, bool trigger_if_greater_,
|
||||
JCAdapter::Joycons* adapter)
|
||||
: port(port_), axis(axis_), threshold(threshold_), trigger_if_greater(trigger_if_greater_),
|
||||
jcadapter(adapter) {}
|
||||
|
||||
bool GetStatus() const override {
|
||||
if (jcadapter->DeviceConnected(port)) {
|
||||
const float axis_value = jcadapter->GetPadState(port).motion.at(axis);
|
||||
if (trigger_if_greater) {
|
||||
// TODO: Might be worthwile to set a slider for the trigger threshold. It is
|
||||
// currently always set to 0.5 in configure_input_player.cpp ZL/ZR HandleClick
|
||||
return axis_value > threshold;
|
||||
}
|
||||
return axis_value < -threshold;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const override {
|
||||
if (jcadapter->DeviceConnected(port)) {
|
||||
jcadapter->SetRumble(port, amp_high, amp_low, freq_high, freq_low);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
const int port;
|
||||
const int axis;
|
||||
float threshold;
|
||||
bool trigger_if_greater;
|
||||
JCAdapter::Joycons* jcadapter;
|
||||
};
|
||||
|
||||
JCButtonFactory::JCButtonFactory(std::shared_ptr<JCAdapter::Joycons> adapter_)
|
||||
: adapter(std::move(adapter_)) {}
|
||||
|
||||
JCButton::~JCButton() = default;
|
||||
|
||||
std::unique_ptr<Input::ButtonDevice> JCButtonFactory::Create(const Common::ParamPackage& params) {
|
||||
const int button_id = params.Get("button", 0);
|
||||
const int port = params.Get("port", 0);
|
||||
|
||||
constexpr int PAD_STICK_ID = static_cast<u32>(JCAdapter::PadButton::STICK);
|
||||
constexpr int PAD_MOTION_ID = static_cast<u32>(JCAdapter::PadButton::MOTION);
|
||||
// button is not an axis/stick button
|
||||
if (button_id != PAD_STICK_ID && button_id != PAD_MOTION_ID) {
|
||||
auto button = std::make_unique<JCButton>(port, button_id, adapter.get());
|
||||
return std::move(button);
|
||||
}
|
||||
|
||||
// For Axis buttons, used by the binary sticks.
|
||||
if (button_id == PAD_STICK_ID && button_id != PAD_MOTION_ID) {
|
||||
const int axis = params.Get("axis", 0);
|
||||
const float threshold = params.Get("threshold", 0.25f);
|
||||
const std::string direction_name = params.Get("direction", "");
|
||||
bool trigger_if_greater;
|
||||
if (direction_name == "+") {
|
||||
trigger_if_greater = true;
|
||||
} else if (direction_name == "-") {
|
||||
trigger_if_greater = false;
|
||||
} else {
|
||||
trigger_if_greater = true;
|
||||
LOG_ERROR(Input, "Unknown direction {}", direction_name);
|
||||
}
|
||||
return std::make_unique<JCAxisButton>(port, axis, threshold, trigger_if_greater,
|
||||
adapter.get());
|
||||
}
|
||||
|
||||
// For Motion buttons, used by the binary motion.
|
||||
if (button_id != PAD_STICK_ID && button_id == PAD_MOTION_ID) {
|
||||
const int axis = params.Get("motion", 0);
|
||||
const float threshold = params.Get("threshold", 0.5f);
|
||||
const std::string direction_name = params.Get("direction", "");
|
||||
bool trigger_if_greater;
|
||||
if (direction_name == "+") {
|
||||
trigger_if_greater = true;
|
||||
} else if (direction_name == "-") {
|
||||
trigger_if_greater = false;
|
||||
} else {
|
||||
trigger_if_greater = true;
|
||||
LOG_ERROR(Input, "Unknown direction {}", direction_name);
|
||||
}
|
||||
return std::make_unique<JCMotionButton>(port, axis, threshold, trigger_if_greater,
|
||||
adapter.get());
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Common::ParamPackage JCButtonFactory::GetNextInput() {
|
||||
Common::ParamPackage params;
|
||||
JCAdapter::JCPadStatus pad;
|
||||
auto& queue = adapter->GetPadQueue();
|
||||
for (std::size_t port = 0; port < queue.size(); ++port) {
|
||||
while (queue[port].Pop(pad)) {
|
||||
// This while loop will break on the earliest detected button
|
||||
params.Set("engine", "jcpad");
|
||||
params.Set("port", static_cast<int>(port));
|
||||
for (const auto& button : JCAdapter::PadButtonArray) {
|
||||
const int button_value = static_cast<u32>(button);
|
||||
if (pad.button & button_value) {
|
||||
params.Set("button", button_value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// For Axis button implementation
|
||||
if (pad.axis != JCAdapter::PadAxes::Undefined) {
|
||||
params.Set("axis", static_cast<u16>(pad.axis));
|
||||
params.Set("button", static_cast<int>(JCAdapter::PadButton::STICK));
|
||||
if (pad.axis_value > 0) {
|
||||
params.Set("direction", "+");
|
||||
params.Set("threshold", "0.25");
|
||||
} else {
|
||||
params.Set("direction", "-");
|
||||
params.Set("threshold", "0.25");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// For Motion button implementation
|
||||
if (pad.motion != JCAdapter::PadMotion::Undefined) {
|
||||
params.Set("motion", static_cast<u16>(pad.motion));
|
||||
params.Set("button", static_cast<int>(JCAdapter::PadButton::MOTION));
|
||||
if (pad.motion_value > 0) {
|
||||
params.Set("direction", "+");
|
||||
params.Set("threshold", "0.5");
|
||||
} else {
|
||||
params.Set("direction", "-");
|
||||
params.Set("threshold", "0.5");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
void JCButtonFactory::BeginConfiguration() {
|
||||
polling = true;
|
||||
adapter->BeginConfiguration();
|
||||
}
|
||||
|
||||
void JCButtonFactory::EndConfiguration() {
|
||||
polling = false;
|
||||
adapter->EndConfiguration();
|
||||
}
|
||||
|
||||
class JCAnalog final : public Input::AnalogDevice {
|
||||
public:
|
||||
JCAnalog(int port_, int axis_x_, int axis_y_, bool invert_x_, bool invert_y_, float deadzone_,
|
||||
float range_, JCAdapter::Joycons* adapter)
|
||||
: port(port_), axis_x(axis_x_), axis_y(axis_y_), invert_x(invert_x_), invert_y(invert_y_),
|
||||
deadzone(deadzone_), range(range_), jcadapter(adapter) {}
|
||||
|
||||
float GetAxis(int axis) const {
|
||||
if (jcadapter->DeviceConnected(port)) {
|
||||
std::lock_guard lock{mutex};
|
||||
if (axis < 10) {
|
||||
return jcadapter->GetPadState(port).axes.at(axis);
|
||||
} else {
|
||||
return jcadapter->GetPadState(port).motion.at(axis - 10);
|
||||
}
|
||||
}
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
std::pair<float, float> GetAnalog(u32 analog_axis_x, u32 analog_axis_y) const {
|
||||
float x = GetAxis(analog_axis_x);
|
||||
float y = GetAxis(analog_axis_y);
|
||||
if (invert_x) {
|
||||
x = -x;
|
||||
}
|
||||
if (invert_y) {
|
||||
y = -y;
|
||||
}
|
||||
// Make sure the coordinates are in the unit circle,
|
||||
// otherwise normalize it.
|
||||
float r = x * x + y * y;
|
||||
if (r > 1.0f) {
|
||||
r = std::sqrt(r);
|
||||
x /= r;
|
||||
y /= r;
|
||||
}
|
||||
|
||||
return {x, y};
|
||||
}
|
||||
|
||||
std::tuple<float, float> GetStatus() const override {
|
||||
const auto [x, y] = GetAnalog(axis_x, axis_y);
|
||||
const float r = std::sqrt((x * x) + (y * y));
|
||||
if (r > deadzone) {
|
||||
return {x / r * (r - deadzone) / (1 - deadzone),
|
||||
y / r * (r - deadzone) / (1 - deadzone)};
|
||||
}
|
||||
return {0.0f, 0.0f};
|
||||
}
|
||||
|
||||
bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
|
||||
const auto [x, y] = GetStatus();
|
||||
const float directional_deadzone = 0.4f;
|
||||
switch (direction) {
|
||||
case Input::AnalogDirection::RIGHT:
|
||||
return x > directional_deadzone;
|
||||
case Input::AnalogDirection::LEFT:
|
||||
return x < -directional_deadzone;
|
||||
case Input::AnalogDirection::UP:
|
||||
return y > directional_deadzone;
|
||||
case Input::AnalogDirection::DOWN:
|
||||
return y < -directional_deadzone;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
const int port;
|
||||
const int axis_x;
|
||||
const int axis_y;
|
||||
const bool invert_x;
|
||||
const bool invert_y;
|
||||
const float deadzone;
|
||||
const float range;
|
||||
JCAdapter::Joycons* jcadapter;
|
||||
mutable std::mutex mutex;
|
||||
};
|
||||
|
||||
/// An analog device factory that creates analog devices from JC Adapter
|
||||
JCAnalogFactory::JCAnalogFactory(std::shared_ptr<JCAdapter::Joycons> adapter_)
|
||||
: adapter(std::move(adapter_)) {}
|
||||
|
||||
/**
|
||||
* Creates analog device from joystick axes
|
||||
* @param params contains parameters for creating the device:
|
||||
* - "port": the nth jcpad on the adapter
|
||||
* - "axis_x": the index of the axis to be bind as x-axis
|
||||
* - "axis_y": the index of the axis to be bind as y-axis
|
||||
*/
|
||||
std::unique_ptr<Input::AnalogDevice> JCAnalogFactory::Create(const Common::ParamPackage& params) {
|
||||
const auto port = static_cast<u32>(params.Get("port", 0));
|
||||
const auto axis_x = static_cast<u32>(params.Get("axis_x", 0));
|
||||
const auto axis_y = static_cast<u32>(params.Get("axis_y", 1));
|
||||
const auto deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f);
|
||||
const auto range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f);
|
||||
const std::string invert_x_value = params.Get("invert_x", "+");
|
||||
const std::string invert_y_value = params.Get("invert_y", "+");
|
||||
const bool invert_x = invert_x_value == "-";
|
||||
const bool invert_y = invert_y_value == "-";
|
||||
|
||||
return std::make_unique<JCAnalog>(port, axis_x, axis_y, invert_x, invert_y, deadzone, range,
|
||||
adapter.get());
|
||||
}
|
||||
|
||||
void JCAnalogFactory::BeginConfiguration() {
|
||||
polling = true;
|
||||
adapter->BeginConfiguration();
|
||||
}
|
||||
|
||||
void JCAnalogFactory::EndConfiguration() {
|
||||
polling = false;
|
||||
adapter->EndConfiguration();
|
||||
}
|
||||
|
||||
Common::ParamPackage JCAnalogFactory::GetNextInput() {
|
||||
JCAdapter::JCPadStatus pad;
|
||||
auto& queue = adapter->GetPadQueue();
|
||||
for (std::size_t port = 0; port < queue.size(); ++port) {
|
||||
while (queue[port].Pop(pad)) {
|
||||
if (pad.axis != JCAdapter::PadAxes::Undefined && std::abs(pad.axis_value) > 0.1) {
|
||||
// An analog device needs two axes, so we need to store the axis for later and wait
|
||||
// for a second input event. The axes also must be from the same joystick.
|
||||
const u16 axis = static_cast<u16>(pad.axis);
|
||||
if (analog_x_axis == -1) {
|
||||
analog_x_axis = axis;
|
||||
controller_number = static_cast<int>(port);
|
||||
} else if (analog_y_axis == -1 && analog_x_axis != axis &&
|
||||
controller_number == static_cast<int>(port)) {
|
||||
analog_y_axis = axis;
|
||||
}
|
||||
} else if (pad.motion != JCAdapter::PadMotion::Undefined &&
|
||||
std::abs(pad.motion_value) > 1) {
|
||||
const u16 axis = 10 + static_cast<u16>(pad.motion);
|
||||
if (analog_x_axis == -1) {
|
||||
analog_x_axis = axis;
|
||||
controller_number = static_cast<int>(port);
|
||||
} else if (analog_y_axis == -1 && analog_x_axis != axis &&
|
||||
controller_number == static_cast<int>(port)) {
|
||||
analog_y_axis = axis;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Common::ParamPackage params;
|
||||
if (analog_x_axis != -1 && analog_y_axis != -1) {
|
||||
params.Set("engine", "jcpad");
|
||||
params.Set("port", controller_number);
|
||||
params.Set("axis_x", analog_x_axis);
|
||||
params.Set("axis_y", analog_y_axis);
|
||||
analog_x_axis = -1;
|
||||
analog_y_axis = -1;
|
||||
controller_number = -1;
|
||||
return params;
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
class JCMotion final : public Input::MotionDevice {
|
||||
public:
|
||||
JCMotion(int port_, JCAdapter::Joycons* adapter) : port(port_), jcadapter(adapter) {}
|
||||
|
||||
std::tuple<Common::Vec3<float>, Common::Vec3<float>, Common::Vec3<float>,
|
||||
std::array<Common::Vec3f, 3>>
|
||||
GetStatus() const override {
|
||||
auto motion = jcadapter->GetPadState(port).motion;
|
||||
if (motion.size() == 18) {
|
||||
auto gyroscope = Common::MakeVec(motion.at(0), motion.at(1), motion.at(2));
|
||||
auto accelerometer = Common::MakeVec(motion.at(3), motion.at(4), motion.at(5));
|
||||
auto rotation = Common::MakeVec(motion.at(6), motion.at(7), motion.at(8));
|
||||
std::array<Common::Vec3f, 3> orientation{};
|
||||
orientation[0] = Common::MakeVec(motion.at(9), motion.at(10), motion.at(11));
|
||||
orientation[1] = Common::MakeVec(motion.at(12), motion.at(13), motion.at(14));
|
||||
orientation[2] = Common::MakeVec(motion.at(15), motion.at(16), motion.at(17));
|
||||
return std::make_tuple(accelerometer, gyroscope, rotation, orientation);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
const int port;
|
||||
JCAdapter::Joycons* jcadapter;
|
||||
mutable std::mutex mutex;
|
||||
};
|
||||
|
||||
/// A motion device factory that creates motion devices from JC Adapter
|
||||
JCMotionFactory::JCMotionFactory(std::shared_ptr<JCAdapter::Joycons> adapter_)
|
||||
: adapter(std::move(adapter_)) {}
|
||||
|
||||
/**
|
||||
* Creates motion device
|
||||
* @param params contains parameters for creating the device:
|
||||
* - "port": the nth jcpad on the adapter
|
||||
*/
|
||||
std::unique_ptr<Input::MotionDevice> JCMotionFactory::Create(const Common::ParamPackage& params) {
|
||||
const int port = params.Get("port", 0);
|
||||
|
||||
return std::make_unique<JCMotion>(port, adapter.get());
|
||||
}
|
||||
|
||||
void JCMotionFactory::BeginConfiguration() {
|
||||
polling = true;
|
||||
adapter->BeginConfiguration();
|
||||
}
|
||||
|
||||
void JCMotionFactory::EndConfiguration() {
|
||||
polling = false;
|
||||
adapter->EndConfiguration();
|
||||
}
|
||||
|
||||
Common::ParamPackage JCMotionFactory::GetNextInput() {
|
||||
Common::ParamPackage params;
|
||||
JCAdapter::JCPadStatus pad;
|
||||
auto& queue = adapter->GetPadQueue();
|
||||
for (std::size_t port = 0; port < queue.size(); ++port) {
|
||||
while (queue[port].Pop(pad)) {
|
||||
if (pad.motion == JCAdapter::PadMotion::Undefined || std::abs(pad.motion_value) < 1) {
|
||||
continue;
|
||||
}
|
||||
params.Set("engine", "jcpad");
|
||||
params.Set("motion", static_cast<u16>(pad.motion));
|
||||
params.Set("port", static_cast<int>(port));
|
||||
return params;
|
||||
}
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
} // namespace InputCommon
|
||||
89
src/input_common/joycon/jc_poller.h
Normal file
89
src/input_common/joycon/jc_poller.h
Normal file
@@ -0,0 +1,89 @@
|
||||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "core/frontend/input.h"
|
||||
#include "input_common/joycon/jc_adapter.h"
|
||||
|
||||
namespace InputCommon {
|
||||
|
||||
/**
|
||||
* A button device factory representing a jcpad. It receives jcpad events and forward them
|
||||
* to all button devices it created.
|
||||
*/
|
||||
class JCButtonFactory final : public Input::Factory<Input::ButtonDevice> {
|
||||
public:
|
||||
explicit JCButtonFactory(std::shared_ptr<JCAdapter::Joycons> adapter_);
|
||||
|
||||
/**
|
||||
* Creates a button device from a button press
|
||||
* @param params contains parameters for creating the device:
|
||||
* - "code": the code of the key to bind with the button
|
||||
*/
|
||||
std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override;
|
||||
|
||||
Common::ParamPackage GetNextInput();
|
||||
|
||||
/// For device input configuration/polling
|
||||
void BeginConfiguration();
|
||||
void EndConfiguration();
|
||||
|
||||
bool IsPolling() const {
|
||||
return polling;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<JCAdapter::Joycons> adapter;
|
||||
bool polling = false;
|
||||
};
|
||||
|
||||
/// An analog device factory that creates analog devices from JC Adapter
|
||||
class JCAnalogFactory final : public Input::Factory<Input::AnalogDevice> {
|
||||
public:
|
||||
explicit JCAnalogFactory(std::shared_ptr<JCAdapter::Joycons> adapter_);
|
||||
|
||||
std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override;
|
||||
Common::ParamPackage GetNextInput();
|
||||
|
||||
/// For device input configuration/polling
|
||||
void BeginConfiguration();
|
||||
void EndConfiguration();
|
||||
|
||||
bool IsPolling() const {
|
||||
return polling;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<JCAdapter::Joycons> adapter;
|
||||
int analog_x_axis = -1;
|
||||
int analog_y_axis = -1;
|
||||
int controller_number = -1;
|
||||
bool polling = false;
|
||||
};
|
||||
|
||||
/// A motion device factory that creates motion devices from JC Adapter
|
||||
class JCMotionFactory final : public Input::Factory<Input::MotionDevice> {
|
||||
public:
|
||||
explicit JCMotionFactory(std::shared_ptr<JCAdapter::Joycons> adapter_);
|
||||
|
||||
std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override;
|
||||
|
||||
Common::ParamPackage GetNextInput();
|
||||
|
||||
/// For device input configuration/polling
|
||||
void BeginConfiguration();
|
||||
void EndConfiguration();
|
||||
|
||||
bool IsPolling() const {
|
||||
return polling;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<JCAdapter::Joycons> adapter;
|
||||
bool polling = false;
|
||||
};
|
||||
|
||||
} // namespace InputCommon
|
||||
@@ -8,6 +8,8 @@
|
||||
#include "input_common/analog_from_button.h"
|
||||
#include "input_common/gcadapter/gc_adapter.h"
|
||||
#include "input_common/gcadapter/gc_poller.h"
|
||||
#include "input_common/joycon/jc_adapter.h"
|
||||
#include "input_common/joycon/jc_poller.h"
|
||||
#include "input_common/keyboard.h"
|
||||
#include "input_common/main.h"
|
||||
#include "input_common/motion_from_button.h"
|
||||
@@ -32,6 +34,14 @@ struct InputSubsystem::Impl {
|
||||
gcvibration = std::make_shared<GCVibrationFactory>(gcadapter);
|
||||
Input::RegisterFactory<Input::VibrationDevice>("gcpad", gcvibration);
|
||||
|
||||
jcadapter = std::make_shared<JCAdapter::Joycons>();
|
||||
jcbuttons = std::make_shared<JCButtonFactory>(jcadapter);
|
||||
Input::RegisterFactory<Input::ButtonDevice>("jcpad", jcbuttons);
|
||||
jcanalog = std::make_shared<JCAnalogFactory>(jcadapter);
|
||||
Input::RegisterFactory<Input::AnalogDevice>("jcpad", jcanalog);
|
||||
jcmotion = std::make_shared<JCMotionFactory>(jcadapter);
|
||||
Input::RegisterFactory<Input::MotionDevice>("jcpad", jcmotion);
|
||||
|
||||
keyboard = std::make_shared<Keyboard>();
|
||||
Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard);
|
||||
Input::RegisterFactory<Input::AnalogDevice>("analog_from_button",
|
||||
@@ -79,6 +89,14 @@ struct InputSubsystem::Impl {
|
||||
gcanalog.reset();
|
||||
gcvibration.reset();
|
||||
|
||||
Input::UnregisterFactory<Input::ButtonDevice>("jcpad");
|
||||
Input::UnregisterFactory<Input::AnalogDevice>("jcpad");
|
||||
Input::UnregisterFactory<Input::MotionDevice>("jcpad");
|
||||
|
||||
jcbuttons.reset();
|
||||
jcanalog.reset();
|
||||
jcmotion.reset();
|
||||
|
||||
Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp");
|
||||
Input::UnregisterFactory<Input::TouchDevice>("cemuhookudp");
|
||||
|
||||
@@ -105,6 +123,8 @@ struct InputSubsystem::Impl {
|
||||
auto sdl_devices = sdl->GetInputDevices();
|
||||
devices.insert(devices.end(), sdl_devices.begin(), sdl_devices.end());
|
||||
#endif
|
||||
auto jcpad_devices = jcadapter->GetInputDevices();
|
||||
devices.insert(devices.end(), jcpad_devices.begin(), jcpad_devices.end());
|
||||
auto udp_devices = udp->GetInputDevices();
|
||||
devices.insert(devices.end(), udp_devices.begin(), udp_devices.end());
|
||||
auto gcpad_devices = gcadapter->GetInputDevices();
|
||||
@@ -120,6 +140,9 @@ struct InputSubsystem::Impl {
|
||||
if (params.Get("class", "") == "gcpad") {
|
||||
return gcadapter->GetAnalogMappingForDevice(params);
|
||||
}
|
||||
if (params.Get("class", "") == "jcpad") {
|
||||
return jcadapter->GetAnalogMappingForDevice(params);
|
||||
}
|
||||
#ifdef HAVE_SDL2
|
||||
if (params.Get("class", "") == "sdl") {
|
||||
return sdl->GetAnalogMappingForDevice(params);
|
||||
@@ -136,6 +159,9 @@ struct InputSubsystem::Impl {
|
||||
if (params.Get("class", "") == "gcpad") {
|
||||
return gcadapter->GetButtonMappingForDevice(params);
|
||||
}
|
||||
if (params.Get("class", "") == "jcpad") {
|
||||
return jcadapter->GetButtonMappingForDevice(params);
|
||||
}
|
||||
#ifdef HAVE_SDL2
|
||||
if (params.Get("class", "") == "sdl") {
|
||||
return sdl->GetButtonMappingForDevice(params);
|
||||
@@ -169,6 +195,10 @@ struct InputSubsystem::Impl {
|
||||
std::shared_ptr<MouseAnalogFactory> mouseanalog;
|
||||
std::shared_ptr<MouseMotionFactory> mousemotion;
|
||||
std::shared_ptr<MouseTouchFactory> mousetouch;
|
||||
std::shared_ptr<JCButtonFactory> jcbuttons;
|
||||
std::shared_ptr<JCAnalogFactory> jcanalog;
|
||||
std::shared_ptr<JCMotionFactory> jcmotion;
|
||||
std::shared_ptr<JCAdapter::Joycons> jcadapter;
|
||||
std::shared_ptr<CemuhookUDP::Client> udp;
|
||||
std::shared_ptr<GCAdapter::Adapter> gcadapter;
|
||||
std::shared_ptr<MouseInput::Mouse> mouse;
|
||||
@@ -234,6 +264,30 @@ const GCButtonFactory* InputSubsystem::GetGCButtons() const {
|
||||
return impl->gcbuttons.get();
|
||||
}
|
||||
|
||||
JCAnalogFactory* InputSubsystem::GetJCAnalogs() {
|
||||
return impl->jcanalog.get();
|
||||
}
|
||||
|
||||
const JCAnalogFactory* InputSubsystem::GetJCAnalogs() const {
|
||||
return impl->jcanalog.get();
|
||||
}
|
||||
|
||||
JCButtonFactory* InputSubsystem::GetJCButtons() {
|
||||
return impl->jcbuttons.get();
|
||||
}
|
||||
|
||||
const JCButtonFactory* InputSubsystem::GetJCButtons() const {
|
||||
return impl->jcbuttons.get();
|
||||
}
|
||||
|
||||
JCMotionFactory* InputSubsystem::GetJCMotions() {
|
||||
return impl->jcmotion.get();
|
||||
}
|
||||
|
||||
const JCMotionFactory* InputSubsystem::GetJCMotions() const {
|
||||
return impl->jcmotion.get();
|
||||
}
|
||||
|
||||
UDPMotionFactory* InputSubsystem::GetUDPMotions() {
|
||||
return impl->udpmotion.get();
|
||||
}
|
||||
@@ -319,4 +373,4 @@ std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left,
|
||||
};
|
||||
return circle_pad_param.Serialize();
|
||||
}
|
||||
} // namespace InputCommon
|
||||
} // namespace InputCommon
|
||||
@@ -58,6 +58,9 @@ public:
|
||||
|
||||
class GCAnalogFactory;
|
||||
class GCButtonFactory;
|
||||
class JCAnalogFactory;
|
||||
class JCButtonFactory;
|
||||
class JCMotionFactory;
|
||||
class UDPMotionFactory;
|
||||
class UDPTouchFactory;
|
||||
class MouseButtonFactory;
|
||||
@@ -132,6 +135,24 @@ public:
|
||||
/// Retrieves the underlying GameCube button handler.
|
||||
[[nodiscard]] const GCButtonFactory* GetGCButtons() const;
|
||||
|
||||
/// Retrieves the underlying joycon analog handler.
|
||||
[[nodiscard]] JCAnalogFactory* GetJCAnalogs();
|
||||
|
||||
/// Retrieves the underlying joycon analog handler.
|
||||
[[nodiscard]] const JCAnalogFactory* GetJCAnalogs() const;
|
||||
|
||||
/// Retrieves the underlying joycon button handler.
|
||||
[[nodiscard]] JCButtonFactory* GetJCButtons();
|
||||
|
||||
/// Retrieves the underlying joycon button handler.
|
||||
[[nodiscard]] const JCButtonFactory* GetJCButtons() const;
|
||||
|
||||
/// Retrieves the underlying joycon motion handler.
|
||||
[[nodiscard]] JCMotionFactory* GetJCMotions();
|
||||
|
||||
/// Retrieves the underlying joycon motion handler.
|
||||
[[nodiscard]] const JCMotionFactory* GetJCMotions() const;
|
||||
|
||||
/// Retrieves the underlying udp motion handler.
|
||||
[[nodiscard]] UDPMotionFactory* GetUDPMotions();
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "core/hle/service/hid/hid.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "input_common/gcadapter/gc_poller.h"
|
||||
#include "input_common/joycon/jc_poller.h"
|
||||
#include "input_common/main.h"
|
||||
#include "input_common/mouse/mouse_poller.h"
|
||||
#include "input_common/udp/udp.h"
|
||||
@@ -128,6 +129,25 @@ QString ButtonToText(const Common::ParamPackage& param) {
|
||||
return GetKeyName(param.Get("code", 0));
|
||||
}
|
||||
|
||||
if (param.Get("engine", "") == "jcpad") {
|
||||
if (param.Has("motion")) {
|
||||
const QString motion_str = QString::fromStdString(param.Get("motion", ""));
|
||||
const QString direction_str = QString::fromStdString(param.Get("direction", ""));
|
||||
return QObject::tr("JC Motion %1%2").arg(motion_str, direction_str);
|
||||
}
|
||||
if (param.Has("axis")) {
|
||||
const QString axis_str = QString::fromStdString(param.Get("axis", ""));
|
||||
const QString direction_str = QString::fromStdString(param.Get("direction", ""));
|
||||
|
||||
return QObject::tr("JC Axis %1%2").arg(axis_str, direction_str);
|
||||
}
|
||||
if (param.Has("button")) {
|
||||
const QString button_str = QString::number(int(std::log2(param.Get("button", 0))));
|
||||
return QObject::tr("JC Button %1").arg(button_str);
|
||||
}
|
||||
return GetKeyName(param.Get("code", 0));
|
||||
}
|
||||
|
||||
if (param.Get("engine", "") == "sdl") {
|
||||
if (param.Has("hat")) {
|
||||
const QString hat_str = QString::fromStdString(param.Get("hat", ""));
|
||||
@@ -201,6 +221,26 @@ QString AnalogToText(const Common::ParamPackage& param, const std::string& dir)
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
if (param.Get("engine", "") == "jcpad") {
|
||||
if (dir == "modifier") {
|
||||
return QObject::tr("[unused]");
|
||||
}
|
||||
|
||||
if (dir == "left" || dir == "right") {
|
||||
const QString axis_x_str = QString::fromStdString(param.Get("axis_x", ""));
|
||||
|
||||
return QObject::tr("JC Axis %1").arg(axis_x_str);
|
||||
}
|
||||
|
||||
if (dir == "up" || dir == "down") {
|
||||
const QString axis_y_str = QString::fromStdString(param.Get("axis_y", ""));
|
||||
|
||||
return QObject::tr("JC Axis %1").arg(axis_y_str);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
return QObject::tr("[unknown]");
|
||||
}
|
||||
} // namespace
|
||||
@@ -529,6 +569,27 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (input_subsystem->GetJCAnalogs()->IsPolling()) {
|
||||
params = input_subsystem->GetJCAnalogs()->GetNextInput();
|
||||
if (params.Has("engine")) {
|
||||
SetPollingResult(params, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (input_subsystem->GetJCButtons()->IsPolling()) {
|
||||
params = input_subsystem->GetJCButtons()->GetNextInput();
|
||||
if (params.Has("engine")) {
|
||||
SetPollingResult(params, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (input_subsystem->GetJCMotions()->IsPolling()) {
|
||||
params = input_subsystem->GetJCMotions()->GetNextInput();
|
||||
if (params.Has("engine")) {
|
||||
SetPollingResult(params, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (auto& poller : device_pollers) {
|
||||
params = poller->GetNextInput();
|
||||
if (params.Has("engine") && IsInputAcceptable(params)) {
|
||||
@@ -845,9 +906,9 @@ void ConfigureInputPlayer::UpdateUI() {
|
||||
|
||||
int slider_value;
|
||||
auto& param = analogs_param[analog_id];
|
||||
const bool is_controller = param.Get("engine", "") == "sdl" ||
|
||||
param.Get("engine", "") == "gcpad" ||
|
||||
param.Get("engine", "") == "mouse";
|
||||
const bool is_controller =
|
||||
param.Get("engine", "") == "sdl" || param.Get("engine", "") == "gcpad" ||
|
||||
param.Get("engine", "") == "mouse" || param.Get("engine", "") == "jcpad";
|
||||
|
||||
if (is_controller) {
|
||||
if (!param.Has("deadzone")) {
|
||||
@@ -1173,6 +1234,14 @@ void ConfigureInputPlayer::HandleClick(
|
||||
input_subsystem->GetMouseTouch()->BeginConfiguration();
|
||||
}
|
||||
|
||||
if (type == InputCommon::Polling::DeviceType::Button) {
|
||||
input_subsystem->GetJCButtons()->BeginConfiguration();
|
||||
} else if (type == InputCommon::Polling::DeviceType::AnalogPreferred) {
|
||||
input_subsystem->GetJCAnalogs()->BeginConfiguration();
|
||||
} else {
|
||||
input_subsystem->GetJCMotions()->BeginConfiguration();
|
||||
}
|
||||
|
||||
timeout_timer->start(2500); // Cancel after 2.5 seconds
|
||||
poll_timer->start(50); // Check for new inputs every 50ms
|
||||
}
|
||||
@@ -1197,6 +1266,10 @@ void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params,
|
||||
input_subsystem->GetMouseMotions()->EndConfiguration();
|
||||
input_subsystem->GetMouseTouch()->EndConfiguration();
|
||||
|
||||
input_subsystem->GetJCButtons()->EndConfiguration();
|
||||
input_subsystem->GetJCAnalogs()->EndConfiguration();
|
||||
input_subsystem->GetJCMotions()->EndConfiguration();
|
||||
|
||||
if (!abort) {
|
||||
(*input_setter)(params);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user