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.
Related
I am programming a GUI with wxWidgets. I got a button to open a serial port and receive data. In there is a while(1) loop to constantly receive the data.
Now I want to do a "Disconnect" button to stop receiving.
Is there an event handler or a callback function to interrupt the while and jump out of the loop from the first button, when I press another button?
A wxButton sends a message when it is clicked. Create a handler for this message where you do your "interrruption".
Now, a loop can be only exited from its inner guts. I mean, inside the loop you should check a flag, and depending on the value of this flag continue or exit.
My advice is that you create a worker thread. When the button "connect" is clicked then you create the thread that receives data.
This thread checks an extern flag and finishes depending on the value of the flag.
The "disconnect" button click-handler just sets that extern flag to a value that makes the thread exits when it checks that flag.
Take a look at the thread sample provided with wxWidgets sources.
If you have a while(1) loop in the callback function for "button pressed", you will hang the UI. Callback functions must return quickly.
The quickest solution is to put the while(1) loop into a worker thread. That way your GUI won't hang.
The next problem is how to stop the loop. That's quite easy. Change the loop into:
while (keep_going.load()) {
....
where keep_going is of type std::atomic_bool. Then your "stop" button just calls keep_going.store(false).
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.
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 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.