Reputation: 9361
I need to listen keyboard key states for my tiny application.
#include <windows.h>
#include <fstream>
#include <iostream>
using namespace std;
int main()
{
while(1)
{
if(GetKeyState(VK_SPACE) & 0x80)
{
cout << "Space pressed.\r\n";
DoSpaceKeyTask();
}
if(GetKeyState(OTHER_KEY) & 0x80)
{
cout << "Other key pressed.\r\n";
DoOtherKeyTask();
}
}
return 0;
}
Once I click some keys from my keyboard, these functions has to run once. They're just some tiny tasks for my applications, which is not related in this topic.
My problem is, when I press a key, it executes the functions few times, due to while(1)
looping few times during key press. I cannot use Sleep()
in this case, because it still won't be effective.
I'm looking for a solution like this.
DoSpaceKeyTask()
executes "once."DoOtherKeyTask()
executes "once."I have like 5 keys that I will be using. Could anyone help me on this case?
Ps. If GetKeyState()
function isn't useful on this task, feel free to suggest yours. My function knowledge is pretty limited on C++.
Upvotes: 3
Views: 20486
Reputation: 13
Try sth. like this;
while (!GetKeyState(VK_SPACE) && !GetKeyState('A') == 1) {
//std::cout << "Key not pressed... \n";
Sleep(40);
}
if (GetKeyState('A')) {
std::cout << "\"A \" key pressed... \n";
}
else if (GetKeyState(VK_SPACE)) {
std::cout << "Space key pressed... \n";
}
Upvotes: 0
Reputation:
I know that this is pretty old thread but I still want to share my solution. I think that creating some kind of "flags" or "switches" is really not needed. Here is my code of looping all keycodes:
while (true)
{
for (int i = 0x01; i < 0xFE; i++)
{
if (GetKeyState(i) & 0x8000)
{
std::cout << (char)i << "\n";
while (GetKeyState(i) & 0x8000) {}
}
}
}
As you can see, you can easily just use while and the same GetKeyState function to wait for the key unpress. Much simpler solution.
Upvotes: 0
Reputation:
I think that you will prefer to execute your functions "once" only when you have released up your keys, not when you have depressed (pressed) them down.
You don't need any additional flags and helper variables to define, allocate, assign to 0, and set each one to 1 and reset to 0 and so on, in order to achieve this goal. All what you need is just: First you have to use GetKeyState function inside the scope of while(1) to check when you have depressed a key. When the expression returns true, the executor pointer (the arrow that carry out a code line and then proceeds forward to the next code line when you either step into or step over) will enter into the if statement's scope. Then immediately trap it inside a loop and keep it trapped in there while the key you have depressed is still down and stop it before it is going to execute the function and free it when you have released that key up and then let it to execute the function.
For example, to execute the DoSpaceKeyTask function only "once" when you have depressed and released the space bar, then do the following code that should work:
while (1)
{
if (GetKeyState(VK_SPACE) & 0x80)
{
//The code here executes ONCE at the moment the space bar was pressed
cout << "Space pressed.\r\n";
while (GetKeyState(VK_SPACE) & 0x80) //You can write there ';' instead '{' and '}' below
{
//The executor pointer is trapped here while space bar is depressed and it will be free once space bar is released
}
//The code here executes ONCE at the moment the space bar was released
cout << "Space released.\r\n";
DoSpaceKeyTask();
}
}
Just the same with DoOtherKeyTask function:
while (1)
{
if (GetKeyState(OTHER_KEY) & 0x80)
{
//The code here executes ONCE at the moment the other key was pressed
cout << "Other key pressed.\r\n";
while (GetKeyState(OTHER_KEY) & 0x80) //You can write there ';' instead '{' and '}' below
{
//The executor pointer is trapped here while other key is depressed and it will be free once other key is released
}
//The code here executes ONCE at the moment the other key was released
cout << "Other key released.\r\n";
DoOtherKeyTask();
}
}
If you have already used either BT_'s idea or Pawel Zubrycki's idea, and now you want to use my idea, then you can delete all flags and variables that they suggested, because you don't need them anymore.
By the way, I have already tried the code that Pawel Zubrycki posted, but it doesn't work for me. The output that says that I have pressed either space bar or other key was not displayed when I have really pressed the space bar or other key that I chose.
Upvotes: 1
Reputation: 933
I was having the same issue. I have several keys that act like toggle buttons and only want to register the key events once per press. My solution was to make a simple object to handle the logic. This keeps the main code clean:
class KeyToggle {
public:
KeyToggle(int key):mKey(key),mActive(false){}
operator bool() {
if(GetAsyncKeyState(mKey)){
if (!mActive){
mActive = true;
return true;
}
}
else
mActive = false;
return false;
}
private:
int mKey;
bool mActive;
};
And here is the usage:
#include <windows.h>
KeyToggle toggleT(0x54); // T key toggle
KeyToggle toggleF(0x46); // F key toggle
void main(void)
{
while(true){
if(toggleT) {;} // do something
if(toggleF) {;} // do something
}
}
Upvotes: 0
Reputation: 51
Your functions are called multiple times because of the duration time the button stays pressed. The system is very sensitive. So this is a workaround.
You could do something like this (set a flag that will assign a value when the key is down, and then reasign it when the key is up).
int k=0;//Flag
while(1){
//check if the key was pressed (key_down)
if((GetAsyncKeyState('0') & 0x8000) && (k == 0)){k=1; cout<<"'0' PRESSED."<<k<<endl;}//do your stuff here
//check if the key was released (key up)
else if(GetAsyncKeyState('0') == 0) k = 0;//reset the flag
}
Upvotes: 5
Reputation: 61970
You want a windows hook to hook the game and react to the keyboard input that game gets. Now I haven't really done this specific type of hook, but I can give you a good idea of the flow. I'll leave it up to you to cut down the space by looping through a map of keys you need rather than a huge, repetitive switch, and also to work out any small kinks I put in.
int main()
{
//I imagine the last argument here is the thread
//containing the game's message loop
HHOOK hook = SetWindowsHookEx (WH_CALLWNDPROC, hookProc, NULL, NULL);
//main loop
UnhookWindowsHookEx (hook);
}
LRESULT CALLBACK hookProc (int code, WPARAM wParam, LPARAM lParam)
{
if (code == HC_ACTION)
{
CWPSTRUCT details = *((LPCWPSTRUCT)lParam);
if (details.message == WM_KEYDOWN)
{
switch (details.wParam)
{
case KEY_ONE:
if (last [KEY_ONE] == UP)
{
DoKeyOneStuff();
last [KEY_ONE] = DOWN;
}
break;
}
}
else if (details.message == WM_KEYUP)
{
switch (details.wParam)
{
case KEY_ONE:
last [KEY_ONE] = UP;
break;
}
}
}
return CallNextHookEx (NULL, code, wParam, lParam);
}
Note how I use last [KEY_ONE]
. I would recommend using an std::map
to store keys you need by their vk code. Then you can just loop through the map and cut down a lot of space that a switch would take.
Upvotes: -1
Reputation: 2713
Try this approach:
#include <windows.h>
#include <fstream>
#include <iostream>
using namespace std;
int main()
{
char lastSpaceState = 0, lastOtherKeyState = 0, spaceState, otherKeyState;
while(1)
{
spaceState = (GetKeyState(VK_SPACE & 0x80) != 0);
lastSpaceState = (spaceState && !lastSpaceState);
if(lastSpaceState)
{
cout << "Space pressed.\r\n";
DoSpaceKeyTask();
}
otherKeyState = (GetKeyState(OTHER_KEY) & 0x80 != 0);
lastOtherKeyState = (otherKeyState && !lastOtherKeyState);
if(lastOtherKeyState)
{
cout << "Other key pressed.\r\n";
DoOtherKeyTask();
}
}
return 0;
}
or as Chris suggest in OP comment more "modern" async approach.
Upvotes: 0