diff --git a/rpcs3/Json/pad_settings.json b/rpcs3/Json/pad_settings.json index f222db6052..4b93cc96f0 100644 --- a/rpcs3/Json/pad_settings.json +++ b/rpcs3/Json/pad_settings.json @@ -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 RPCS3 Wiki 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 RPCS3 Wiki.", "evdev": "The evdev handler should work with any controller that has linux support.\nIf your joystick is not being centered properly, read the RPCS3 Wiki 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." } diff --git a/rpcs3/xinput_pad_handler.cpp b/rpcs3/xinput_pad_handler.cpp index a099d679ff..484dfb1ec5 100644 --- a/rpcs3/xinput_pad_handler.cpp +++ b/rpcs3/xinput_pad_handler.cpp @@ -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(device_number), &state); - if (dwResult != ERROR_SUCCESS) + const auto state = GetState(static_cast(device_number)); + if (std::get(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 pressed_button = { 0, "" }; - auto data = GetButtonValues(state); + ASSERT(std::get>(state).has_value()); + std::pair pressed_button = {0, ""}; + const PadButtonValues& data = *std::get>(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 xinput_pad_handler::GetButtonValues(const XINPUT_STATE& state) +std::array xinput_pad_handler::GetButtonValues_Base(const XINPUT_STATE& state) { std::array values; @@ -263,6 +303,91 @@ std::array xinput_pad_han return values; } +std::array xinput_pad_handler::GetButtonValues_SCP(const SCP_EXTN& state) +{ + std::array values; + + // Triggers + values[xinput_pad_handler::XInputKeyCodes::LT] = static_cast(state.SCP_L2 * 255.0f); + values[xinput_pad_handler::XInputKeyCodes::RT] = static_cast(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(lx * -32768.0f) : 0; + values[xinput_pad_handler::XInputKeyCodes::LSXPos] = lx > 0.0f ? static_cast(lx * 32767.0f) : 0; + + // Left Stick Y Axis + values[xinput_pad_handler::XInputKeyCodes::LSYNeg] = ly < 0.0f ? static_cast(ly * -32768.0f) : 0; + values[xinput_pad_handler::XInputKeyCodes::LSYPos] = ly > 0.0f ? static_cast(ly * 32767.0f) : 0; + + // Right Stick X Axis + values[xinput_pad_handler::XInputKeyCodes::RSXNeg] = rx < 0.0f ? static_cast(rx * -32768.0f) : 0; + values[xinput_pad_handler::XInputKeyCodes::RSXPos] = rx > 0.0f ? static_cast(rx * 32767.0f) : 0; + + // Right Stick Y Axis + values[xinput_pad_handler::XInputKeyCodes::RSYNeg] = ry < 0.0f ? static_cast(ry * -32768.0f) : 0; + values[xinput_pad_handler::XInputKeyCodes::RSYPos] = ry > 0.0f ? static_cast(ry * 32767.0f) : 0; + + // A, B, X, Y + values[xinput_pad_handler::XInputKeyCodes::A] = static_cast(state.SCP_X * 255.0f); + values[xinput_pad_handler::XInputKeyCodes::B] = static_cast(state.SCP_C * 255.0f); + values[xinput_pad_handler::XInputKeyCodes::X] = static_cast(state.SCP_S * 255.0f); + values[xinput_pad_handler::XInputKeyCodes::Y] = static_cast(state.SCP_T * 255.0f); + + // D-Pad + values[xinput_pad_handler::XInputKeyCodes::Left] = static_cast(state.SCP_LEFT * 255.0f); + values[xinput_pad_handler::XInputKeyCodes::Right] = static_cast(state.SCP_RIGHT * 255.0f); + values[xinput_pad_handler::XInputKeyCodes::Up] = static_cast(state.SCP_UP * 255.0f); + values[xinput_pad_handler::XInputKeyCodes::Down] = static_cast(state.SCP_DOWN * 255.0f); + + // LB, RB, LS, RS + values[xinput_pad_handler::XInputKeyCodes::LB] = static_cast(state.SCP_L1 * 255.0f); + values[xinput_pad_handler::XInputKeyCodes::RB] = static_cast(state.SCP_R1 * 255.0f); + values[xinput_pad_handler::XInputKeyCodes::LS] = static_cast(state.SCP_L3 * 255.0f); + values[xinput_pad_handler::XInputKeyCodes::RS] = static_cast(state.SCP_R3 * 255.0f); + + // Start, Back, Guide + values[xinput_pad_handler::XInputKeyCodes::Start] = static_cast(state.SCP_START * 255.0f); + values[xinput_pad_handler::XInputKeyCodes::Back] = static_cast(state.SCP_SELECT * 255.0f); + values[xinput_pad_handler::XInputKeyCodes::Guide] = static_cast(state.SCP_PS * 255.0f); + + return values; +} + +auto xinput_pad_handler::GetState(u32 device_number) -> std::tuple> +{ + std::tuple> result; + std::get(result) = ERROR_NOT_CONNECTED; + + // Try SCP first, if it fails for that pad then try normal XInput + if (xinputGetExtended) + { + SCP_EXTN stateExtn; + std::get(result) = xinputGetExtended(device_number, &stateExtn); + if (std::get(result) == ERROR_SUCCESS) + { + std::get>(result) = GetButtonValues_SCP(stateExtn); + } + } + + if (std::get(result) != ERROR_SUCCESS) + { + XINPUT_STATE stateBase; + std::get(result) = xinputGetState(device_number, &stateBase); + if (std::get(result) == ERROR_SUCCESS) + { + std::get>(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(GetProcAddress(library, "XInputEnable")); + xinputGetExtended = reinterpret_cast(GetProcAddress(library, "XInputGetExtended")); // Optional xinputGetState = reinterpret_cast(GetProcAddress(library, reinterpret_cast(100))); if (!xinputGetState) xinputGetState = reinterpret_cast(GetProcAddress(library, "XInputGetState")); @@ -281,7 +406,7 @@ bool xinput_pad_handler::Init() xinputSetState = reinterpret_cast(GetProcAddress(library, "XInputSetState")); xinputGetBatteryInformation = reinterpret_cast(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(state)) { case ERROR_DEVICE_NOT_CONNECTED: { @@ -350,7 +477,8 @@ void xinput_pad_handler::ThreadProc() connected++; } - std::array button_values = GetButtonValues(state); + ASSERT(std::get>(state).has_value()); + const PadButtonValues& button_values = *std::get>(state); // Translate any corresponding keycodes to our normal DS3 buttons and triggers for (auto& btn : pad->m_buttons) diff --git a/rpcs3/xinput_pad_handler.h b/rpcs3/xinput_pad_handler.h index ad94d9222b..3c3d334698 100644 --- a/rpcs3/xinput_pad_handler.h +++ b/rpcs3/xinput_pad_handler.h @@ -6,20 +6,9 @@ #include #include #include +#include -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; + private: int GetDeviceNumber(const std::string& padId); - std::array GetButtonValues(const XINPUT_STATE& state); + std::tuple> 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 blacklist; std::vector, std::shared_ptr>> bindings; std::shared_ptr m_dev; - - // holds internal controller state change - XINPUT_STATE state; - DWORD result{ 0 }; };