[C++][DirectX]Joystick分享

张嘉
2023-12-01
#pragma once
#include <windows.h>
#include <basetsd.h>
#include <commctrl.h>
#include <dinput.h>
#include <dinputd.h>
#include <oleauto.h>
#include <shellapi.h>
#include <wbemidl.h>
#pragma warning( disable : 4996 ) // disable deprecated warning 
#include <strsafe.h>
#pragma warning( disable : 4996 ) // disable deprecated warning 


struct DI_ENUM_CONTEXT
{
	DIJOYCONFIG* pPreferredJoyCfg;
	bool bPreferredJoyCfgValid;
};

struct XINPUT_DEVICE_NODE
{
	DWORD dwVidPid;
	XINPUT_DEVICE_NODE* pNext;
};

class Joystick
{
public:
	Joystick();
	~Joystick();

public:
	bool InitDirectInput();
	void FreeDirectInput();

	bool UpdateDirectInput(DIJOYSTATE2 &js);
private:
	static BOOL CALLBACK EnumObjectsCallback(const DIDEVICEOBJECTINSTANCE* pdidoi, VOID* user);
	static BOOL CALLBACK EnumJoysticksCallback(const DIDEVICEINSTANCE* pdidInstance, VOID* user);
private:
	LPDIRECTINPUT8          g_pDI;
	LPDIRECTINPUTDEVICE8    g_pJoystick;
	DI_ENUM_CONTEXT         g_EnumContext;
};

#include "stdafx.h"
#include "JoyStick.h"

#define SAFE_DELETE(p)  { if(p) { delete (p);     (p)=NULL; } }
#define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }

Joystick::Joystick()
{
	g_pDI = NULL;
	g_pJoystick = NULL;
}


Joystick::~Joystick()
{
    FreeDirectInput()
}


BOOL Joystick::EnumObjectsCallback(const DIDEVICEOBJECTINSTANCE * pdidoi, VOID * user)
{
	Joystick* pJoy = (Joystick*)user;

	// For axes that are returned, set the DIPROP_RANGE property for the
	// enumerated axis in order to scale min/max values.
	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; // Specify the enumerated axis
		diprg.lMin = -1000;
		diprg.lMax = +1000;

		// Set the range for the axis
		if (FAILED(pJoy->g_pJoystick->SetProperty(DIPROP_RANGE, &diprg.diph)))
			return DIENUM_STOP;

	}
	return DIENUM_CONTINUE;
}

//-----------------------------------------------------------------------------
// 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 Joystick::EnumJoysticksCallback(const DIDEVICEINSTANCE* pdidInstance,VOID* user)
{
	Joystick* pJoy = (Joystick*)user;
	//
	HRESULT hr;

	// Skip anything other than the perferred Joystick device as defined by the control panel.  
	// Instead you could store all the enumerated Joysticks and let the user pick.
	if (pJoy->g_EnumContext.bPreferredJoyCfgValid &&
		!IsEqualGUID(pdidInstance->guidInstance, pJoy->g_EnumContext.pPreferredJoyCfg->guidInstance))
		return DIENUM_CONTINUE;

	// Obtain an interface to the enumerated Joystick.
	hr = pJoy->g_pDI->CreateDevice(pdidInstance->guidInstance, &pJoy->g_pJoystick, NULL);

	// If it failed, then we can't use this Joystick. (Maybe the user unplugged
	// it while we were in the middle of enumerating it.)
	if (FAILED(hr))
		return DIENUM_CONTINUE;

	// Stop enumeration. Note: we're just taking the first Joystick we get. You
	// could store all the enumerated Joysticks and let the user pick.
	return DIENUM_STOP;
}

//-----------------------------------------------------------------------------
// Name: InitDirectInput()
// Desc: Initialize the DirectInput variables.
//-----------------------------------------------------------------------------
bool Joystick::InitDirectInput()
{
	HRESULT hr= FALSE;
	//1.创建Direct输出
	if (FAILED(hr = DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION,IID_IDirectInput8, (VOID**)&g_pDI, NULL)))
		return false;

	//2.Joystick配置
	DIJOYCONFIG PreferredJoyCfg = { 0 };
	g_EnumContext.pPreferredJoyCfg = &PreferredJoyCfg;
	g_EnumContext.bPreferredJoyCfgValid = false;
	//2.1 创建配置
	IDirectInputJoyConfig8* pJoyConfig = NULL;
	if (FAILED(hr = g_pDI->QueryInterface(IID_IDirectInputJoyConfig8, (void**)&pJoyConfig)))
		return false;
	//2.2 获取配置参数
	PreferredJoyCfg.dwSize = sizeof(PreferredJoyCfg);
	// This function is expected to fail if no Joystick is attached
	if (SUCCEEDED(pJoyConfig->GetConfig(0, &PreferredJoyCfg, DIJC_GUIDINSTANCE))) 
		g_EnumContext.bPreferredJoyCfgValid = true;
	//2.3 释放
	SAFE_RELEASE(pJoyConfig);

	// 3.查找Joystick
	if (FAILED(hr = g_pDI->EnumDevices(DI8DEVCLASS_GAMECTRL,EnumJoysticksCallback,this, DIEDFL_ATTACHEDONLY)))
		return false;

	// 4.判断是否有
	if (NULL == g_pJoystick)
	{
		MessageBox(NULL, TEXT("Joystick not found. The sample will now exit."),
			TEXT("Joystick"),
			MB_ICONERROR | MB_OK);;
		return false;
	}

	// Set the data format to "simple Joystick" - a predefined data format 
	// A data format specifies which controls on a device we are interested in,
	// and how they should be reported. This tells DInput that we will be
	// passing a DIJOYSTATE2 structure to IDirectInputDevice::GetDeviceState().
	if (FAILED(hr = g_pJoystick->SetDataFormat(&c_dfDIJoystick2)))
		return false;
	//
	// Set the cooperative level to let DInput know how this device should
	// interact with the system and with other DInput applications.
	/*if (FAILED(hr = g_pJoystick->SetCooperativeLevel(NULL, DISCL_EXCLUSIVE |
		DISCL_FOREGROUND)))
		return false;*/

	// Enumerate the Joystick objects. The callback function enabled user
	// interface elements for objects that are found, and sets the min/max
	// values property for discovered axes.
	if (FAILED(hr = g_pJoystick->EnumObjects(EnumObjectsCallback,this, DIDFT_ALL)))
		return false;

	return true;
}

void Joystick::FreeDirectInput()
{
	// Unacquire the device one last time just in case 
	// the app tried to exit while the device is still acquired.
	if (g_pJoystick)
		g_pJoystick->Unacquire();

	// Release any DirectInput objects.
	SAFE_RELEASE(g_pJoystick);
	SAFE_RELEASE(g_pDI);
}

bool Joystick::UpdateDirectInput(DIJOYSTATE2 & js)
{
	HRESULT hr;

	if (NULL == g_pJoystick)
		return false;
	// Make sure the device is acquired, if we are gaining focus.
	hr = g_pJoystick->Acquire();
	// Poll the device to read the current state
	hr |= g_pJoystick->Poll();
	if (FAILED(hr))
	{
		// DInput is telling us that the input stream has been
		// interrupted. We aren't tracking any state between polls, so
		// we don't have any special reset that needs to be done. We
		// just re-acquire and try again.
		hr = g_pJoystick->Acquire();
		while (hr == DIERR_INPUTLOST)
			hr = g_pJoystick->Acquire();
		// hr may be DIERR_OTHERAPPHASPRIO or other errors.  This
		// may occur when the app is minimized or in the process of 
		// switching, so just try again later 
		return false;
	}
	// Get the input's device state
	if (FAILED(hr = g_pJoystick->GetDeviceState(sizeof(DIJOYSTATE2), &js)))
		return false; // The device should have been acquired during the Poll()
	return true;
}

 类似资料: