How to get just the moment a key was pressed with GetKeyState - c++

Using GetKeyState, I can do some task when a key is pressed. However, if I have if (GetKeyState(VK_UP) & 0x80), it returns true the entire time the key is being held.
How do I handle this if I just want this to be true the exact moment the key is pressed (so the contents of the if statement don't run several times)?

You can use a bool flag = false and use it like this:
if((GetKeyState(VK_UP)&0x80) && ! flag)
{
flag = true;
}
else if((GetKeyState(VK_DOWN)&0x80) && flag)
{
flag = false;
}
if(flag) //key is just pressed
{
//TODO: Your handling here
}

Remember what the state was last time through the loop, and only execute your code if the key was up before and is now down.
Also, you seem to be checking if the high bit is set by masking the result with 0x80, but SHORT is two bytes. I think you should be using 0x8000; alternatively, you can check if the result is < 0, since the high bit indicates whether a signed integer is negative or not.
The code would look something like this:
bool prevUpKeyState = false; // false indicates key was up
while (...) {
// ...
bool currentUpKeyState = GetKeyState(VK_UP) < 0;
if ((currentUpKeyState) && (!prevUpKeyState)) {
// Key just depressed, do stuff
}
// ...
// Update previous key state for next pass through loop:
prevUpKeyState = currentUpKeyState;
}

I know this thread is old and all, but i approached this a bit different than the others and thought of sharing my solution here.
bool previousKeyDown = GetKeyState(VK_...) & 1;
while (true) {
bool keyDown = GetKeyState(VK_...) & 1;
if(keyDown != previousKeyDown) {
previousKeyDown = f6Down;
}
}
Here i am using the toggle bit to exploit the toggle behaviour of usually non toggleable keys.
Its nice and short.

Related

Arduino - Measuring the time interval between one button press and release - Add Velocity to MIDI Keyboard

I hope you are doing all really well, I am trying to make a MIDI Piano Keyboard, Pretty basic I press a key the MIDI signal is send and the sounds comes.
But I want to add the velocity to the my keys, there are one contacts per key (the keyboard I am using is Fatar Keyboard).
I need to calculate the time interval between the first contact and then the second contact ( Circuit Diagram is attached below).
All the keys are in set as input pull_up
when a key is pressed it goes low … Of course
Mentioned Below is the function where I am reading the keys. what do I need to do to get the following situation done
[they are 49 keys which are arranged in to two diode matrices. There are actually 98 switches in the matrix. The reason for this is that there are two switches under each key. When a key is pressed, one of the switches closes slightly before the other one. By measuring the time of flight between the switches we can derive the velocity]
Situation 1
Key is pressed
Start Time
Time for how long its pressed
Key Released
Code
void read_keys() {
for (uint8_t key = 0; key < 49; key ++) {
digitalWrite(output_main[key], LOW); //turn off output main key
if (digitalRead(input_pullup[key]) == LOW) {
//check main input key is presses
//check with key_activated array
firstcontactdownmills = millis();
Serial.println(key);
Velocity = map(currentmills - firstcontactdownmills, 0, 256, 127, 0);
if (key_activated[key] == 0) {
//send midi on command
my_midi.sendNoteOn(key + note_offset, Velocity, 1);
main_midi.sendNoteOn(key + note_offset, Velocity, 1);
//update array
key_activated[key] = 1;
}
}
else { //if key released
//check with key_activated array
if (key_activated[key] == 1) {
//send midi off command
my_midi.sendNoteOff(key + note_offset, 0, 1);
main_midi.sendNoteOff(key + note_offset, 0, 1);
//update array
key_activated[key] = 0;
}
}
digitalWrite(output_main[key], HIGH); //turn on output main key
}
}
Circuit Diagram of the Keyboard
You can add a state variable to your keys, that keeps track of where your key is. You can then start the timer at the transition from not_pressed to half_pressed. You then evaluate the velocity at a transition from half_pressed to full_pressed.
You should also add a timeout that resets it back to unpressed in case a key press is missed.
But I am not sure if your loop will be fast enough after adding this kind of logic.
Here's an idea that assumes that if a keyboard player is holding down a key, the contact pin will stay LOW and that there will be 3 interesting state changes
First HIGH->LOW : First contact - record the current time using millis()
Second HIGH->LOW : Second contact - calculate velocity and send key on.
Third HIGH->LOW : Release contact - send key off
Since it doesn't seem possible to actually know if it's contact1 or contact2 that causes the pin to go LOW, it's very sensitive. If you start the program while holding down a key, that will cause the program to think it's the first contact - and everything after that will be messed up.
First, you need something to convert the time between two contact events into velocity. Here's a linear conversion funciton. You'll need to find the proper constants for min and max for your keyboard by measuring.
unsigned char calc_velocity(unsigned ms) {
static constexpr unsigned min = 2; // the fastest time you've measured
static constexpr unsigned max = 80; // the slowest time you've measured
static constexpr unsigned mul = 127000 / (max - min);
if(ms < min) return 127; // harder than "possible", log and recalibrate
if(ms > max) return 0; // softer than "possible", log and recalibrate
return (127000 - ((ms - min) * mul)) / 1000; // 0(min vel) - 127(max vel)
}
Then, you could create a class to keep track of the state for one key alone. It makes it easier than to have a lot of separate arrays.
// an enum for keyboard events
enum class Event { ev_nothing, ev_key_on, ev_key_off };
struct Key {
unsigned long first_contact{};
int contact_count = 0;
unsigned char velocity{};
bool contact_state = false;
// set contact state and return an Event to act upon
Event set(bool contact) { // false = no contact, true = contact
// if the state has not changed, do nothing
if(contact == contact_state) return Event::ev_nothing;
contact_state = contact; // set the new state
// only care about when state changes to having contact
if(contact_state) {
// count HIGH->LOW transitions
contact_count = (contact_count + 1) % 3;
// 1 = first contact
// 2 = second contact (key on)
// 0 = release contact (key off)
switch(contact_count) {
case 2: // second contact
velocity = calc_velocity(millis() - first_contact);
return Event::ev_key_on;
case 0: return Event::ev_key_off; // release contact
case 1: first_contact = millis(); // first contact
}
}
return Event::ev_nothing;
}
};
Then define these globally:
constexpr std::uint8_t kNumberOfKeys = 49;
Key keys[kNumberOfKeys];
With that, your read_keys() function could look this this:
void read_keys() {
for (uint8_t key = 0; key < kNumberOfKeys; ++key) {
digitalWrite(output_main[key], LOW); //turn off output main key
// Do a digitalRead() and call the set() function for that key which will
// return an Event.
switch(keys[key].set( digitalRead(input_pullup[key]) == LOW )) {
case Event::ev_key_on:
my_midi.sendNoteOn(key + note_offset, keys[key].velocity, 1);
main_midi.sendNoteOn(key + note_offset, keys[key].velocity, 1);
break;
case Event::ev_key_off:
my_midi.sendNoteOff(key + note_offset, 0, 1);
main_midi.sendNoteOff(key + note_offset, 0, 1);
break;
case Event::ev_nothing:
default:
break;
}
digitalWrite(output_main[key], HIGH); //turn on output main key
}
}
This could be made so that each Key object would know which key number it actually is and so that it could read its own pin etc. It could also return an Event object which starts by turning off output main key and turns it back on when it's destroyed - but I think this leaves it more open since I don't know much about why the output main key should be turned off and on.
Disclaimer: I've ofcourse not been able to test this - so see it as building blocks.

My game of Snake crashes when you eat an apple, and the apple spawns inside your body after picking a new position (C++, SDL)

I thought I'd attempt to make Snake since it is a pretty easy game to make. I was having an issue where the apple would spawn inside the snakes body, and so I came up with a way to prevent that from happening:
void getRandomApplePos() {
// variable that tells the while loop whether the moving of the apple was successful
bool success;
// variable that is set to false if the apple is inside the snakes body
bool appleNotInside;
// Tells the collision to stop testing for collision until the apple has successfully moved
bool appleHasMoved;
// sets the variables
success = false;
appleNotInside = false;
appleHasMoved = false;
// while the apple spawns inside the snake, it keeps generating new positions
while (!success) {
// random seed
srand((unsigned int)time(NULL));
// gets a random position
int randomX = rand() % 769;
int randomY = rand() % 673;
// resets the two variables if this while loop as ran again
apple.delta_pos_x = 0;
apple.delta_pos_y = 0;
// checks to see if the apple has spawned in the same exact position
while (apple.delta_pos_x == 0 && apple.delta_pos_y == 0) {
// gets the previous poition of the apple
apple.prevPos_x = apple.x;
apple.prevPos_y = apple.y;
// picks a new apple position
apple.x = round((randomX) / 32) * 32;
apple.y = round((randomY) / 32) * 32;
// gets the new apple position
apple.currentPos_x = apple.x;
apple.currentPos_y = apple.y;
// sets the difference between the positions, if it's 0, then it has spawned in the same exact location
apple.delta_pos_x = (float)(apple.currentPos_x - apple.prevPos_x);
apple.delta_pos_y = (float)(apple.currentPos_y - apple.prevPos_y);
}
// checks to see if the snake length is only one, as to make the list not go out of index
if (snake.bodyLength == 1) {
// if the apple happens to spawn inside the snake with a length of 1, it will add false to the appleInSnake vector, else it adds true
if (apple.x == snakeBody[0][0] && apple.y == snakeBody[0][1]) {
appleNotInside = false;
appleInSnake.push_back(appleNotInside);
}
else {
appleNotInside = true;
appleInSnake.push_back(appleNotInside);
}
}
else {
// if the apple happens to spawn inside the currently compared snakeBodyPosition, it will add false to the appleInSnake vector, else it adds true
for (int i = 0; i < snakeBody.size(); i++) {
if (apple.x == snakeBody[i][0] && apple.y == snakeBody[i][1]){
appleNotInside = false;
appleInSnake.push_back(appleNotInside);
}
else {
appleNotInside = true;
appleInSnake.push_back(appleNotInside);
}
}
}
// if false appears inside the appleInSnake vector at all, it sets success to false and goes through the loop again. Else it breaks out.
if (std::find(appleInSnake.begin(), appleInSnake.end(), false) != appleInSnake.end()) {
success = false;
}
else {
success = true;
}
//clears appleInSnake so that it can take in a new comparision
appleInSnake.clear();
}
// tells the collision to start back up again
appleHasMoved = true;
}
So, whenever the apple does end up spawning inside of the snakes body, it crashes, just outright. I suspect some kind of infinite loop, but I can't put my finger on why this happens.
You are initializing your random number generator within your loop.
Note that the RNG is deterministic. It means that you will end up drawing the same numbers all over again as in the previous loop.
Initialize the RNG once at the start of your program. This way, the numbers drawn may be expected to be different within every loop.
You might wonder, that the crude use of time() should prevent this. A typical implementation of time() will have the granularity of seconds. So you would only expect the return value to change once a second, hence, you get the same initialization over and over again in your loop.

Calling function once

if (GetKeyState(VK_DOWN) & 0x80)
{
func();
}
It calls func() like 4 times when I press key
I want it to call only once when I press key
EDIT:
SHORT keyState;
SHORT keyState2;
SHORT keyState3;
static bool toogle1 = false;
static bool toogle2 = false;
static bool toogle3 = false;
if (keyState = GetAsyncKeyState(VK_DOWN) && !toogle1)
{
toogle1 = true;
}
else
toogle1 = !toogle1;
if (keyState2 = GetAsyncKeyState(VK_NUMPAD0) && !toogle2)
{
toogle2 = true;
}
else
toogle2 = !toogle2;
if (keyState3 = GetAsyncKeyState(VK_NUMPAD1) && !toogle3)
{
toogle3 = true;
}
else
toogle3 = !toogle3;
Here is how I did it, will it work?
static bool once = false;
if (GetKeyState(VK_DOWN) & 0x80)
{
if (!once)
{ once = true; func(); }
}
I guess you run this in a loop. Your idea with toggling a flag when called is not bad, but since you toggle it back in the else case, you call func() half as often as before (every 2nd time).
When you want to call it again (I think you want, according to your code), when the key is pressed again, but not spam the function call, you can use a variable to store which key was pressed last and only call func(), when it was another key (you can also add a "no key pressed" state.
If you really just want to call it once, just remove your else statements.

Checking if given process is running

When I use the following function as isRunning("example.exe"); it always returns 0 no matter if the process is running or not.
I tried making it std::cout << pe.szExeFile; in the do-while loop and it outputs all the processes in the same format as I am trying to pass the function.
The project is multi-byte character set, in case that makes a difference.
bool isRunning(CHAR process_[])
{
HANDLE pss = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);
PROCESSENTRY32 pe = { 0 };
pe.dwSize = sizeof(pe);
if (Process32First(pss, &pe))
{
do
{
if (pe.szExeFile == process_) // if(!strcmp(pe.szExeFile, process_)) is the correct line here
return true; // If you use this remember to close the handle here too with CloseHandle(pss);
} while (Process32Next(pss, &pe));
}
CloseHandle(pss);
return false;
}
Can't seem to find my mistake.
Thanks for your time.
You are using if (pe.szExeFile == process_) which compares the pointer values. You should be using something like strcmp or _stricmp to compare the actual string values instead.
e.g.
if(strcmp (pe.szExeFile, process_) == 0)
return true;

Sending Key Presses with Interception

I have tried all the normal methods of faking keyboard actions (SendInput/SendKeys/etc) but none of them seemed to work for games that used DirectInput. After a lot of reading and searching I stumbled across Interception, which is a C++ Library that allows you to hook into your devices.
It has been a very long time since I worked with C++ (Nothing existed for C#) so I am having some trouble with this. I have pasted in the sample code below.
Does it look like there would be anyway to initiate key actions from the code using this? The samples all just hook into the devices and rewrite actions (x key prints y, inverts mouse axis, etc).
enum ScanCode
{
SCANCODE_X = 0x2D,
SCANCODE_Y = 0x15,
SCANCODE_ESC = 0x01
};
int main()
{
InterceptionContext context;
InterceptionDevice device;
InterceptionKeyStroke stroke;
raise_process_priority();
context = interception_create_context();
interception_set_filter(context, interception_is_keyboard, INTERCEPTION_FILTER_KEY_DOWN | INTERCEPTION_FILTER_KEY_UP);
/*
for (int i = 0; i < 10; i++)
{
Sleep(1000);
stroke.code = SCANCODE_Y;
interception_send(context, device, (const InterceptionStroke *)&stroke, 1);
}
*/
while(interception_receive(context, device = interception_wait(context), (InterceptionStroke *)&stroke, 1) > 0)
{
if(stroke.code == SCANCODE_X) stroke.code = SCANCODE_Y;
interception_send(context, device, (const InterceptionStroke *)&stroke, 1);
if(stroke.code == SCANCODE_ESC) break;
}
The code I commented out was something I tried that didn't work.
You need to tweak key states for UP and DOWN states to get key presses. Pay attention at the while loop that the variable device is returned by interception_wait, your commented out code would send events to what?? device is not initialized! Forget your code and try some more basic. Look at the line inside the loop with the interception_send call, make more two calls after it, but don't forget to change stroke.state before each call using INTERCEPTION_KEY_DOWN and INTERCEPTION_KEY_UP so that you fake down and up events. You'll get extra keys at each keyboard event.
Also, you may try use INTERCEPTION_FILTER_KEY_ALL instead of INTERCEPTION_FILTER_KEY_DOWN | INTERCEPTION_FILTER_KEY_UP. The arrow keys may be special ones as mentioned at the website.
void ThreadMethod()
{
while (true)
{
if (turn)
{
for (int i = 0; i < 10; i++)
{
Sleep(1000);
InterceptionKeyStroke stroke;
stroke.code = SCANCODE_Y;
stroke.state = 0;
interception_send(context, device, (const InterceptionStroke *)&stroke, 1);
Sleep(1);
stroke.state = 1;
interception_send(context, device, (const InterceptionStroke *)&stroke, 1);
turn = false;
}
}
else Sleep(1);
}
}
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)ThreadMethod, NULL, NULL, NULL);
while (interception_receive(context, device = interception_wait(context), (InterceptionStroke*)&stroke, 1) > 0)
{
if (stroke.code == SCANCODE_F5) turn = true;
interception_send(context, device, (InterceptionStroke*)&stroke, 1);
if (stroke.code == SCANCODE_ESC) break;
}