Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 619
judeclarke Avatar asked Dec 27 '11 00:12

judeclarke


4 Answers

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.

like image 116
selbie Avatar answered Oct 16 '22 06:10

selbie


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.

like image 44
Bukes Avatar answered Oct 16 '22 05:10

Bukes


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.

like image 3
Andrew Avatar answered Oct 16 '22 05:10

Andrew


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.

like image 2
Erik Philips Avatar answered Oct 16 '22 05:10

Erik Philips