mirror of https://github.com/PCSX2/pcsx2.git
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.
This commit is contained in:
parent
5e913b0e9e
commit
502a44e9f5
|
@ -97,10 +97,12 @@ XInputSource::~XInputSource() = default;
|
||||||
bool XInputSource::Initialize(SettingsInterface& si)
|
bool XInputSource::Initialize(SettingsInterface& si)
|
||||||
{
|
{
|
||||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
#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)
|
if (!m_xinput_module)
|
||||||
{
|
{
|
||||||
m_xinput_module = LoadLibraryW(L"xinput1_3");
|
m_xinput_module = LoadLibraryW(L"xinput1_4");
|
||||||
}
|
}
|
||||||
if (!m_xinput_module)
|
if (!m_xinput_module)
|
||||||
{
|
{
|
||||||
|
@ -121,10 +123,15 @@ bool XInputSource::Initialize(SettingsInterface& si)
|
||||||
reinterpret_cast<decltype(m_xinput_set_state)>(GetProcAddress(m_xinput_module, "XInputSetState"));
|
reinterpret_cast<decltype(m_xinput_set_state)>(GetProcAddress(m_xinput_module, "XInputSetState"));
|
||||||
m_xinput_get_capabilities =
|
m_xinput_get_capabilities =
|
||||||
reinterpret_cast<decltype(m_xinput_get_capabilities)>(GetProcAddress(m_xinput_module, "XInputGetCapabilities"));
|
reinterpret_cast<decltype(m_xinput_get_capabilities)>(GetProcAddress(m_xinput_module, "XInputGetCapabilities"));
|
||||||
|
|
||||||
|
// SCP extension, only exists when the bridge xinput1_3.dll is in use
|
||||||
|
m_xinput_get_extended =
|
||||||
|
reinterpret_cast<decltype(m_xinput_get_extended)>(GetProcAddress(m_xinput_module, "XInputGetExtended"));
|
||||||
#else
|
#else
|
||||||
m_xinput_get_state = XInputGetState;
|
m_xinput_get_state = XInputGetState;
|
||||||
m_xinput_set_state = XInputSetState;
|
m_xinput_set_state = XInputSetState;
|
||||||
m_xinput_get_capabilities = XInputGetCapabilities;
|
m_xinput_get_capabilities = XInputGetCapabilities;
|
||||||
|
m_xinput_get_extended = nullptr;
|
||||||
#endif
|
#endif
|
||||||
if (!m_xinput_get_state || !m_xinput_set_state || !m_xinput_get_capabilities)
|
if (!m_xinput_get_state || !m_xinput_set_state || !m_xinput_get_capabilities)
|
||||||
{
|
{
|
||||||
|
@ -158,15 +165,29 @@ void XInputSource::Shutdown()
|
||||||
m_xinput_get_state = nullptr;
|
m_xinput_get_state = nullptr;
|
||||||
m_xinput_set_state = nullptr;
|
m_xinput_set_state = nullptr;
|
||||||
m_xinput_get_capabilities = nullptr;
|
m_xinput_get_capabilities = nullptr;
|
||||||
|
m_xinput_get_extended = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void XInputSource::PollEvents()
|
void XInputSource::PollEvents()
|
||||||
{
|
{
|
||||||
for (u32 i = 0; i < NUM_CONTROLLERS; i++)
|
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;
|
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);
|
||||||
|
|
||||||
|
CheckForStateChangesSCP(i, new_state_scp);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
XINPUT_STATE new_state;
|
||||||
|
result = m_xinput_get_state(i, &new_state);
|
||||||
|
|
||||||
if (result == ERROR_SUCCESS)
|
if (result == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
if (!was_connected)
|
if (!was_connected)
|
||||||
|
@ -184,6 +205,7 @@ void XInputSource::PollEvents()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::pair<std::string, std::string>> XInputSource::EnumerateDevices()
|
std::vector<std::pair<std::string, std::string>> XInputSource::EnumerateDevices()
|
||||||
{
|
{
|
||||||
|
@ -362,6 +384,8 @@ void XInputSource::HandleControllerConnection(u32 index)
|
||||||
cd.connected = true;
|
cd.connected = true;
|
||||||
cd.has_large_motor = caps.Vibration.wLeftMotorSpeed != 0;
|
cd.has_large_motor = caps.Vibration.wLeftMotorSpeed != 0;
|
||||||
cd.has_small_motor = caps.Vibration.wRightMotorSpeed != 0;
|
cd.has_small_motor = caps.Vibration.wRightMotorSpeed != 0;
|
||||||
|
cd.last_state = {};
|
||||||
|
cd.last_state_scp = {};
|
||||||
|
|
||||||
Host::OnInputDeviceConnected(StringUtil::StdStringFromFormat("XInput-%u", index),
|
Host::OnInputDeviceConnected(StringUtil::StdStringFromFormat("XInput-%u", index),
|
||||||
StringUtil::StdStringFromFormat("XInput Controller %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)
|
if (new_state.dwPacketNumber == cd.last_state.dwPacketNumber)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
cd.last_state.dwPacketNumber = new_state.dwPacketNumber;
|
|
||||||
|
|
||||||
XINPUT_GAMEPAD& ogp = cd.last_state.Gamepad;
|
XINPUT_GAMEPAD& ogp = cd.last_state.Gamepad;
|
||||||
const XINPUT_GAMEPAD& ngp = new_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( \
|
InputManager::InvokeEvents( \
|
||||||
MakeGenericControllerAxisKey(InputSourceType::XInput, index, axis), \
|
MakeGenericControllerAxisKey(InputSourceType::XInput, index, axis), \
|
||||||
static_cast<float>(ngp.field) / ((ngp.field < 0) ? min_value : max_value)); \
|
static_cast<float>(ngp.field) / ((ngp.field < 0) ? min_value : max_value)); \
|
||||||
ogp.field = ngp.field; \
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Y axes is inverted in XInput when compared to SDL.
|
// 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),
|
MakeGenericControllerButtonKey(InputSourceType::XInput, index, button),
|
||||||
(new_button_bits & button_mask) != 0);
|
(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)
|
void XInputSource::UpdateMotorState(InputBindingKey key, float intensity)
|
||||||
|
|
|
@ -21,6 +21,39 @@
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
// 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 SettingsInterface;
|
||||||
|
|
||||||
class XInputSource final : public InputSource
|
class XInputSource final : public InputSource
|
||||||
|
@ -63,7 +96,11 @@ private:
|
||||||
|
|
||||||
struct ControllerData
|
struct ControllerData
|
||||||
{
|
{
|
||||||
XINPUT_STATE last_state = {};
|
union
|
||||||
|
{
|
||||||
|
XINPUT_STATE last_state;
|
||||||
|
SCP_EXTN last_state_scp;
|
||||||
|
};
|
||||||
XINPUT_VIBRATION last_vibration = {};
|
XINPUT_VIBRATION last_vibration = {};
|
||||||
bool connected = false;
|
bool connected = false;
|
||||||
bool has_large_motor = false;
|
bool has_large_motor = false;
|
||||||
|
@ -73,6 +110,7 @@ private:
|
||||||
using ControllerDataArray = std::array<ControllerData, NUM_CONTROLLERS>;
|
using ControllerDataArray = std::array<ControllerData, NUM_CONTROLLERS>;
|
||||||
|
|
||||||
void CheckForStateChanges(u32 index, const XINPUT_STATE& new_state);
|
void CheckForStateChanges(u32 index, const XINPUT_STATE& new_state);
|
||||||
|
void CheckForStateChangesSCP(u32 index, const SCP_EXTN& new_state);
|
||||||
void HandleControllerConnection(u32 index);
|
void HandleControllerConnection(u32 index);
|
||||||
void HandleControllerDisconnection(u32 index);
|
void HandleControllerDisconnection(u32 index);
|
||||||
|
|
||||||
|
@ -82,6 +120,7 @@ private:
|
||||||
DWORD(WINAPI* m_xinput_get_state)(DWORD, XINPUT_STATE*);
|
DWORD(WINAPI* m_xinput_get_state)(DWORD, XINPUT_STATE*);
|
||||||
DWORD(WINAPI* m_xinput_set_state)(DWORD, XINPUT_VIBRATION*);
|
DWORD(WINAPI* m_xinput_set_state)(DWORD, XINPUT_VIBRATION*);
|
||||||
DWORD(WINAPI* m_xinput_get_capabilities)(DWORD, DWORD, XINPUT_CAPABILITIES*);
|
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_axis_names[NUM_AXES];
|
||||||
static const char* s_button_names[NUM_BUTTONS];
|
static const char* s_button_names[NUM_BUTTONS];
|
||||||
|
|
Loading…
Reference in New Issue