What is the fastest way to determine a key press and key holding in Win32?

33,872

Solution 1

GetAsyncKeyState() is what you're looking for. It reads the physical state of the keyboard, regardless of the input queue state. If the high-bit is set, then the key was down at the time of the call.

// Fetch tab key state.
SHORT tabKeyState = GetAsyncKeyState( VK_TAB );

// Test high bit - if set, key was down when GetAsyncKeyState was called.
if( ( 1 << 15 ) & tabKeyState )
{
    // TAB key down... 
}

Also, for the record, Windows is not a real-time operating system. If your application requires real-time precision, you may want to select another platform.

Solution 2

If you just want to poll the keyboard state so as to discover which keys are up/down as well as the shift/alt/ctrl state, just call GetKeyboardState (MSDN reference).

When I worked in a game studio, this is exactly how we got keyboard state for each frame. Should be applicable to your simulation code.

Solution 3

TL;DR: you can use GetAsyncKeyState for checking if a key is currently down, but for best application responsiveness to key presses and releases, you want to use the Win32 pipeline code near the bottom of my post.

GetAsyncKeyState works perfectly fine for determining if a key is currently down, but in terms of determining whether a key was first pressed or released and how many times this was done, GetAsyncKeyState misses keystrokes in a CPU-intensive application, even after storing the previous key state.

This was what I tried:

static const unsigned int NumberOfKeys = 256U;

bool previousKeyboardState[NumberOfKeys];

//Get the current state of each key as the application starts to ensure that keys held down beforehand are not processed as pressed keys.
for (unsigned int keyNum = 0U; keyNum < NumberOfKeys; ++keyNum)
{
    previousKeyboardState[keyNum] = isKeyDown(keyNum);
}

//Works fine.
bool isKeyDown(int key)
{
    return (GetAsyncKeyState(key) & (1 << 16));
}

//Misses key presses when application is bogged down.
bool isKeyFirstPressed(int key)
{
    bool previousState = previousKeyboardState[key];

    previousKeyboardState[key] = isKeyDown(key);

    return (previousKeyboardState[key] && !previousState);
}

//Misses key releases when application is bogged down.
bool isKeyFirstReleased(int key)
{
    bool previousState = previousKeyboardState[key];

    previousKeyboardState[key] = isKeyDown(key);

    return (!previousKeyboardState[key] && previousState);
}


//Example usage:

if (isKeyDown(VK_W))
{
    //W key.
}

if (isKeyFirstReleased(VK_SNAPSHOT))
{
    //Print screen.
}

GetKeyboardState is no good either, as it does not keep track of the number of key presses or releases. As Erik Philips said in his answer, these are unbuffered solutions, which are no good if you are e.g. writing a game. You would have to process all keystrokes faster than they are received.

Now, my code above works decently well, and may be suitable for many people, but I much prefer not to miss a single keystroke. I hate using applications that are unresponsive. I think the best solution for Win32 applications is to catch WM_KEYDOWN and WM_KEYUP messages in the pipeline and process them. What's nice is that WM_KEYDOWN also provides an auto-repeat count, which could be useful for applications that support entering text (e.g. chat, IDE's, etc.). This also adds a slight complication, which is mentioned in the WM_KEYDOWN documentation:

Because of the autorepeat feature, more than one WM_KEYDOWN message may be posted before a WM_KEYUP message is posted. The previous key state (bit 30) can be used to determine whether the WM_KEYDOWN message indicates the first down transition or a repeated down transition.

There are also Windows keyboard hooks you could look into, but those are more difficult to use. They're good for receiving global key presses though.

Solution 4

Considering that all inter-windows communications are through windows messaging (keyboard events, mouse events, pretty much all events you can imagine), there isn't a lower level way to access the keyboard events (unless you write your own keyboard driver) that I know of.

DirectX still uses the windows keyboard messaging to provide DirectX programmers easier access to keyboard events.

Updated

My note about DirectX was not to use it, but that when Microsoft wanted to make an interface for programmers to use for real time games, they still wrote DirectX on top of the Windows Message Queue.

I would suggest taking a look at how to write a program that can read directly from the message queue. I believe there is a good example Code Project Windows Message Handling - Part 1.

Your two options are to either read from the message queue (buffered) or read directly from the keyboard state (as Bukes states) which means your own loop could techinically miss a keyboard event for any number of reasons.

Share:
33,872
judeclarke
Author by

judeclarke

Updated on March 17, 2020

Comments

  • judeclarke
    judeclarke about 4 years

    What is the fastest way to determine a key press and also how to determine if a key is being held? It appears that window messaging is slow. Please provide an example of how to do so, and why it is faster than an alternative.

    To be clear, this for a real time loop (a simulation) so I am looking for the fastest way to determine if a key has been pressed and also to check to see if it is being held.

  • judeclarke
    judeclarke over 12 years
    If by DirectX, you mean DirectInput, it has been deprecated. If by DirectX you mean XInput, there has been numerous sources saying to use raw input over XInput\DirectInput. There are alternatives to those and messages, such as GetKeyState and GetKeyboardState, however, I am not sure if those are the best solution.
  • judeclarke
    judeclarke over 12 years
    Can you please provide an example of its usage?
  • Bukes
    Bukes over 12 years
    Updated with sample, as requested.
  • Saurav Seth
    Saurav Seth over 12 years
    Update. I'm likely going to downvote my own answer in favor of @Bukes response below. According to MSDN for GetKeyboardState: An application can call this function to retrieve the current status of all the virtual keys. The status changes as a thread removes keyboard messages from its message queue. The status does not change as keyboard messages are posted to or retrieved from message queues of other threads.
  • Saurav Seth
    Saurav Seth over 12 years
    With the above in mind, if you are pumping messages (GetMessage and DispatchMessage on each frame of your simulation, then you might be OK to use GetKeyboardState. Otherwise, GetAsyncKeyState.
  • judeclarke
    judeclarke over 12 years
    After doing numerous tests, it appears this is exactly what I was looking for. This is definitely more responsive than simple window messaging works great. Thank you for the help.
  • judeclarke
    judeclarke over 12 years
    Thank you for the help, I was pumping messages on each frame. I ended up picking Bukes answer, but you were definitely helpful (so I upvoted, as yours is also a very likely scenerio for someone else possibly).
  • Andrew
    Andrew almost 9 years
    This works excellently for checking the current state of the key, but it misses key strokes when checking for key presses and releases. See my answer for more info.
  • Jesse Chisholm
    Jesse Chisholm about 7 years
    Um. test high bit would be if ( ( 1 << 15 ) & tabKetState )
  • Jesse Chisholm
    Jesse Chisholm about 7 years
    Just saying: a SHORT is 16-bits, (1 << 16) cast as a SHORT is zero. Don't you mean (1 << 15) to mean the high bit of a SHORT?
  • Andrew
    Andrew about 7 years
    Are you sure a SHORT is 16 bits?
  • Jesse Chisholm
    Jesse Chisholm about 7 years
    By definition. Unless someone does something silly, like typedef long SHORT;
  • Andrew
    Andrew about 7 years
    By definition for "short", yes, but not for SHORT. SHORT is not a language type, it's a typedef, "by definition". I'm thinking my "SHORT" that I was using may have actually been a long.
  • Jesse Chisholm
    Jesse Chisholm about 7 years
    To be safe, use (1 << (sizeof(SHORT)*8-1)).
  • Andrew
    Andrew about 7 years
    Probably pointless, because it's probably all hardcoded into the Windows API.
  • Jesse Chisholm
    Jesse Chisholm about 7 years
    Agreed ... unless someone does something silly ... (see above). But "never assume" as a paradigm suggests using sizeof whenever there is a chance for someone else's silliness to bite you. A litle extra typing, the compiler deals with the constants for you, still safe if the reality of SHORT changes.
  • Jesse Chisholm
    Jesse Chisholm about 7 years
    And if your SHORT is a long then you didn't use the hard coded Windows API definition.
  • KulaGGin
    KulaGGin over 3 years
    XInput is just cringe, don't ever use it, unless you want your game to be cringe, too. DirectInput is old and limited. RawInput is the way to go. Fast, events, you won't lose inputs, works with all devices.
  • KulaGGin
    KulaGGin over 3 years
    You can miss inputs with GetAsyncKeyState calls. For example, let's say at the start of a game iteration working at 60 Hz you do a call to GetAsyncKeyState and key is not pressed. So far so good. Then 5 ms later you press a key, let's say VK_TAB, and hold it for 5ms. Then at the start of the next game iteration(about ~6.67ms later) you call GetAsyncKeyState again. But the key is not pressed again. And from the game's perspective it was never pressed! It might look like a too far reaching, but it's not. I played games which use this and miss inputs at 60 FPS. Frustrating and unnecessary.
  • KulaGGin
    KulaGGin over 3 years
    What's the solution? Raw input. Message-based, fast, efficient, no chances of missing inputs. That's why you see video games implement raw input for mouse movement. Not for everything else because they're lazy.