Skip to content

Commit 3248d94

Browse files
committed
XInputSource: Support SCP XInput Bridge
1 parent 5707977 commit 3248d94

File tree

2 files changed

+172
-48
lines changed

2 files changed

+172
-48
lines changed

src/util/xinput_source.cpp

Lines changed: 121 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,19 @@ static constexpr std::array<u16, XInputSource::NUM_BUTTONS> s_button_masks = {{
6767
XINPUT_GAMEPAD_Y,
6868
0x400, // XINPUT_GAMEPAD_GUIDE
6969
}};
70+
71+
static constexpr const std::array s_scp_axis_fields = {
72+
&SCP_EXTN::SCP_LX, &SCP_EXTN::SCP_LY, &SCP_EXTN::SCP_RX, &SCP_EXTN::SCP_RY, &SCP_EXTN::SCP_L2, &SCP_EXTN::SCP_R2,
73+
};
74+
static_assert(std::size(s_scp_axis_fields) == XInputSource::NUM_AXES);
75+
76+
static constexpr const std::array s_scp_button_fields = {
77+
&SCP_EXTN::SCP_UP, &SCP_EXTN::SCP_DOWN, &SCP_EXTN::SCP_LEFT, &SCP_EXTN::SCP_RIGHT, &SCP_EXTN::SCP_START,
78+
&SCP_EXTN::SCP_SELECT, &SCP_EXTN::SCP_L3, &SCP_EXTN::SCP_R3, &SCP_EXTN::SCP_L1, &SCP_EXTN::SCP_R1,
79+
&SCP_EXTN::SCP_X, &SCP_EXTN::SCP_C, &SCP_EXTN::SCP_S, &SCP_EXTN::SCP_T, &SCP_EXTN::SCP_PS,
80+
};
81+
static_assert(std::size(s_scp_button_fields) == XInputSource::NUM_BUTTONS);
82+
7083
static constexpr std::array<const char*, XInputSource::NUM_BUTTONS> s_button_icons = {{
7184
ICON_PF_XBOX_DPAD_UP, // XINPUT_GAMEPAD_DPAD_UP
7285
ICON_PF_XBOX_DPAD_DOWN, // XINPUT_GAMEPAD_DPAD_DOWN
@@ -144,6 +157,14 @@ bool XInputSource::Initialize(const SettingsInterface& si, std::unique_lock<std:
144157
return false;
145158
}
146159

160+
// Only present with SCP extension (DSHidMini)
161+
m_xinput_get_extended =
162+
reinterpret_cast<decltype(m_xinput_get_extended)>(GetProcAddress(m_xinput_module, "XInputGetExtended"));
163+
if (m_xinput_get_extended)
164+
INFO_COLOR_LOG(StrongGreen, "XInputGetExtended() is available, SCP extension features enabled.");
165+
else
166+
INFO_COLOR_LOG(StrongOrange, "XInputGetExtended() is not available, SCP extension features disabled.");
167+
147168
ReloadDevices();
148169
return true;
149170
}
@@ -152,14 +173,26 @@ void XInputSource::UpdateSettings(const SettingsInterface& si, std::unique_lock<
152173
{
153174
}
154175

176+
bool XInputSource::UseSCPExtn() const
177+
{
178+
return (m_xinput_get_extended != nullptr);
179+
}
180+
181+
DWORD XInputSource::GetControllerState(u32 index, ControllerState* state)
182+
{
183+
if (UseSCPExtn())
184+
return m_xinput_get_extended(index, &state->scp_extn);
185+
else
186+
return m_xinput_get_state(index, &state->xinput);
187+
}
188+
155189
bool XInputSource::ReloadDevices()
156190
{
157191
bool changed = false;
158192
for (u32 i = 0; i < NUM_CONTROLLERS; i++)
159193
{
160-
XINPUT_STATE new_state;
161-
DWORD result = m_xinput_get_state(i, &new_state);
162-
194+
ControllerState new_state;
195+
const DWORD result = GetControllerState(i, &new_state);
163196
if (result == ERROR_SUCCESS)
164197
{
165198
if (m_controllers[i].connected)
@@ -198,6 +231,7 @@ void XInputSource::Shutdown()
198231
m_xinput_get_state = nullptr;
199232
m_xinput_set_state = nullptr;
200233
m_xinput_get_capabilities = nullptr;
234+
m_xinput_get_extended = nullptr;
201235
}
202236

203237
void XInputSource::PollEvents()
@@ -208,9 +242,8 @@ void XInputSource::PollEvents()
208242
if (!was_connected)
209243
continue;
210244

211-
XINPUT_STATE new_state;
212-
DWORD result = m_xinput_get_state(i, &new_state);
213-
245+
ControllerState new_state;
246+
const DWORD result = GetControllerState(i, &new_state);
214247
if (result == ERROR_SUCCESS)
215248
{
216249
if (!was_connected)
@@ -457,7 +490,7 @@ bool XInputSource::GetGenericBindingMapping(std::string_view device, GenericInpu
457490
return true;
458491
}
459492

460-
void XInputSource::HandleControllerConnection(u32 index, const XINPUT_STATE& state)
493+
void XInputSource::HandleControllerConnection(u32 index, const ControllerState& state)
461494
{
462495
INFO_LOG("XInput controller {} connected.", index);
463496

@@ -485,14 +518,16 @@ void XInputSource::HandleControllerDisconnection(u32 index)
485518
GetDeviceIdentifier(index));
486519
}
487520

488-
void XInputSource::CheckForStateChanges(u32 index, const XINPUT_STATE& new_state)
521+
void XInputSource::CheckForStateChanges(u32 index, const ControllerState& new_state)
489522
{
490523
ControllerData& cd = m_controllers[index];
491-
if (new_state.dwPacketNumber == cd.last_state.dwPacketNumber)
492-
return;
524+
if (!UseSCPExtn())
525+
{
526+
if (new_state.xinput.dwPacketNumber == cd.last_state.xinput.dwPacketNumber)
527+
return;
493528

494-
XINPUT_GAMEPAD& ogp = cd.last_state.Gamepad;
495-
const XINPUT_GAMEPAD& ngp = new_state.Gamepad;
529+
XINPUT_GAMEPAD& ogp = cd.last_state.xinput.Gamepad;
530+
const XINPUT_GAMEPAD& ngp = new_state.xinput.Gamepad;
496531

497532
#define CHECK_AXIS(field, axis, min_value, max_value) \
498533
if (ogp.field != ngp.field) \
@@ -502,28 +537,57 @@ void XInputSource::CheckForStateChanges(u32 index, const XINPUT_STATE& new_state
502537
s_xinput_generic_binding_axis_mapping[axis][BoolToUInt8(ngp.field >= 0)]); \
503538
}
504539

505-
// Y axes is inverted in XInput when compared to SDL.
506-
CHECK_AXIS(sThumbLX, AXIS_LEFTX, 32768, 32767);
507-
CHECK_AXIS(sThumbLY, AXIS_LEFTY, -32768, -32767);
508-
CHECK_AXIS(sThumbRX, AXIS_RIGHTX, 32768, 32767);
509-
CHECK_AXIS(sThumbRY, AXIS_RIGHTY, -32768, -32767);
510-
CHECK_AXIS(bLeftTrigger, AXIS_LEFTTRIGGER, 0, 255);
511-
CHECK_AXIS(bRightTrigger, AXIS_RIGHTTRIGGER, 0, 255);
540+
// Y axes is inverted in XInput when compared to SDL.
541+
CHECK_AXIS(sThumbLX, AXIS_LEFTX, 32768, 32767);
542+
CHECK_AXIS(sThumbLY, AXIS_LEFTY, -32768, -32767);
543+
CHECK_AXIS(sThumbRX, AXIS_RIGHTX, 32768, 32767);
544+
CHECK_AXIS(sThumbRY, AXIS_RIGHTY, -32768, -32767);
545+
CHECK_AXIS(bLeftTrigger, AXIS_LEFTTRIGGER, 0, 255);
546+
CHECK_AXIS(bRightTrigger, AXIS_RIGHTTRIGGER, 0, 255);
512547

513548
#undef CHECK_AXIS
514549

515-
const u16 old_button_bits = ogp.wButtons;
516-
const u16 new_button_bits = ngp.wButtons;
517-
if (old_button_bits != new_button_bits)
550+
const u16 old_button_bits = ogp.wButtons;
551+
const u16 new_button_bits = ngp.wButtons;
552+
if (old_button_bits != new_button_bits)
553+
{
554+
for (u32 button = 0; button < NUM_BUTTONS; button++)
555+
{
556+
const u16 button_mask = s_button_masks[button];
557+
if ((old_button_bits & button_mask) != (new_button_bits & button_mask))
558+
{
559+
const GenericInputBinding generic_key = s_xinput_generic_binding_button_mapping[button];
560+
const float value = ((new_button_bits & button_mask) != 0) ? 1.0f : 0.0f;
561+
InputManager::InvokeEvents(MakeGenericControllerButtonKey(InputSourceType::XInput, index, button), value,
562+
generic_key);
563+
}
564+
}
565+
}
566+
}
567+
else
518568
{
519-
for (u32 button = 0; button < NUM_BUTTONS; button++)
569+
for (u32 i = 0; i < NUM_AXES; i++)
570+
{
571+
const float old_value = (cd.last_state.scp_extn.*s_scp_axis_fields[i]);
572+
const float new_value = (new_state.scp_extn.*s_scp_axis_fields[i]);
573+
if (old_value != new_value)
574+
{
575+
// Y axes is inverted in XInput when compared to SDL.
576+
const bool invert = (i == AXIS_LEFTY || i == AXIS_RIGHTY);
577+
InputManager::InvokeEvents(MakeGenericControllerAxisKey(InputSourceType::XInput, index, i),
578+
invert ? (new_value * -1.0f) : new_value,
579+
s_xinput_generic_binding_axis_mapping[i][BoolToUInt8(new_value >= 0)]);
580+
}
581+
}
582+
583+
for (u32 i = 0; i < NUM_BUTTONS; i++)
520584
{
521-
const u16 button_mask = s_button_masks[button];
522-
if ((old_button_bits & button_mask) != (new_button_bits & button_mask))
585+
const float old_value = (cd.last_state.scp_extn.*s_scp_button_fields[i]);
586+
const float new_value = (new_state.scp_extn.*s_scp_button_fields[i]);
587+
if (old_value != new_value)
523588
{
524-
const GenericInputBinding generic_key = s_xinput_generic_binding_button_mapping[button];
525-
const float value = ((new_button_bits & button_mask) != 0) ? 1.0f : 0.0f;
526-
InputManager::InvokeEvents(MakeGenericControllerButtonKey(InputSourceType::XInput, index, button), value,
589+
const GenericInputBinding generic_key = s_xinput_generic_binding_button_mapping[i];
590+
InputManager::InvokeEvents(MakeGenericControllerButtonKey(InputSourceType::XInput, index, i), new_value,
527591
generic_key);
528592
}
529593
}
@@ -544,28 +608,45 @@ std::optional<float> XInputSource::GetCurrentValue(InputBindingKey key)
544608

545609
if (key.source_subtype == InputSubclass::ControllerAxis && key.data < NUM_AXES)
546610
{
547-
const XINPUT_GAMEPAD& state = cd.last_state.Gamepad;
611+
if (!UseSCPExtn())
612+
{
613+
const XINPUT_GAMEPAD& state = cd.last_state.xinput.Gamepad;
548614
#define CHECK_AXIS(field, axis, min_value, max_value) \
549615
case axis: \
550616
ret = static_cast<float>(state.field) / ((state.field < 0) ? min_value : max_value); \
551617
break;
552618

553-
// Y axes is inverted in XInput when compared to SDL.
554-
switch (key.data)
619+
// Y axes is inverted in XInput when compared to SDL.
620+
switch (key.data)
621+
{
622+
CHECK_AXIS(sThumbLX, AXIS_LEFTX, 32768, 32767);
623+
CHECK_AXIS(sThumbLY, AXIS_LEFTY, -32768, -32767);
624+
CHECK_AXIS(sThumbRX, AXIS_RIGHTX, 32768, 32767);
625+
CHECK_AXIS(sThumbRY, AXIS_RIGHTY, -32768, -32767);
626+
CHECK_AXIS(bLeftTrigger, AXIS_LEFTTRIGGER, 0, 255);
627+
CHECK_AXIS(bRightTrigger, AXIS_RIGHTTRIGGER, 0, 255);
628+
}
629+
}
630+
else
555631
{
556-
CHECK_AXIS(sThumbLX, AXIS_LEFTX, 32768, 32767);
557-
CHECK_AXIS(sThumbLY, AXIS_LEFTY, -32768, -32767);
558-
CHECK_AXIS(sThumbRX, AXIS_RIGHTX, 32768, 32767);
559-
CHECK_AXIS(sThumbRY, AXIS_RIGHTY, -32768, -32767);
560-
CHECK_AXIS(bLeftTrigger, AXIS_LEFTTRIGGER, 0, 255);
561-
CHECK_AXIS(bRightTrigger, AXIS_RIGHTTRIGGER, 0, 255);
632+
const float value = (cd.last_state.scp_extn.*s_scp_axis_fields[key.data]);
633+
634+
// Y axes is inverted in XInput when compared to SDL.
635+
ret = ((key.data == AXIS_LEFTY || key.data == AXIS_RIGHTY) ? (value * -1.0f) : value);
562636
}
563637
}
564638
else if (key.source_subtype == InputSubclass::ControllerButton && key.data < NUM_BUTTONS)
565639
{
566-
const XINPUT_GAMEPAD& state = cd.last_state.Gamepad;
567-
const u16 button_mask = s_button_masks[key.data];
568-
ret = BoolToFloat((state.wButtons & button_mask) != 0);
640+
if (!UseSCPExtn())
641+
{
642+
const XINPUT_GAMEPAD& state = cd.last_state.xinput.Gamepad;
643+
const u16 button_mask = s_button_masks[key.data];
644+
ret = BoolToFloat((state.wButtons & button_mask) != 0);
645+
}
646+
else
647+
{
648+
ret = (cd.last_state.scp_extn.*s_scp_button_fields[key.data]);
649+
}
569650
}
570651

571652
return ret;

src/util/xinput_source.h

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,39 @@
1212

1313
class SettingsInterface;
1414

15+
// https://github.com/nefarius/DsHidMini/blob/master/include/DsHidMini/ScpTypes.h
16+
struct SCP_EXTN
17+
{
18+
FLOAT SCP_UP;
19+
FLOAT SCP_RIGHT;
20+
FLOAT SCP_DOWN;
21+
FLOAT SCP_LEFT;
22+
23+
FLOAT SCP_LX;
24+
FLOAT SCP_LY;
25+
26+
FLOAT SCP_L1;
27+
FLOAT SCP_L2;
28+
FLOAT SCP_L3;
29+
30+
FLOAT SCP_RX;
31+
FLOAT SCP_RY;
32+
33+
FLOAT SCP_R1;
34+
FLOAT SCP_R2;
35+
FLOAT SCP_R3;
36+
37+
FLOAT SCP_T;
38+
FLOAT SCP_C;
39+
FLOAT SCP_X;
40+
FLOAT SCP_S;
41+
42+
FLOAT SCP_SELECT;
43+
FLOAT SCP_START;
44+
45+
FLOAT SCP_PS;
46+
};
47+
1548
class XInputSource final : public InputSource
1649
{
1750
public:
@@ -60,28 +93,38 @@ class XInputSource final : public InputSource
6093
std::unique_ptr<ForceFeedbackDevice> CreateForceFeedbackDevice(std::string_view device, Error* error) override;
6194

6295
private:
96+
union ControllerState
97+
{
98+
XINPUT_STATE xinput;
99+
SCP_EXTN scp_extn;
100+
};
101+
63102
struct ControllerData
64103
{
65-
XINPUT_STATE last_state;
66-
XINPUT_VIBRATION last_vibration = {};
67-
bool connected = false;
68-
bool has_large_motor = false;
69-
bool has_small_motor = false;
104+
ControllerState last_state;
105+
XINPUT_VIBRATION last_vibration;
106+
bool connected;
107+
bool has_large_motor;
108+
bool has_small_motor;
70109
};
71110

72111
using ControllerDataArray = std::array<ControllerData, NUM_CONTROLLERS>;
73112

74-
void CheckForStateChanges(u32 index, const XINPUT_STATE& new_state);
75-
void HandleControllerConnection(u32 index, const XINPUT_STATE& state);
113+
bool UseSCPExtn() const;
114+
DWORD GetControllerState(u32 index, ControllerState* state);
115+
116+
void CheckForStateChanges(u32 index, const ControllerState& new_state);
117+
void HandleControllerConnection(u32 index, const ControllerState& state);
76118
void HandleControllerDisconnection(u32 index);
77119

78120
static std::string GetDeviceIdentifier(u32 index);
79121
static std::string GetDeviceName(u32 index);
80122

81-
ControllerDataArray m_controllers;
123+
ControllerDataArray m_controllers = {};
82124

83125
HMODULE m_xinput_module{};
84126
DWORD(WINAPI* m_xinput_get_state)(DWORD, XINPUT_STATE*) = nullptr;
85127
DWORD(WINAPI* m_xinput_set_state)(DWORD, XINPUT_VIBRATION*) = nullptr;
86128
DWORD(WINAPI* m_xinput_get_capabilities)(DWORD, DWORD, XINPUT_CAPABILITIES*) = nullptr;
129+
DWORD(WINAPI* m_xinput_get_extended)(DWORD, SCP_EXTN*) = nullptr;
87130
};

0 commit comments

Comments
 (0)