anonymous anonymous - 7 months ago 26
C Question

Polling the Keyboard in C and Win32 api

This code isn't doing what it supposed to do.

enter image description here

When I press cursor keys once, the menus jump more than once.

How can I solve this issue?

.

Relevant Source Code

#include <windows.h>
#include <conio.h>

BOOL IsKeyPressed(KEY_CODE key)
{
if (GetAsyncKeyState(key) < 0)
{
return TRUE;
}
else
{
return FALSE;
}
}

int select_menu(char**menus, int x, int y, int items_count)
{
int selection = 0;
show_menu(x, y, menus, items_count);
gotoxy(x, y);

while (1)
{
if (IsKeyPressed(vk_Down))
{
int xx = wherex();
int yy = wherey() + 1;

if ((yy - x + 1) > items_count)
{
gotoxy(x, y);
}
else
{
gotoxy(xx, yy);
}
}
if (IsKeyPressed(vk_Up))
{
int xx = wherex();
int yy = wherey() - 1;

if (yy < y)
{
gotoxy(x, y + items_count);
}
else
{
gotoxy(xx, yy);
}
}
if (IsKeyPressed(vk_Return))
{
int xx = wherex();
int yy = wherey();

selection = yy - y + 1;
}
}

return selection;
}


Ref:

https://sourceforge.net/projects/conio/

Answer Source

You have a software bounce.

From the Microsoft page. GetAsyncKeyState:

Determines whether a key is up or down at the time the function is called, and whether the key was pressed after a previous call to GetAsyncKeyState.

The function return provides two pieces of information:
1. instantaneous state of the key.
2. historical state of the key (since a previous call).
You should be aware of, and use both to make your decisions.

To prevent bouncing, take a look at the description of the return value:

If the function succeeds, the return value specifies whether the key was pressed since the last call to GetAsyncKeyState, and whether the key is currently up or down. If the most significant bit is set, the key is down, and if the least significant bit is set, the key was pressed after the previous call to GetAsyncKeyState. However, you should not rely on this last behavior; for more information, see the Remarks. [emhpasis mine]

For example, you could write your functions specifically for each condition:

BOOL IsKeyPressed(KEY_CODE key)
{
    if(0x80000000 & GetAsyncKeyState(key))//most significant bit is high
    return TRUE;                          //key IS down
    else return FALSE;
} 

BOOL WasKeyDown(KEY_CODE key)
{
    if(0x00000001 & GetAsyncKeyState(key))//least significant bit is high
    return TRUE;                          //key WAS down
    else return FALSE;
} 

Sometimes it may be necessary to look at the history and current state at the same time you are monitoring for multiple key presses. This is an example that was once used to define a set of keys that would kill an application:

void KillThisApp(void)
{
    if ((0x80000000 & GetAsyncKeyState(VK_CONTROL)) || 
        (0x00000001 & GetAsyncKeyState(VK_CONTROL))) 
    {
        if ((0x80000000 & GetAsyncKeyState('k')) || 
            (0x00000001 & GetAsyncKeyState('k')) ||
            (0x80000000 & GetAsyncKeyState('K')) ||
            (0x00000001 & GetAsyncKeyState('K')) ) 
        {
            if((0x80000000 & GetAsyncKeyState(VK_SHIFT)) || 
               (0x00000001 & GetAsyncKeyState(VK_SHIFT)))
            {    
                gRunning = FALSE;
            }
        }
    }
}

This version does not care whether key is down, or was down, but could easily enough be edited to require all keys are down at the time of the cal by looking only at the high bit during each call.