Input for Modern PC Games (AltDevBlogADay)

| 0 Comments | 0 TrackBacks

Keyboard Question MarkAs I said at the end of my last post, I was going to write an article listing all the little handy utilities/settings that make my life easier as a programmer, but in a change in schedule I've decided to postpone that and write about something I've been coding this week instead whilst it's still fresh in my mind.

As I'm writing my own engine, one of the first things that needs to be done is input processing. In the latest DirectX SDK, there are two options listed for input, namely DirectInput8 and XInput. DirectInput8 is long in the tooth, being over a decade old and has localisation issues as I'll discuss below. XInput is newer and is a cleaner API, but seems to exist entirely for using Xbox 360 controllers (presumably as an aid to porting) and supports nothing else. Neither seemed to be an ideal solution to me.

Twitter to the Rescue

I asked about this on Twitter and got a few replies suggesting I use the Raw Input API which I wasn't previously aware of. Richard Sim pointed me to this MSDN articlecomparing the APIs (in this case the example is reading mouse input). As you can see, the code for using Raw Input is a lot cleaner and simpler than DirectInput8. The article also says that DirectInput8 is built on top of the Raw Input API and uses a separate thread to capture the WM_INPUT messages, adding overhead in the process. It felt to me that using Raw Input directly was the better solution.

Using Raw Input

The MSDN documentation for the Raw Input API is missing a few things which I've had to dig around and find out for myself, so I'll explain what I've found out here. The basic process is that in your initialisation code you register the devices you'd like input from using RegisterRawInputDevices(), and then you receive WM_INPUT messages from the device in the Windows message loop from which you extract the actual keys/mouse input/etc. If you want to register for input from the keyboard, you would use the following code.

RAWINPUTDEVICE keyboard_device;
keyboard_device.usUsagePage = 0x01;
keyboard_device.usUsage = 0x06;
keyboard_device.hwndTarget = hWnd;
keyboard_device.dwFlags = 0;

BOOL ret = RegisterRawInputDevices(&keyboard_device, 1, sizeof(RAWINPUTDEVICE));

The question is - where on Earth do those UsagePage and Usage numbers come from? The MSDN documentation doesn't explain, but after a bit of digging I found out that they're part of the USB HID standard (pdf). On page 26 is table 1 for generic desktop devices, and the keyboard is device number 6, hence the numbers passed to the code above. For the mouse you would use page 1, usage 2. In fact, the Raw Input API lets you register multiple device at once as shown here.

RAWINPUTDEVICE keyboard_and_mouse_devices[2];
keyboard_and_mouse_devices[0].usUsagePage = 0x01; // Generic desktop page
keyboard_and_mouse_devices[0].usUsage = 0x06;     // Keyboard
keyboard_and_mouse_devices[0].hwndTarget = hWnd;
keyboard_and_mouse_devices[0].dwFlags = 0;
keyboard_and_mouse_devices[1].usUsagePage = 0x01; // Generic desktop page
keyboard_and_mouse_devices[1].usUsage = 0x02;     // Mouse
keyboard_and_mouse_devices[1].hwndTarget = hWnd;
keyboard_and_mouse_devices[1].dwFlags = 0;

BOOL ret = RegisterRawInputDevices(keyboard_and_mouse_devices, 2, sizeof(RAWINPUTDEVICE));

I have no idea why the API insists you pass the size of the RAWINPUTDEVICE struct as the final parameter, though it seems to be a common trait for Win32 API functions.

I think the MSDN docs explain actually getting the data well enough, so I won't repeat that here.

Once your application is registered to recieve WM_INPUT messages, it will also receive WM_INPUT_DEVICE_CHANGE messages whenever a device is added/removed from the system so you can print up "Controller Removed" messages, or switch to an alternate controller as your game requires.

Wrapping Up

I mentioned above that DirectInput8 has localisation issues. What I mean by this is that often in games you need to show the name of the key on screen in control select screens or tutorials. To do this you would use GetKeyNameText() which will return a string from a scan code. Unfortunately, DirectInput8 uses its own DIK_ enums for the keys, which don't exactly map onto scan codes. On previous games I've worked on, we've ended up with a large remapping table, with a few exceptions for various locales. The Raw Input API gives you the scan code directly as well as the virtual VK_ enum, so in theory this problem disappears (I still need to confirm this).

This is a crosspost from AltDevBlogADay - 

No TrackBacks

TrackBack URL:

Leave a comment

About this Entry

This page contains a single entry by KeefJudge published on April 25, 2011 10:57 AM.

Workstation Set Up for Game Developers (AltDevBlogADay) was the previous entry in this blog.

So You Want To Be A Graphics Programmer... (AltDevBlogADay) is the next entry in this blog.

Find recent content on the main index or look in the archives to find all content.