Correctly implement exclusions of DInput devices that are also XInput

This commit is contained in:
Henrik Rydgård committed 2025-05-16 22:39:42 +02:00
1 parent a58e24838b
commit b2bd844a01
4 files changed
+100 -45

No files matched your search

+85 -37
View File
@@ -24,6 +24,9 @@
#include <XInput.h>
#include <wrl/client.h>
#include <wbemidl.h>
#include <comdef.h>
#include <set>
#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<IDirectInput8> DinputDevice::pDI;
std::vector<DIDEVICEINSTANCE> DinputDevice::devices;
std::set<u32> 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<u32> 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<u32> DetectXInputVIDPIDs() {
std::set<u32> 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;
}