DirectX 8 and the Keyboard

Published January 02, 2002 by Mason Smith, posted by Myopic Rhino
Do you see issues with this article? Let us know.
Advertisement
This tutorial assumes that you know basic Win32 programming.

DirectX is an API that has direct access to your hardware, assuming that you have Windows9X/NT/2000/XP. The beauty of it is that you don't have to write separate code for each graphic card, sound card, and input device. DirectX handles the details, which is good because it isn't the easiest thing in the world to program, anyway. This tutorial will show you how to access the keyboard using DirectInput8. First of all, you have to include dinput.h. Also, you have to link dxguid.lib and dinput8.lib.

DirectInput is the easiest part of the DirectX API that I have worked with. Let's take a look at some initialization code.

// Globals
LPDIRECTINPUT lpdi;
LPDIRECTINPUTDEVICE m_keyboard;
unsigned char keystate[256];

void Init(void)
{
if (FAILED(DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION,
IID_IDirectInput8, (void**)&lpdi, NULL)))
{
// error code
}

if (FAILED(lpdi->CreateDevice(GUID_SysKeyboard, &m_keyboard, NULL)))
{ /* error code */ }

if (FAILED(m_keyboard->SetDataFormat(&c_dfDIKeyboard)))
{ /* error code */ }

if (FAILED(m_keyboard->SetCooperativeLevel(hWND, DISCL_BACKGROUND |
DISCL_NONEXCLUSIVE)))
{ /* error code */ }

if (FAILED(m_keyboard->Acquire()))
{ /* error code */ }
}
These functions will initiate DirectInput, followed by the keyboard. The FAILED macro return true if the function returns anything other than DI_OK, which basically says that the function was successful.

The first function starts DirectInput8. I'd be wasting your time explaining all the parameters because they will most likely stay the same.

The next four functions initiate and calibrate a DIRECTINPUTDEVICE, specifically your keyboard. Again, I won't explain the parameters because you will rarely ever change them. If you must know the billions of insignificant details, just look at the DX8 documentation (although it probably won't help, trust me on this one).

Now, let's get some input.

void Render(void)
{
if (FAILED(m_keyboard->GetDeviceState(sizeof(unsigned char[256]), (LPVOID)keystate)))
{ /* error code */ }

if (keystate[DIK_LCONTROL] & 0x80)
{
// shoot gun, jump, react somehow
}
}
The GetDeviceState function updates the status of the keyboard and must be called at the beginning of every frame in order to receive input. The parameters are the size of the input variable used to contain the data and the actually variable. In the other two major forms of input, the mouse and the joystick, there are specific data structures defined by DirectX, but the keyboard is so simple that you merely need an array of unsigned chars. The second block of code checks to see if the left control button is down. The array of unsigned chars contains the status of all the keys on the keyboard. This status is updated by GetDeviceState. Since that syntax is a little on the ugly side, I like to use a nifty little macro to handle it.

#define KeyDown(data, n) ((data[n] & 0x80) ? true : false)
#define KeyUp(data, n) ((data[n] & 0x80) ? false : true)
That's much better, isn't it? You could just call it like this:

if (KeyDown(keystate, DIK_LCONTROL))
{ /* do something */ }
The last thing that we need to do is create the Destroy function to release DirectInput.

void Destroy(void)
{
if (m_keyboard)
m_keyboard->Release();

if (lpdi)
lpdi->Release();
}
Now that we have all of that done, let's see a full pseudo windows program using the code we've created.

#define WIN32_LEAN_AND_MEAN

#include
#include

// Globals
LPDIRECTINPUT lpdi;
LPDIRECTINPUTDEVICE m_keyboard;
unsigned char keystate[256];
HWND hWND;
HINSTANCE g_hinstance;
bool done = false;

// Defines
#define KeyDown(data, n) ((data[n] & 0x80) ? true : false)
#define KeyUp(data, n) ((data[n] & 0x80) ? false : true)

// Function declarations
void Init(void);
void Render(void);
void Destroy(void);

// Message Loop CallBack Function
LRESULT CALLBACK WinProc ( HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam )
{
HDC hDC;

switch( iMsg )
{
// Called when window is first created
case WM_CREATE:
Init();
return( 0 );
// Called when the window is refreshed
case WM_PAINT:
hDC = BeginPaint(hWnd, &paintStruct);
EndPaint(hWnd, &paintStruct);
return( 0 );
// Called when the user closes the window or terminates the application
case WM_DESTROY:
Destroy();
PostQuitMessage( 0 );
return( 0 );
}

return DefWindowProc( hWnd, iMsg, wParam, lParam );
}

// Function to Create the Window and Display it
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
// basic windows creation stuff

while (!done)
{
PeekMessage(&msg, hWnd, NULL, NULL, PM_REMOVE);

if (msg.message == WM_QUIT) // do we receive a WM_QUIT message?
{
done = true; // if so, time to quit the application
}
else
{
Render();

TranslateMessage(&msg); // translate and dispatch to event queue
DispatchMessage(&msg);
}
}

return ( msg.wParam );
}


void Init(void)
{
if (FAILED(DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION,
IID_IDirectInput8, (void**)&lpdi, NULL)))
{
// error code
}

if (FAILED(lpdi->CreateDevice(GUID_SysKeyboard, &m_keyboard, NULL)))
{ /* error code */ }

if (FAILED(m_keyboard->SetDataFormat(&c_dfDIKeyboard)))
{ /* error code */ }

if (FAILED(m_keyboard->SetCooperativeLevel(hWND, DISCL_BACKGROUND |
DISCL_NONEXCLUSIVE)))
{ /* error code */ }

if (FAILED(m_keyboard->Acquire()))
{ /* error code */ }
}

void Render(void)
{
if (FAILED(m_keyboard->GetDeviceState(sizeof(unsigned char[256]), (LPVOID)keystate)))
{ /* error code */ }

if (KeyDown(keystate, DIK_ESCAPE))
{
PostQuitMessage(0);
}
}

void Destroy(void)
{
if (m_keyboard)
m_keyboard->Release();

if (lpdi)
lpdi->Release();
}
Remember to link dxguid.lib and dinput8.lib to your project. If you don't know how, see your complier manual. For a full list of constants for the keyboard, see your DirectX documentation.

I've included the source code for a very easy to use wrapper system for DirectInput8. To use these in your game/application, all you need to do is call a few functions. The following is a pseudo program that uses the system.

// whole lot of stuff
// initiates the program
void Init(void)
{
// other important stuff
Init_CInput8(hWND); // takes handle to the window as parameter
Init_Keyboard(g_hinstance); // takes instance handle as parameter
Init_Mouse(g_hinstance); // takes instance handle as parameter
// other important stuff
}

// renders the program
void Render(void)
{
Read_Keyboard();
Read_Mouse();

// stuff

if (KeyDown(DIK_SPACE))
{
shooting = true;
}

if (KeyUp(DIK_SPACE))
{
shooting = false;
}

if (KeyPress(DIK_RETURN))
{
FireRockets(5);
}

// stuff

float mx, my;
Get_Mouse_Movement(mx, my);

cursor_x += mx;
cursor_y += my;

if (Button_Down(LEFT_BUTTON))
{
LaunchGrenade();
}
}

// de-initiates the program
void Destroy(void)
{
Release_Mouse();
Release_Keyboard();
Shutdown_CInput8();
}
There are more functions in the source than this, but they are very self-explanatory.

For more information on using the mouse, see my follow-up article DirectX 8 and the Mouse.
Cancel Save
0 Likes 0 Comments

Comments

Nobody has left a comment. You can be the first!
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Advertisement