Play-/Source/ui_qt/win32/InputProviderXInput.cpp

201 lines
5.4 KiB
C++
Raw Normal View History

2019-06-18 21:29:09 -04:00
#include "InputProviderXInput.h"
#include "string_format.h"
2019-06-18 21:29:09 -04:00
#define PROVIDER_ID 'xinp'
2019-06-21 12:40:19 -04:00
// clang-format off
const char* CInputProviderXInput::g_keyNames[CInputProviderXInput::KEYID_MAX] =
{
"Left Thumb (X Axis)",
"Left Thumb (Y Axis)",
"Right Thumb (X Axis)",
"Right Thumb (Y Axis)",
"Left Trigger",
"Right Trigger",
"DPad Up",
"DPad Down",
"DPad Left",
"DPad Right",
"Start",
"Back",
"Left Thumb",
"Right Thumb",
"Left Shoulder",
"Right Shoulder",
"A",
"B",
"X",
"Y"
};
const CInputProviderXInput::KEYID CInputProviderXInput::g_buttonToKey[MAX_BUTTONS] =
{
KEYID_DPAD_UP,
KEYID_DPAD_DOWN,
KEYID_DPAD_LEFT,
KEYID_DPAD_RIGHT,
KEYID_START,
KEYID_BACK,
KEYID_LTHUMB,
KEYID_RTHUMB,
KEYID_LSHOULDER,
KEYID_RSHOULDER,
KEYID_MAX,
KEYID_MAX,
KEYID_A,
KEYID_B,
KEYID_X,
KEYID_Y
};
2019-06-21 12:40:19 -04:00
// clang-format on
2019-06-18 21:29:09 -04:00
CInputProviderXInput::CInputProviderXInput()
{
2019-06-21 12:40:19 -04:00
m_pollingThread = std::thread([this]() { PollDevices(); });
2019-06-18 21:29:09 -04:00
}
CInputProviderXInput::~CInputProviderXInput()
{
m_pollingEnabled = false;
m_pollingThread.join();
}
uint32 CInputProviderXInput::GetId() const
{
return PROVIDER_ID;
}
std::string CInputProviderXInput::GetTargetDescription(const BINDINGTARGET& target) const
{
return string_format("XInput Device %d : %s", target.deviceId[0], g_keyNames[target.keyId]);
2019-06-18 21:29:09 -04:00
}
void CInputProviderXInput::SetVibration(DeviceIdType deviceId, uint8 largeMotor, uint8 smallMotor)
{
WORD scaledLargeMotor = largeMotor == 255 ? 65535 : largeMotor * 256;
WORD scaledSmallMotor = smallMotor ? 65535 : 0;
auto& currState = m_states[deviceId[0]];
if(!currState.connected)
return;
XINPUT_VIBRATION vibration;
ZeroMemory(&vibration, sizeof(XINPUT_VIBRATION));
vibration.wLeftMotorSpeed = scaledLargeMotor;
vibration.wRightMotorSpeed = scaledSmallMotor;
XInputSetState(deviceId[0], &vibration);
}
2019-06-18 21:29:09 -04:00
void CInputProviderXInput::PollDevices()
{
while(m_pollingEnabled)
{
for(unsigned int deviceIndex = 0; deviceIndex < MAX_DEVICES; deviceIndex++)
2019-06-18 21:29:09 -04:00
{
auto& currState = m_states[deviceIndex];
if(currState.idleTime > 0)
{
currState.idleTime--;
continue;
}
2019-06-18 21:29:09 -04:00
XINPUT_STATE state;
DWORD result = XInputGetState(deviceIndex, &state);
2019-06-18 21:29:09 -04:00
if(result == ERROR_SUCCESS)
{
2019-06-19 20:00:57 -04:00
UpdateConnectedDevice(deviceIndex, state);
2019-06-18 21:29:09 -04:00
}
else
{
2019-06-19 20:00:57 -04:00
UpdateDisconnectedDevice(deviceIndex);
2019-06-18 21:29:09 -04:00
}
}
Sleep(16);
2019-06-18 21:29:09 -04:00
}
}
2019-06-19 20:00:57 -04:00
void CInputProviderXInput::UpdateConnectedDevice(uint32 deviceIndex, const XINPUT_STATE& state)
{
auto& currState = m_states[deviceIndex];
if((currState.connected == false) || (currState.packetNumber != state.dwPacketNumber))
{
ReportAxis(deviceIndex, KEYID_LTHUMB_X, state.Gamepad.sThumbLX);
2019-06-19 20:19:33 -04:00
ReportAxis(deviceIndex, KEYID_LTHUMB_Y, -static_cast<int32>(state.Gamepad.sThumbLY));
2019-06-19 20:00:57 -04:00
ReportAxis(deviceIndex, KEYID_RTHUMB_X, state.Gamepad.sThumbRX);
2019-06-19 20:19:33 -04:00
ReportAxis(deviceIndex, KEYID_RTHUMB_Y, -static_cast<int32>(state.Gamepad.sThumbRY));
2019-06-19 20:00:57 -04:00
bool lTriggerState = state.Gamepad.bLeftTrigger >= XINPUT_GAMEPAD_TRIGGER_THRESHOLD;
bool rTriggerState = state.Gamepad.bRightTrigger >= XINPUT_GAMEPAD_TRIGGER_THRESHOLD;
if(lTriggerState != currState.lTriggerPressed)
{
ReportButton(deviceIndex, KEYID_LTRIGGER, lTriggerState);
currState.lTriggerPressed = lTriggerState;
}
if(rTriggerState != currState.rTriggerPressed)
{
ReportButton(deviceIndex, KEYID_RTRIGGER, rTriggerState);
currState.rTriggerPressed = rTriggerState;
}
for(unsigned int keyIndex = 0; keyIndex < MAX_BUTTONS; keyIndex++)
{
auto keyId = g_buttonToKey[keyIndex];
if(keyId == KEYID_MAX) continue;
bool newState = (state.Gamepad.wButtons & (1 << keyIndex)) != 0;
bool oldState = (currState.buttonState & (1 << keyIndex));
if(newState != oldState)
{
ReportButton(deviceIndex, keyId, newState);
}
}
currState.buttonState = state.Gamepad.wButtons;
currState.packetNumber = state.dwPacketNumber;
}
currState.connected = true;
}
void CInputProviderXInput::UpdateDisconnectedDevice(uint32 deviceIndex)
{
auto& currState = m_states[deviceIndex];
if(currState.connected)
{
//Going from connected to disconnected, reset state
ReportAxis(deviceIndex, KEYID_LTHUMB_X, BINDINGTARGET::AXIS_NEUTRAL);
ReportAxis(deviceIndex, KEYID_LTHUMB_Y, BINDINGTARGET::AXIS_NEUTRAL);
ReportAxis(deviceIndex, KEYID_RTHUMB_X, BINDINGTARGET::AXIS_NEUTRAL);
ReportAxis(deviceIndex, KEYID_RTHUMB_Y, BINDINGTARGET::AXIS_NEUTRAL);
ReportButton(deviceIndex, KEYID_LTRIGGER, false);
ReportButton(deviceIndex, KEYID_RTRIGGER, false);
for(unsigned int keyIndex = 0; keyIndex < MAX_BUTTONS; keyIndex++)
{
auto keyId = g_buttonToKey[keyIndex];
if(keyId == KEYID_MAX) continue;
ReportButton(deviceIndex, keyId, false);
}
currState.lTriggerPressed = false;
currState.rTriggerPressed = false;
currState.buttonState = 0;
}
currState.idleTime = 100;
currState.connected = false;
}
void CInputProviderXInput::ReportButton(uint32 deviceIndex, KEYID keyId, bool pressed)
{
BINDINGTARGET tgt;
tgt.providerId = PROVIDER_ID;
2019-06-19 20:00:57 -04:00
tgt.deviceId[0] = deviceIndex;
tgt.keyType = BINDINGTARGET::KEYTYPE::BUTTON;
tgt.keyId = keyId;
OnInput(tgt, pressed ? 1 : 0);
}
2019-06-19 20:19:33 -04:00
void CInputProviderXInput::ReportAxis(uint32 deviceIndex, KEYID keyId, int32 rawValue)
{
BINDINGTARGET tgt;
tgt.providerId = PROVIDER_ID;
2019-06-19 20:00:57 -04:00
tgt.deviceId[0] = deviceIndex;
tgt.keyType = BINDINGTARGET::KEYTYPE::AXIS;
tgt.keyId = keyId;
2019-06-19 20:19:33 -04:00
uint32 cvtValue = std::min<uint32>((rawValue + 0x8000) >> 8, BINDINGTARGET::AXIS_MAX);
OnInput(tgt, cvtValue);
}