From 502a44e9f58d03c6e5fa9b010d9816948bc320bd Mon Sep 17 00:00:00 2001 From: Silent Date: Wed, 13 Apr 2022 21:32:56 +0200 Subject: [PATCH] XInputSource: Add support for SCP API extension Allows to query pressure sensitive DualShock 3 buttons via XInput when DsHidMini is used in SXS mode, with its xinput1_3.dll dropped in the emulator directory. --- pcsx2/Frontend/XInputSource.cpp | 107 +++++++++++++++++++++++++++----- pcsx2/Frontend/XInputSource.h | 43 ++++++++++++- 2 files changed, 134 insertions(+), 16 deletions(-) diff --git a/pcsx2/Frontend/XInputSource.cpp b/pcsx2/Frontend/XInputSource.cpp index 1565d9ccf1..933dbce494 100644 --- a/pcsx2/Frontend/XInputSource.cpp +++ b/pcsx2/Frontend/XInputSource.cpp @@ -97,10 +97,12 @@ XInputSource::~XInputSource() = default; bool XInputSource::Initialize(SettingsInterface& si) { #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) - m_xinput_module = LoadLibraryW(L"xinput1_4"); + // xinput1_3.dll is flawed and obsolete, but it's also commonly used by wrappers. + // For this reason, try to load it *only* from the application directory, and not system32. + m_xinput_module = LoadLibraryExW(L"xinput1_3", nullptr, LOAD_LIBRARY_SEARCH_APPLICATION_DIR); if (!m_xinput_module) { - m_xinput_module = LoadLibraryW(L"xinput1_3"); + m_xinput_module = LoadLibraryW(L"xinput1_4"); } if (!m_xinput_module) { @@ -121,10 +123,15 @@ bool XInputSource::Initialize(SettingsInterface& si) reinterpret_cast(GetProcAddress(m_xinput_module, "XInputSetState")); m_xinput_get_capabilities = reinterpret_cast(GetProcAddress(m_xinput_module, "XInputGetCapabilities")); + + // SCP extension, only exists when the bridge xinput1_3.dll is in use + m_xinput_get_extended = + reinterpret_cast(GetProcAddress(m_xinput_module, "XInputGetExtended")); #else m_xinput_get_state = XInputGetState; m_xinput_set_state = XInputSetState; m_xinput_get_capabilities = XInputGetCapabilities; + m_xinput_get_extended = nullptr; #endif if (!m_xinput_get_state || !m_xinput_set_state || !m_xinput_get_capabilities) { @@ -158,29 +165,44 @@ void XInputSource::Shutdown() m_xinput_get_state = nullptr; m_xinput_set_state = nullptr; m_xinput_get_capabilities = nullptr; + m_xinput_get_extended = nullptr; } void XInputSource::PollEvents() { for (u32 i = 0; i < NUM_CONTROLLERS; i++) { - XINPUT_STATE new_state; - const DWORD result = m_xinput_get_state(i, &new_state); const bool was_connected = m_controllers[i].connected; + + SCP_EXTN new_state_scp; + DWORD result = m_xinput_get_extended ? m_xinput_get_extended(i, &new_state_scp) : ERROR_NOT_SUPPORTED; if (result == ERROR_SUCCESS) { if (!was_connected) HandleControllerConnection(i); - CheckForStateChanges(i, new_state); + CheckForStateChangesSCP(i, new_state_scp); } else { - if (result != ERROR_DEVICE_NOT_CONNECTED) - Console.Warning("XInputGetState(%u) failed: 0x%08X / 0x%08X", i, result, GetLastError()); + XINPUT_STATE new_state; + result = m_xinput_get_state(i, &new_state); - if (was_connected) - HandleControllerDisconnection(i); + if (result == ERROR_SUCCESS) + { + if (!was_connected) + HandleControllerConnection(i); + + CheckForStateChanges(i, new_state); + } + else + { + if (result != ERROR_DEVICE_NOT_CONNECTED) + Console.Warning("XInputGetState(%u) failed: 0x%08X / 0x%08X", i, result, GetLastError()); + + if (was_connected) + HandleControllerDisconnection(i); + } } } } @@ -362,6 +384,8 @@ void XInputSource::HandleControllerConnection(u32 index) cd.connected = true; cd.has_large_motor = caps.Vibration.wLeftMotorSpeed != 0; cd.has_small_motor = caps.Vibration.wRightMotorSpeed != 0; + cd.last_state = {}; + cd.last_state_scp = {}; Host::OnInputDeviceConnected(StringUtil::StdStringFromFormat("XInput-%u", index), StringUtil::StdStringFromFormat("XInput Controller %u", index)); @@ -381,8 +405,6 @@ void XInputSource::CheckForStateChanges(u32 index, const XINPUT_STATE& new_state if (new_state.dwPacketNumber == cd.last_state.dwPacketNumber) return; - cd.last_state.dwPacketNumber = new_state.dwPacketNumber; - XINPUT_GAMEPAD& ogp = cd.last_state.Gamepad; const XINPUT_GAMEPAD& ngp = new_state.Gamepad; @@ -392,7 +414,6 @@ void XInputSource::CheckForStateChanges(u32 index, const XINPUT_STATE& new_state InputManager::InvokeEvents( \ MakeGenericControllerAxisKey(InputSourceType::XInput, index, axis), \ static_cast(ngp.field) / ((ngp.field < 0) ? min_value : max_value)); \ - ogp.field = ngp.field; \ } // Y axes is inverted in XInput when compared to SDL. @@ -418,10 +439,68 @@ void XInputSource::CheckForStateChanges(u32 index, const XINPUT_STATE& new_state MakeGenericControllerButtonKey(InputSourceType::XInput, index, button), (new_button_bits & button_mask) != 0); } - - ogp.wButtons = ngp.wButtons; } } + + cd.last_state = new_state; +} + +void XInputSource::CheckForStateChangesSCP(u32 index, const SCP_EXTN& new_state) +{ + ControllerData& cd = m_controllers[index]; + + SCP_EXTN& ogp = cd.last_state_scp; + const SCP_EXTN& ngp = new_state; + + s32 axis = 0, button = 0; + +#define CHECK_AXIS(field) \ + if (ogp.field != ngp.field) \ + { \ + InputManager::InvokeEvents( \ + MakeGenericControllerAxisKey(InputSourceType::XInput, index, axis), ngp.field); \ + } \ + axis++; + +#define CHECK_BUTTON(field) \ + if (ogp.field != ngp.field) \ + { \ + InputManager::InvokeEvents( \ + MakeGenericControllerButtonKey(InputSourceType::XInput, index, button), ngp.field); \ + } \ + button++; + + CHECK_AXIS(SCP_LX); + CHECK_AXIS(SCP_LY); + CHECK_AXIS(SCP_RX); + CHECK_AXIS(SCP_RY); + CHECK_AXIS(SCP_L2); + CHECK_AXIS(SCP_R2); + + CHECK_BUTTON(SCP_UP); + CHECK_BUTTON(SCP_DOWN); + CHECK_BUTTON(SCP_LEFT); + CHECK_BUTTON(SCP_RIGHT); + + CHECK_BUTTON(SCP_START); + CHECK_BUTTON(SCP_SELECT); + + CHECK_BUTTON(SCP_L3); + CHECK_BUTTON(SCP_R3); + CHECK_BUTTON(SCP_L1); + CHECK_BUTTON(SCP_R1); + + CHECK_BUTTON(SCP_X); + CHECK_BUTTON(SCP_C); + CHECK_BUTTON(SCP_S); + CHECK_BUTTON(SCP_T); + + CHECK_BUTTON(SCP_PS); + +#undef CHECK_BUTTON +#undef CHECK_AXIS + + cd.last_state_scp = new_state; } void XInputSource::UpdateMotorState(InputBindingKey key, float intensity) diff --git a/pcsx2/Frontend/XInputSource.h b/pcsx2/Frontend/XInputSource.h index 419ad42c7a..9574701e1b 100644 --- a/pcsx2/Frontend/XInputSource.h +++ b/pcsx2/Frontend/XInputSource.h @@ -21,6 +21,39 @@ #include #include +// SCP XInput extension +typedef struct +{ + 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; +} SCP_EXTN; + class SettingsInterface; class XInputSource final : public InputSource @@ -63,9 +96,13 @@ private: struct ControllerData { - XINPUT_STATE last_state = {}; + union + { + XINPUT_STATE last_state; + SCP_EXTN last_state_scp; + }; XINPUT_VIBRATION last_vibration = {}; - bool connected = false; + bool connected = false; bool has_large_motor = false; bool has_small_motor = false; }; @@ -73,6 +110,7 @@ private: using ControllerDataArray = std::array; void CheckForStateChanges(u32 index, const XINPUT_STATE& new_state); + void CheckForStateChangesSCP(u32 index, const SCP_EXTN& new_state); void HandleControllerConnection(u32 index); void HandleControllerDisconnection(u32 index); @@ -82,6 +120,7 @@ private: DWORD(WINAPI* m_xinput_get_state)(DWORD, XINPUT_STATE*); DWORD(WINAPI* m_xinput_set_state)(DWORD, XINPUT_VIBRATION*); DWORD(WINAPI* m_xinput_get_capabilities)(DWORD, DWORD, XINPUT_CAPABILITIES*); + DWORD(WINAPI* m_xinput_get_extended)(DWORD, SCP_EXTN*); static const char* s_axis_names[NUM_AXES]; static const char* s_button_names[NUM_BUTTONS];