Initialization of DirectX-based components is usually a pretty technical task. That means there won't be much theory in this tutorial. I will show you how to initialize and use a Direct Input interface in this tutorial. Both keyboard and mouse input are covered. The source code combines files: dierr.h, dierr.cpp, directinput.h and directinput.cpp:

I always think it's more convenient to look at the header file of a particular code block first and only then read the contents of the actual code (the c, cpp files). So here's directinput.h, it contains all of the main Direct Input function definitions and some structures:

The #defines MOUSEL, MOUSER and MOUSEM define the constant values for the left, right and middle mouse button controls. The mouse_t structure defines all variables needed to utilize the mouse controls. The keyboard_t struct defines the 256-key array that will be used to identify a key stroke.

The functions defined by I_name are commented and should be self-explanatory. We will look at the contents of each in a while. For now, lets look at lpdi, lpKeyboard and lpMouse. lpdi is the main Direct Input interface, it should be first initialized (which is done within I_Init) and destroyed at the end of your program (this is accomplished with a call to I_Shutdown). lpKeyboard is a Direct Input device.

A DirectInput (DI) device is simply an interface for defining an input system. There could be keyboard, mouse and joystick DI input devices. In this tutorial we're only concerned with keyboard and mouse input. The DI input device that describes the mouse controls is lpMouse. Each input device must be created. Also, you have to set cooperative level for each input device, set its data format, and finally acquire it.

These four steps for each of the two input devices (mouse, keyboard) are accomplished within I_Init. If, during the execution of your code, an input device is interrupted (this could happen when switching between windowed and full-screen modes or when your application conflicts with another one which also uses Direct Input), it needs to be reaquired. And that's exactly what I_ReaquireKeyboard(void) and I_ReaquireMouse(void) functions are for.

After an input device is created and acquired, you can start getting input from it. For the described in this tutorial mouse and keyboard input interfaces, this is accomplished by calling I_GetMouse(void) and I_GetKeyboard(void).

To get both simultaneously, call the I_Get(void) command. I_DeviceInit(void) is called to reset all parameters of the used mouse and keyboard structs (mouse_t mse; and keyboard_t key;) to 0, to make sure that all mouse buttons and all keys on the keyboard are set to unpressed state.

Before we step right into the Direct Input code, I want to mention how Direct Input is handled in case of an error during initialization or during a call to a Direct Input command. The files dierr.h and dierr.cpp take care of this functionality. You see, when a call to a Direct Input command is made, in case of an error a specific function writes the error code out into the system log file describing the error encountered as that command was executing.

The reason for this is that the code would start to look extremely unreadable if we included all error-code variations within the I_Init(void) function itself. dierr.h and dierr.cpp makes it all look a lot cleaner. dierr.cpp is rather lengthy, as it contains functions to output all possible Direct Input error codes for almost every Direct Input function we will be using. However, we'll take a look at dierr.h:

The Direct Input interface provides the programmer with some generic functions. These functions manage the DI interface. For example the DirectInputCreate function creates the DI interface.

The function CreateDevice is called to create a DI device, and so on. Each of these functions usually returns DI_OK. But when they don't, that means there is an error. In that case, dierr.h and dierr.cpp come to help. When a DI function fails, execution falls back to one of the functions specified in these modules.

These functions define the "error-code" versions of each generic DI command. So for example, if the function DirectInputCreate fails with a return value that doesn't match DI_OK, we will use the dierrCreate function to find out what the error code actually was. The return value of dierrCreate is a C, NULL-terminated string. This string is then passed to the LogErr(...); function which logs it to the system log file(the LogErr(...) function is explained in tutorial c & misc. - 1).

The program then exits and you are able to look up what the problem was in the system log file and then try to fix the situation. But that's all in case of an error, which shouldn't be the case(perhaps, unless DirectX isn't installed on the machine you're running this code on).

Every separate DirectX component must be first initialized. Here, we're dealing with Direct Input, so let's see what we need to do in order to initialize Direct Input. Direct Input initialization consists of creating the DI interface by calling the generic function DirectInputCreate.

The parameters of this function are the windows instance handle (hinstance), the version of DI you are initializing (DIRECTINPUT_VERSION; could be anywhere from 0x0300 to 0x0700, in this case 0x0700) and the address of the DI object you are initializing (and that is the object already described above, defined as LPDIRECTINPUT lpdi; (remember this is the main DI interface object throughout the code).

After the DirectInput interface object is successfully created, we have to use it to create the keyboard and mouse input devices. As already mentioned for each input device to be operational, it must be first created, then you have to set its cooperative level, then you have to set its data format and finally acquire it. And this is exactly what we're doing in the remaining part of I_Init. Take a look:

Creating the Input Device. Two interface devices are created after the main DI object (lpdi) is initialized. Note, to create an input device, the CreateDevice function is used which is a member of the initialized DI object.

To create a keyboard device, pass GUID_SysKeyboard to CreateDevice(...) as the first parameter. To create a mouse device, GUID_SysMouse is used as the first parameter. These GUIDs are already defined by DI.

The second parameter is the address of the device that will be used to obtain input. As previously noted, the keyboard device is defined as lpKeyboard. The mouse device is defined as lpMouse. The third parameter of CreateDevice is not used, pass NULL to it.

Setting Cooperative Level. Setting cooperative level of a DI input device is accomplished with the SetCooperativeLevel member function of the device object. The window handle(HWND hwnd) is passed as the first parameter, but the key points here are the flags used to set cooperative levels of both input devices (keyboard and mouse).

DISCL_BACKGROUND and DISCL_NONEXCLUSIVE are OR-ed together. As DI documentation states, DISCL_BACKGROUND is used when the application requires "background access". If background access is granted, the device may be acquired at any time, even when the associated window is not the active window. What this basically means is that input can be aquired by your program even if its not currently active. Your program could be running in background but input would still be taken from the device you're setting this flag to.

DISCL_NONEXCLUSIVE means that the application requires non-exclusive access. Access to the device will not interfere with other applications that are accessing the same device. You see, we would have used DISCL_FOREGROUND (as opposed to DISCL_BACKGROUND) but if we do, each time our OpenGL window becomes inactive, the input devices are automatically unacquired (and lost). Just for the purpose of these tutorials we're making things simple.

However, if you're using this code for your own purposes, such as a game you're developing, you could specify the DISCL_FOREGROUND flag here. But then you will have to take care of reacquiring the input device interfaces, of course. Note also, from DI documentation: Applications must specify either DISCL_FOREGROUND or DISCL_BACKGROUND; it is an error to specify both or neither. Similarly, applications must specify either DISCL_EXCLUSIVE or DISCL_NONEXCLUSIVE.

The final two steps in creation of both input devices are setting data format and acquiring the device. These steps should be easy to understand. Look up more information about these processes in the MSDN documentation if you need to. Once the device is acquired, it's ready to be used by your program. How to get keyboard and mouse input from the two acquired devices is explained below.

Getting Keyboard and Mouse Input

So you've initialized Direct Input and acquired both the mouse and keyboard input devices. How exactly do we get input from these devices? First, I will explain keyboard input. The functions that manage input from the keyboard are I_GetKeyboard and I_ReacquireKeyboard. Let's take a look at the code:

To get input from keyboard, in your main loop, you would simply call I_GetKeyboard. What it does is it calls the GetDeviceState member function of the keyboard device and if the device happened to be unacquired it tries to reacquire it.

Note that I pass the keyboard array(containing 256 entries) key.state to GetDeviceState as the last parameter. If Direct Input identifies a key stroke, it places a value other than 0 into one of the keyboard array entries(this array is defined by the keyboard_t key struct).

For each of the 256 keyboard array entries DI defines a keyboard device constant. For example to specify the Escape key, DI defines it as DIK_ESCAPE. To specify the enter key, DIK_RETURN is defined. So then, to see if the Escape key has been pressed, you would code something like this:

Similarly, the mouse input relies on two functions I_GetMouse and I_ReacquireMouse. The I_GetMouse function is a bit more involved than I_GetKeyboard, it checks for a single-click (a pin click) and also constrains the mouse x and y coordinates by a bounding box of the screen (the code for this is commented and it's optional):

The mse.pin variable works to identify a single left-button mouse click. The way it works is first you would call I_GetMouse. I_GetMouse checks if the left mouse button has been pressed.

If that's the case, mse.pin is set to 1 and you can test this value in the remaining block of your code to identify a single "pin" click. mse.pin is set to 1 only for the next code execution "run". It is automatically reset to 0 again, even if you keep holding the left mouse button down.

This functionality is very useful when developing a GUI system. Sometimes, you don't want the left mouse button to be constantly active.

To conclude this tutorial, here is how you would get input by using the Direct Input library presented here: