2018年9月17日 星期一

DirectInput的學習心得(2)

DirectInput的學習心得(2)

前言

  續前篇DirectInput的學習心得(1),本次紀錄用DirectInput來偵測手把(Gamepad)。為何不直接使用XInuput呢?為了相容一些較舊的手把,就這麼簡單。如果同時使用DirectInput與XInuput時會有一些要注意的,就是DirectInput可以抓到XInuput的手把,本篇會記錄如何濾除,在此做個紀錄。

內容

  在開始偵測手把時,要先製造出IDirectInput8,如果不知道如何製造,請參照前篇DirectInput的學習心得(1),接著跟上次鍵盤的狀況不太一樣,由於系統鍵盤只會有一個,所以不需要去列舉裝置,但手把可能不只一個,所以透過EnumDevices()來抓出有多少個手把,接著對每個手把做SetCooperativeLevel()與SetDataFormat(),這部分和上次的鍵盤一樣,但這次要多一個步驟,就是透過EnumObjects()來取得類比按鈕的資訊,最後完成後一樣要Acqurie()。
  來看看範例程式,如下
#include "dinput.h"
#include "dinputd.h"
struct SEnumJoystickContext
{
  IDirectInput8* pDirectInput;
  HWND hWnd;
  std::vector<IDirectInputDevice8>& deviceList;
};
BOOL CALLBACK EnumJoystickCallBack(const DIDEVICEINSTANCE* pdidInstance,VOID* pContext)
{
  //Cast context...
  SEnumJoystickContext* pEnumJoystickContext=(SEnumJoystickContext*)pContext;

  //
  LPDIRECTINPUTDEVICE8 pNewDevice=NULL;
  HRESULT hr=pEnumJoystickContext->pDirectInput->CreateDevice(pdidInstance->guidProduct,&pNewDevice,NULL);
  if(hr != DI_OK)
    return DIENUM_STOP;
  //
  pEnumJoystickContext->deviceList.push_back(pNewDevice);
  return DIENUM_CONTINUE;
}
BOOL CALLBACK EnumObjectsCallBack(const DIDEVICEOBJECTINSTANCE* pdidoi,VOID* pContext)
{
  IDirectInput8* pDevice=(IDirectInput8*)pContext;
  if(pdidoi->dwType &DIDFT_AXIS)
  {
    DIPROPRANGE diprg;
    diprg.diph.dwSize=sizeof(DIPROPRANGE);
    diprg.diph.dwHeaderSize=sizeof(DIPROPHEADER);
    diprg.diph.dwHow=DIPH_BYID;
    diprg.diph.dwObj=pdidoi->dwType;
    diprg.lMin=-1000;
    diprg.lMax=1000;
    //
    if(pDevice->SetProperty(DIPROP_RANGE,&diprg.diph)!=DI_OK)
      return DIENUM_STOP;
  }
  return DIENUM_CONTINUE;
}
void DetectedGamepad(IDirectInputDevice8* pDIDeviceGamepad)
{
  DIJOYSTATE2 gamepadState;
  pDIDeviceGamepad->GetDeviceState(sizeof(DIJOYSTATE2), &gamepadState);
  //Get x axis was left
  if(gamepadState.lX < 0)
  {
    //do something...
  }
  //Get button 0 state
  if(gamepadState.rgbButtons[0] &0x80)
  {
   //do something...
  }
}
int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow )
{
  //Init main window and IDirectInput8
  //...
  HWND mainWin = initMainWindow;
  IDirectInput8* pDirectInput = initDirectInput;
  std::vector<IDirectinputdevice8*> deviceList;
  //
  SEnumJoystickContext enumJoystickContext = { pDirectInput, mainWin, deviceList};
  pDirectInput-&gt;EnumDevices(DI8DEVCLASS_GAMECTRL, EnumJoystickCallBack, &enumJoystickContext, DIEDFL_ATTACHEDONLY) 
  for(int i=0;i<deviceList.size();i++)
    pGamepadDevice->SetCooperativeLevel(mainWin,DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);
    pGamepadDevice->SetDataFormat(&c_dfDIJoystick2);
    //
    pGamepadDevice->EnumObjects(EnumObjectsCallBack, pGamepadDevice, DIDFT_ALL);
    pGamepadDevice->Acquire();
  }
  //Init gamepad end...
}

在製造完IDirectInput8後,會看到一個SEnumJoystickContext的Struct與 EnumDevices(),SEnumJoystickContext是要傳給EnumDevices()的參數,再看看EnumDevices()的參數會發現一個CallBack function名為EnumJoystickCallBack,這個CallBack 會在每次被IDirectInput8列許到裝置後被喚起,看看喚起的EnumJoystickCallBack(),有個引數名為"pContext",會發現在開頭就被轉型為SEnumJoystickContext,也會看到另一個引數"pdidInstance",這次必須透過DIDEVICEINSTANCE來製造出IDirectInputDevice8,這個CallBack 的回傳值有"DIENUM_CONTINUE"與"DIENUM_STOP","DIENUM_CONTINUE"表示持持續列舉,"DIENUM_STOP"則代表中止列舉,在EnumDevices()結束後,所有的IDirectInputDevice8都會在"deviceList",接著就初始化每個裝置。初始化裝置和鍵盤很像,一樣是SetCooperativeLevel()與SetDataFormat(),但這次要多一個步驟,就是EnumObjects(),EnumObjects()一樣需要一個CallBack ,這個CallBack 主要用來初始化類比按鍵,最後一樣要Acquire()。偵測的按鍵的部分可以參考範例的DetectedGamepad(),透過GetDeviceState()可以得到"DIJOYSTATE2",裡面就會放置所有按鍵的值,詳細地的說明可以到DIJOYSTATE2 Structure查詢。
  如果需要與XInuput同時使用,官方的提供的做法是過濾掉XInuput的裝置,官方提供一個檢查是否為XInuput裝置的function,這個function可以用在EnumJoystickCallBack()裡去做濾除的動作,詳細的程式碼可以在XInput and DirectInput裡找到。

參考資料

XInput and DirectInput
DIJOYSTATE2 Structure

相關文章

DirectInput的學習心得(1)

沒有留言:

張貼留言