I am making a primitive time keeper that I would like to pause and resume with the simple press of a key. I've been told that
system("pause>null")
pauses the program until a key is hit, but how is the conditional written to pause to begin with?
Preferred structure in pseudo-code:
if (certain_key_pressed)
{
pause_program_until_any_key_hit;
}
There are 2 idioms to choose from here. Polling or event driven programming.
Polling is the more simple but often less elegant solution where the program would periodically check to see if the pause button has been hit within a loop.
While(not_quit)
{
if(key_pressed)
Etc...
In event driven programming, you would register the pauseButtonPressed event with an event handler. When the pause button is pressed, a special function assigned the task of handling the event would call the pause function.
if ( certain_key_pressed )
while ( true )
{
if ( any_key_hit )
break;
}
Something like this would suspend any loop it is placed in (presumably your program loop) when the particular key is pressed. It would then keep it suspended until any key is hit.
Related
I'm making a slot machine game, and I've been on the problem of preventing multiple spins from being queued up. The spinReelsRandom() function is what starts a series of loops that generate random numbers in here.
The game operates perfectly right now except for the fact that a player who repeatedly hits the Space button during a spin (common practice for slot players) is queuing up spins that play out immediately after the user is done. I want to ignore input starting right after the first time the user presses the spacebar to start the spin until the spinReelsRandom() function is complete.
What I've already tried:
I'm able to get the desired result by using SDL_SetEventFilter (commented out below) in the main loop. My eventFilter is simply returning 1. However, for some reason, this prevents SDL_Quit from working. My guess as tp why this works is because the filter is only returning 1 while there are no events waiting to be polled since it's outside the event polling loop, preventing users from queuing spins until the spinReelsRandom function is complete. If there's an explanation, and a way to re-enable SDL_Quit, this could be it!
Moving the filter into the procedure right after pressing the spacebar doesn't seem to work, and I've tried following up with a SDL_SetEventFilter(NULL, NULL) to reset the event filter after the spin is complete but it doesn't seem to work.
I've also tried using an "isSpinning" flag that flips true while the reel is spinning and using a check on initiating a spin, but as soon as the flag flips back to false once the spin is complete, the additional polled spins begin.
Is there something I can be doing with SDL_PeepEvents?
Here's the main loop after initialization, with my event being "e":
while (!quit)
{
while (SDL_PollEvent(&e) != 0)
{
// When an event is polled, this sets a filter to stop polling for more events until that action is completed.
// Note: This is what's stopping the repeated spins, however it has disabled the quit functionality.
// SDL_SetEventFilter(eventFilter, &e);
switch (e.type)
{
case SDL_QUIT:
quit = true;
break;
case SDL_KEYDOWN:
switch (e.key.keysym.sym)
{
case SDLK_SPACE:
//Function that continuously spins the reels until they reach a random destination
spinReelsRandom();
SDL_Delay(25);
break;
case SDLK_0:
cout << "This works";
break;
case SDLK_ESCAPE:
quit = true;
break;
}
}
//Final Spin Cleanup
spinCleanup();
}
}
Add a second condition:
while (SDL_PollEvent(&e) != 0 && quit == false)
Keltar's solution only works when your 'spinReels' (or any other time-consuming function) can be separated into chunks that are fast enough to leave time for the main event loop to handle the rate at which new events are coming in.
If that is not the case, you have 2 alternatives:
1) Put another event loop into the blocking function. There you can react very specifically to incoming events in a different way than in the main loop. (Either react in some way, or, which can be very useful, discard unwanted events, if you want the blocking function to complete. In your example: Abort function on Escape, but maybe ignore Space? For the latter, you could simply discard all queued SDLK_SPACE events.)
2) Put the blocking function into a separate thread, and use the main event loop to set signals to your threaded function.
At some point in my program, I want to wait for the user to either press [return] or [escape].
This is what I did:
while(1)
{
Sleep(100);
if( GetAsyncKeyState( VK_RETURN ) )
{
//do something
}
if( GetAsyncKeyState( VK_ESCAPE ) )
{
//do something else
}
}
But (only in the release build) after waiting for about 2 seconds, Windows says it's not responding, and it crashes.
What should I do?
Your application is a GUI subsystem application and its main thread must regularly pump its message queue. You are not doing that because you enter a tight loop looking for specific key state. Because you don't service your queue, the system concludes that your application is broken and ghosts your window.
Before we go on to how to do it right, your existing approach is broken in other ways. Suppose that the key is pressed and released during the Sleep(100). Then you miss that event. Or suppose your app is not in the foreground. Then it responds to key presses meant for other applications.
To solve the problem you simply need to let your normal message loop process and dispatch messages. When you get a WM_KEYDOWN message for the appropriate key you can react accordingly.
Using the message loop in the intended way not only fixes the behaviour you observe in the question, but also the issues I describe above.
Basically exactly what the title says. I would like to update the text that a button contains every 1 second when the user presses that particular button. I have noted that when the program doesn't have focus it works alright and the text refreshes correctly but when I am hovering over the program or when I am trying to click on it's menu Windows inform me that the program is unresponsive and asks me if I want it terminated. When the loop finishes the program returns to its normal state. Also any action I might have done (like moving it around or closing it) while it was Sleep()-ing is executed after the loop. Here is a bit of code:
case ID_BUTTON_START:
// Code executed when pressing Start Button.
char startButtonText[30]; // Storing next loop text
for (int i=5; i>0; i--)
{
sprintf(startButtonText, "Starting in ... %d", i);
SendMessage(hwndButtonStart, WM_SETTEXT, 0, (LPARAM)(startButtonText));
Sleep(1000);
}
Is this normal? If not what's causing this?
The WndProc does not process messages asynchronously within an application which means all messages are expected to be handled quickly and a return value delivered immediately. You must not Sleep in the UI thread since it will block other UI events from being processed. Any heavy work or synchronous requests/jobs which are likely to take a long time should be performed in worker threads. There are at least three viable options:
Create a new (worker thread) for the task.
If the task is likely to be done often, use a thread pool instead.
Set and subscribe to timer events.
I think the call to Sleep() might be keeping you from returning from the WndProc, so your application is not processing the incomming events for 5 secs. I suggest you try to subscribe to 5 timer events in 1s, 2s,..., 5s. Like when the timer message is recieved the button text must change. I don't know a way how to do that off the top of my head.
I am writing a C++ CLI application how can I detect if any key is pressed by the user. I've seen that in c# but how can it be implement in c++
while(1)
{
while(/* code to check if any key is pressed*/)
{ //rest of the code
// sleep function
}
}
Hint: like in CLI games to move or to take certain action when a key is pressed or don't do any thing if no input is given.
On windows at least you could use GetKeyState
we can use _kbhit() function in c++. _kbhit is equal to 1 if any key is pressed. You have to clear the _kbhit buffer else it will remain 1. Method for clearing is character = getch(); This will save the last entered key in character which you can compare and decide which action to perform on which key.
While loop can be CPU consuming, i do not advice busy waiting method, instead you should think of event hooking.
Here you can read about winapi keystroke event hooking C++ Win32 keyboard events
If you are still interested to use the while loop, you should also free some resources by sleeping after checking that a condition is false (e.g. nanosleep )
Basically, when one types, a keydown event happens. If the key is held for more than a certain time (~1 sec) then the key is repeatedly pressed until keyup hapens. I would like to change the time it takes for the key to be automatically repressed in my c++ application. How can this be done?
Thanks
The speed at which a keypress becomes automatically recurring is controlled by Windows.
If you want to manipulate automatic recurrences of key-presses, it might be more advantageous to poll for the state of the key rather than waiting for the keydown event. It depends on how responsive you need your application to be.
This article may help you in figuring out how to query for key states: link
You can use the SystemParametersInfo function to change the keyboard delay and refresh rate, as described in this newsgroup thread.
A simple way to handle this is to establish a buffer of time around the OnKeyDown event. Setup a timer that determines whether control passes to a secondary event handler. If the timer has expired, then it is OK to pass control. If the timer hasn't expired, then you should return and leave the event unhandled. Start the timer right before passing control to your secondary event handler.
void KeyDownHandler(...)
{
// ...
if (TimeLeft() <= 0)
{
StartTimer();
handleKeyDown();
}
}
A timer is better than counting duplicate events because you can't assume that a given system will have the same repeat rate set as yours.
I agree with Stuart that polling for the state of the key might work better. It depends upon what you are trying to accomplish.
Also note that this type of behavior might be highly annoying to your user - why do you need to ignore duplicates?
You might be able to tap into a Windows API but this might be controlled by the OS. Not sure...
You might need to manually draw a command such as to simulate a key press multiple times after a set number of seconds after the key has been pressed.
Use SetKeySpeed api (Kernel)