mirror of
https://github.com/RPCS3/rpcs3.git
synced 2024-11-26 04:32:35 +01:00
Add SCP extension support to XInput pad handler (#6524)
* Add SCP extension support to XInput pad handler * Add SCP mention in xinput handler description tooltip
This commit is contained in:
parent
88388f1efc
commit
a44b1018b5
@ -8,7 +8,7 @@
|
||||
"ds4_windows": "If you have any issues with the DualShock 4 handler, it might be caused by third-party tools such as DS4Windows. It's recommended that you disable them while using this handler.",
|
||||
"ds4_linux": "In order to use the DualShock 4 handler, you might need to add udev rules to let RPCS3 access the controller.\nSee the <a href=\"https://wiki.rpcs3.net/index.php?title=Help:Controller_Configuration\">RPCS3 Wiki</a> for instructions.",
|
||||
"ds4_other": "The DualShock 4 handler is recommended for official DualShock 4 controllers.",
|
||||
"xinput": "The XInput handler will work with Xbox controllers and many third-party PC-compatible controllers.",
|
||||
"xinput": "The XInput handler will work with Xbox controllers and many third-party PC-compatible controllers. Pressure sensitive buttons from SCP are supported when SCP's XInput1_3.dll is placed in the main RPCS3 directory. For more details, see the <a href=\"https://wiki.rpcs3.net/index.php?title=Help:Controller_Configuration\">RPCS3 Wiki</a>.",
|
||||
"evdev": "The evdev handler should work with any controller that has linux support.\nIf your joystick is not being centered properly, read the <a href=\"https://wiki.rpcs3.net/index.php?title=Help:Controller_Configuration\">RPCS3 Wiki</a> for instructions.",
|
||||
"mmjoy": "The MMJoystick handler should work with almost any controller recognized by Windows. However, it is recommended that you use the more specific handlers if you have a controller that supports them."
|
||||
}
|
||||
|
@ -2,6 +2,49 @@
|
||||
#ifdef _WIN32
|
||||
#include "xinput_pad_handler.h"
|
||||
|
||||
namespace XINPUT_INFO
|
||||
{
|
||||
const DWORD GUIDE_BUTTON = 0x0400;
|
||||
const LPCWSTR LIBRARY_FILENAMES[] = {
|
||||
L"xinput1_3.dll", // Prioritizing 1_3 because of SCP
|
||||
L"xinput1_4.dll",
|
||||
L"xinput9_1_0.dll"
|
||||
};
|
||||
} // namespace XINPUT_INFO
|
||||
|
||||
// ScpToolkit defined structure for pressure sensitive button query
|
||||
struct SCP_EXTN
|
||||
{
|
||||
float SCP_UP;
|
||||
float SCP_RIGHT;
|
||||
float SCP_DOWN;
|
||||
float SCP_LEFT;
|
||||
|
||||
float SCP_LX;
|
||||
float SCP_LY;
|
||||
|
||||
float SCP_L1;
|
||||
float SCP_L2;
|
||||
float SCP_L3;
|
||||
|
||||
float SCP_RX;
|
||||
float SCP_RY;
|
||||
|
||||
float SCP_R1;
|
||||
float SCP_R2;
|
||||
float SCP_R3;
|
||||
|
||||
float SCP_T;
|
||||
float SCP_C;
|
||||
float SCP_X;
|
||||
float SCP_S;
|
||||
|
||||
float SCP_SELECT;
|
||||
float SCP_START;
|
||||
|
||||
float SCP_PS;
|
||||
};
|
||||
|
||||
xinput_pad_handler::xinput_pad_handler() : PadHandlerBase(pad_handler::xinput)
|
||||
{
|
||||
init_configs();
|
||||
@ -83,20 +126,17 @@ void xinput_pad_handler::GetNextButtonPress(const std::string& padId, const std:
|
||||
if (device_number < 0)
|
||||
return fail_callback(padId);
|
||||
|
||||
DWORD dwResult;
|
||||
XINPUT_STATE state;
|
||||
ZeroMemory(&state, sizeof(XINPUT_STATE));
|
||||
|
||||
// Simply get the state of the controller from XInput.
|
||||
dwResult = (*xinputGetState)(static_cast<u32>(device_number), &state);
|
||||
if (dwResult != ERROR_SUCCESS)
|
||||
const auto state = GetState(static_cast<u32>(device_number));
|
||||
if (std::get<DWORD>(state) != ERROR_SUCCESS)
|
||||
return fail_callback(padId);
|
||||
|
||||
// Check for each button in our list if its corresponding (maybe remapped) button or axis was pressed.
|
||||
// Return the new value if the button was pressed (aka. its value was bigger than 0 or the defined threshold)
|
||||
// Use a pair to get all the legally pressed buttons and use the one with highest value (prioritize first)
|
||||
std::pair<u16, std::string> pressed_button = { 0, "" };
|
||||
auto data = GetButtonValues(state);
|
||||
ASSERT(std::get<std::optional<PadButtonValues>>(state).has_value());
|
||||
std::pair<u16, std::string> pressed_button = {0, ""};
|
||||
const PadButtonValues& data = *std::get<std::optional<PadButtonValues>>(state);
|
||||
for (const auto& button : button_list)
|
||||
{
|
||||
u32 keycode = button.first;
|
||||
@ -204,7 +244,7 @@ int xinput_pad_handler::GetDeviceNumber(const std::string& padId)
|
||||
return device_number;
|
||||
}
|
||||
|
||||
std::array<u16, xinput_pad_handler::XInputKeyCodes::KeyCodeCount> xinput_pad_handler::GetButtonValues(const XINPUT_STATE& state)
|
||||
std::array<u16, xinput_pad_handler::XInputKeyCodes::KeyCodeCount> xinput_pad_handler::GetButtonValues_Base(const XINPUT_STATE& state)
|
||||
{
|
||||
std::array<u16, xinput_pad_handler::XInputKeyCodes::KeyCodeCount> values;
|
||||
|
||||
@ -263,6 +303,91 @@ std::array<u16, xinput_pad_handler::XInputKeyCodes::KeyCodeCount> xinput_pad_han
|
||||
return values;
|
||||
}
|
||||
|
||||
std::array<u16, xinput_pad_handler::XInputKeyCodes::KeyCodeCount> xinput_pad_handler::GetButtonValues_SCP(const SCP_EXTN& state)
|
||||
{
|
||||
std::array<u16, xinput_pad_handler::XInputKeyCodes::KeyCodeCount> values;
|
||||
|
||||
// Triggers
|
||||
values[xinput_pad_handler::XInputKeyCodes::LT] = static_cast<u16>(state.SCP_L2 * 255.0f);
|
||||
values[xinput_pad_handler::XInputKeyCodes::RT] = static_cast<u16>(state.SCP_R2 * 255.0f);
|
||||
|
||||
// Sticks
|
||||
float lx = state.SCP_LX;
|
||||
float ly = state.SCP_LY;
|
||||
float rx = state.SCP_RX;
|
||||
float ry = state.SCP_RY;
|
||||
|
||||
// Left Stick X Axis
|
||||
values[xinput_pad_handler::XInputKeyCodes::LSXNeg] = lx < 0.0f ? static_cast<u16>(lx * -32768.0f) : 0;
|
||||
values[xinput_pad_handler::XInputKeyCodes::LSXPos] = lx > 0.0f ? static_cast<u16>(lx * 32767.0f) : 0;
|
||||
|
||||
// Left Stick Y Axis
|
||||
values[xinput_pad_handler::XInputKeyCodes::LSYNeg] = ly < 0.0f ? static_cast<u16>(ly * -32768.0f) : 0;
|
||||
values[xinput_pad_handler::XInputKeyCodes::LSYPos] = ly > 0.0f ? static_cast<u16>(ly * 32767.0f) : 0;
|
||||
|
||||
// Right Stick X Axis
|
||||
values[xinput_pad_handler::XInputKeyCodes::RSXNeg] = rx < 0.0f ? static_cast<u16>(rx * -32768.0f) : 0;
|
||||
values[xinput_pad_handler::XInputKeyCodes::RSXPos] = rx > 0.0f ? static_cast<u16>(rx * 32767.0f) : 0;
|
||||
|
||||
// Right Stick Y Axis
|
||||
values[xinput_pad_handler::XInputKeyCodes::RSYNeg] = ry < 0.0f ? static_cast<u16>(ry * -32768.0f) : 0;
|
||||
values[xinput_pad_handler::XInputKeyCodes::RSYPos] = ry > 0.0f ? static_cast<u16>(ry * 32767.0f) : 0;
|
||||
|
||||
// A, B, X, Y
|
||||
values[xinput_pad_handler::XInputKeyCodes::A] = static_cast<u16>(state.SCP_X * 255.0f);
|
||||
values[xinput_pad_handler::XInputKeyCodes::B] = static_cast<u16>(state.SCP_C * 255.0f);
|
||||
values[xinput_pad_handler::XInputKeyCodes::X] = static_cast<u16>(state.SCP_S * 255.0f);
|
||||
values[xinput_pad_handler::XInputKeyCodes::Y] = static_cast<u16>(state.SCP_T * 255.0f);
|
||||
|
||||
// D-Pad
|
||||
values[xinput_pad_handler::XInputKeyCodes::Left] = static_cast<u16>(state.SCP_LEFT * 255.0f);
|
||||
values[xinput_pad_handler::XInputKeyCodes::Right] = static_cast<u16>(state.SCP_RIGHT * 255.0f);
|
||||
values[xinput_pad_handler::XInputKeyCodes::Up] = static_cast<u16>(state.SCP_UP * 255.0f);
|
||||
values[xinput_pad_handler::XInputKeyCodes::Down] = static_cast<u16>(state.SCP_DOWN * 255.0f);
|
||||
|
||||
// LB, RB, LS, RS
|
||||
values[xinput_pad_handler::XInputKeyCodes::LB] = static_cast<u16>(state.SCP_L1 * 255.0f);
|
||||
values[xinput_pad_handler::XInputKeyCodes::RB] = static_cast<u16>(state.SCP_R1 * 255.0f);
|
||||
values[xinput_pad_handler::XInputKeyCodes::LS] = static_cast<u16>(state.SCP_L3 * 255.0f);
|
||||
values[xinput_pad_handler::XInputKeyCodes::RS] = static_cast<u16>(state.SCP_R3 * 255.0f);
|
||||
|
||||
// Start, Back, Guide
|
||||
values[xinput_pad_handler::XInputKeyCodes::Start] = static_cast<u16>(state.SCP_START * 255.0f);
|
||||
values[xinput_pad_handler::XInputKeyCodes::Back] = static_cast<u16>(state.SCP_SELECT * 255.0f);
|
||||
values[xinput_pad_handler::XInputKeyCodes::Guide] = static_cast<u16>(state.SCP_PS * 255.0f);
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
auto xinput_pad_handler::GetState(u32 device_number) -> std::tuple<DWORD, std::optional<PadButtonValues>>
|
||||
{
|
||||
std::tuple<DWORD, std::optional<PadButtonValues>> result;
|
||||
std::get<DWORD>(result) = ERROR_NOT_CONNECTED;
|
||||
|
||||
// Try SCP first, if it fails for that pad then try normal XInput
|
||||
if (xinputGetExtended)
|
||||
{
|
||||
SCP_EXTN stateExtn;
|
||||
std::get<DWORD>(result) = xinputGetExtended(device_number, &stateExtn);
|
||||
if (std::get<DWORD>(result) == ERROR_SUCCESS)
|
||||
{
|
||||
std::get<std::optional<PadButtonValues>>(result) = GetButtonValues_SCP(stateExtn);
|
||||
}
|
||||
}
|
||||
|
||||
if (std::get<DWORD>(result) != ERROR_SUCCESS)
|
||||
{
|
||||
XINPUT_STATE stateBase;
|
||||
std::get<DWORD>(result) = xinputGetState(device_number, &stateBase);
|
||||
if (std::get<DWORD>(result) == ERROR_SUCCESS)
|
||||
{
|
||||
std::get<std::optional<PadButtonValues>>(result) = GetButtonValues_Base(stateBase);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool xinput_pad_handler::Init()
|
||||
{
|
||||
if (is_init)
|
||||
@ -273,7 +398,7 @@ bool xinput_pad_handler::Init()
|
||||
library = LoadLibrary(it);
|
||||
if (library)
|
||||
{
|
||||
xinputEnable = reinterpret_cast<PFN_XINPUTENABLE>(GetProcAddress(library, "XInputEnable"));
|
||||
xinputGetExtended = reinterpret_cast<PFN_XINPUTGETEXTENDED>(GetProcAddress(library, "XInputGetExtended")); // Optional
|
||||
xinputGetState = reinterpret_cast<PFN_XINPUTGETSTATE>(GetProcAddress(library, reinterpret_cast<LPCSTR>(100)));
|
||||
if (!xinputGetState)
|
||||
xinputGetState = reinterpret_cast<PFN_XINPUTGETSTATE>(GetProcAddress(library, "XInputGetState"));
|
||||
@ -281,7 +406,7 @@ bool xinput_pad_handler::Init()
|
||||
xinputSetState = reinterpret_cast<PFN_XINPUTSETSTATE>(GetProcAddress(library, "XInputSetState"));
|
||||
xinputGetBatteryInformation = reinterpret_cast<PFN_XINPUTGETBATTERYINFORMATION>(GetProcAddress(library, "XInputGetBatteryInformation"));
|
||||
|
||||
if (xinputEnable && xinputGetState && xinputSetState && xinputGetBatteryInformation)
|
||||
if (xinputGetState && xinputSetState && xinputGetBatteryInformation)
|
||||
{
|
||||
is_init = true;
|
||||
break;
|
||||
@ -289,8 +414,9 @@ bool xinput_pad_handler::Init()
|
||||
|
||||
FreeLibrary(library);
|
||||
library = nullptr;
|
||||
xinputEnable = nullptr;
|
||||
xinputGetExtended = nullptr;
|
||||
xinputGetState = nullptr;
|
||||
xinputSetState = nullptr;
|
||||
xinputGetBatteryInformation = nullptr;
|
||||
}
|
||||
}
|
||||
@ -307,8 +433,9 @@ void xinput_pad_handler::Close()
|
||||
{
|
||||
FreeLibrary(library);
|
||||
library = nullptr;
|
||||
xinputGetExtended = nullptr;
|
||||
xinputGetState = nullptr;
|
||||
xinputEnable = nullptr;
|
||||
xinputSetState = nullptr;
|
||||
xinputGetBatteryInformation = nullptr;
|
||||
}
|
||||
}
|
||||
@ -323,9 +450,9 @@ void xinput_pad_handler::ThreadProc()
|
||||
auto profile = m_dev->config;
|
||||
auto pad = bind.second;
|
||||
|
||||
result = (*xinputGetState)(padnum, &state);
|
||||
const auto state = GetState(padnum);
|
||||
|
||||
switch (result)
|
||||
switch (std::get<DWORD>(state))
|
||||
{
|
||||
case ERROR_DEVICE_NOT_CONNECTED:
|
||||
{
|
||||
@ -350,7 +477,8 @@ void xinput_pad_handler::ThreadProc()
|
||||
connected++;
|
||||
}
|
||||
|
||||
std::array<u16, XInputKeyCodes::KeyCodeCount> button_values = GetButtonValues(state);
|
||||
ASSERT(std::get<std::optional<PadButtonValues>>(state).has_value());
|
||||
const PadButtonValues& button_values = *std::get<std::optional<PadButtonValues>>(state);
|
||||
|
||||
// Translate any corresponding keycodes to our normal DS3 buttons and triggers
|
||||
for (auto& btn : pad->m_buttons)
|
||||
|
@ -6,20 +6,9 @@
|
||||
#include <Windows.h>
|
||||
#include <Xinput.h>
|
||||
#include <chrono>
|
||||
#include <optional>
|
||||
|
||||
namespace XINPUT_INFO
|
||||
{
|
||||
const DWORD THREAD_TIMEOUT = 1000;
|
||||
const DWORD THREAD_SLEEP = 10;
|
||||
const DWORD THREAD_SLEEP_INACTIVE = 100;
|
||||
const DWORD GUIDE_BUTTON = 0x0400;
|
||||
const LPCWSTR LIBRARY_FILENAMES[] = {
|
||||
L"xinput1_4.dll",
|
||||
L"xinput1_3.dll",
|
||||
L"xinput1_2.dll",
|
||||
L"xinput9_1_0.dll"
|
||||
};
|
||||
}
|
||||
struct SCP_EXTN;
|
||||
|
||||
class xinput_pad_handler final : public PadHandlerBase
|
||||
{
|
||||
@ -112,28 +101,28 @@ public:
|
||||
void init_config(pad_config* cfg, const std::string& name) override;
|
||||
|
||||
private:
|
||||
typedef void (WINAPI * PFN_XINPUTENABLE)(BOOL);
|
||||
typedef DWORD (WINAPI * PFN_XINPUTGETEXTENDED)(DWORD, SCP_EXTN *);
|
||||
typedef DWORD (WINAPI * PFN_XINPUTGETSTATE)(DWORD, XINPUT_STATE *);
|
||||
typedef DWORD (WINAPI * PFN_XINPUTSETSTATE)(DWORD, XINPUT_VIBRATION *);
|
||||
typedef DWORD (WINAPI * PFN_XINPUTGETBATTERYINFORMATION)(DWORD, BYTE, XINPUT_BATTERY_INFORMATION *);
|
||||
|
||||
using PadButtonValues = std::array<u16, XInputKeyCodes::KeyCodeCount>;
|
||||
|
||||
private:
|
||||
int GetDeviceNumber(const std::string& padId);
|
||||
std::array<u16, XInputKeyCodes::KeyCodeCount> GetButtonValues(const XINPUT_STATE& state);
|
||||
std::tuple<DWORD, std::optional<PadButtonValues>> GetState(u32 device_number);
|
||||
PadButtonValues GetButtonValues_Base(const XINPUT_STATE& state);
|
||||
PadButtonValues GetButtonValues_SCP(const SCP_EXTN& state);
|
||||
void TranslateButtonPress(u64 keyCode, bool& pressed, u16& val, bool ignore_threshold = false) override;
|
||||
|
||||
bool is_init{ false };
|
||||
HMODULE library{ nullptr };
|
||||
PFN_XINPUTGETEXTENDED xinputGetExtended{ nullptr };
|
||||
PFN_XINPUTGETSTATE xinputGetState{ nullptr };
|
||||
PFN_XINPUTSETSTATE xinputSetState{ nullptr };
|
||||
PFN_XINPUTENABLE xinputEnable{ nullptr };
|
||||
PFN_XINPUTGETBATTERYINFORMATION xinputGetBatteryInformation{ nullptr };
|
||||
|
||||
std::vector<u32> blacklist;
|
||||
std::vector<std::pair<std::shared_ptr<XInputDevice>, std::shared_ptr<Pad>>> bindings;
|
||||
std::shared_ptr<XInputDevice> m_dev;
|
||||
|
||||
// holds internal controller state change
|
||||
XINPUT_STATE state;
|
||||
DWORD result{ 0 };
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user