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:
Sour 2019-01-20 13:38:14 -05:00
parent 51075ff334
commit 1ef80d8cec
3 changed files with 104 additions and 73 deletions

View file

@ -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();

View file

@ -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);
}; };

View file

@ -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);
} }