DirectInput: Various improvements
-Only initialize the controller's default state once (prevents issues when holding down a key on reset/power cycle) -Improve logic to reacquire a controller after it has been lost -Do not pause the emulator thread while trying to update directinput devices
This commit is contained in:
parent
51075ff334
commit
1ef80d8cec
3 changed files with 104 additions and 73 deletions
|
@ -13,34 +13,36 @@
|
||||||
|
|
||||||
LPDIRECTINPUT8 DirectInputManager::_directInput = nullptr;
|
LPDIRECTINPUT8 DirectInputManager::_directInput = nullptr;
|
||||||
vector<DirectInputData> DirectInputManager::_joysticks;
|
vector<DirectInputData> DirectInputManager::_joysticks;
|
||||||
|
vector<DirectInputData> DirectInputManager::_joysticksToAdd;
|
||||||
|
std::vector<GUID> DirectInputManager::_processedGuids;
|
||||||
std::vector<GUID> DirectInputManager::_xinputDeviceGuids;
|
std::vector<GUID> DirectInputManager::_xinputDeviceGuids;
|
||||||
std::vector<GUID> DirectInputManager::_directInputDeviceGuids;
|
|
||||||
bool DirectInputManager::_needToUpdate = false;
|
|
||||||
HWND DirectInputManager::_hWnd = nullptr;
|
HWND DirectInputManager::_hWnd = nullptr;
|
||||||
|
|
||||||
bool DirectInputManager::Initialize()
|
void DirectInputManager::Initialize()
|
||||||
{
|
{
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
|
||||||
// Register with the DirectInput subsystem and get a pointer to a IDirectInput interface we can use.
|
// Register with the DirectInput subsystem and get a pointer to a IDirectInput interface we can use.
|
||||||
// Create a DInput object
|
// Create a DInput object
|
||||||
if(FAILED(hr = DirectInput8Create(GetModuleHandle(nullptr), DIRECTINPUT_VERSION, IID_IDirectInput8, (VOID**)&_directInput, nullptr))) {
|
if(FAILED(hr = DirectInput8Create(GetModuleHandle(nullptr), DIRECTINPUT_VERSION, IID_IDirectInput8, (VOID**)&_directInput, nullptr))) {
|
||||||
return false;
|
MessageManager::Log("[DInput] DirectInput8Create failed: " + std::to_string(hr));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
IDirectInputJoyConfig8* pJoyConfig = nullptr;
|
IDirectInputJoyConfig8* pJoyConfig = nullptr;
|
||||||
if(FAILED(hr = _directInput->QueryInterface(IID_IDirectInputJoyConfig8, (void**)&pJoyConfig))) {
|
if(FAILED(hr = _directInput->QueryInterface(IID_IDirectInputJoyConfig8, (void**)&pJoyConfig))) {
|
||||||
return false;
|
MessageManager::Log("[DInput] QueryInterface failed: " + std::to_string(hr));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(pJoyConfig) {
|
if(pJoyConfig) {
|
||||||
pJoyConfig->Release();
|
pJoyConfig->Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
return UpdateDeviceList();
|
UpdateDeviceList();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DirectInputManager::ProcessDevice(const DIDEVICEINSTANCE* pdidInstance, bool checkOnly)
|
bool DirectInputManager::ProcessDevice(const DIDEVICEINSTANCE* pdidInstance)
|
||||||
{
|
{
|
||||||
const GUID* deviceGuid = &pdidInstance->guidInstance;
|
const GUID* deviceGuid = &pdidInstance->guidInstance;
|
||||||
|
|
||||||
|
@ -51,16 +53,14 @@ bool DirectInputManager::ProcessDevice(const DIDEVICEINSTANCE* pdidInstance, boo
|
||||||
memcmp(guid.Data4, deviceGuid->Data4, sizeof(guid.Data4)) == 0;
|
memcmp(guid.Data4, deviceGuid->Data4, sizeof(guid.Data4)) == 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool knownXInputDevice = std::find_if(_xinputDeviceGuids.begin(), _xinputDeviceGuids.end(), comp) != _xinputDeviceGuids.end();
|
bool wasProcessedBefore = std::find_if(_processedGuids.begin(), _processedGuids.end(), comp) != _processedGuids.end();
|
||||||
bool knownDirectInputDevice = std::find_if(_directInputDeviceGuids.begin(), _directInputDeviceGuids.end(), comp) != _directInputDeviceGuids.end();
|
if(wasProcessedBefore) {
|
||||||
if(knownXInputDevice || knownDirectInputDevice) {
|
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
bool isXInput = IsXInputDevice(&pdidInstance->guidProduct);
|
bool isXInput = IsXInputDevice(&pdidInstance->guidProduct);
|
||||||
if(!checkOnly) {
|
if(isXInput) {
|
||||||
if(isXInput) {
|
_xinputDeviceGuids.push_back(*deviceGuid);
|
||||||
_xinputDeviceGuids.push_back(*deviceGuid);
|
_processedGuids.push_back(*deviceGuid);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return !isXInput;
|
return !isXInput;
|
||||||
}
|
}
|
||||||
|
@ -188,51 +188,36 @@ LCleanup:
|
||||||
return bIsXinputDevice;
|
return bIsXinputDevice;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DirectInputManager::NeedToUpdate()
|
void DirectInputManager::UpdateDeviceList()
|
||||||
{
|
{
|
||||||
HRESULT hr;
|
if(_needToUpdate) {
|
||||||
_needToUpdate = false;
|
//An update is already pending, skip
|
||||||
// Enumerate devices
|
return;
|
||||||
if(FAILED(hr = _directInput->EnumDevices(DI8DEVCLASS_GAMECTRL, NeedToUpdateCallback, nullptr, DIEDFL_ALLDEVICES))) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
return _needToUpdate;
|
|
||||||
}
|
|
||||||
|
|
||||||
int DirectInputManager::NeedToUpdateCallback(const DIDEVICEINSTANCE* pdidInstance, void* pContext)
|
HRESULT hr;
|
||||||
{
|
|
||||||
if(ProcessDevice(pdidInstance, true)) {
|
// Enumerate devices
|
||||||
|
if(SUCCEEDED(hr = _directInput->EnumDevices(DI8DEVCLASS_GAMECTRL, EnumJoysticksCallback, nullptr, DIEDFL_ALLDEVICES))) {
|
||||||
|
if(!_joysticksToAdd.empty()) {
|
||||||
|
//Sleeping apparently lets us read accurate "default" values, otherwise a PS4 controller returns all 0s, despite not doing so normally
|
||||||
|
for(DirectInputData &joystick : _joysticksToAdd) {
|
||||||
|
UpdateInputState(joystick);
|
||||||
|
}
|
||||||
|
std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(100));
|
||||||
|
|
||||||
|
for(DirectInputData &joystick : _joysticksToAdd) {
|
||||||
|
UpdateInputState(joystick);
|
||||||
|
joystick.defaultState = joystick.state;
|
||||||
|
}
|
||||||
|
_needToUpdate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_requestUpdate) {
|
||||||
|
_requestUpdate = false;
|
||||||
_needToUpdate = true;
|
_needToUpdate = true;
|
||||||
return DIENUM_STOP;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return DIENUM_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool DirectInputManager::UpdateDeviceList()
|
|
||||||
{
|
|
||||||
HRESULT hr;
|
|
||||||
|
|
||||||
// Enumerate devices
|
|
||||||
if(FAILED(hr = _directInput->EnumDevices(DI8DEVCLASS_GAMECTRL, EnumJoysticksCallback, nullptr, DIEDFL_ALLDEVICES))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure we got a joystick
|
|
||||||
if(_joysticks.empty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Sleeping apparently lets us read accurate "default" values, otherwise a PS4 controller returns all 0s, despite not doing so normally
|
|
||||||
RefreshState();
|
|
||||||
std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(100));
|
|
||||||
RefreshState();
|
|
||||||
for(DirectInputData &joystick : _joysticks) {
|
|
||||||
joystick.defaultState = joystick.state;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
@ -244,7 +229,9 @@ int DirectInputManager::EnumJoysticksCallback(const DIDEVICEINSTANCE* pdidInstan
|
||||||
{
|
{
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
|
||||||
if(ProcessDevice(pdidInstance, false)) {
|
if(ProcessDevice(pdidInstance)) {
|
||||||
|
_processedGuids.push_back(pdidInstance->guidInstance);
|
||||||
|
|
||||||
// Obtain an interface to the enumerated joystick.
|
// Obtain an interface to the enumerated joystick.
|
||||||
LPDIRECTINPUTDEVICE8 pJoystick = nullptr;
|
LPDIRECTINPUTDEVICE8 pJoystick = nullptr;
|
||||||
hr = _directInput->CreateDevice(pdidInstance->guidInstance, &pJoystick, nullptr);
|
hr = _directInput->CreateDevice(pdidInstance->guidInstance, &pJoystick, nullptr);
|
||||||
|
@ -263,11 +250,18 @@ int DirectInputManager::EnumJoysticksCallback(const DIDEVICEINSTANCE* pdidInstan
|
||||||
if(SUCCEEDED(hr = data.joystick->SetCooperativeLevel(_hWnd, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND))) {
|
if(SUCCEEDED(hr = data.joystick->SetCooperativeLevel(_hWnd, DISCL_NONEXCLUSIVE | DISCL_BACKGROUND))) {
|
||||||
// Enumerate the joystick objects. The callback function enabled user interface elements for objects that are found, and sets the min/max values property for discovered axes.
|
// Enumerate the joystick objects. The callback function enabled user interface elements for objects that are found, and sets the min/max values property for discovered axes.
|
||||||
if(SUCCEEDED(hr = data.joystick->EnumObjects(EnumObjectsCallback, data.joystick, DIDFT_ALL))) {
|
if(SUCCEEDED(hr = data.joystick->EnumObjects(EnumObjectsCallback, data.joystick, DIDFT_ALL))) {
|
||||||
_directInputDeviceGuids.push_back(pdidInstance->guidInstance);
|
_joysticksToAdd.push_back(data);
|
||||||
_joysticks.push_back(data);
|
} else {
|
||||||
|
MessageManager::Log("[DInput] Failed to enumerate objects: " + std::to_string(hr));
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
MessageManager::Log("[DInput] Failed to set cooperative level: " + std::to_string(hr));
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
MessageManager::Log("[DInput] Failed to set data format: " + std::to_string(hr));
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
MessageManager::Log("[DInput] Failed to create directinput device" + std::to_string(hr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return DIENUM_CONTINUE;
|
return DIENUM_CONTINUE;
|
||||||
|
@ -283,9 +277,6 @@ int DirectInputManager::EnumObjectsCallback(const DIDEVICEOBJECTINSTANCE* pdidoi
|
||||||
{
|
{
|
||||||
LPDIRECTINPUTDEVICE8 joystick = (LPDIRECTINPUTDEVICE8)pContext;
|
LPDIRECTINPUTDEVICE8 joystick = (LPDIRECTINPUTDEVICE8)pContext;
|
||||||
|
|
||||||
static int nSliderCount = 0; // Number of returned slider controls
|
|
||||||
static int nPOVCount = 0; // Number of returned POV controls
|
|
||||||
|
|
||||||
// For axes that are returned, set the DIPROP_RANGE property for the enumerated axis in order to scale min/max values.
|
// For axes that are returned, set the DIPROP_RANGE property for the enumerated axis in order to scale min/max values.
|
||||||
if(pdidoi->dwType & DIDFT_AXIS) {
|
if(pdidoi->dwType & DIDFT_AXIS) {
|
||||||
DIPROPRANGE diprg;
|
DIPROPRANGE diprg;
|
||||||
|
@ -308,6 +299,41 @@ int DirectInputManager::EnumObjectsCallback(const DIDEVICEOBJECTINSTANCE* pdidoi
|
||||||
|
|
||||||
void DirectInputManager::RefreshState()
|
void DirectInputManager::RefreshState()
|
||||||
{
|
{
|
||||||
|
if(_needToUpdate) {
|
||||||
|
vector<DirectInputData> joysticks;
|
||||||
|
//Keep exisiting joysticks, if they still work, otherwise remove them from the list
|
||||||
|
for(DirectInputData &joystick : _joysticks) {
|
||||||
|
if(joystick.stateValid) {
|
||||||
|
joysticks.push_back(joystick);
|
||||||
|
} else {
|
||||||
|
MessageManager::Log("[DInput] Device lost, trying to reacquire...");
|
||||||
|
|
||||||
|
//Release the joystick, we'll try to initialize it again if it still exists
|
||||||
|
const GUID* deviceGuid = &joystick.instanceInfo.guidInstance;
|
||||||
|
|
||||||
|
auto comp = [=](GUID guid) {
|
||||||
|
return guid.Data1 == deviceGuid->Data1 &&
|
||||||
|
guid.Data2 == deviceGuid->Data2 &&
|
||||||
|
guid.Data3 == deviceGuid->Data3 &&
|
||||||
|
memcmp(guid.Data4, deviceGuid->Data4, sizeof(guid.Data4)) == 0;
|
||||||
|
};
|
||||||
|
_processedGuids.erase(std::remove_if(_processedGuids.begin(), _processedGuids.end(), comp), _processedGuids.end());
|
||||||
|
|
||||||
|
joystick.joystick->Unacquire();
|
||||||
|
joystick.joystick->Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Add the newly-found joysticks
|
||||||
|
for(DirectInputData &joystick : _joysticksToAdd) {
|
||||||
|
joysticks.push_back(joystick);
|
||||||
|
}
|
||||||
|
|
||||||
|
_joysticks = joysticks;
|
||||||
|
_joysticksToAdd.clear();
|
||||||
|
_needToUpdate = false;
|
||||||
|
}
|
||||||
|
|
||||||
for(DirectInputData &joystick : _joysticks) {
|
for(DirectInputData &joystick : _joysticks) {
|
||||||
UpdateInputState(joystick);
|
UpdateInputState(joystick);
|
||||||
}
|
}
|
||||||
|
@ -373,13 +399,16 @@ void DirectInputManager::UpdateInputState(DirectInputData &data)
|
||||||
// switching, so just try again later
|
// switching, so just try again later
|
||||||
if(FAILED(hr)) {
|
if(FAILED(hr)) {
|
||||||
data.stateValid = false;
|
data.stateValid = false;
|
||||||
|
_requestUpdate = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the input's device state
|
// Get the input's device state
|
||||||
if(FAILED(hr = data.joystick->GetDeviceState(sizeof(DIJOYSTATE2), &newState))) {
|
if(FAILED(hr = data.joystick->GetDeviceState(sizeof(DIJOYSTATE2), &newState))) {
|
||||||
|
MessageManager::Log("[DInput] Failed to get device state: " + std::to_string(hr));
|
||||||
data.stateValid = false;
|
data.stateValid = false;
|
||||||
|
_requestUpdate = true;
|
||||||
return; // The device should have been acquired during the Poll()
|
return; // The device should have been acquired during the Poll()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -400,7 +429,12 @@ DirectInputManager::~DirectInputManager()
|
||||||
data.joystick->Unacquire();
|
data.joystick->Unacquire();
|
||||||
data.joystick->Release();
|
data.joystick->Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_needToUpdate = false;
|
||||||
_joysticks.clear();
|
_joysticks.clear();
|
||||||
|
_joysticksToAdd.clear();
|
||||||
|
_processedGuids.clear();
|
||||||
|
_xinputDeviceGuids.clear();
|
||||||
|
|
||||||
if(_directInput) {
|
if(_directInput) {
|
||||||
_directInput->Release();
|
_directInput->Release();
|
||||||
|
|
|
@ -16,17 +16,19 @@ class DirectInputManager
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
static HWND _hWnd;
|
static HWND _hWnd;
|
||||||
static bool _needToUpdate;
|
bool _needToUpdate = false;
|
||||||
|
bool _requestUpdate = false;
|
||||||
static LPDIRECTINPUT8 _directInput;
|
static LPDIRECTINPUT8 _directInput;
|
||||||
static vector<DirectInputData> _joysticks;
|
static vector<DirectInputData> _joysticks;
|
||||||
static std::vector<GUID> _xinputDeviceGuids;
|
static vector<DirectInputData> _joysticksToAdd;
|
||||||
static std::vector<GUID> _directInputDeviceGuids;
|
|
||||||
|
|
||||||
bool Initialize();
|
static std::vector<GUID> _processedGuids;
|
||||||
|
static std::vector<GUID> _xinputDeviceGuids;
|
||||||
|
|
||||||
|
void Initialize();
|
||||||
void UpdateInputState(DirectInputData& joystick);
|
void UpdateInputState(DirectInputData& joystick);
|
||||||
static bool ProcessDevice(const DIDEVICEINSTANCE* pdidInstance, bool checkOnly);
|
static bool ProcessDevice(const DIDEVICEINSTANCE* pdidInstance);
|
||||||
static bool IsXInputDevice(const GUID* pGuidProductFromDirectInput);
|
static bool IsXInputDevice(const GUID* pGuidProductFromDirectInput);
|
||||||
static int __stdcall NeedToUpdateCallback(const DIDEVICEINSTANCE* pdidInstance, void* pContext);
|
|
||||||
static int __stdcall EnumJoysticksCallback(const DIDEVICEINSTANCE* pdidInstance, void* pContext);
|
static int __stdcall EnumJoysticksCallback(const DIDEVICEINSTANCE* pdidInstance, void* pContext);
|
||||||
static int __stdcall EnumObjectsCallback(const DIDEVICEOBJECTINSTANCE* pdidoi, void* pContext);
|
static int __stdcall EnumObjectsCallback(const DIDEVICEOBJECTINSTANCE* pdidoi, void* pContext);
|
||||||
|
|
||||||
|
@ -35,8 +37,7 @@ public:
|
||||||
~DirectInputManager();
|
~DirectInputManager();
|
||||||
|
|
||||||
void RefreshState();
|
void RefreshState();
|
||||||
bool NeedToUpdate();
|
void UpdateDeviceList();
|
||||||
bool UpdateDeviceList();
|
|
||||||
int GetJoystickCount();
|
int GetJoystickCount();
|
||||||
bool IsPressed(int port, int button);
|
bool IsPressed(int port, int button);
|
||||||
};
|
};
|
||||||
|
|
|
@ -260,11 +260,7 @@ void WindowsKeyManager::StartUpdateDeviceThread()
|
||||||
if(_xInput->NeedToUpdate()) {
|
if(_xInput->NeedToUpdate()) {
|
||||||
_xInput->UpdateDeviceList();
|
_xInput->UpdateDeviceList();
|
||||||
}
|
}
|
||||||
if(_directInput->NeedToUpdate()) {
|
_directInput->UpdateDeviceList();
|
||||||
_console->Pause();
|
|
||||||
_directInput->UpdateDeviceList();
|
|
||||||
_console->Resume();
|
|
||||||
}
|
|
||||||
|
|
||||||
_stopSignal.Wait(5000);
|
_stopSignal.Wait(5000);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue