Home
What's New?
Statistics
 

CDIJoystick
Artwork Blog Java Games Software Make A Donation Contact Info

(more info)

Up

        

CDIJoystick - Direct Input 7 Joystick Handler

This class does not have support for Force Feedback, as I don't have a force feedback device to test my code on.  However full support will be provided in the future.  However if you would like the source code for my untested ForceFeed options, please e-mail me.

I have provided a Project to demonstrate how to use this Class and read joystick information.
Click to download the Demo Source (Requires Microsoft Visual C++ 6.0 Service Pack 4)

To use the class, define a global variable :-

CDIJoystick myJoystick1;

The class will automatically enumerate all attached joysticks on the target PC upon creation.
If there are more than one attached device, you could give the games player a choice of device to use.
Alternatively, you can use multiple instances of this class so that two player games can use different joysticks for instance!

The class has the following public methods:-

Method

Description
void CDIJoystick.SetHWND(HWND hwnd)Set the windows handle to which Direct Input Will be attached to.  Must be the Parent Window.
LPCDIDEVICEINSTANCE CDIJoystick.GetFirstJoystickID(void)Get the first Device Instance of Enumerated Devices.  Returns NULL if none attached.
Must be called before proceeding Method.
LPCDIDEVICEINSTANCE CDIJoystick.GetNextJoystickID(void)Get the Next Device Instance of Enumerated Devices.  Returns NULL if no more attached.
void CDIJoystick.SetPreferredDevice(&guid);Sets the current Joystick Device Based on it GUID
int CDIJoystick.HowManyButtons(void);Returns the number of buttons attached to the prefferred device.  0 = None or a problem with the device.  I.e. unplugged or removed from system.
TCHAR* CDIJoystick.GetFirstButtonName(void)Returns the friendly button name for the attached device.
Must be called before proceeding Method.
TCHAR* CDIJoystick.GetNextButtonName(void)Returns the next friendly button name for the attached device.
bool CDIJoystick.PollDevice(void)Must be called to update joystick state information.  Returns true if data obtained.  false if there is a problem polling the device.
bool CDIJoystick.IsJoystickLeft(void)true if the joystick is being pushed left, false if not.
bool CDIJoystick.IsJoystickRight(void)true if the joystick is being pushed right, false if not.
bool CDIJoystick.IsJoystickUp(void)true if the joystick is being pushed up, false if not.
bool CDIJoystick.IsJoystickDown(void)true if the joystick is being pushed down, false if not.
bool CDIJoystick.IsJoystickFire(void)true if any of the joystick buttons are pressed, false if not.
int CDIJoystick.IsJoystickFire(int button)returns true if button has been pressed, false otherwise.  Button = 0 to Number of Buttons Available -1
LPDIJOYSTATE2 CDIJoystick.GetJoystickStateInfo(void);Returns a LPDIJOYSTATE2 pointer, This enables you to probe more of the functionality of the joystick if more advance features are required.
void CDIJoystick.RunControlPanel(void)Start the windows control panel.

Before using or obtaining information about the device you must supply a HWND to the Parent window from which you will receive input.

If you are using MFC you can either set the HWND from the Application from using :-

myJoystick1.SetHWND(m_pMainWnd->m_hWnd); 

if your inside a Dialog Based Application :-

CWnd *jb=this;
myJoystick1.SetHWND(jb->m_hWnd);

if your not using MFC you will have to Initiailise COM and set HWND according to your global window

HRESULT hr=CoInitialize(NULL);
myJoystick1.SetHWND(g_hwnd);
// Remember to CoUninitialize when exiting your application though!

For the purpose of demonstration, the following code snippet shows how to populate a list control with the names of each attached device to your system.

CWnd *jb=this;
myJoystick1.SetHWND(jb->m_hWnd);

// Reset Combo Control
m_ctlJoystickName.ResetContent();

LPCDIDEVICEINSTANCE lpddi=NULL;

// Ensure you have First Joystick ID to start search for additional Devices!
lpddi=myJoystick1.GetFirstJoystickID();

if(!lpddi)
{
	// No joysticks have been found!
	MessageBox("I have not been able to find a joystick on your system.","No Joystick Detected",MB_ICONHAND|MB_OK);
	OnCancel();
}
else
{

	while(lpddi)
	{
		int x=m_ctlJoystickName.AddString(lpddi->tszInstanceName);
		m_ctlJoystickName.SetItemDataPtr(x,(void*)lpddi);
		lpddi=myJoystick1.GetNextJoystickID();
	}

	m_ctlJoystickName.SetCurSel(0);
	OnSelchangeButtonNames();

	SetTimer(1,50,NULL);

	return TRUE; // return TRUE unless you set the focus to a control
}
return TRUE; 

Now that the list control has been populated, You may wish to find out how many fire buttons are attached to the device.  Using a snippet from the demo project we have :-

CWnd*jb=this;
myJoystick1.SetHWND(jb->m_hWnd);

LPCDIDEVICEINSTANCE lpddi=NULL;

m_ctlButtonNames.ResetContent();

int x=m_ctlJoystickName.GetCurSel();

lpddi=(LPCDIDEVICEINSTANCE)m_ctlJoystickName.GetItemDataPtr(x);

GUID myguid;

if(lpddi && ((int)lpddi!=-1))
{

	memcpy(&myguid,&(lpddi->guidInstance),sizeof(GUID));

	myJoystick1.SetPreferredDevice(&myguid);

	m_Buttons=myJoystick1.HowManyButtons();
	
	TCHAR* name=NULL;
	name=myJoystick1.GetFirstButtonName();

	while(name)
	{
		m_ctlButtonNames.AddString(name);
		name=myJoystick1.GetNextButtonName();
	}
}

m_ctlButtonNames.SetCurSel(0); 

Now that a Joystick Device has been selected, all that remains is to read the device.
You must allways all the PollDevice Method to obtain the latest joystick data.  
If you are only interested in Up, Down, Left, Right and Fire movements the following code snippet will work :-

myJoystick1.PollDevice();

if(myJoystick1.IsJoystickLeft()) m_Left=true; else m_Left=false;
if(myJoystick1.IsJoystickRight()) m_Right=true; else m_Right=false;
if(myJoystick1.IsJoystickUp()) m_Up=true; else m_Up=false;
if(myJoystick1.IsJoystickDown()) m_Down=true; else m_Down=false;
if(myJoystick1.IsJoystickFire()) m_Fire=true; else m_Fire=false;

If on the other hand your interested in precision data, i.e. your device is analogue you can use :-


myJoystick1.PollDevice();
LPDIJOYSTATE2 joy=myJoystick1.GetJoystickStateInfo();

m_XAxis.Format("%d",joy->lX);
m_YAxis.Format("%d",joy->lY);
m_ZAxis.Format("%d",joy->lZ);

m_RXAxis.Format("%d",joy->lRx);
m_RYAxis.Format("%d",joy->lRy);
m_RZAxis.Format("%d",joy->lRz);


m_FireText.Empty();

for(int i=0;i<m_Buttons;i++)
{
	if(myJoystick1.IsJoystickFire(i))
	{
		CString temp;
		temp.Format("Fire Button : %d\r\n",i);
		m_FireText+=temp;
	}
}

The Source Code

CDIJoystick Class

Header File

// DIJoystick.h: interface for the CDIJoystick class.
//
//////////////////////////////////////////////////////////////////////
// DIJoystick.cpp: implementation of the CDIJoystick class.
//
// Written and Developed by Jason Brooks
// (C) 2000 Jason Brooks
//
// You may use this code freely, however a mention in your credits
// would be nice.
//
// For more information, Bug Reports and so on I can be contacted :-
//
// E-Mail:   DirectInput@muckypaws.com
//
// Web:      www.muckypaws.com
// ICQ:      9609400
//
//////////////////////////////////////////////////////////////////////
#if !defined(AFX_DIJOYSTICK_H__D1950720_938F_4491_A3E3_583B1FB98E2C__INCLUDED_)
#define AFX_DIJOYSTICK_H__D1950720_938F_4491_A3E3_583B1FB98E2C__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// Ensure that Direct input 7 Header files are in your search path.
#include "dinput.h"
// Ensure these Libraries are in your Search Path
#pragma comment (lib, "dxguid.lib")
#pragma comment (lib, "dinput.lib")

#define MAXJOYBUTTONS 256
class CDIJoystick  
{
public:
	CDIJoystick();
	virtual ~CDIJoystick();

	bool PollDevice();				// Update the device state.
	void SetPreferredDevice(GUID* pguid);  // Set the current GUID for joystick device.
	void SetHWND(HWND hwnd);		// The the HWND to which the joystick will be attached.
	bool InitDevice(void);			// Initialise the Device Pointer
	bool InitJoystick(void);		// Initialise the Joystick
	int HowManyButtons(void);		// Find out how many buttons the attached device has
	bool CreateDevice(GUID *guid);	// Create A Device Pointer for a GUID

	LPCDIDEVICEINSTANCE GetFirstJoystickID(void);	// Get First Joystick Device Data For Enumerated Devices (Must be called before GetNextJoystickID()
	LPCDIDEVICEINSTANCE GetNextJoystickID();		// Get Next Joystick Device Data For Enumerated Devices
	TCHAR* GetFirstButtonName(void);// Get First Joystick Button Friendly Name For Enumerated Device (Must be called before GetNextJoystickID()
	TCHAR* GetNextButtonName();		// Get First Joystick Button Friendly Name For Enumerated Device

	bool IsJoystickLeft(void) {return m_JoyLeft;};	// Is Joystick Being Moved Left?
	bool IsJoystickRight(void) {return m_JoyRight;};// Is Joystick Being Moved Right?
	bool IsJoystickUp(void) {return m_JoyUp;};		// Is Joystick Being Moved Up?
	bool IsJoystickDown(void) { return m_JoyDown;};	// Is Joystick Being Moved Down?
	bool IsJoystickFire(void) { return m_JoyFire1;};// Has Any Fire Button Been Pressed?
	bool IsJoystickFire(int button) { return m_JoyFire[button%MAXJOYBUTTONS];};// Has a specific button been pressed?

	DIJOYSTATE2* GetJoystickStateInfo(void) {return &m_dijs;};
	void RunControlPanel(void);		// Run the Control Panel

protected:
	bool Initialise(void);			// Initialise Direct Input 7
	void Shutdown(void);			// Close down and de-allocate any memory assigned to this object.
	bool Enumerate_Joysticks(void);	// Start Enumeration of Attached Joystick Devices

	TCHAR* GetDIError(HRESULT error);// Return Friendly Text to DI7 Error HR
	bool ReInitialise(void);		// ReInitialise DI7 (Not Implemented)

	void ClearFriendlyButtonNames(void);	// Clear Down Allocated Memory for Friendly Button Names
	bool AddDeviceInfo(LPCDIDEVICEINSTANCE lpddi); // Add DI7 Device Info for Enumerated Device
	static BOOL CALLBACK EnumDevicesProc( LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef); // Callback procedure for Enumerating Attached Joystick Devices
	bool Acquire(bool state);		// Acquire/UnAcquire the Joystick

private:
	POSITION m_DevicePOS;			// Used in CPtrList to keep track of next item.
	POSITION m_ButtonPOS;			// Used in CPtrList to keep track of next item.

	bool m_EnumerationStarted;		// Has enumeration started and joystick been found?

	DIJOYSTATE2   m_dijs;				// Holds Joystick State Information
	
	GUID m_JoystickGUID;			// Current Joystick GUID
	
	CPtrList	m_DIJoystickList;	// Contains a pointer list to Attached Joystick Devices
	CPtrList	m_DIButtonNames;	// Contains a pointer list to Button Names for selected Joystick

	bool m_JoyLeft;					// Generic Direction, Is Joystick Moving Left
	bool m_JoyUp;					// Generic Direction, Is Joystick Moving Up
	bool m_JoyDown;					// Generic Direction, Is Joystick Moving Down
	bool m_JoyRight;				// Generic Direction, Is Joystick Moving Right
	bool m_JoyFire[MAXJOYBUTTONS];	// Which Fire Button Has Been Pressed?
	bool m_JoyFire1;				// Has any fire button been pressed?
	
	
	HCURSOR m_hCursorWait;			// Wait Cursor Handle when Enumerating Feedback Effects
	HWND m_hwnd;					// HWND to which Direct Input will be attached to.
	
	bool m_Initialised;				// Has Direct Input Been Initialised?
	char m_buffer[256];				// A Generic buffer

	HINSTANCE m_hInstance;				// This modules Instance
	LPDIRECTINPUT7  m_lpDI;				// Pointer to Direct Input 7
	LPDIRECTINPUTDEVICE7  m_lpDIDevice;	// Pointer to Direct Input 7 Device

	//////////////////////////////////////////////////////////////////////
	// Used for Force Feed Back, Not Yet Implemented
	//////////////////////////////////////////////////////////////////////
	LPDIRECTINPUTEFFECT  m_lpTriggerEffect;	// For Force Feedback Effects
	LPDIRECTINPUTEFFECT  m_lpStickyEffect;	// For Force Feedback Effects
	LPDIRECTINPUTEFFECT  m_lpResistEffect;	// For Force Feedback Effects
	bool m_TriggerOK;
	BOOL m_FFAvailable;				// Is device Force Feedback Compatible?
	GUID m_TriggerGuid;				// GUID For Force Feed Back Device?
};
#endif // !defined(AFX_DIJOYSTICK_H__D1950720_938F_4491_A3E3_583B1FB98E2C__INCLUDED_)

Implementation

DIJoystick.CPP File

 
// DIJoystick.cpp: implementation of the CDIJoystick class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "JoystickDemo.h"
#include "DIJoystick.h"
#include "dinput.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

// DIJoystick.cpp: implementation of the CDIJoystick class.
//
// Written and Developed by Jason Brooks
// (C) 2000 Jason Brooks
//
// You may use this code freely, however a mention in your credits
// would be nice.
//
// For more information, Bug Reports and so on I can be contacted :-
//
// E-Mail:   DirectInput@muckypaws.com
//
// Web:      www.muckypaws.com
// ICQ:      9609400
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "DIJoystick.h"

#define BUFFERSIZE 16

// Set the maxmimum range to which we'll gauge the swing
#define JOYMAX 10000
#define JOYMIN -10000

/*				Y
				
				^
				|
				|
X	-10,000 <---*---> +10,000
				|
				|
				\/
*/
// Dead zone is the amount of sway the joystick can have before we start registering movement
// In this case 20%

#define JOYDEAD 2000

// The Saturation Point Is Where the Joystick is deemed to be at Full Swing, in this case 95%
#define JOYSAT  9500

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CDIJoystick::CDIJoystick()
{
	// Initialise Member Variables
	m_EnumerationStarted=false;
	m_Initialised=false;
	m_hInstance=GetModuleHandle(NULL);

	// Initialise Direct Input
	Initialise();

	// Start Enumeration of Attached Joysticks.
	Enumerate_Joysticks();
}

//////////////////////////////////////////////////////////////////////
//
// Destroy The Direct Input Joystick Control and tidy up.
//
//////////////////////////////////////////////////////////////////////
CDIJoystick::~CDIJoystick()
{
	Shutdown();
}

//////////////////////////////////////////////////////////////////////
//
// Initialise Direct Input
//
//////////////////////////////////////////////////////////////////////
bool CDIJoystick::Initialise()
{
	HRESULT         hr; 

	hr = DirectInputCreateEx(m_hInstance, DIRECTINPUT_VERSION, 
			IID_IDirectInput7, (void**)&m_lpDI, NULL); 
	if FAILED(hr) 
	{ 
	    // DirectInput not available; 
	    // take appropriate action
		OutputDebugString("Failed To Initialise Direct Input 7 in CDIJoystick::Initialise\n");
		OutputDebugString(GetDIError(hr));
		return false;
	}

	m_hwnd=NULL;

	if FAILED(hr)
	{ 
		OutputDebugString(GetDIError(hr));
		Shutdown(); 
		return false; 
	}

	m_Initialised=true;
	return true;	// Successfully Created Direct Input 7 Object
}

//////////////////////////////////////////////////////////////////////
//
// Shutdown the Direct input object and release it.
//
// Basically clean up any memory allocated to this object
//
//////////////////////////////////////////////////////////////////////
void CDIJoystick::Shutdown()
{
	ClearFriendlyButtonNames();

	// Remove Joystick Information
	if(!m_DIJoystickList.IsEmpty())
	{
		POSITION pos=m_DIJoystickList.GetHeadPosition();
		LPVOID del=NULL;

		while(pos)
		{
			del=static_cast<LPVOID>(m_DIJoystickList.GetNext(pos));
			if(del)
			{
				delete del;
			}
		}
		m_DIJoystickList.RemoveAll();
	}

	// Shutdown Direct Input!
	if (m_lpDI) 
	{ 
		if (m_lpDIDevice) 
		{ 
				// Always unacquire device before calling Release().
			try
			{
				Acquire(false);
				m_lpDIDevice->Release();
				m_lpDIDevice = NULL; 
			}
			catch(...)
			{
				OutputDebugString("Failed to Release Pointer in CDIJoystick::Shutdown\n");
			}
		} 
		try
		{
			m_lpDI->Release();
			m_lpDI = NULL; 
		}
		catch(...)
		{
			OutputDebugString("Failed to Release DI7 Pointer in CDIJoystick::Shutdown\n");
		}
	} 
	m_Initialised=false;
}

//////////////////////////////////////////////////////////////////////
//
// Start the Enumeration Of Attached Joystick Devices.
//
//////////////////////////////////////////////////////////////////////
bool CDIJoystick::Enumerate_Joysticks()
{
	HRESULT hr=NULL;

	if(!m_Initialised) Initialise();

	if(!m_lpDI) // Has a Direct Input Interface Already Been Initialised?
	{
		hr = DirectInputCreateEx(m_hInstance, DIRECTINPUT_VERSION, 
							   IID_IDirectInput7, (void**)&m_lpDI, NULL);

		if FAILED(hr) 
		{
			OutputDebugString("Error in CDIJoystick::Enumerate_Joysticks()\n");
			OutputDebugString(GetDIError(hr));
			return false; 
		}
	}
	
	if(m_lpDI) hr=m_lpDI->EnumDevices(DIDEVTYPE_JOYSTICK ,EnumDevicesProc,this,DIEDFL_ATTACHEDONLY);
    if FAILED(hr) 
	{
		OutputDebugString("Error in CDIJoystick::Enumerate_Joysticks()\n");
		OutputDebugString(GetDIError(hr));
		return false; 
	}

	OutputDebugString("Enumerating Joystick Devices\n");
	return true;
}

//////////////////////////////////////////////////////////////////////
//
// Add Enumerated Devices To A Link List Object
// Continue Enumeration Of The Device
//
//////////////////////////////////////////////////////////////////////
BOOL CDIJoystick::EnumDevicesProc(LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef)
{
	CDIJoystick *obj=(CDIJoystick*)(pvRef);
	obj->AddDeviceInfo(lpddi);

	return DIENUM_CONTINUE;   
}

//////////////////////////////////////////////////////////////////////
//
// Add Device Info To The List Of Pointers Held in m_DIJoystickList
//
// return  true = Successfully Added 
//        false = Failed To Add
//
//////////////////////////////////////////////////////////////////////
bool CDIJoystick::AddDeviceInfo(LPCDIDEVICEINSTANCE lpddi)
{
	m_EnumerationStarted=true;

	LPCDIDEVICEINSTANCE lpdi2=new DIDEVICEINSTANCE;
	if(lpdi2)
	{
		memcpy((void*)lpdi2,lpddi,sizeof(DIDEVICEINSTANCE));

		if(m_DIJoystickList.AddHead((void*)lpdi2))return true;
		OutputDebugString("Failed To Add Device Info in CDIJoystick::AddDeviceInfo(LPCDIDEVICEINSTANCE lpddi)\n");
	}
	return false;
}

//////////////////////////////////////////////////////////////////////
//
// Return First Joystick Device Data If Possible
//
// return  NULL if Failed or No Joysticks Available
//
//////////////////////////////////////////////////////////////////////
LPCDIDEVICEINSTANCE CDIJoystick::GetFirstJoystickID()
{
	if(!m_EnumerationStarted) 
	{
		OutputDebugString("Joystick Have Not Yet Been Enumerated Returning NULL from CDIJoystick::GetFirstJoystickID()\n");
		return NULL;
	}

	if(m_DIJoystickList.IsEmpty()) 
	{
		OutputDebugString("Joysticks Have Been Enumerated However None Were Found Attached To This System\n"
						  "Therefore I am Returning NULL from CDIJoystick::GetFirstJoystickID()\n");
		return NULL;
	}

	m_DevicePOS=m_DIJoystickList.GetHeadPosition();

	return GetNextJoystickID();

	LPCDIDEVICEINSTANCE info=static_cast<LPCDIDEVICEINSTANCE>(m_DIJoystickList.GetNext(m_DevicePOS));

	return info;
}

//////////////////////////////////////////////////////////////////////
//
// Return Next Joystick Device Data If Possible
//
// return  NULL if Failed or No Joysticks Available
//
//////////////////////////////////////////////////////////////////////
LPCDIDEVICEINSTANCE CDIJoystick::GetNextJoystickID()
{
	if(!m_DevicePOS) return NULL;

	return static_cast<LPCDIDEVICEINSTANCE>(m_DIJoystickList.GetNext(m_DevicePOS));
}

//////////////////////////////////////////////////////////////////////
//
// Return First Joystick Button Name Data If Possible
//
// return  NULL if Failed or No Joysticks Available
//
//////////////////////////////////////////////////////////////////////
TCHAR* CDIJoystick::GetFirstButtonName()
{
	if(!m_EnumerationStarted) 
	{
		OutputDebugString("Joystick Have Not Yet Been Enumerated\nor None attached to this systemReturning NULL from CDIJoystick::GetFirstJoystickID()\n");
		return NULL;
	}

	if(m_DIButtonNames.IsEmpty()) 
	{
		OutputDebugString("Joysticks Have Been Enumerated However None Were Found Attached To This System\n"
						  "Therefore I am Returning NULL from CDIJoystick::GetFirstButtonName()\n");
		return NULL;
	}

	m_ButtonPOS=m_DIButtonNames.GetHeadPosition();

	TCHAR* info=static_cast<TCHAR*>(m_DIButtonNames.GetNext(m_ButtonPOS));
	return info;	
}

//////////////////////////////////////////////////////////////////////
//
// Return First Joystick Button Name Data If Possible
//
// return  NULL if Failed or No Joysticks Available
//
//////////////////////////////////////////////////////////////////////
TCHAR* CDIJoystick::GetNextButtonName()
{
	if(!m_ButtonPOS) return NULL;

	return static_cast<TCHAR*>(m_DIButtonNames.GetNext(m_ButtonPOS));
}


//////////////////////////////////////////////////////////////////////
//
//  Create the Joystick Device Using GUID Passed
//
//////////////////////////////////////////////////////////////////////
bool CDIJoystick::CreateDevice(GUID *guid)
{
	HRESULT hr=NULL;

	// If a device already exists and is created, Release it.
	if (m_lpDIDevice) 
    { 
		// Always unacquire device before calling Release().
		try
		{
			hr=m_lpDIDevice->Unacquire(); 
		}
		catch(...)
		{
		}
		try
		{
			hr=m_lpDIDevice->Release();
			if(FAILED(hr))
			{
				OutputDebugString(GetDIError(hr));
			}
			else m_lpDIDevice = NULL; 
		}
		catch(...)
		{
			OutputDebugString("Failed to Release Pointer in CDIJoystick::CreateDevice(GUID *guid)\n");
		}
    }
	
	// Check to see that Direct Input has been created and initialised.
	if(!m_lpDI) 
	{
		OutputDebugString("Failed to Create DI 7 interface to device in CDIJoystick::CreateDevice(GUID *guid) m_lpDI not initialised.\n");
		return false;
	}

	// Attempt to create the device based on the GUID passed to this routine
	hr=m_lpDI->CreateDeviceEx(*guid,IID_IDirectInputDevice7,(void**)&m_lpDIDevice,NULL);
	if(FAILED(hr))
	{
		OutputDebugString("Failed to Create DI 7 interface to device in CDIJoystick::CreateDevice(GUID *guid)\n");
		return false;
	}

	// We must have been successful at this point.
	// Therefore copy the GUID to this members instance for future reference.
	memcpy(&m_JoystickGUID,guid,sizeof(GUID));

	return true;
}

//////////////////////////////////////////////////////////////////////
//
// Return How Many Buttons The Attached Device Has Installed
// When giving the player an option of which joystick to use
// You may wish to evaluate the buttons available per attached device.
// Returns the number of buttons for the currently selected device.
//
//////////////////////////////////////////////////////////////////////
int CDIJoystick::HowManyButtons()
{
    DIDEVICEOBJECTINSTANCE didoi;
    DWORD x;
    DWORD dwOfs;
	int count=0;
	HRESULT hr=NULL;
	
	ClearFriendlyButtonNames();

	//if(m_lpDIDevice)
	if(InitDevice())
	{
		ZeroMemory(&didoi,sizeof(DIDEVICEOBJECTINSTANCE));
		didoi.dwSize = sizeof( didoi );

		for ( x = 0; x < 32; x++ )
		{
			dwOfs = DIJOFS_BUTTON( x ); 

			hr=m_lpDIDevice->GetObjectInfo( &didoi, dwOfs, DIPH_BYOFFSET );
            
			if ( SUCCEEDED( hr ) )
			{
				count++;
				TCHAR* name=new char[sizeof(didoi.tszName)];

				// Should include UNICODE support here.
				strcpy(name,didoi.tszName);

				// Add the button name to the Pointer List for future reference.
				m_DIButtonNames.AddTail(name);
			}
			else
			{
				OutputDebugString(GetDIError(hr));
			}
		}
	}
	return count;	// How many buttons did we find?
}

////////////////////////////////////////////////////////////////////////
//
// Shutdown the the link list of Friendly Button Names
//
////////////////////////////////////////////////////////////////////////
void CDIJoystick::ClearFriendlyButtonNames()
{
	try
	{
		if(!m_DIButtonNames.IsEmpty())
		{
			POSITION pos=m_DIButtonNames.GetHeadPosition();
			LPVOID del=NULL;

			while(pos)
			{
				del=static_cast<LPVOID>(m_DIButtonNames.GetNext(pos));
				if(del)
				{
					delete del;
				}
			}
		}
		m_DIButtonNames.RemoveAll();
	}
	catch(...)
	{
		OutputDebugString("Some unforseen error occurred in CDIJoystick::ClearFriendlyButtonNames()\n");
	}
}

//////////////////////////////////////////////////////////////////////
//
// Initialise The Joystick!
//
//////////////////////////////////////////////////////////////////////
bool CDIJoystick::InitJoystick()
{
    // Set range. 
    // Note: range, deadzone, and saturation are being set for the
    // entire device. This could have unwanted effects on
    // sliders, dials, etc.

    DIPROPRANGE diprg; 
 
    diprg.diph.dwSize       = sizeof( diprg ); 
    diprg.diph.dwHeaderSize = sizeof( diprg.diph ); 
    diprg.diph.dwObj        = 0; 
    diprg.diph.dwHow        = DIPH_DEVICE; 
    diprg.lMin              = JOYMIN; 
    diprg.lMax              = JOYMAX; 
 
    if ( FAILED( m_lpDIDevice->SetProperty( DIPROP_RANGE, &diprg.diph ) ) )
        return FALSE; 

    // Set deadzone.
    DIPROPDWORD dipdw;

    dipdw.diph.dwSize       = sizeof( dipdw ); 
    dipdw.diph.dwHeaderSize = sizeof( dipdw.diph ); 
    dipdw.diph.dwObj        = 0;
    dipdw.diph.dwHow        = DIPH_DEVICE;
    dipdw.dwData            = JOYDEAD;

    if ( FAILED( m_lpDIDevice->SetProperty( DIPROP_DEADZONE, &dipdw.diph ) ) )
        return FALSE;

    // Set saturation.
    dipdw.dwData            = JOYSAT;
    if ( FAILED( m_lpDIDevice->SetProperty( DIPROP_SATURATION, &dipdw.diph ) ) )
        return FALSE;

    // Find out if force feedback available.
    DIDEVCAPS didc;
    didc.dwSize = sizeof( DIDEVCAPS );
    if ( FAILED( m_lpDIDevice->GetCapabilities( &didc ) ) ) 
        return FALSE;
    m_FFAvailable = ( didc.dwFlags & DIDC_FORCEFEEDBACK );

    // If it's a force feedback stick, turn off autocenter so it doesn't
    // get in the way of our effects.
    if ( m_FFAvailable )
    {
        DIPROPDWORD DIPropAutoCenter;
        DIPropAutoCenter.diph.dwSize = sizeof( DIPropAutoCenter );
        DIPropAutoCenter.diph.dwHeaderSize = sizeof( DIPROPHEADER );
        DIPropAutoCenter.diph.dwObj = 0;
        DIPropAutoCenter.diph.dwHow = DIPH_DEVICE;
        DIPropAutoCenter.dwData = 0;

        m_lpDIDevice->SetProperty( DIPROP_AUTOCENTER, 
                               &DIPropAutoCenter.diph );
    } 
    return TRUE;
}

//////////////////////////////////////////////////////////////////////
//
// Initialise The Joystick Device
//
//////////////////////////////////////////////////////////////////////
bool CDIJoystick::InitDevice()
{
    HRESULT hr;
    DIDEVICEINSTANCE    diDeviceInstance;

	// Just a precaution when Enumerating Devices and button Names if you create
	// An options Dialog before creating your main gaming window.
	// Then simply use the desktop window, temporarily!
	if(!m_hwnd)m_hwnd=::GetDesktopWindow();

    // Release device if it already exists.
    if ( m_lpDIDevice ) 
    {
        //if ( m_FFAvailable ) ReleaseEffects(); 
        
		Acquire( false);

        hr=m_lpDIDevice->Release();
		if(FAILED(hr))
		{
			OutputDebugString("Error Releasing Interface in CDIJoystick::InitDevice()\n");
			OutputDebugString(GetDIError(hr));
		} 
		else m_lpDIDevice=NULL;
    }

    // Create game device and set IDirectInputDevice7 interface in m_lpDIDevice.
    if(!CreateDevice( &m_JoystickGUID ))
	{
		OutputDebugString("Failed to create device in CDIJoystick::InitDevice()\n");
		return false;
	}

    // Find out what type it is and set data format accordingly.

    diDeviceInstance.dwSize = sizeof( DIDEVICEINSTANCE );

    hr=m_lpDIDevice->GetDeviceInfo( &diDeviceInstance );
	if ( FAILED( hr ) ) 
	{
		OutputDebugString("Failed to obtain device info in CDIJoystick::InitDevice()\n");
		OutputDebugString(GetDIError(hr));
		return false;
	}

	// Set the data format to be a Joystick
	hr = m_lpDIDevice->SetDataFormat( &c_dfDIJoystick2 );
	if ( FAILED( hr ) ) 
	{
		OutputDebugString("Failed to create device in CDIJoystick::InitDevice()\n");
		OutputDebugString(GetDIError(hr));
		return false;
	}

    // Set cooperative level. 
    DWORD cl, cl1;
	cl = DISCL_EXCLUSIVE;

	// Set foreground level for release version, but use background level
	// for debugging so we don't keep losing the device when switching to 
	// a debug window. 

    //cl1 = DISCL_FOREGROUND;
	cl1 = DISCL_BACKGROUND;
#ifdef _DEBUG
    cl1 = DISCL_BACKGROUND;
#endif

	// now set the co-operation level.
	hr=m_lpDIDevice->SetCooperativeLevel( m_hwnd, cl | cl1 );
    if ( FAILED( hr ))
    {
        OutputDebugString( "Failed to set game device cooperative level.\n" );
		OutputDebugString(GetDIError(hr));
        return false;
    }

    // Set up the data buffer.
    DIPROPDWORD dipdw =
    {
        // The header.
        {
            sizeof( DIPROPDWORD ),      // diph.dwSize
            sizeof( DIPROPHEADER ),     // diph.dwHeaderSize
            0,                          // diph.dwObj
            DIPH_DEVICE,                // diph.dwHow
        },
        // Number of elements in data buffer.
        BUFFERSIZE              // dwData
    };

    hr=m_lpDIDevice->SetProperty( DIPROP_BUFFERSIZE, &dipdw.diph );
	if(FAILED(hr))
    {
        OutputDebugString( "Failed to set up Data Buffer property.\n" );
		OutputDebugString(GetDIError(hr));
        return false;
    }

	// Resest the Force Feedback Flag
    m_FFAvailable = FALSE;

	// Try and initialise the Joystick!
    if ( !InitJoystick() ) return FALSE;  

    // In case you were wondering, this is not a good time to acquire the 
    // device. For one thing, we can't acquire in foreground mode here 
    // because the options dialog may be open. We'll acquire it when the
    // main window is activated.

    return TRUE;
}

////////////////////////////////////////////////////////////////////////////////
//
// Either Acquire or Unacquire the Device!
//
////////////////////////////////////////////////////////////////////////////////
bool CDIJoystick::Acquire(bool state)
{
    HRESULT hr;

	if(!m_lpDIDevice)
	{
		OutputDebugString("Error in CDIJoystick::Acquire(bool state)\nDevice Has Not Been Created.\n");
		return false;
	}

    if ( !state )  // Unacquire.
    {
        hr = m_lpDIDevice->Unacquire();
    }
    else       // Acquire.
    {
        // This could take a while with FF.
        ::SetCursor( m_hCursorWait );

        hr = m_lpDIDevice->Acquire();
        if ( SUCCEEDED( hr ) ) 
        {
            // Initialize effects; ignore failure and just
            // pretend FF not there.
            if ( m_FFAvailable )      // First set when device initialized.
            {
                //m_FFAvailable = InitFFEffects();
            }
        }
    }

	if(FAILED(hr))
	{
		OutputDebugString("Failed in CDIJoystick::Acquire(bool state)\n");
		OutputDebugString(GetDIError(hr));
		return false;
	}
	return true;
}

//////////////////////////////////////////////////////////////////////
//
// Set the HWND that will be used when Acquiring Devices!
//
//////////////////////////////////////////////////////////////////////
void CDIJoystick::SetHWND(HWND hwnd)
{
	//Shutdown();
	m_hwnd=hwnd;
	//m_EnumerationStarted=false;
	m_hInstance=GetModuleHandle(NULL);

	// Initialise Direct Input
	//Initialise();

	// Start Enumeration of Attached Joysticks.
	//Enumerate_Joysticks();
}

//////////////////////////////////////////////////////////////////////
//
// Set the preferred GUID for the joystick device
//
//////////////////////////////////////////////////////////////////////
void CDIJoystick::SetPreferredDevice(GUID *pguid)
{
	memcpy(&m_JoystickGUID,pguid,sizeof(GUID));
}

//////////////////////////////////////////////////////////////////////
//
// Re-Initialise this object, used when changing HWND or Device
// Not yet implemented.
//
//////////////////////////////////////////////////////////////////////
bool CDIJoystick::ReInitialise()
{
//	m_EnumerationStarted=false;
//	m_hInstance=GetModuleHandle(NULL);
//	Enumerate_Joysticks();
	return true;
}

//////////////////////////////////////////////////////////////////////
//
// Return Error Text From HRESULT
//
//////////////////////////////////////////////////////////////////////
TCHAR* CDIJoystick::GetDIError(HRESULT error)
{
	switch(error)
	{
	case E_PENDING : return _T("E_PENDING : Data Not Available.\n");
		break;
	case E_HANDLE :  return _T("E_HANDLE : The HWND parameter is not a valid top-level window that belongs to the process.\n");
		break;
	case DIERR_UNSUPPORTED : return _T("DIERR_UNSUPPORTED : The function called is not supported at this time. This value is equal to the E_NOTIMPL standard COM return value.\n");
		break;
	case DIERR_UNPLUGGED : return _T("DIERR_UNPLUGGED : The operation could not be completed because the device is not plugged in.\n");
		break;
	case DIERR_REPORTFULL : return _T("DIERR_REPORTFULL : More information was requested to be sent than can be sent to the device.\n");
		break;
	case DIERR_READONLY : return _T("DIERR_READONLY : The specified property cannot be changed. This value is equal to the E_ACCESSDENIED standard COM return value.\n");
		break;
	case DIERR_OUTOFMEMORY : return _T("DIERR_OUTOFMEMORY : The DirectInput subsystem could not allocate sufficient memory to complete the call. This value is equal to the E_OUTOFMEMORY standard COM return value.\n");
		break;
//	case DIERR_OTHERAPPHASPRIO : return _T("DIERR_OTHERAPPHASPRIO : Another application has a higher priority level, preventing this call from succeeding. This value is equal to the E_ACCESSDENIED standard COM return value. This error can be returned when an application has only foreground access to a device but is attempting to acquire the device while in the background. ");
//		break;
	case DIERR_OLDDIRECTINPUTVERSION : return _T("DIERR_OLDDIRECTINPUTVERSION : The application requires a newer version of DirectInput.\n");
		break;
	case DIERR_OBJECTNOTFOUND : return _T("DIERR_OBJECTNOTFOUND : The requested object does not exist.\n");
		break;
	case DIERR_NOTINITIALIZED : return _T("DIERR_NOTINITIALIZED : This object has not been initialized.\n");
		break;
//	case DIERR_NOTFOUND : return _T("DIERR_NOTFOUND : The requested object does not exist.\n");
//		break;
	case DIERR_NOTEXCLUSIVEACQUIRED : return _T("DIERR_NOTEXCLUSIVEACQUIRED : The operation cannot be performed unless the device is acquired in DISCL_EXCLUSIVE mode.\n");
		break;
	case DIERR_NOTDOWNLOADED : return _T("DIERR_NOTDOWNLOADED : The effect is not downloaded.\n");
		break;
	case DIERR_NOTBUFFERED : return _T("DIERR_NOTBUFFERED : The device is not buffered. Set the DIPROP_BUFFERSIZE property to enable buffering.\n");
		break;
	case DIERR_NOTACQUIRED : return _T("DIERR_NOTACQUIRED : The operation cannot be performed unless the device is acquired.\n");
		break;
	case DIERR_NOINTERFACE : return _T("DIERR_NOINTERFACE : The specified interface is not supported by the object. This value is equal to the E_NOINTERFACE standard COM return value.\n");
		break;
	case DIERR_NOAGGREGATION : return _T("DIERR_NOAGGREGATION : This object does not support aggregation.\n");
		break;
	case DIERR_MOREDATA : return _T("DIERR_MOREDATA : Not all the requested information fit into the buffer.\n");
		break;
	case DIERR_INVALIDPARAM : return _T("DIERR_INVALIDPARAM : An invalid parameter was passed to the returning function, or the object was not in a state that permitted the function to be called. This value is equal to the E_INVALIDARG standard COM return value.\n");
		break;
	case DIERR_INPUTLOST : return _T("DIERR_INPUTLOST : Access to the input device has been lost. It must be reacquired.\n");
		break;
	case DIERR_INCOMPLETEEFFECT : return _T("DIERR_INCOMPLETEEFFECT : The effect could not be downloaded because essential information is missing. For example, no axes have been associated with the effect, or no type-specific information has been supplied.\n");
		break;
//	case DIERR_HANDLEEXISTS : return _T("DIERR_HANDLEEXISTS : The device already has an event notification associated with it. This value is equal to the E_ACCESSDENIED standard COM return value.\n");
//		break;
	case DIERR_GENERIC : return _T("DIERR_GENERIC : An undetermined error occurred inside the DirectInput subsystem. This value is equal to the E_FAIL standard COM return value.\n");
		break;
	case DIERR_HASEFFECTS : return _T("DIERR_HASEFFECTS : The device cannot be reinitialized because there are still effects attached to it.\n");
		break;
	case DIERR_EFFECTPLAYING : return _T("DIERR_EFFECTPLAYING : The parameters were updated in memory but were not downloaded to the device because the device does not support updating an effect while it is still playing.\n");
		break;
	case DIERR_DEVICENOTREG : return _T("DIERR_DEVICENOTREG : The device or device instance is not registered with DirectInput. This value is equal to the REGDB_E_CLASSNOTREG standard COM return value.\n");
		break;
	case DIERR_DEVICEFULL : return _T("DIERR_DEVICEFULL : The device is full.\n");
		break;
	case DIERR_BETADIRECTINPUTVERSION : return _T("DIERR_BETADIRECTINPUTVERSION : The application was written for an unsupported prerelease version of DirectInput.\n");
		break;
	case DIERR_BADDRIVERVER : return _T("DIERR_BADDRIVERVER : The object could not be created due to an incompatible driver version or mismatched or incomplete driver components.\n");
		break;
	case DIERR_ALREADYINITIALIZED : return _T("DIERR_ALREADYINITIALIZED : This object is already initialized\n");
		break;
	case DIERR_ACQUIRED : return _T("DIERR_ACQUIRED : The operation cannot be performed while the device is acquired.\n");
		break;
	case DI_TRUNCATEDANDRESTARTED : return _T("DI_TRUNCATEDANDRESTARTED : Equal to DI_EFFECTRESTARTED | DI_TRUNCATED\n");
		break;
	case DI_TRUNCATED : return _T("DI_TRUNCATED : The parameters of the effect were successfully updated, but some of them were beyond the capabilities of the device and were truncated to the nearest supported value.\n");
		break;
	case DI_PROPNOEFFECT : return _T("DI_PROPNOEFFECT : The change in device properties had no effect. This value is equal to the S_FALSE standard COM return value.\n");
		break;
	case DI_POLLEDDEVICE : return _T("DI_POLLEDDEVICE : The device is a polled device. As a result, device buffering does not collect any data and event notifications is not signaled until the IDirectInputDevice7::Poll method is called.\n");
		break;
	case DI_OK : return _T("DI_OK : The operation completed successfully. This value is equal to the S_OK standard COM return value.\n");
		break;
//	case DI_NOTATTACHED : return _T("DI_NOTATTACHED : The device exists but is not currently attached. This value is equal to the S_FALSE standard COM return value.\n");
//		break;
//	case DI_NOEFFECT : return _T("DI_NOEFFECT : The operation had no effect. This value is equal to the S_FALSE standard COM return value.\n");
//		break;
	case DI_EFFECTRESTARTED : return _T("DI_EFFECTRESTARTED : The effect was stopped, the parameters were updated, and the effect was restarted.\n");
		break;
	case DI_DOWNLOADSKIPPED : return _T("The parameters of the effect were successfully updated, but the effect could not be downloaded because the associated device was not acquired in exclusive mode.\n");
		break;
//	case DI_BUFFEROVERFLOW : return _T("DI_BUFFEROVERFLOW : The device buffer overflowed and some input was lost. This value is equal to the S_FALSE standard COM return value.\n");
//		break;
	default: return _T("Unknown Error Code.\n");
	}
}


//////////////////////////////////////////////////////////////////////////////////////////
//
// Update member variables to reflect the state of the device
//
//////////////////////////////////////////////////////////////////////////////////////////
bool CDIJoystick::PollDevice()
{
	static		 loopcount=0;
     
    HRESULT            hr;
    //DIDEVICEOBJECTDATA rgdod[BUFFERSIZE];
    //DWORD              dwItems; 

	ZeroMemory(&m_dijs,sizeof(m_dijs));

	// Has device been initialised ?
	if (!m_lpDIDevice) 
	{
		// Try and initialise device
		if(!InitDevice())
		OutputDebugString("Failed To Initialise and Poll Joystick in CDIJoystick::PollDevice()\n");
		return false;
	}

	hr=m_lpDIDevice->Poll();  // May be unnecessary but never hurts.
	if(FAILED(hr))
	{
		OutputDebugString("Failed To Poll Joystick in CDIJoystick::PollDevice()\n");
		OutputDebugString(GetDIError(hr));
	}

getImmediateData:
	if(loopcount>20) return false;	// Infinite Loop Protection
    hr = m_lpDIDevice->GetDeviceState( sizeof( DIJOYSTATE2 ), &m_dijs ); 

    // The data stream was interrupted. Reacquire the device and try again.
	if ( hr == DIERR_INPUTLOST )
    {
		OutputDebugString("Failed To Obtain Immediate Device State in CDIJoystick::PollDevice()\n");
		OutputDebugString(GetDIError(hr));

		// Increment Infinite Loop Protection Counter and try again.
		loopcount++;

		// Try and acquire device and start again.
        if ( Acquire( true ) )
            goto getImmediateData;
    }

        // We can't get the device because it has not been acquired so try and acquire it.
    if ( hr == DIERR_NOTACQUIRED )
    {
		// Increment Infinite Loop Protection Counter.
		loopcount++;

		OutputDebugString("Device Not Acquired Trying Again immediate CDIJoystick::PollDevice()\n");
		if(!Acquire(true))
		{
			OutputDebugString("Unable to acquire Immediate Device in CDIJoystick::PollDevice() Quitting\n");
			return false;
		}
		// Try and get buffered data if device is buffered!
		goto getImmediateData;
    }

	if ( FAILED(hr))
    {
		OutputDebugString("Unable to obtain Immediate Data from Device in CDIJoystick::PollDevice() Quitting\n");
		return false;
    }

	// First set immediate direction if your only interested in basic movement
    if ( m_dijs.lX < 0 ) m_JoyLeft=true; else m_JoyLeft=false;
	if ( m_dijs.lX > 0 ) m_JoyRight=true; else m_JoyRight=false;
	if ( m_dijs.lY < 0 ) m_JoyUp=true; else m_JoyUp=false;
	if ( m_dijs.lY > 0 ) m_JoyDown=true; else m_JoyDown=false;

	m_JoyFire1=false;

	for(int i=0;i<sizeof(m_dijs.rgbButtons);i++)
	{
		if(m_dijs.rgbButtons[i]&0x80)
		{
			m_JoyFire[i]=true;
			m_JoyFire1=true;
		}
		else m_JoyFire[i]=false;
	}
	return true;
}

//////////////////////////////////////////////////////////////////////
//
// Run the Control Panel!
//
//////////////////////////////////////////////////////////////////////
void CDIJoystick::RunControlPanel()
{
	if(!m_lpDI) return;
	m_lpDI->RunControlPanel(m_hwnd,NULL);
}

 


 with questions or comments about this web site.
Last modified: Saturday, 25 April 2009 19:42:55.

(C) 1998-2007 Jason Brooks - All Rights Reserved