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:
int idBtn[] = { IDC_CHECK1,IDC_CHECK2,IDC_CHECK3,IDC_CHECK4,
IDC_CHECK5,IDC_CHECK6, 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, };
POINT ptXY; POINT ptZ; POINT ptXY0;
double m_scaleX = 1; double m_scaleY = 1;
_TCHAR stroka[256];
and the procedure of the Cross Drawing replacing itself when drawing next time in the same place:
void DrawCrossXOR(HWND hwnd, POINT pt)
{
HDC hDC = GetDC(hwnd); int r2 = SetROP2(hDC, R2_NOTXORPEN); MoveToEx(hDC, pt.x, pt.y + 5, NULL); 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); }
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):
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:
HWND hXY = GetDlgItem(hWnd, IDC_STATIC_XY); RECT rr;
GetClientRect(hXY, &rr);
ptZ.x = ptXY.x = ptXY0.x = (rr.right + rr.left) / 2; ptZ.y = ptXY.y = ptXY0.y = (rr.bottom + rr.top) / 2;
m_scaleX = (rr.right - rr.left) / 256.; m_scaleY = (rr.bottom - rr.top) / 256.;
DrawCrossXOR(hXY, ptXY); HWND hZ = GetDlgItem(hWnd, IDC_STATIC_Z);
DrawCrossXOR(hZ, ptXY);
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:
for (int i = 0; i < 12; i++) {
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++) CheckDlgButton(hWnd, idHat[i],i == lHat ? BST_CHECKED : BST_UNCHECKED);
Static XY Box condition:
int xx = ptXY0.x + (int)(lAxisX*m_scaleX); int yy = ptXY0.y + (int)(lAxisY*m_scaleY);
if (xx != ptXY.x || yy != ptXY.y) {
HWND hXY = GetDlgItem(hWnd, IDC_STATIC_XY); DrawCrossXOR(hXY, ptXY); ptXY.x = xx; ptXY.y = yy;
DrawCrossXOR(hXY, ptXY);
hXY = GetDlgItem(hWnd, IDC_STATIC_XY_TEXT); _stprintf_s(stroka, _T("x = %d y = %d"), lAxisX, lAxisY); SetWindowText(hXY, stroka); }
Static Z Box condition:
xx = ptXY0.x + (int)(lAxisRz*m_scaleX); yy = ptXY0.y + (int)(lAxisZ*m_scaleY);
if (xx != ptZ.x || yy != ptZ.y) {
HWND hXY = GetDlgItem(hWnd, IDC_STATIC_Z); DrawCrossXOR(hXY, ptZ); ptZ.x = xx; ptZ.y = yy;
DrawCrossXOR(hXY, ptZ);
hXY = GetDlgItem(hWnd, IDC_STATIC_Z_TEXT); _stprintf_s(stroka, _T("x = %d y = %d"), lAxisRz, lAxisZ); SetWindowText(hXY, stroka); }
To call the About Box, click the question mark in the Title Bar; the code for About box call inserted by V.Petrov:
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):
int m_x; int m_y; int m_zr; int m_z; int m_h;
CPoint m_ptXY; CPoint m_ptZ; CPoint m_ptXY0; double m_scaleX; double m_scaleY;
UINT g_NumberOfButtons; BOOL bButtonStates[MAX_BUTTONS];
and in the ParseRawInput
procedure borrowed from Alexander Böcken's project variables have been changed just in correspondence.
Additional global variables required:
int idBtn[] = { IDC_CHECK1,IDC_CHECK2,IDC_CHECK3,IDC_CHECK4,
IDC_CHECK5,IDC_CHECK6, 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, };
and the procedure of the Cross Drawing replacing itself when drawing next time in the same place:
void DrawCrossXOR(CDC * pDC, CPoint pt)
{
int r2 = pDC->SetROP2(R2_NOTXORPEN); pDC->MoveTo(pt + CSize(0, 5)); pDC->LineTo(pt + CSize(0, -5));
pDC->MoveTo(pt + CSize(5, 0));
pDC->LineTo(pt + CSize(-5, 0));
pDC->SetROP2(r2); }
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:
CStatic * pWnd = (CStatic *)GetDlgItem(IDC_STATIC_XY); CRect rect;
pWnd->GetClientRect(&rect); m_ptXY0 = CPoint(rect.Width() / 2, rect.Height() / 2); m_scaleX = rect.Width() / 256.; m_scaleY = rect.Height() / 256.; m_ptXY = m_ptZ = CPoint(-1000, -1000);
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:
for (int i = 0; i < 12; i++) {
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++) CheckDlgButton(idHat[i], i == m_h ? BST_CHECKED : BST_UNCHECKED);;
Static XY Box and Static Z Box condition:
CPoint pt = m_ptXY0 + CSize((int)(m_x*m_scaleX),
(int)(m_y*m_scaleY)); CPoint ptZ = m_ptXY0 + CSize((int)(m_zr*m_scaleX),
(int)(m_z*m_scaleY)); if (pt != m_ptXY || ptZ != m_ptZ) {
DrawScene(); m_ptXY = pt; m_ptZ = ptZ;
DrawScene();
CStatic * pWnd = (CStatic *)GetDlgItem(IDC_STATIC_XY_TEXT); CString aString;
aString.Format(_T("x = %d y = %d"), m_x, m_y); pWnd->SetWindowText(aString);
pWnd = (CStatic *)GetDlgItem(IDC_STATIC_Z_TEXT); aString.Format(_T("z = %d rz = %d"), m_z, m_zr); pWnd->SetWindowText(aString); }
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.