Let's take a look at how to set up DirectInput. We will do this by building
a simple program to access a keyboard, and use the arrow keys to rotate a teapot.
For this we have five steps:
1. Define global variables and function prototypes
2. Initialize DirectInput and its devices
3. Get the keyboard information from DirectInput
4. Check specific keys as needed
5. Close DirectInput when done
Let's go over each part before we look at the whole program combined.
The following code is excerpt from the Direct3D Meshes tutorial's first program,
but with the added DirectInput code in bold.
// include the necessary header files
#include <windows.h>
#include <windowsx.h>
#include <d3d9.h>
#include <d3dx9.h>
#include <dinput.h>
// define the screen resolution and keyboard macros
#define SCREEN_WIDTH 800
#define SCREEN_HEIGHT 600
// #define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 :
0)
// #define KEY_UP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)
// include the DirectX Library files
#pragma comment (lib, "d3d9.lib")
#pragma comment (lib, "d3dx9.lib")
#pragma comment (lib, "dinput8.lib")
#pragma comment (lib, "dxguid.lib")
// global declarations
LPDIRECT3D9 d3d;
LPDIRECT3DDEVICE9 d3ddev;
LPD3DXMESH meshTeapot;
LPDIRECTINPUT8 din; // the pointer to our DirectInput interface
LPDIRECTINPUTDEVICE8 dinkeyboard; // the pointer to the keyboard device
BYTE keystate[256]; // the storage for the key-information
// function prototypes
void initD3D(HWND hWnd);
void render_frame(void);
void cleanD3D(void);
void init_graphics(void);
void init_light(void);
void initDInput(HINSTANCE hInstance, HWND hWnd); // sets up and
initializes DirectInput
void detect_input(void); // gets the current input state
void cleanDInput(void); // closes DirectInput and releases memory
// the WindowProc function prototype
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
This includes the DirectInput header file. This file contains various declarations
to actual methods contained in the DirectInput library.
This includes the DirectInput 8 library files.
"dxguid.lib" is an additional library that can get the GUID for any device DirectInput
used, and this will be
needed in a bit.
Why the DirectInput 8 library instead of 9? Well, the fact is, Microsoft did
not update DirectInput when the new DirectX 9 was released. There really was
no need, as there were no real breakthroughs in keyboard and mouse technology.
Maybe a future DirectInput will include multi-touch input, but until then, we're
just sticking with version 8.
This variable is a long pointer to DirectInput. What this means is that we
will create a class called iDirectInput8. When COM makes this class, we will
ignore it, and access it only indirectly using this pointer. The class is created the same way Direct3D is created.
This is a long pointer to the keyboard device. This contains all the information
having to do with the keyboard and how our program will relate to it. Later,
we will create additional devices, but we'll stick to the keyboard for now.
This is an array of bytes which are used to store the state of the keyboard.
Each byte represents one key on the keyboard. If a certain byte is equal to
1, the key is pressed, and if 0, the key is not pressed. We'll look more into
how this array is used in a bit.
Before we use DirectInput, we have to initialize it and the devices it controls.
In this lesson, we'll initialize DirectInput and the keyboard device. I didn't
bold the new parts here, because the entire thing is new.
// this is the function that initializes DirectInput
void initDInput(HINSTANCE hInstance, HWND hWnd)
{
// create the DirectInput interface
DirectInput8Create(hInstance, // the handle to the application
DIRECTINPUT_VERSION, // the compatible version
IID_IDirectInput8, // the DirectInput interface version
(void**)&din, // the pointer to the interface
NULL); // COM stuff, so we'll set it to NULL
// create the keyboard device
din->CreateDevice(GUID_SysKeyboard, // the default
keyboard ID being used
&dinkeyboard,
// the pointer to the device interface
NULL);
// COM stuff, so we'll set it to NULL
// set the data format to keyboard format
dinkeyboard->SetDataFormat(&c_dfDIKeyboard);
// set the control you will have over the keyboard
dinkeyboard->SetCooperativeLevel(hWnd, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND);
}
If the comments here are good
enough for you, excellent. Otherwise, I've described
each of these commands below.
This function is probably bigger than it needs to be, but I think we'll be okay.
What this function does is create the DirectInput interface. Let's take a
look at the prototype, then define each of the parameters.
HRESULT WINAPI DirectInput8Create(HINSTANCE hinst, // application handle
DWORD dwVersion, // DirectInput
version
REFIID riidltf, // unicode or ANSI
LPVOID *lplpDirectInput, // pointer
to DirectInput
LPUNKNOWN punkOuter // COM stuff,
so we'll put NULL
This first one is simple. We just plug in the handle to the application instance. It is passed in the WinMain()
parameters, so it is readily accessible.
This tells DirectInput what version it is.
If you studied the Direct3D tutorials, you'll recall that we had to tell Direct3D what version is was. This is for
compatibility reasons. The version you are using may be different from the
version your user is using. This parameter helps DirectX overcome the confusion
that creates.
The parameter to put here is DIRECTINPUT_VERSION. It doesn't matter what the
version is, just know that DirectX has taken care of it all for you.
There is more than one DirectInput interface. There is one for ANSI and one
for Unicode. However, most programmers just use the default. To get
the default, simply put IID_IDirectInput8 into the parameter.
[
Table 1.1 - riidltf
Values]
|
Value |
Description |
|
IID_IDirectInput8A |
Uses the ANSI version of DirectInput. |
|
IID_IDirectInput8W |
Uses the Unicode version of DirectInput. |
|
IID_IDirectInput8 |
Uses the default based on your compiler settings. If your compiler is using
Unicode, this value will be replaced with IID_IDirectInput8W, and the same goes
for ANSI. This is the value you'll most likely use. |
|
[Close Table] |
The pointer 'din' is the pointer we are using for the DirectInput interface.
DirectInput 8 requires that we convert this to a void**. We do this, and it
gets filled with the address of the interface. Just set it to (void**)&din.
This parameter is COM related, so we won't get into it. We'll just set it
to NULL.
Now we have the next function to talk about. Fortunately, it is significantly
smaller and simpler.
This function only has three parameters, only two of which you will use.
What this function does is create an interface through which you can access a device.
In this case, we will use it to access the keyboard. Let's look at the prototype:
HRESULT CreateDevice(REFGUID rguid,
LPDIRECTINPUTDEVICE8
*lplpDirectInputDevice,
LPUNKNOWN
pUnkOuter);
Remember the GUIDs (Globally Unique Identifiers) I talked about before? This
is where we enter that in. You do this by entering the flag GUID_SysKeyboard.
This flag contains the GUID for the current keyboard. As there is only one
keyboard, we don't have to worry about picking the right one.
This parameter is the address of the device. Before, we named this dinkeyboard,
so just put '&dinkeyboard' in this parameter.
This parameter, just like the last pUnkOuter we saw, is COM related, and we'll set
it to NULL.
Now let's cover the next function, which fortunately has only one parameter.
Let's take a look at the prototype:
HRESULT SetDataFormat(LPCDIDATAFORMAT lpdf);
That one parameter can get rather complex, but we'll keep it simple. It is
a pointer to a struct that sets the format of a device. We can get into this
at great detail and I could spend weeks dreaming up a new tutorial covering data
formats in all their detail. Fortunately, we have some default, pre-set structs
provided by DirectInput. The one we want now is the format for a keyboard,
so we'll load this with c_dfDIKeyboard. This takes care of all the details.
This parameter is just a little more annoying than SetDataFormat(). It's first
parameter is simple, but it's second one is more complex. Here is the prototype:
HRESULT SetCooperativeLevel(HWND hwnd,
DWORD dwFlags);
The handle to the window that will have control of the keyboard. This was
passed in earlier, so we'll use hWnd here.
This is the last little confusing part we have to talk about. There are five
flags we can place in this parameter:
[
Table 1.2 - SetCooperativeLevel Values]
|
Value |
Description |
|
DISCL_BACKGROUND |
Allows us to access input
even when our program is not selected. |
|
DISCL_FOREGROUND |
Makes it so we only read input when the window is selected. This is handy,
and helps keep players from running off cliffs when they are typing a quick IM.
Note: either
background or foreground must be selected, and you cannot select both. |
|
DISCL_EXCLUSIVE |
As soon as we finish setting up the device, no other application can touch the input.
This means no other application can use the keyboard or mouse when we are controlling it. |
|
DISCL_NONEXCLUSIVE |
Allows us to have access to the keyboard, but another application could also use
it.
Note: either exclusive or nonexclusive must be selected, and you cannot select both. |
|
DISCL_NOWINKEY |
Disable the Windows logo key. Setting this flag ensures that the user cannot
accidentally break out of the application using it. |
|
[Close Table] |
For these examples, we'll be using DISCL_FOREGROUND | DISCL_NONEXCLUSIVE, which
is workable for most situations.
Now let's take one more look at the whole function that creates DirectInput and
accesses the keyboard:
// this is the function that initializes DirectInput
void initDInput(HINSTANCE hInstance, HWND hWnd)
{
// create the DirectInput interface
DirectInput8Create(hInstance, // the handle to the application
DIRECTINPUT_VERSION, // the compatible version
IID_IDirectInput8, // the DirectInput interface version
(void**)&din, // the pointer to the interface
NULL); // COM stuff, so we'll set it to NULL
// create the keyboard device
din->CreateDevice(GUID_SysKeyboard, // the default
keyboard ID being used
&dinkeyboard,
// the pointer to the device interface
NULL);
// COM stuff, so we'll set it to NULL
// set the data format to keyboard format
dinkeyboard->SetDataFormat(&c_dfDIKeyboard);
// set the control we will have over the keyboard
dinkeyboard->SetCooperativeLevel(hWnd, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND);
}
Read that code through a couple of times and you'll really 'get it'.
Although reading keys in DirectInput is not as simple as using the GetAsyncKeyState()
function, we can easily set it up to be just as simple. The following code
is a function that gets keystroke information, then stores it in a 256-byte array.
// this is the function that gets the latest input data
void detect_input(void)
{
// get access if we don't have it already
dinkeyboard->Acquire();
// get the input data
dinkeyboard->GetDeviceState(256, (LPVOID)keystate);
}
Let's go over these commands briefly.
This is a simple function with no parameters which allows access the device
(in this case, the keyboard).
The reason this is called every time we want to look at the keyboard, is that sometimes
Windows will let other programs use the keyboard, and to do so, it must tell our
program to "unaquire" the keyboard, or close further access until this
function is called again. To prevent this, we simply call the Aquire() function
each time, and we'll get input whenever we're allowed to.
This function is actually quite simple. All it does is get the keyboard information
and stuff it into the memory location of our choice. Let's look at the prototype:
HRESULT GetDeviceState(DWORD cbData,
LPVOID lpvData);
The first parameter is the size of the location we will place the keyboard information
in. For keyboard input, this is always 256 bytes. Earlier, we created
an array called 'keystate' for this exact purpose.
The second parameter is the location of the array. We'll cast the 'keystate'
array address into a
void pointer to fill this parameter: (LPVOID)keystate.
The last function got the keyboard information regarding the whole keyboard.
However, we only need specific keys, and we want an easy way to check which keys
are pressed and which ones aren't. Up until now, we've used KEY_DOWN() to
do this. In this step, we'll create a function that can be used the same way.
As explained before, each byte in the 'keystate' array is dedicated to the status
of one key on the keyboard. Therefore, if we want to know the status of a
particular key, all we need to do is check the appropriate byte and the appropriate
bit within that byte.
The code to do this can be represented in one line. Where before we had:
if(KEY_DOWN(VK_LEFT))
// do something...
we now have:
if(keystate[DIK_LEFT] & 0x80)
// do something...
It's pretty obvious that this detects if the left arrow key has been pressed.
As you'll recall from GetAsyncKeyState(), you have to include the "& 0x80".
This is because only the high-order bit in the byte is relevant, and it is not guaranteed
that the others will be set to 0.
DIK_LEFT is equal to 203. The 203rd slot in the array is always
the left arrow key. Table 1.3 is a table of other key values that might be
useful to you.
[
Table 1.3 - Key Values]
|
Value |
Description |
|
DIK_ESCAPE |
The Escape key |
|
DIK_0 - 9 |
Numbers 0 though 9 on the main keyboard |
|
DIK_MINUS |
Minus key |
|
DIK_EQUALS |
Equals key |
|
DIK_BACK |
Backspace key |
|
DIK_TAB |
Tab key |
|
DIK_A - Z |
Letters A through Z, upper or lower case |
|
DIK_LBRACKET |
Left bracket key |
|
DIK_RBRACKET |
Right bracket key |
|
DIK_RETURN |
Enter on the main keyboard |
|
DIK_LCONTROL |
Left control key |
|
DIK_RCONTROL |
Right control key |
|
DIK_LSHIFT |
Left shift key |
|
DIK_RSHIFT |
Right shift key |
|
DIK_LMENU |
Left alt key |
|
DIK_RMENU |
Right alt key |
|
DIK_SPACE |
Spacebar |
|
DIK_F1 - F15 |
Function keys 1 through 15 |
|
DIK_NUMPAD0 - 9 |
Numeric keypad keys 0 through 9 |
|
DIK_ADD |
'+' key on numeric keypad |
|
DIK_NUMPADENTER |
Enter key on numeric keypad |
|
DIK_HOME |
Home key (not on numeric keypad) |
|
DIK_END |
End key (not on numeric keypad) |
|
DIK_PRIOR |
Page Up key (not on numeric keypad) |
|
DIK_NEXT |
Page Down key (not on numeric keypad) |
|
DIK_UP |
Up arrow key (not on numeric keypad) |
|
DIK_DOWN |
Down arrow key (not on numeric keypad) |
|
DIK_LEFT |
Left arrow key (not on numeric keypad) |
|
DIK_RIGHT |
Right arrow key (not on numeric keypad) |
|
DIK_INSERT |
Insert key (not on numeric keypad) |
|
DIK_DELETE |
Delete key (not on numeric keypad) |
|
[Close Table] |
In the example program for this lesson, we will be taking a teapot and rotating
using the left and right arrow keys. We place the above if() statements right
where we previously put "index++". This way, instead of index constantly increasing,
it can be controlled by the keys. See the finished program for the code.
Well, we have one last part here and we're done. Let's take a look at the
code:
// this is the function that closes DirectInput
void cleanDInput(void)
{
dinkeyboard->Unacquire(); // make sure the keyboard
is unacquired
din->Release(); // close DirectInput before exiting
}
This function tells DirectInput to give up our access to the keyboard. If
we have exclusive control (which we will sometimes), this is vital, but it should still be called no
matter what kind of control you have.
All this function does is close DirectInput. Just as with Direct3D, DirectInput
must be released, or it will continue to exist after the program has closed.
Well, we did it! We pieced together a program that will detect keys directly
off the keyboard. Now let's look at the whole program and see what happened.
This code is extended from
Direct3D Meshes Lesson 1. All the DirectInput code has been shown
in bold.
What this code does is replace the automatic rotation from before and put it in
control of the left and right mouse buttons.
[
Show Code]
// include the necessary header files
#include <windows.h>
#include <windowsx.h>
#include <d3d9.h>
#include <d3dx9.h>
#include <dinput.h>
// define the screen resolution and keyboard macros
#define SCREEN_WIDTH 800
#define SCREEN_HEIGHT 600
// #define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 :
0)
// #define KEY_UP(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)
// include the DirectX Library files
#pragma comment (lib, "d3d9.lib")
#pragma comment (lib, "d3dx9.lib")
#pragma comment (lib, "dinput8.lib")
#pragma comment (lib, "dxguid.lib")
// global declarations
LPDIRECT3D9 d3d;
LPDIRECT3DDEVICE9 d3ddev;
LPD3DXMESH meshTeapot;
LPDIRECTINPUT8 din; // the pointer to our DirectInput interface
LPDIRECTINPUTDEVICE8 dinkeyboard; // the pointer to the keyboard device
BYTE keystate[256]; // the storage for the key-information
// function prototypes
void initD3D(HWND hWnd);
void render_frame(void);
void cleanD3D(void);
void init_graphics(void);
void init_light(void);
void initDInput(HINSTANCE hInstance, HWND hWnd); // sets up and
initializes DirectInput
void detect_input(void); // gets the current input state
void cleanDInput(void); // closes DirectInput and releases memory
// the WindowProc function prototype
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
// the entry point for any Windows program
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
HWND hWnd;
WNDCLASSEX wc;
ZeroMemory(&wc, sizeof(WNDCLASSEX));
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.lpszClassName = L"WindowClass";
RegisterClassEx(&wc);
hWnd = CreateWindowEx(NULL, L"WindowClass", L"Our DirectInput Program",
WS_OVERLAPPEDWINDOW, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT,
NULL, NULL, hInstance, NULL);
ShowWindow(hWnd, nCmdShow);
// set up and initialize DirectX
initD3D(hWnd);
initDInput(hInstance, hWnd); // initialize DirectInput
// enter the main loop:
MSG msg;
while(TRUE)
{
while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if(msg.message == WM_QUIT)
break;
detect_input(); // update the input
data before rendering
render_frame();
if(keystate[DIK_ESCAPE] & 0x80)
PostMessage(hWnd, WM_DESTROY,
0, 0);
}
// clean up DirectX and COM
cleanD3D();
cleanDInput(); // release DirectInput
return msg.wParam;
}
// this is the main message handler for the program
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
} break;
}
return DefWindowProc (hWnd, message, wParam, lParam);
}
// this function initializes and prepares Direct3D for use
void initD3D(HWND hWnd)
{
d3d = Direct3DCreate9(D3D_SDK_VERSION);
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory(&d3dpp, sizeof(d3dpp));
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.hDeviceWindow = hWnd;
d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
d3dpp.BackBufferWidth = SCREEN_WIDTH;
d3dpp.BackBufferHeight = SCREEN_HEIGHT;
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
// create a device class using this information and the info from
the d3dpp stuct
d3d->CreateDevice(D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,
hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp,
&d3ddev);
init_graphics();
init_light();
d3ddev->SetRenderState(D3DRS_ZENABLE, TRUE);
d3ddev->SetRenderState(D3DRS_AMBIENT, D3DCOLOR_XRGB(50, 50, 50));
d3ddev->SetSamplerState(0, D3DSAMP_MAXANISOTROPY, 8);
// anisotropic level
d3ddev->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_ANISOTROPIC);
// minification
d3ddev->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
// magnification
d3ddev->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
// mipmap
}
// this is the function used to render a single frame
void render_frame(void)
{
d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0),
1.0f, 0);
d3ddev->Clear(0, NULL, D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0),
1.0f, 0);
d3ddev->BeginScene();
// set the view transform
D3DXMATRIX matView;
D3DXMatrixLookAtLH(&matView,
&D3DXVECTOR3 (0.0f, 2.0f, 6.0f), // the camera position
&D3DXVECTOR3 (0.0f, 0.0f, 0.0f), // the look-at
position
&D3DXVECTOR3 (0.0f, 1.0f, 0.0f)); // the up direction
d3ddev->SetTransform(D3DTS_VIEW, &matView);
// set the projection transform
D3DXMATRIX matProjection;
D3DXMatrixPerspectiveFovLH(&matProjection,
D3DXToRadian(45),
(FLOAT)SCREEN_WIDTH / (FLOAT)SCREEN_HEIGHT,
1.0f, // the near view-plane
100.0f); // the far view-plane
d3ddev->SetTransform(D3DTS_PROJECTION, &matProjection);
// set the world transform
static float index = 0.0f; // index+=0.03f;
if(keystate[DIK_LEFT] & 0x80)
index += 0.03f;
if(keystate[DIK_RIGHT] & 0x80)
index -= 0.03f;
D3DXMATRIX matRotateY;
D3DXMatrixRotationY(&matRotateY, index);
d3ddev->SetTransform(D3DTS_WORLD, &(matRotateY));
// draw the teapot
meshTeapot->DrawSubset(0);
d3ddev->EndScene();
d3ddev->Present(NULL, NULL, NULL, NULL);
}
// this is the function that cleans up Direct3D and COM
void cleanD3D(void)
{
meshTeapot->Release();
d3ddev->Release();
d3d->Release();
}
// this is the function that puts the 3D models into video RAM
void init_graphics(void)
{
D3DXCreateTeapot(d3ddev, &meshTeapot, NULL); // create
the teapot
}
// this is the function that sets up the lights and materials
void init_light(void)
{
D3DLIGHT9 light; // create the light struct
D3DMATERIAL9 material; // create the material
struct
ZeroMemory(&light, sizeof(light)); // clear out
the light struct for use
light.Type = D3DLIGHT_DIRECTIONAL; // make the light
type 'directional light'
light.Diffuse = D3DXCOLOR(0.5f, 0.5f, 0.5f, 1.0f); //
set the light's color
light.Direction = D3DXVECTOR3(-1.0f, -0.3f, -1.0f);
d3ddev->SetLight(0, &light); // send the light
struct properties to light #0
d3ddev->LightEnable(0, TRUE); // turn on light #0
ZeroMemory(&material, sizeof(D3DMATERIAL9));
// clear out the struct for use
material.Diffuse = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f);
// set diffuse color to white
material.Ambient = D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f);
// set ambient color to white
d3ddev->SetMaterial(&material); // set the globably-used
material to &material
}
// this is the function that initializes DirectInput
void initDInput(HINSTANCE hInstance,
HWND hWnd)
{
// create the DirectInput
interface
DirectInput8Create(hInstance,
// the handle to the application
DIRECTINPUT_VERSION, //
the compatible version
IID_IDirectInput8,
// the DirectInput interface version
(void**)&din, //
the pointer to the interface
NULL); // COM stuff,
so we'll set it to NULL
// create the keyboard
device
din->CreateDevice(GUID_SysKeyboard,
// the default keyboard ID being used
&dinkeyboard, // the
pointer to the device interface
NULL); // COM stuff, so we'll
set it to NULL
// set the data format
to keyboard format
dinkeyboard->SetDataFormat(&c_dfDIKeyboard);
// set the control
we will have over the keyboard
dinkeyboard->SetCooperativeLevel(hWnd,
DISCL_NONEXCLUSIVE | DISCL_FOREGROUND);
}
// this is the function that gets the latest input
data
void detect_input(void)
{
// get access if
we don't have it already
dinkeyboard->Acquire();
// get the input
data
dinkeyboard->GetDeviceState(256, (LPVOID)keystate);
}
// this is the function that closes DirectInput
void cleanDInput(void)
{
dinkeyboard->Unacquire(); // make sure the keyboard
is unacquired
din->Release(); // close DirectInput before exiting
}
If you run this program, you should get something like this, only it will rotate
with the arrow keys instead of rotating automatically.

Image 1.1 - DirectInput In Action