Share via


Comparaison des fonctionnalités XInput et DirectInput

Important

Consultez API GameInput pour plus d’informations sur l’API d’entrée nouvelle génération prise en charge sur PC et Xbox via le Kit de développement de jeux Microsoft (GDK).

Ce document compare les implémentations XInput et DirectInput de la manette Xbox et comment prendre en charge les appareils XInput et les appareils DirectInput hérités.

Les applications du Windows Store ne prennent pas en charge DirectInput.

Vue d’ensemble

XInput permet aux applications de recevoir des entrées à partir de la manette Xbox pour Windows. Les API sont disponibles via le Kit de développement logiciel (SDK) DirectX, et le pilote est disponible via Windows Update.

L’utilisation de XInput par rapport à DirectInput présente plusieurs avantages :

  • XInput est plus facile à utiliser et nécessite moins de configuration que DirectInput
  • La programmation Xbox et Windows utilise les mêmes ensembles d’API principales, ce qui permet à la programmation de traduire plus facilement plusieurs plateformes
  • Il y aura une grande base installée de contrôleurs Xbox
  • Les appareils XInput (c’est-à-dire les contrôleurs Xbox) n’auront des fonctionnalités de vibration que lors de l’utilisation des API XInput
  • Les futurs contrôleurs publiés pour la console Xbox (c’est-à-dire les volants) fonctionneront également sur Windows

Utilisation de la manette Xbox avec DirectInput

La manette Xbox est correctement énumérée sur DirectInput et peut être utilisée avec les API DirectInput. Toutefois, certaines fonctionnalités fournies par XInput seront manquantes dans l’implémentation de DirectInput :

  • Les boutons de déclencheur gauche et droit agissent comme un seul bouton, et non indépendamment
  • Les effets de vibration ne seront pas disponibles
  • L’interrogation des appareils de casque ne sera pas disponible

La combinaison des déclencheurs gauche et droit dans DirectInput est par défaut. Les jeux ont toujours supposé que les axes d’appareil DirectInput sont centrés lorsqu’il n’y a aucune interaction utilisateur avec l’appareil. Toutefois, la manette Xbox a été conçue pour inscrire la valeur minimale, et non centrale, lorsque les déclencheurs ne sont pas conservés. Les jeux plus anciens supposent donc l’interaction de l’utilisateur.

La solution a été de combiner les déclencheurs, en définissant un déclencheur sur une direction positive et l’autre sur une direction négative, de sorte qu’aucune interaction utilisateur n’indique à DirectInput que le « contrôle » est au centre.

Pour tester les valeurs de déclencheur séparément, vous devez utiliser XInput.

XInput et DirectInput côte à côte

En prenant en charge XInput uniquement, votre jeu ne fonctionnera pas avec les appareils DirectInput hérités. XInput ne reconnaît pas ces appareils.

Si vous souhaitez que votre jeu prend en charge les appareils DirectInput hérités , vous pouvez utiliser DirectInput et XInput côte à côte. Lors de l’énumération de vos appareils DirectInput, tous les appareils DirectInput s’énumèrent correctement. Tous les appareils XInput s’affichent en tant qu’appareils XInput et DirectInput, mais ils ne doivent pas être gérés via DirectInput. Vous devez déterminer quels appareils DirectInput sont des appareils hérités, et quels sont des appareils XInput, et les supprimer de l’énumération des appareils DirectInput.

Pour ce faire, insérez ce code dans votre rappel d’énumération 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_045E&PID_028E&IG_00").  If it does, then it's an XInput device
// Unfortunately this information can not 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 can not 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;    
}

Une version légèrement améliorée de ce code se trouve dans l’exemple Joystick DirectInput hérité.

Prise en main avec XInput

Guide de référence de programmation