diff --git a/Windows/DinputDevice.cpp b/Windows/DinputDevice.cpp index b8959b6be9..d1ab4ab610 100644 --- a/Windows/DinputDevice.cpp +++ b/Windows/DinputDevice.cpp @@ -24,6 +24,9 @@ #include #include +#include +#include +#include #include "Common/Input/InputState.h" #include "Common/Input/KeyCodes.h" #include "Common/StringUtils.h" @@ -32,10 +35,12 @@ #include "Windows/DinputDevice.h" #pragma comment(lib,"dinput8.lib") -//initialize static members of DinputDevice +// static members of DinputDevice unsigned int DinputDevice::pInstances = 0; Microsoft::WRL::ComPtr DinputDevice::pDI; std::vector DinputDevice::devices; +std::set DinputDevice::ignoreDevices_; + bool DinputDevice::needsCheck_ = true; // In order from 0. There can be 128, but most controllers do not have that many. @@ -64,23 +69,7 @@ static const InputKeyCode dinput_buttons[] = { #define JOY_POVBACKWARD_LEFT JOY_POVBACKWARD + DIFF #define JOY_POVLEFT_FORWARD JOY_POVLEFT + DIFF -struct XINPUT_DEVICE_NODE { - DWORD dwVidPid; - XINPUT_DEVICE_NODE* pNext; -}; -XINPUT_DEVICE_NODE* g_pXInputDeviceList = NULL; - -bool IsXInputDevice( const GUID* pGuidProductFromDirectInput ) { - XINPUT_DEVICE_NODE* pNode = g_pXInputDeviceList; - while( pNode ) - { - if( pNode->dwVidPid == pGuidProductFromDirectInput->Data1 ) - return true; - pNode = pNode->pNext; - } - - return false; -} +static std::set DetectXInputVIDPIDs(); LPDIRECTINPUT8 DinputDevice::getPDI() { @@ -94,30 +83,28 @@ LPDIRECTINPUT8 DinputDevice::getPDI() return pDI.Get(); } -BOOL CALLBACK DinputDevice::DevicesCallback( - LPCDIDEVICEINSTANCE lpddi, - LPVOID pvRef - ) -{ +BOOL CALLBACK DinputDevice::DevicesCallback(LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef) { //check if a device with the same Instance guid is already saved auto res = std::find_if(devices.begin(), devices.end(), [lpddi](const DIDEVICEINSTANCE &to_consider){ return lpddi->guidInstance == to_consider.guidInstance; }); - if (res == devices.end()) //not yet in the devices list - { - // Ignore if device supports XInput - if (!IsXInputDevice(&lpddi->guidProduct)) { + if (res == devices.end()) { + // not yet in the devices list + // Ignore if device supports XInput - we'll get the input through there instead. + const u32 vidpid = lpddi->guidProduct.Data1; + const bool isXinputDevice = ignoreDevices_.find(vidpid) != ignoreDevices_.end(); + if (!isXinputDevice) { devices.push_back(*lpddi); } } return DIENUM_CONTINUE; } -void DinputDevice::getDevices(bool refresh) -{ - if (refresh) - { +void DinputDevice::getDevices(bool refresh) { + if (refresh) { + // We don't want duplicate reporting from XInput devices through DInput. + ignoreDevices_ = DetectXInputVIDPIDs(); getPDI()->EnumDevices(DI8DEVCLASS_GAMECTRL, &DinputDevice::DevicesCallback, NULL, DIEDFL_ATTACHEDONLY); } } @@ -126,8 +113,6 @@ DinputDevice::DinputDevice(int devnum) { pInstances++; pDevNum = devnum; pJoystick = nullptr; - memset(lastButtons_, 0, sizeof(lastButtons_)); - memset(lastPOV_, 0, sizeof(lastPOV_)); last_lX_ = 0; last_lY_ = 0; last_lZ_ = 0; @@ -135,13 +120,11 @@ DinputDevice::DinputDevice(int devnum) { last_lRy_ = 0; last_lRz_ = 0; - if (getPDI() == NULL) - { + if (!getPDI()) { return; } - if (devnum >= MAX_NUM_PADS) - { + if (devnum >= MAX_NUM_PADS) { return; } @@ -325,3 +308,68 @@ size_t DinputDevice::getNumPads() needsCheck_ = false; return devices.size(); } + +static std::set DetectXInputVIDPIDs() { + std::set xinputVidPids; + + /* + if (FAILED(CoInitializeEx(nullptr, COINIT_MULTITHREADED))) { + return xinputVidPids; + } + */ + + IWbemLocator* pIWbemLocator = nullptr; + if (FAILED(CoCreateInstance(__uuidof(WbemLocator), nullptr, CLSCTX_INPROC_SERVER, + __uuidof(IWbemLocator), (void**)&pIWbemLocator))) + return xinputVidPids; + + IWbemServices* pIWbemServices = nullptr; + if (FAILED(pIWbemLocator->ConnectServer(_bstr_t(L"root\\cimv2"), nullptr, nullptr, nullptr, 0, + nullptr, nullptr, &pIWbemServices))) { + pIWbemLocator->Release(); + return xinputVidPids; + } + + CoSetProxyBlanket(pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, nullptr, + RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, EOAC_NONE); + + IEnumWbemClassObject* pEnumDevices = nullptr; + if (FAILED(pIWbemServices->CreateInstanceEnum(_bstr_t(L"Win32_PNPEntity"), 0, nullptr, &pEnumDevices))) { + pIWbemServices->Release(); + pIWbemLocator->Release(); + return xinputVidPids; + } + + IWbemClassObject* pDevices[20] = { 0 }; + ULONG uReturned = 0; + + while (SUCCEEDED(pEnumDevices->Next(10000, 20, pDevices, &uReturned)) && uReturned > 0) { + for (ULONG i = 0; i < uReturned; i++) { + VARIANT var; + if (SUCCEEDED(pDevices[i]->Get(L"DeviceID", 0, &var, nullptr, nullptr))) + { + if (wcsstr(var.bstrVal, L"IG_")) + { + DWORD vid = 0, pid = 0; + const WCHAR *strVid = wcsstr(var.bstrVal, L"VID_"); + const WCHAR *strPid = wcsstr(var.bstrVal, L"PID_"); + + if (strVid) swscanf_s(strVid, L"VID_%4x", &vid); + if (strPid) swscanf_s(strPid, L"PID_%4x", &pid); + + const DWORD vidpid = MAKELONG(vid, pid); + xinputVidPids.insert((u32)vidpid); + } + VariantClear(&var); + } + pDevices[i]->Release(); + } + } + + pEnumDevices->Release(); + pIWbemServices->Release(); + pIWbemLocator->Release(); + // CoUninitialize(); + + return xinputVidPids; +} diff --git a/Windows/DinputDevice.h b/Windows/DinputDevice.h index 2a80de3769..ead15f017f 100644 --- a/Windows/DinputDevice.h +++ b/Windows/DinputDevice.h @@ -18,6 +18,7 @@ #pragma once #include +#include #include #include #define DIRECTINPUT_VERSION 0x0800 @@ -25,6 +26,8 @@ #include "InputDevice.h" #include +// TODO: This needs a major refactor into a DinputManager and individual devices inside. + class DinputDevice final : public InputDevice { @@ -39,6 +42,10 @@ public: needsCheck_ = true; } + static void SetDevicesToIgnore(std::set &&ignoreDevices) { + ignoreDevices_ = std::move(ignoreDevices); + } + private: void ApplyButtons(DIJOYSTATE2 &state); //unfortunate and unclean way to keep only one DirectInput instance around @@ -49,20 +56,19 @@ private: //also, it excludes the devices that are compatible with XInput static void getDevices(bool refresh); //callback for the WinAPI to call - static BOOL CALLBACK DevicesCallback( - LPCDIDEVICEINSTANCE lpddi, - LPVOID pvRef - ); - static unsigned int pInstances; + static BOOL CALLBACK DevicesCallback(LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef); + + static unsigned int pInstances; static std::vector devices; static Microsoft::WRL::ComPtr pDI; static bool needsCheck_; + static std::set ignoreDevices_; int pDevNum; Microsoft::WRL::ComPtr pJoystick; DIJOYSTATE2 pPrevState; bool analog; - BYTE lastButtons_[128]; - WORD lastPOV_[4]; + BYTE lastButtons_[128]{}; + WORD lastPOV_[4]{}; int last_lX_; int last_lY_; int last_lZ_; diff --git a/Windows/WindowsHost.cpp b/Windows/WindowsHost.cpp index 3e8c0fc706..b5972f3832 100644 --- a/Windows/WindowsHost.cpp +++ b/Windows/WindowsHost.cpp @@ -52,6 +52,7 @@ void WindowsInputManager::Init() { //add first XInput device to respond input.push_back(std::make_unique()); #ifndef _M_ARM + //find all connected DInput devices of class GamePad numDinputDevices_ = DinputDevice::getNumPads(); for (size_t i = 0; i < numDinputDevices_; i++) { diff --git a/Windows/XinputDevice.cpp b/Windows/XinputDevice.cpp index 43c8a95ad6..a38c4fd91b 100644 --- a/Windows/XinputDevice.cpp +++ b/Windows/XinputDevice.cpp @@ -183,7 +183,7 @@ int XinputDevice::UpdateState() { // If we get XInput, skip the others. This might not actually be a good idea, // and was done to avoid conflicts between DirectInput and XInput. - return anySuccess ? UPDATESTATE_SKIP_PAD : 0; + return 0; // anySuccess ? UPDATESTATE_SKIP_PAD : 0; } void XinputDevice::UpdatePad(int pad, const XINPUT_STATE &state, XINPUT_VIBRATION &vibration) {