Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / MFC

Joystick Win32 and MFC Projects

5.00/5 (13 votes)
19 Dec 2016CPOL5 min read 21K   1.3K  
Joystick Win32 and MFC Projects Templates ready to use

Image 1

Introduction

When I bought a joystick, I was sure that the codes and instructions were available on CodeProject. The idea was the same as in the CodeProject article by Work Damnit!

"When I wanted to create a Joystick driver so I could control my robot via a joystick plugged into my computer, one of the first things I did was look for a simple joystick driver that could set up everything for me and give me the readings I wanted without me having to code anything myself or even understand how it works. Unfortunately, despite all the people using joysticks in their programs and projects before me, it seems I couldn’t find anyone had created anything like what I was looking for and shared it online."

And the author of Work Damnit! has done a good job of the joystick driver development. Unfortunately, just for DirectX environment.

Background

But his idea of "plug and play" project proved useful. After all, CodeProject is a great source for things developers require.

I also found the fine article Using the Raw Input API to Process Joystick Input by Alexander Böcken. The idea and sub-script codes are fine. Nothing more is required for joystick driver handling development.

Just out of the routine, the demo implementation occurred to be not as fine. It is a poor idea to redraw of all the Dialog Box every time the WM_INPUT message occurs.

The Dialog Box seemed oscillating and unstable. Therefore, I've taken the liberty of improving the performance by means of the standard MSVS AppWizard procedures.

1. Win32 Joystick Project

1.1. Win32 Project Template with AppWizard

Menu File->New->Project selected. In the New Project dialog appeared Win32->Win32 Project selected. The Win32 project template JoystickWin32 can be created by following the instructions provided.

1.2. Dialog Boxes Handling with the Resource Appwizard

Next, in the Resource window, the new Dialog template IDD_JOYSTICKTEST_DIALOG is created with the AppWizard standard procedures.

Then the background image of Joystick inserted as a Bitmap resource and 13 numerated Check Boxes IDC_CHECK1...IDC_CHECK13 created and 8 unnumerated Check Boxes IDC_HAT1...IDC_HAT8 created.

Two Static resources IDC_STATIC_XY and IDC_STATIC_Z created of the same size and two Static Text resources IDC_STATIC_XY_TEXT and IDC_STATIC_Z_TEXT located below the Static boxes corresponding.

Context Help property set to TRUE.

Also in the IDD_ABOUTBOX dialog, some corrections of authorization occurred.

1.3. Global Variables and Procedures

The global variables and the ParseRawInput procedure borrowed from Alexander Böcken's project completely without any changes.

Additional global variables required:

C++
int idBtn[] = { IDC_CHECK1,IDC_CHECK2,IDC_CHECK3,IDC_CHECK4, 
                IDC_CHECK5,IDC_CHECK6, //Numerated Check Boxes Array
IDC_CHECK7,IDC_CHECK8,IDC_CHECK9,IDC_CHECK10,IDC_CHECK11,IDC_CHECK12,IDC_CHECK13, };  //

int idHat[] = { IDC_HAT1,IDC_HAT2,IDC_HAT3,IDC_HAT4,IDC_HAT5,
                IDC_HAT6,IDC_HAT7,IDC_HAT8, };//Unnumerated Check Boxes Array

POINT ptXY;             //Point in IDC_STATIC_BOX Static Box
POINT ptZ;              //Point in IDC_STATIC_Z Static Box
POINT ptXY0;            //Initial Point Position 

double m_scaleX = 1;	//Scale in X direction 
double m_scaleY = 1;	//Scale in Y direction

_TCHAR stroka[256];     //Text Info String

and the procedure of the Cross Drawing replacing itself when drawing next time in the same place:

C++
void DrawCrossXOR(HWND hwnd, POINT pt)
{
	HDC hDC = GetDC(hwnd);               //Handle DC of the window
	int r2 = SetROP2(hDC, R2_NOTXORPEN); //Set DC XOR style
	MoveToEx(hDC, pt.x, pt.y + 5, NULL); //drawing cross
	LineTo(hDC, pt.x, pt.y - 5);
	MoveToEx(hDC, pt.x +5, pt.y, NULL);
	LineTo(hDC, pt.x -5, pt.y );
	SetROP2(hDC, r2);                     //restore XOR style   
}

1.4. Initialization Installations

In the InitInstance procedure, all the RegisterClass steps are replaced with the next code as far as the predesigned Dialog Box is used):

C++
hWnd = CreateDialog(hInst, MAKEINTRESOURCE(IDD_JOYSTICKTEST_DIALOG),
     nullptr, (DLGPROC)WndProc);

The Joystick Initialization steps have been borrowed from Alexander Böcken's project completely without any change.

For the graphic performance to follow some initial calculations required:

C++
//Center Point and Scale calculations(V.Petrov)
   HWND hXY = GetDlgItem(hWnd, IDC_STATIC_XY);           //Get the XY Static Box window
   RECT rr;
   GetClientRect(hXY, &rr);                              //Rect Size of the window

   ptZ.x = ptXY.x = ptXY0.x = (rr.right + rr.left) / 2;  //X Center Calculation 
   ptZ.y = ptXY.y = ptXY0.y = (rr.bottom + rr.top) / 2;  //Y Center Calculation 
    
   m_scaleX = (rr.right - rr.left) / 256.;    //X Scale Calculation
   m_scaleY = (rr.bottom - rr.top) / 256.;    //Y Scale Calculation

   DrawCrossXOR(hXY, ptXY);                   //Draw	First Cross in the XY Static Box window
   HWND hZ = GetDlgItem(hWnd, IDC_STATIC_Z);
   DrawCrossXOR(hZ, ptXY);                    //Draw	First Cross in the Z Static Box window

1.5. Messages Handling

In the WndProc procedure at the case WM_INPUT: the code borrowed from Alexander Böcken's project at the same place until the drawings. After ParseRawInput inserted the code for current Joystick condition handling.

Check Boxes condition:

C++
for (int i = 0; i < 12; i++)       //Update All the Numerated Check Boxes
    {
        CheckDlgButton(hWnd, idBtn[i], bButtonStates[i] ? BST_CHECKED : BST_UNCHECKED);
        if(i == 1)
           CheckDlgButton(hWnd, idBtn[12], bButtonStates[i] ? BST_CHECKED : BST_UNCHECKED);
    }
    for (int i = 0; i < 8; i++)    //Update All the UnNumerated Check Boxes
        CheckDlgButton(hWnd, idHat[i],i == lHat ? BST_CHECKED : BST_UNCHECKED);

Static XY Box condition:

C++
int xx = ptXY0.x + (int)(lAxisX*m_scaleX);      //Calc Joystick X position
int yy = ptXY0.y + (int)(lAxisY*m_scaleY);      //Calc Joystick Y position

if (xx != ptXY.x || yy != ptXY.y)	            //If any position changed
{
	HWND hXY = GetDlgItem(hWnd, IDC_STATIC_XY); //Get Static XY window
	DrawCrossXOR(hXY, ptXY);                    //Clear the Cross in Previous position
	ptXY.x = xx;                                //Apply new pos to Global var 
	ptXY.y = yy;
	DrawCrossXOR(hXY, ptXY);                    //Draw Cross in the new pos

	hXY = GetDlgItem(hWnd, IDC_STATIC_XY_TEXT); //Get Text Info Window
	_stprintf_s(stroka, _T("x = %d  y = %d"), lAxisX, lAxisY);//Type position
	SetWindowText(hXY, stroka);                 //Show new position
}

Static Z Box condition:

C++
xx = ptXY0.x + (int)(lAxisRz*m_scaleX);         //Calc Joystick Rz position
yy = ptXY0.y + (int)(lAxisZ*m_scaleY);	         //Calc Joystick Z position

if (xx != ptZ.x || yy != ptZ.y)	                 //If any position changed
{
	HWND hXY = GetDlgItem(hWnd, IDC_STATIC_Z);   //Get Static Z window
	DrawCrossXOR(hXY, ptZ);	                     //Clear the Cross in Previous position
	ptZ.x = xx;                                  //Apply new pos to Global var 
	ptZ.y = yy;
	DrawCrossXOR(hXY, ptZ);	                     //Draw Cross in the new pos

	hXY = GetDlgItem(hWnd, IDC_STATIC_Z_TEXT);   //Get Text Info Window
	_stprintf_s(stroka, _T("x = %d  y = %d"), lAxisRz, lAxisZ);//Type position
	SetWindowText(hXY, stroka);                  //Show new position
}

To call the About Box, click the question mark in the Title Bar; the code for About box call inserted by V.Petrov:

C++
case 0x2a2:
    if (!bMod)
    {
        bMod = TRUE;
        DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
        bMod = FALSE;
    }
    break;

The global variable bMod is inserted because the main Dialog Box is modal itself. If the About Box starts, all the messages for the main Dialog Box to be ignored.

2.MFC Joystick Project

2.1. MFC Project Template with AppWizard

Menu File->New->Project selected. In the New Project dialog appeared MFC->MFC Application selected. The Win32 project template JoystickMFC is created by following the instructions provided and select Dialog based.

2.2. Dialog Boxes Predesigning

The Dialog template is already created with the Master AppWizard. Just copy all the components from Win32 project from every Dialog Box into the new ones.

2.3. Variables and Procedures

All the variables encountered declared as the internal variables of the CJoystickMfcDlg class (the names also changed):

C++
//Joystick Condition Variables
	int m_x;     //X pos of XY box
	int m_y;     //Y pos of XY box
	int m_zr;    //X pos of Z box
	int m_z;     //Y pos of Z box
	int m_h;	 //hat switch number pressed 	

	CPoint m_ptXY;    //Point in IDC_STATIC_BOX Static Box 
	CPoint m_ptZ;	  //Point in IDC_STATIC_Z Static Box
	CPoint m_ptXY0;	  //Initial Point Position 
	double m_scaleX;  //Scale in X direction 
	double m_scaleY;  //Scale in Y direction

	UINT g_NumberOfButtons;          //Number of numerated buttons
	BOOL bButtonStates[MAX_BUTTONS]; //Array of numerated buttons conditions

and in the ParseRawInput procedure borrowed from Alexander Böcken's project variables have been changed just in correspondence.

Additional global variables required:

C++
int idBtn[] = { IDC_CHECK1,IDC_CHECK2,IDC_CHECK3,IDC_CHECK4,
                IDC_CHECK5,IDC_CHECK6, //Numerated Check Boxes Array
IDC_CHECK7,IDC_CHECK8,IDC_CHECK9,IDC_CHECK10,IDC_CHECK11,IDC_CHECK12,IDC_CHECK13, };  //

int idHat[] = { IDC_HAT1,IDC_HAT2,IDC_HAT3,IDC_HAT4,IDC_HAT5,
                IDC_HAT6,IDC_HAT7,IDC_HAT8, };//Unnumerated Check Boxes Array

and the procedure of the Cross Drawing replacing itself when drawing next time in the same place:

C++
void DrawCrossXOR(CDC * pDC, CPoint pt)
{
	int r2 = pDC->SetROP2(R2_NOTXORPEN); //Set DC XOR style
	pDC->MoveTo(pt + CSize(0, 5));       //drawing cross
	pDC->LineTo(pt + CSize(0, -5));
	pDC->MoveTo(pt + CSize(5, 0));
	pDC->LineTo(pt + CSize(-5, 0));
	pDC->SetROP2(r2);                    //restore XOR style 
}

2.4. Initialization Installations

The Joystick Initialization steps are borrowed from Alexander Böcken's project and are completely unchanged in the CJoystickMFCDlg::OnInitDialog procedure.

For the graphic performance to follow some initial calculations required in the CJoystickMFCDlg::OnInitDialog procedure:

C++
//Center Point and Scale calculations(V.Petrov)
   CStatic * pWnd = (CStatic *)GetDlgItem(IDC_STATIC_XY); //Get the XY Static Box window
	CRect rect;
	pWnd->GetClientRect(&rect);                           //Rect Size of the window
	m_ptXY0 = CPoint(rect.Width() / 2, rect.Height() / 2);//Center Point Calculation
	m_scaleX = rect.Width() / 256.;                       //X Scale Calculation
	m_scaleY = rect.Height() / 256.;                      //Y Scale Calculation
	m_ptXY = m_ptZ = CPoint(-1000, -1000);                //Initially point located out of rects

2.5. Messages Handling

For WM_INPUT message handling, the procedure void CJoystickMFCDlg::OnRawInput(UINT nInputcode, HRAWINPUT lParam) has been created with the Class Wizard command. The code has been borrowed from Alexander Böcken's project at the WM_INPUT message handling procedure until the drawings. After ParseRawInput, I have inserted the code for current Joystick condition handling.

Check Boxes condition:

C++
for (int i = 0; i < 12; i++)  //Update All the Numerated Check Boxes
{
   CheckDlgButton( idBtn[i], bButtonStates[i] ? BST_CHECKED : BST_UNCHECKED);
   if (i == 1)
      CheckDlgButton( idBtn[12], bButtonStates[i] ? BST_CHECKED : BST_UNCHECKED);
}
for (int i = 0; i < 8; i++)   //Update All the UnNumerated Check Boxes
   CheckDlgButton(idHat[i], i == m_h ? BST_CHECKED : BST_UNCHECKED);;

Static XY Box and Static Z Box condition:

C++
CPoint pt = m_ptXY0 + CSize((int)(m_x*m_scaleX), 
            (int)(m_y*m_scaleY));     //Calc Joystick XY position
CPoint ptZ = m_ptXY0 + CSize((int)(m_zr*m_scaleX), 
             (int)(m_z*m_scaleY));   //Calc Joystick zr and z position
if (pt != m_ptXY || ptZ != m_ptZ) //If any position changed
{
  DrawScene();                  //Clear the Cross in Previous position
  m_ptXY = pt;                  //Apply new pos as current
  m_ptZ = ptZ;
  DrawScene();                  //Draw Cross in the new pos

  CStatic * pWnd = (CStatic *)GetDlgItem(IDC_STATIC_XY_TEXT);//Get Text Info Window
  CString aString;
  aString.Format(_T("x = %d  y = %d"), m_x, m_y);//Type position
  pWnd->SetWindowText(aString);                  //Show new position

  pWnd = (CStatic *)GetDlgItem(IDC_STATIC_Z_TEXT);//Get Text Info Window
  aString.Format(_T("z = %d  rz = %d"), m_z, m_zr);//Type position
  pWnd->SetWindowText(aString);                    //Show new position
}

Image 2

To call the About Box, just click the About button.

Important Note

The projects provided has been developed with MSVS-2015 pro using the instruments of MSVS-2010. Therefore, the EXE files are valid even for Windows-XP.

The problem is that external library hid.lib used. If you should use the instruments of MSVS-2015, no problems expected with the projects provided.

Before you start to build the projects provided with the instruments of MSVS-2010, you must prepare two points:

  • In the Project Property Box in Configuration Properties->VC++Directories->Include Directories, select Change command and set directory to ..\Windows Kits\8.1\Include\shared (just for #include <hidsdi.h> command to use)
  • The library hid.lib better to include directly with the menu PROJECT->Add Existing Item from the directory ..\Windows Kits\8.1\Lib\winv6.3\um\x86\hid.lib,

Points of Interest and Acknowledgements

Once more, I became certain that the codes and instructions for all intents and purposes are available on CodeProject. Just three days ago, I knew nothing about Joystick handling (I'm not a game player, and never touch a thing before).

And now, I've already used the Joystick in my real ship's simulator program. And it is as easy as inserting this JoystickMFCDlg.cpp program in the environment just making it unmodal and invisible (and do not forget to remove the bitmap picture because it makes the EXE enormous). It is working exactly as per the quote from Work Damnit! in the beginning of this article.

Many thanks to Alexander Böcken for his fine job. And I hope my article also should be useful for developers' people.

And the main thing is that my grand daughters should be perfectly happy to play with the Joystick.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)