1
0
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:
Silent 2019-09-22 11:22:42 +02:00 committed by Megamouse
parent 88388f1efc
commit a44b1018b5
3 changed files with 154 additions and 37 deletions

View File

@ -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_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_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.", "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.", "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." "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."
} }

View File

@ -2,6 +2,49 @@
#ifdef _WIN32 #ifdef _WIN32
#include "xinput_pad_handler.h" #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) xinput_pad_handler::xinput_pad_handler() : PadHandlerBase(pad_handler::xinput)
{ {
init_configs(); init_configs();
@ -83,20 +126,17 @@ void xinput_pad_handler::GetNextButtonPress(const std::string& padId, const std:
if (device_number < 0) if (device_number < 0)
return fail_callback(padId); return fail_callback(padId);
DWORD dwResult;
XINPUT_STATE state;
ZeroMemory(&state, sizeof(XINPUT_STATE));
// Simply get the state of the controller from XInput. // Simply get the state of the controller from XInput.
dwResult = (*xinputGetState)(static_cast<u32>(device_number), &state); const auto state = GetState(static_cast<u32>(device_number));
if (dwResult != ERROR_SUCCESS) if (std::get<DWORD>(state) != ERROR_SUCCESS)
return fail_callback(padId); return fail_callback(padId);
// Check for each button in our list if its corresponding (maybe remapped) button or axis was pressed. // 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) // 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) // 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, "" }; ASSERT(std::get<std::optional<PadButtonValues>>(state).has_value());
auto data = GetButtonValues(state); std::pair<u16, std::string> pressed_button = {0, ""};
const PadButtonValues& data = *std::get<std::optional<PadButtonValues>>(state);
for (const auto& button : button_list) for (const auto& button : button_list)
{ {
u32 keycode = button.first; u32 keycode = button.first;
@ -204,7 +244,7 @@ int xinput_pad_handler::GetDeviceNumber(const std::string& padId)
return device_number; 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; 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; 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() bool xinput_pad_handler::Init()
{ {
if (is_init) if (is_init)
@ -273,7 +398,7 @@ bool xinput_pad_handler::Init()
library = LoadLibrary(it); library = LoadLibrary(it);
if (library) 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))); xinputGetState = reinterpret_cast<PFN_XINPUTGETSTATE>(GetProcAddress(library, reinterpret_cast<LPCSTR>(100)));
if (!xinputGetState) if (!xinputGetState)
xinputGetState = reinterpret_cast<PFN_XINPUTGETSTATE>(GetProcAddress(library, "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")); xinputSetState = reinterpret_cast<PFN_XINPUTSETSTATE>(GetProcAddress(library, "XInputSetState"));
xinputGetBatteryInformation = reinterpret_cast<PFN_XINPUTGETBATTERYINFORMATION>(GetProcAddress(library, "XInputGetBatteryInformation")); xinputGetBatteryInformation = reinterpret_cast<PFN_XINPUTGETBATTERYINFORMATION>(GetProcAddress(library, "XInputGetBatteryInformation"));
if (xinputEnable && xinputGetState && xinputSetState && xinputGetBatteryInformation) if (xinputGetState && xinputSetState && xinputGetBatteryInformation)
{ {
is_init = true; is_init = true;
break; break;
@ -289,8 +414,9 @@ bool xinput_pad_handler::Init()
FreeLibrary(library); FreeLibrary(library);
library = nullptr; library = nullptr;
xinputEnable = nullptr; xinputGetExtended = nullptr;
xinputGetState = nullptr; xinputGetState = nullptr;
xinputSetState = nullptr;
xinputGetBatteryInformation = nullptr; xinputGetBatteryInformation = nullptr;
} }
} }
@ -307,8 +433,9 @@ void xinput_pad_handler::Close()
{ {
FreeLibrary(library); FreeLibrary(library);
library = nullptr; library = nullptr;
xinputGetExtended = nullptr;
xinputGetState = nullptr; xinputGetState = nullptr;
xinputEnable = nullptr; xinputSetState = nullptr;
xinputGetBatteryInformation = nullptr; xinputGetBatteryInformation = nullptr;
} }
} }
@ -323,9 +450,9 @@ void xinput_pad_handler::ThreadProc()
auto profile = m_dev->config; auto profile = m_dev->config;
auto pad = bind.second; 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: case ERROR_DEVICE_NOT_CONNECTED:
{ {
@ -350,7 +477,8 @@ void xinput_pad_handler::ThreadProc()
connected++; 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 // Translate any corresponding keycodes to our normal DS3 buttons and triggers
for (auto& btn : pad->m_buttons) for (auto& btn : pad->m_buttons)

View File

@ -6,20 +6,9 @@
#include <Windows.h> #include <Windows.h>
#include <Xinput.h> #include <Xinput.h>
#include <chrono> #include <chrono>
#include <optional>
namespace XINPUT_INFO struct SCP_EXTN;
{
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"
};
}
class xinput_pad_handler final : public PadHandlerBase class xinput_pad_handler final : public PadHandlerBase
{ {
@ -112,28 +101,28 @@ public:
void init_config(pad_config* cfg, const std::string& name) override; void init_config(pad_config* cfg, const std::string& name) override;
private: 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_XINPUTGETSTATE)(DWORD, XINPUT_STATE *);
typedef DWORD (WINAPI * PFN_XINPUTSETSTATE)(DWORD, XINPUT_VIBRATION *); typedef DWORD (WINAPI * PFN_XINPUTSETSTATE)(DWORD, XINPUT_VIBRATION *);
typedef DWORD (WINAPI * PFN_XINPUTGETBATTERYINFORMATION)(DWORD, BYTE, XINPUT_BATTERY_INFORMATION *); typedef DWORD (WINAPI * PFN_XINPUTGETBATTERYINFORMATION)(DWORD, BYTE, XINPUT_BATTERY_INFORMATION *);
using PadButtonValues = std::array<u16, XInputKeyCodes::KeyCodeCount>;
private: private:
int GetDeviceNumber(const std::string& padId); 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; void TranslateButtonPress(u64 keyCode, bool& pressed, u16& val, bool ignore_threshold = false) override;
bool is_init{ false }; bool is_init{ false };
HMODULE library{ nullptr }; HMODULE library{ nullptr };
PFN_XINPUTGETEXTENDED xinputGetExtended{ nullptr };
PFN_XINPUTGETSTATE xinputGetState{ nullptr }; PFN_XINPUTGETSTATE xinputGetState{ nullptr };
PFN_XINPUTSETSTATE xinputSetState{ nullptr }; PFN_XINPUTSETSTATE xinputSetState{ nullptr };
PFN_XINPUTENABLE xinputEnable{ nullptr };
PFN_XINPUTGETBATTERYINFORMATION xinputGetBatteryInformation{ nullptr }; PFN_XINPUTGETBATTERYINFORMATION xinputGetBatteryInformation{ nullptr };
std::vector<u32> blacklist; std::vector<u32> blacklist;
std::vector<std::pair<std::shared_ptr<XInputDevice>, std::shared_ptr<Pad>>> bindings; std::vector<std::pair<std::shared_ptr<XInputDevice>, std::shared_ptr<Pad>>> bindings;
std::shared_ptr<XInputDevice> m_dev; std::shared_ptr<XInputDevice> m_dev;
// holds internal controller state change
XINPUT_STATE state;
DWORD result{ 0 };
}; };