Partilhar via


Comparação dos recursos XInput e DirectInput

Importante

Consulte da API de entrada do GameInput para obter detalhes sobre a API de entrada de próxima geração suportada no PC e no Xbox por meio do Microsoft Game Development Kit (GDK).

Este documento compara as implementações XInput e DirectInput da entrada do controlador e como suportar dispositivos XInput e dispositivos DirectInput herdados.

as aplicações da Loja Windows não suportam DirectInput.

Visão geral

XInput permite que os aplicativos recebam entrada dos controladores XUSB. As APIs estão disponíveis através do SDK do DirectX e o driver está disponível através do Windows Update.

Há várias vantagens em usar XInput sobre DirectInput:

  • XInput é mais fácil de usar e requer menos configuração do que DirectInput
  • A programação do Xbox e do Windows usará os mesmos conjuntos de APIs principais, permitindo que a programação traduza entre plataformas muito mais facilmente
  • Haverá uma grande base instalada de controladores
  • O dispositivo XInput terá funcionalidade de vibração somente ao usar APIs XInput

Usando controladores XUSB com DirectInput

Os controladores XUSB são enumerados corretamente em DirectInput e podem ser usados com as APIs DirectInput. No entanto, algumas funcionalidades fornecidas pelo XInput estarão ausentes da implementação do DirectInput:

  • Os botões de gatilho esquerdo e direito atuarão como um único botão, não de forma independente
  • Os efeitos da vibração não estarão disponíveis
  • A consulta de dispositivos de auricular não estará disponível

A combinação dos gatilhos esquerdo e direito em DirectInput é por design. Os jogos sempre assumiram que os eixos do dispositivo DirectInput são centralizados quando não há interação do usuário com o dispositivo. No entanto, os controladores mais recentes foram projetados para registrar o valor mínimo, e não centralizado, quando os gatilhos não estão sendo mantidos. Os jogos mais antigos pressupunham, portanto, a interação do utilizador.

A solução foi combinar os gatilhos, definindo um gatilho para uma direção positiva e o outro para uma direção negativa, para que nenhuma interação do usuário seja indicativa para DirectInput do "controle" estar no centro.

Para testar os valores de gatilho separadamente, você deve usar XInput.

XInput e DirectInput lado a lado

Ao suportar apenas XInput, o seu jogo não funcionará com dispositivos DirectInput legados. XInput não reconhecerá esses dispositivos.

Se pretender que o seu jogo suporte dispositivos DirectInput legados, pode utilizar DirectInput e XInput lado a lado. Ao enumerar seus dispositivos DirectInput, todos os dispositivos DirectInput serão enumerados corretamente. Todos os dispositivos XInput aparecerão como dispositivos XInput e DirectInput, mas não devem ser manipulados através do DirectInput. Você precisará determinar quais de seus dispositivos DirectInput são dispositivos herdados e quais são dispositivos XInput e removê-los da enumeração de dispositivos DirectInput.

Para fazer isso, insira este código em seu retorno de chamada de enumeração DirectInput:

#include <wbemidl.h>
#include <oleauto.h>

#ifndef SAFE_RELEASE
#define SAFE_RELEASE(p) { if (p) { (p)->Release(); (p) = nullptr; } }
#endif

//-----------------------------------------------------------------------------
// Enum each PNP device using WMI and check each device ID to see if it contains 
// "IG_" (ex. "VID_0000&PID_0000&IG_00"). If it does, then it's an XInput device
// Unfortunately this information cannot be found by just using DirectInput 
//-----------------------------------------------------------------------------
BOOL IsXInputDevice( const GUID* pGuidProductFromDirectInput )
{
    IWbemLocator*           pIWbemLocator = nullptr;
    IEnumWbemClassObject*   pEnumDevices = nullptr;
    IWbemClassObject*       pDevices[20] = {};
    IWbemServices*          pIWbemServices = nullptr;
    BSTR                    bstrNamespace = nullptr;
    BSTR                    bstrDeviceID = nullptr;
    BSTR                    bstrClassName = nullptr;
    bool                    bIsXinputDevice = false;
    
    // CoInit if needed
    HRESULT hr = CoInitialize(nullptr);
    bool bCleanupCOM = SUCCEEDED(hr);

    // So we can call VariantClear() later, even if we never had a successful IWbemClassObject::Get().
    VARIANT var = {};
    VariantInit(&var);

    // Create WMI
    hr = CoCreateInstance(__uuidof(WbemLocator),
        nullptr,
        CLSCTX_INPROC_SERVER,
        __uuidof(IWbemLocator),
        (LPVOID*)&pIWbemLocator);
    if (FAILED(hr) || pIWbemLocator == nullptr)
        goto LCleanup;

    bstrNamespace = SysAllocString(L"\\\\.\\root\\cimv2");  if (bstrNamespace == nullptr) goto LCleanup;
    bstrClassName = SysAllocString(L"Win32_PNPEntity");     if (bstrClassName == nullptr) goto LCleanup;
    bstrDeviceID = SysAllocString(L"DeviceID");             if (bstrDeviceID == nullptr)  goto LCleanup;
    
    // Connect to WMI 
    hr = pIWbemLocator->ConnectServer(bstrNamespace, nullptr, nullptr, 0L,
        0L, nullptr, nullptr, &pIWbemServices);
    if (FAILED(hr) || pIWbemServices == nullptr)
        goto LCleanup;

    // Switch security level to IMPERSONATE. 
    hr = CoSetProxyBlanket(pIWbemServices,
        RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, nullptr,
        RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE,
        nullptr, EOAC_NONE);
    if ( FAILED(hr) )
        goto LCleanup;

    hr = pIWbemServices->CreateInstanceEnum(bstrClassName, 0, nullptr, &pEnumDevices);
    if (FAILED(hr) || pEnumDevices == nullptr)
        goto LCleanup;

    // Loop over all devices
    for (;;)
    {
        ULONG uReturned = 0;
        hr = pEnumDevices->Next(10000, _countof(pDevices), pDevices, &uReturned);
        if (FAILED(hr))
            goto LCleanup;
        if (uReturned == 0)
            break;

        for (size_t iDevice = 0; iDevice < uReturned; ++iDevice)
        {
            // For each device, get its device ID
            hr = pDevices[iDevice]->Get(bstrDeviceID, 0L, &var, nullptr, nullptr);
            if (SUCCEEDED(hr) && var.vt == VT_BSTR && var.bstrVal != nullptr)
            {
                // Check if the device ID contains "IG_".  If it does, then it's an XInput device
                // This information cannot be found from DirectInput 
                if (wcsstr(var.bstrVal, L"IG_"))
                {
                    // If it does, then get the VID/PID from var.bstrVal
                    DWORD dwPid = 0, dwVid = 0;
                    WCHAR* strVid = wcsstr(var.bstrVal, L"VID_");
                    if (strVid && swscanf_s(strVid, L"VID_%4X", &dwVid) != 1)
                        dwVid = 0;
                    WCHAR* strPid = wcsstr(var.bstrVal, L"PID_");
                    if (strPid && swscanf_s(strPid, L"PID_%4X", &dwPid) != 1)
                        dwPid = 0;

                    // Compare the VID/PID to the DInput device
                    DWORD dwVidPid = MAKELONG(dwVid, dwPid);
                    if (dwVidPid == pGuidProductFromDirectInput->Data1)
                    {
                        bIsXinputDevice = true;
                        goto LCleanup;
                    }
                }
            }
            VariantClear(&var);
            SAFE_RELEASE(pDevices[iDevice]);
        }
    }

LCleanup:
    VariantClear(&var);
    
    if(bstrNamespace)
        SysFreeString(bstrNamespace);
    if(bstrDeviceID)
        SysFreeString(bstrDeviceID);
    if(bstrClassName)
        SysFreeString(bstrClassName);
        
    for (size_t iDevice = 0; iDevice < _countof(pDevices); ++iDevice)
        SAFE_RELEASE(pDevices[iDevice]);

    SAFE_RELEASE(pEnumDevices);
    SAFE_RELEASE(pIWbemLocator);
    SAFE_RELEASE(pIWbemServices);

    if(bCleanupCOM)
        CoUninitialize();

    return bIsXinputDevice;
}


//-----------------------------------------------------------------------------
// Name: EnumJoysticksCallback()
// Desc: Called once for each enumerated joystick. If we find one, create a
//       device interface on it so we can play with it.
//-----------------------------------------------------------------------------
BOOL CALLBACK EnumJoysticksCallback( const DIDEVICEINSTANCE* pdidInstance,
                                     VOID* pContext )
{
    if( IsXInputDevice( &pdidInstance->guidProduct ) )
        return DIENUM_CONTINUE;

     // Device is verified not XInput, so add it to the list of DInput devices

     return DIENUM_CONTINUE;    
}

Uma versão ligeiramente melhorada deste código está no exemplo de DirectInput Joystick herdado.

Introdução ao XInput

de referência de programação