原文鏈接:https://msdn.microsoft.com/en-us/library/windows/desktop/ee417014(v=vs.85).aspx
XInput是一個允許應用從Windows平台Xbox 360控制器中接收輸入的API。該文檔描述XInput和DirectInput關於Xbox 360控制器接口間的不同,並說明如何在同一時間同時支持XInput設備和傳統DirectInput設備。
注意:傳統DirectInput目前不推薦使用,並且在Windows Store里面的應用已經不支持DirectInput。
新的標准:XInput
XInput目前可以應用於游戲開發,這個新的輸入標准可以應用於Xbox 360、Windows XP SP1及以上版本以及Windows Vista。該APIs在DirectX SDK中包含,並且通過Windows Update可以更新驅動。
XInput相較於DirectInput有以下方面的優勢:
- XInput比DirectInput更方便使用並且創建步驟更少
- Xbox 360和Windows編程使用相同的核心APIs集,並且跨平台編程更方便
- Xbox 360有一個非常大的用戶群
- XInput設備(也就是指Xbox 360控制器)將在僅使用XInput APIs時具備震動功能
- 未來發布關於Xbox 360操縱台的控制器(也就是方向盤)將同樣可以應用於Windows
將DirectInput應用於Xbox 360控制器
Xbox 360可以通過DirectInput進行枚舉,並且可以應用DirectInput APIs。然而,某些XInput提供的功能在DirectInput中不存在:
- 左、右扳柄不作為獨立的單一按鈕
- 震動效果不可用
- 無法查詢頭戴式設備
在DirectInput中設計左右扳柄處於組合狀態,游戲常常假定當沒有用戶與設備交互時DirectInput設備軸處於中間。然而,Xbox 360控制器設計該位置為最小值,並不是中間,當觸發器沒有被控制時,老的游戲因此假定用戶處於交互狀態。
解決方案是配置扳柄,設置一個為負向和另一個為正向,所以當沒有用戶交互時表示DirectInput控制處於中間狀態。
為了分別測試扳柄的值,此時必須用XInput。
同時支持XInput和DirectInput
假如僅僅支持XInput,游戲將不能工作在傳統的DirectInput設備上。XInput將不會識別這些設備。
如何想讓游戲支持傳統DirectInput設備,需要同時使用DirectInput和XInput。當枚舉DirectInput設備時,所有DirectInput設備將枚舉成功。所有的XInput設備將同時顯示在XInput和DirectInput設備中,但是他們不應該通過DirectInput處理。你需要決定那些DirectInput設備是傳統設備,那些是XInput設備並且從DirectInput設備枚舉集里面將他們移除。
為實現這個目的,將下面這些代碼插入到DirectInput回調函數里面:
#include <wbemidl.h> #include <oleauto.h> #include <wmsstd.h> //----------------------------------------------------------------------------- // 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 = NULL; IEnumWbemClassObject* pEnumDevices = NULL; IWbemClassObject* pDevices[20] = {0}; IWbemServices* pIWbemServices = NULL; BSTR bstrNamespace = NULL; BSTR bstrDeviceID = NULL; BSTR bstrClassName = NULL; DWORD uReturned = 0; bool bIsXinputDevice= false; UINT iDevice = 0; VARIANT var; HRESULT hr; // CoInit if needed hr = CoInitialize(NULL); bool bCleanupCOM = SUCCEEDED(hr); // Create WMI hr = CoCreateInstance( __uuidof(WbemLocator), NULL, CLSCTX_INPROC_SERVER, __uuidof(IWbemLocator), (LPVOID*) &pIWbemLocator); if( FAILED(hr) || pIWbemLocator == NULL ) goto LCleanup; bstrNamespace = SysAllocString( L"\\\\.\\root\\cimv2" );if( bstrNamespace == NULL ) goto LCleanup; bstrClassName = SysAllocString( L"Win32_PNPEntity" ); if( bstrClassName == NULL ) goto LCleanup; bstrDeviceID = SysAllocString( L"DeviceID" ); if( bstrDeviceID == NULL ) goto LCleanup; // Connect to WMI hr = pIWbemLocator->ConnectServer( bstrNamespace, NULL, NULL, 0L, 0L, NULL, NULL, &pIWbemServices ); if( FAILED(hr) || pIWbemServices == NULL ) goto LCleanup; // Switch security level to IMPERSONATE. CoSetProxyBlanket( pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE ); hr = pIWbemServices->CreateInstanceEnum( bstrClassName, 0, NULL, &pEnumDevices ); if( FAILED(hr) || pEnumDevices == NULL ) goto LCleanup; // Loop over all devices for( ;; ) { // Get 20 at a time hr = pEnumDevices->Next( 10000, 20, pDevices, &uReturned ); if( FAILED(hr) ) goto LCleanup; if( uReturned == 0 ) break; for( iDevice=0; iDevice<uReturned; iDevice++ ) { // For each device, get its device ID hr = pDevices[iDevice]->Get( bstrDeviceID, 0L, &var, NULL, NULL ); if( SUCCEEDED( hr ) && var.vt == VT_BSTR && var.bstrVal != NULL ) { // 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( strVid, L"VID_%4X", &dwVid ) != 1 ) dwVid = 0; WCHAR* strPid = wcsstr( var.bstrVal, L"PID_" ); if( strPid && swscanf( 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; } } } SAFE_RELEASE( pDevices[iDevice] ); } } LCleanup: if(bstrNamespace) SysFreeString(bstrNamespace); if(bstrDeviceID) SysFreeString(bstrDeviceID); if(bstrClassName) SysFreeString(bstrClassName); for( iDevice=0; iDevice<20; 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 ) { HRESULT hr; if( IsXInputDevice( &pdidInstance->guidProduct ) ) return DIENUM_CONTINUE; // Device is verified not XInput, so add it to the list of DInput devices return DIENUM_CONTINUE; }
