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:
parent
88388f1efc
commit
a44b1018b5
@ -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."
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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 };
|
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user