XInput ve DirectInput özelliklerinin karşılaştırması

Önemli

Microsoft Game Development Kit (GDK)aracılığıyla PC ve Xbox'ta desteklenen yeni nesil giriş API'sinin ayrıntıları için bkz. GameInput API.

Bu belgede XInput ve DirectInput denetleyici girişi uygulamaları ve hem XInput cihazları hem de eski DirectInput cihazları nasıl desteklenmektedir.

Windows Mağazası uygulamaları directInput desteklemez.

Genel bakış

XInput, uygulamaların XUSB denetleyicilerinden giriş almasını sağlar. API'ler DirectX SDK'sı aracılığıyla, sürücü ise Windows Update aracılığıyla kullanılabilir.

DirectInputyerine XInput kullanmanın çeşitli avantajları vardır:

  • XInput kullanımı daha kolaydır ve DirectInput daha az kurulum gerektirir
  • Hem Xbox hem de Windows programlaması aynı çekirdek API kümelerini kullanarak programlamanın platformlar arası çeviriyi çok daha kolay hale getirebilmesini sağlar
  • Yüklü büyük bir denetleyici tabanı olacaktır
  • XInput cihazı yalnızca XInput API'lerini kullanırken titreşim işlevselliğine sahip olur

DirectInput ile XUSB denetleyicilerini kullanma

XUSB denetleyicileri, DirectInputüzerinde düzgün numaralandırılır ve DirectInputAPIs ile kullanılabilir. Ancak, XInput tarafından sağlanan bazı işlevler DirectInput uygulamasında eksik olacaktır:

  • Sol ve sağ tetikleyici düğmeleri bağımsız olarak değil tek bir düğme olarak çalışır
  • Titreşim efektleri kullanılamayacak
  • Kulaklık cihazları için sorgulama kullanılamayacak

DirectInput sol ve sağ tetikleyicilerin birleşimi tasarım gereğidir. Oyunlar her zaman, cihazla kullanıcı etkileşimi olmadığında DirectInput cihaz eksenlerinin ortalandığını varsayar. Ancak daha yeni denetleyiciler, tetikleyiciler tutulmadığında en düşük değeri kaydedecek şekilde tasarlanmıştır. Bu nedenle eski oyunlar kullanıcı etkileşimi olduğunu varsayar.

Çözüm tetikleyicileri birleştirmek, tetikleyicilerden birini pozitif yönde, diğerini negatif yönde ayarlamaktı, bu nedenle hiçbir kullanıcı etkileşiminin ortada olan "denetimin" DirectInput göstermemesi.

Tetikleyici değerlerini ayrı olarak test etmek için XInput kullanmanız gerekir.

XInput ve DirectInput Yan Yana

Yalnızca XInput'u destekleyerek, oyununuz eski DirectInput cihazlarıyla çalışmaz. XInput bu cihazları tanımaz.

Oyununuzun eski DirectInput cihazları desteklemesini istiyorsanız DirectInput ve XInput'u yan yana kullanabilirsiniz. DirectInput cihazlarınızı numaralandırırken, tüm DirectInput cihazları doğru numaralandırılır. Tüm XInput cihazları hem XInput hem de DirectInput cihazları olarak görünür, ancak DirectInput aracılığıyla işlenmemelidir. DirectInput cihazlarınızdan hangilerinin eski cihazlar olduğunu ve hangilerinin XInput cihazları olduğunu belirlemeniz ve bunları DirectInput cihazlarının numaralandırmasından kaldırmanız gerekir.

Bunu yapmak için bu kodu DirectInput numaralandırma geri aramanıza ekleyin:

#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;    
}

Bu kodun biraz geliştirilmiş bir sürümü eski DirectInput Oyun Çubuğu örneğindedir.

XInput Kullanmaya Başlama

Programlama Başvurusu