Getting True Keyboard Input into your XNA Games

One of the things that PC games often need is a form of in-game keyboard text input. In my case, I needed a login screen that allows the user to type in their username and password before the game continues on. It turns out that this process is more complicated in XNA than I would have wished, so I post my solution here in hopes that it may help others with a similar need.

The first solution that comes to mind is to use the KeyboardState.GetPressedKeys() method to get all the keys currently pressed on the keyboard. Running this each frame will, in theory, enable us to get all the keys the player pressed, right? While this seems simple enough, this method will not work for several reasons. Firstly, GetPressedKeys() is not buffered, meaning if you type keys faster than the update rate of your game you will get missing keys. In simple terms, a user typing faster than 60wpm (if your game runs at 60fps) will have issues. Another issue is that the return array does not give any indication of the order in which keys are pressed, so that gives you no idea on how to construct the final string if two or more keys are pressed simultaneously. The biggest problem is that you have to do all the key to char translation yourself. GetPressedKeys does not return a nice char or string array that contains the actual value you typed. No, it returns an array of the Keys enumeration. This forces the programmer to either build a huge Dictionary that maps each Key to a character value, or build a huge switch(Keys) statement that checks through some 50+ odd cases and handles each one. But even with all this work this method will not function, because Keys does not differentiate between case, meaning you have to handle additional translation if you want the character to be lowercase. Add that to the fact that the Shift key combinations (e.g. Shift+1 = !) and localization are not supported leaves you in some seriously rough waters.

That method being wrought with issues, we have to look for another method to resolve the problem. The next logical solution is to hook into the KeyDown, KeyPress, and KeyUp events on the XNA form. Alas, this too is filled with issues. For one, the XNA form will never get the KeyDown or KeyPress events. It appears that the form eats them up before you can use them. The other issue is that the KeyUp event passes a Keys enum…which gives us the same translation and localization problems as using the KeyboardState.GetPressedKeys() method.

So…where can we go from here? After doing some research I heard mention of the IMessageFilter interface. This interface allows messages to be intercepted before they are dispatched to the form, meaning we can grab the KeyDown and KeyPressed messages before the form devours them. Better yet, the messages are not transformed into the Keys enum, thus we can get the true translated key (WM_CHAR) that the user pressed as a character.

There is another alternative that involves creating a low level keyboard hook using SetWindowsHookEx and grabbing the key events from there, but that involves more pInvoked native calls and more complexity in general then the solution I came up with.

The solution I ended up using involves a bit of unsafe code and a pInvoked call to the user32.dll function TranslateMessage. I would have preferred to avoid native or unsafe calls, but I couldn’t get anything to work without them. The basic rundown is this: there is a classes called KeyFilter that inherits from the IMessageFilter interface. This class watches for two Window Message Code‘s. The first, 0×0100, is a key event. If the message has this ID we use the Marshal class to grab us some unmanaged memory, store the message in a pointer, and pass the message to the unmanaged TranslateMessage function. This function will translate the message into the true character that was pressed and post the 0×0102 (WM_CHAR) message to the form’s message queue. The WM_CHAR message happens to be the second message that KeyFilter looks for. Stored in this message’s WParam is the transformed char we have been wanting all along! All that needs done is to convert the thing from an unsigned to a char with a simple cast and we can use it!

So that is basically the process necessary to get true Keyboard input into your XNA game. All that needs done now is to set up some events in a class that will fire when a new character is inbound. I wrote a sample that goes along with this post. You can find it here.

facebooktwittergoogle_plusredditpinterestlinkedinmailfacebooktwittergoogle_plusredditpinterestlinkedinmail

3 Thoughts.

  1. >I'm using the same technique in my Nuclex.Input library.

    Originally, I subclassed the window (via .NET's NativeWindow class) and read WM_CHAR from there, but it turned out that some of the more exotic key combinations (like the German 'Alt Gr' key) were not handled correctly (seemingly TranslateMessage() is the culprit).

    Here's my implementation of the IMessageFilter, if interested: https://devel.nuclex.org/framework/browser/Nuclex.Input/trunk/Source/WindowMessageFilter.cs

    It's handling mouse and other keyboard messages, too, because my library also does event-based input (eg. this is the interface for the keyboard: https://devel.nuclex.org/framework/browser/Nuclex.Input/trunk/Source/Devices/IKeyboard.cs) and that was the approach with the least overhead ;)

  2. “In simple terms, a user typing faster than 60wpm (if your game runs at 60fps) will have issues.”

    This is plain wrong. While you have the right idea that typing too fast will cause issues, your math is wrong. WPM is words per Minute, and fps is frames per Second. 60 wpm is 1 word per second. According to Wikipedia, a word for the WPM measurement is defined as 5 characters or keystrokes. That leaves us with 5 keys per second. At 60 frames per second, you won’t have any issue. A user would have to type at 720 wpm to have an issue.

    • Ahh, thanks for the correction Dan. Different bases always gets me mixed up. It was somewhat of a poor example for me to use the term words per minute. I should have instead expressed it at the rate at which the user can press and release a key.

      Let me elaborate.

      Let’s say that instead of running at 60 updates per second the game is running at a measly 10 updates per second. Via your math, 10 updates per second is 10 chars per second or two words per second (using the data you provided) or 120 wpm, so, in theory, it should work for almost everybody. But it does not. The problem, seemingly, is that XNA does not buffer keystrokes. If you press and release a key faster than what the game can update (in this case 100 milliseconds) XNA will simply never know you pressed it. Same thing when running at 60ups. If you press and release a key faster than 16ms XNA will know nothing of it.

      Most of the time involved in typing is the process of moving the finger from one key to the next, not actually pressing and releasing the key. I can easily press and release a key in under a tenth of a second, which leads to real problems when creating an in-game text input field. Though the problem doesn’t happen quite as often when running at 60ups, I have, in practice, ran into it there as well. The IMessageFilter fixed this issue (as well as all the other nasties mentioned in the post.)

      I’ve whipped up a quick sample to more clearly display the underlying issue at hand.

      You can grab it here:
      http://www.mediafire.com/?vthtalunw2cvnii

Leave a Reply