SDL Event Handling Delay - c++

I am getting a major (1-2 second) delay between key presses.
Here is main.cpp (the lagging input handling):
#include <iostream>
#include "src/Input/InputManager.h"
#include "src/Graphics/Display.h"
#define LOG(x) std::cout << x << std::endl;
using namespace Rambug;
int main(int arc, char** argv)
{
Graphics::Display display(900, 600, "Rambug Engine Tester", true);
display.createDisplay();
SDL_Event event;
Input::InputManager inputManager;
// "Game" Loop
while (!display.isClosed())
{
display.update();
glClearColor(0.0f, 0.02f, 0.5f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
while (SDL_PollEvent(&event))
{
if (event.type == SDL_KEYDOWN)
{
std::cout << "Keydowner" << std::endl;
}
if (event.type == SDL_KEYUP)
{
std::cout << "Keyupper" << std::endl;
}
}
// inputManager.update();
}
display.destroyDisplay();
system("PAUSE");
return 0;
}
Here is Display.cpp, which runs PERFECTLY without any delay when I run the same code (SDL_KEYDOWN, SDL_KEYUP) I just run SDL_QUIT down there.
#include "Display.h"
namespace Rambug
{
namespace Graphics
{
Display::Display(int width, int height, std::string title, bool log)
{
m_displayWidth = width;
m_displayHeight = height;
m_displayTitle = title;
m_log = log;
m_window = nullptr;
}
Display::Display()
{
}
Display::~Display()
{
}
void Display::createDisplay()
{
// Initialize SDL
SDL_Init(SDL_INIT_EVERYTHING);
// Setting attributes to our window
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE, 32);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
// Create window
m_window = SDL_CreateWindow((m_displayTitle.c_str()), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, m_displayWidth, m_displayHeight, SDL_WINDOW_OPENGL);
// Error Check Window
if (m_window == nullptr)
{
if (m_log)
std::cerr << "Window could not be created!" << std::endl;
}
else
{
if (m_log)
std::cout << "Window Created Successfully With SDL!" << std::endl;
}
// Create OpenGL Context
m_glContext = SDL_GL_CreateContext(m_window);
// Initialize GLEW
glewExperimental = GL_TRUE;
GLenum status = glewInit();
if (glewExperimental)
{
if (m_log)
std::cout << "Glew Experimental: On" << std::endl;
}
// Error Check GLEW
if (status != GLEW_OK)
{
if (m_log)
{
std::cerr << "GLEW could not be initialized!" << std::endl;
}
}
else
{
if (m_log)
{
std::cout << "GLEW Was Initilized Successfully!" << std::endl;
}
}
// Log OpenGL Version Number
if (m_log)
{
std::cout << "Using OpenGL Version: " << glGetString(GL_VERSION) << std::endl;
}
m_closed = false;
}
void Display::destroyDisplay()
{
SDL_GL_DeleteContext(m_glContext);
SDL_DestroyWindow(m_window);
SDL_Quit();
}
void Display::update()
{
SDL_GL_SwapWindow(m_window);
// Check for Input
while (SDL_PollEvent(&m_sdlEvent))
{
if (m_sdlEvent.type == SDL_QUIT)
{
m_closed = true;
}
}
}
bool Display::isClosed()
{
return m_closed;
}
}
}
I also tried experimenting with an Input manager class, but that was the same deal: delays. The update method is what I would call in main.cpp (I believe that it is commented out)
#include "InputManager.h"
#include <iostream>
#define LOG(x) std::cout << x << std::endl;
namespace Rambug
{
namespace Input
{
InputManager::InputManager()
{
}
InputManager::~InputManager()
{
}
void InputManager::keyPressed(unsigned int keyCode)
{
m_keyMap[keyCode] = true;
}
void InputManager::keyReleased(unsigned int keyCode)
{
m_keyMap[keyCode] = false;
}
bool InputManager::isKeyDown(unsigned int keyCode)
{
auto it = m_keyMap.find(keyCode);
if (it != m_keyMap.end())
{
return it->second;
}
else
{
return false;
}
}
void InputManager::update()
{
while (SDL_PollEvent(&m_event))
{
switch (m_event.type)
{
case SDL_KEYDOWN:
LOG("SDL_KEYDOWN");
keyPressed(m_event.key.keysym.sym);
break;
case SDL_KEYUP:
LOG("SDL_KEYUP");
keyReleased(m_event.key.keysym.sym);
break;
}
}
}
}
}
So InputManager and main.cpp have major delays, while Display.cpp runs perfectly. Is it because I cannot run SDL_PollEvents twice?

Is it because I cannot run SDL_PollEvents twice?
Your problem isn't what I'd expect, but, yes, it's a bad idea to run SDL_PollEvents twice. SDL keeps an event stack which is added to as your program runs. SDL_PollEvents pops events from the stack until it is empty. As a result, running two polling loops, one will remove events which the other will then not see. Blind luck (or execution bottlenecks) will determine which loop is more likely to see any particular event occur. (See http://wiki.libsdl.org/SDL_PollEvent).
If you really want to run two polling loops, you can store unhandled events in your default case, and push the list of events back after each loop with SDL_PushEvent: http://wiki.libsdl.org/SDL_PushEvent
This said, I'm surprised that your events "get through" after a delay: I would expect them to vanish. Are you holding the keys down? Then, your OS key-repeat delay might be what you're seeing, after which the event queue is being flooded between each loop. You might want to check the repeat flag of the key event: http://wiki.libsdl.org/SDL_KeyboardEvent
I would say this points to a design problem. You should ask yourself, why does the Display delegate the Game ending? Would it not be more sensible to inform the Display, along with everything else, of this fact?

SDL keeps an event stack which is added to as your program runs. SDL_PollEvents pops events from the stack until it is empty.
I am fairly sure that it is not a stack, but a queue. The SDL_PushEvent has a somewhat misleading name there; what it really does is shove the event back into the queue from the "wrong" end. (It might be implemented internally as a stack, but it's behaviour is that of a queue.)
Still, Qualia's answer is the right way to go.
However, I don't agree that it is necessarily bad to have multiple event loops - they can be very useful. 2 scenarios:
1) You catch something like a resize event in your main event loop. If the ensuing operations are very time-consuming the event queue will be flooded with more resize events as long as the user keeps resizing the window.
In this case, on can have a separate event loop after the time-consuming repaint, which simply loops until it finds the first non-resize event, then pushes the last 2 events it saw back into the queue, and returns control to the main loop. That way you can discard the accumulated resize events. (It can be done more elegantly using the SDL_PeepEvents function, especially if there is a really huge pile-up of events in the queue.)
2) The action your program takes after catching a specific event will trigger other events, like when using SDL_RaiseWindow, which may trigger a multitude of focus and window related subsequent events, especially if you have more than one SDL window. Here, having a separate event loop can be used to deal with these triggered events, especially if the response to these events should be suppressed.
On the issue of delays, I have also encountered all sorts of strange behaviour with the SDL_KEYDOWN event, usually the event being triggered multiple times, and definitely not related to OS key repetition. This seems to occur only when using SDL_PollEvent; SDL_WaitEventTimeout, even with the timeout delay set to just '1', seems to inhibit this strange behaviour. Interestingly, the SDL_KEYUP event does not exhibit this behaviour.

Related

Why isn't SDL_CreateWindow showing a window when called from another class?

Rewriting this to try to provide some clarity and update the code with some things that have changed.
I am restructuring a project that used SDL2, and have encountered issues trying to create a blank window. I have attempted to structure the project similarly to the original by separating all functionality dealing with SDL_Window into its own class. If I move the call to SDL_CreateWindow into the same class as the event loop or move the event loop to the same class as the window, the window is created and shown as expected, however as it is now, the window appears to be created successfully (SDL_CreateWindow is not returning NULL) and the program doesn't seem to be hanging, but it does not display a window while the program is running.
The SDL_Window is created in the Graphics class and stored in a member variable:
Graphics::Graphics(const char* title, unsigned int w, unsigned int h, unsigned int flags, int& status) {
screen = SDL_CreateWindow(title,
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
w, h,
flags);
status = 0;
if (screen == NULL)
status = 1;
}
Graphics is instantiated in the Window class and stored in a member variable.
Window::Window(const char* title, unsigned int w, unsigned int h, unsigned int flags, int& status) {
g = Graphics(title, w,h, flags, status);
}
Window is instantiated in main, and if the window is created successfully, it starts the event loop.
{
int status;
Window window("Mirari", 640,480, SDL_WINDOW_SHOWN, status);
if (status == 0) {
window.eventLoop();
} else {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window and renderer: %s", SDL_GetError());
return 1;
}
}
The event loop itself to be thorough (update and draw are both currently empty functions).
void Window::eventLoop() {
SDL_Event ev;
bool running = true;
while (running) {
const int start_time = SDL_GetTicks();
while (SDL_PollEvent(&ev)) {
switch (ev.type) {
case SDL_QUIT:
running = false;
break;
default:
break;
}
}
//update();
//draw();
std::cout << "." << std::endl;
const int elapsed = SDL_GetTicks() - start_time;
if (elapsed < 1000 / FPS)
SDL_Delay(1000 / FPS - elapsed);
}
}
SDL is initialized with this static function and these flags.
void Window::init(unsigned int sdl_flags, IMG_InitFlags img_flags) {
SDL_Init(sdl_flags);
IMG_Init(img_flags);
TTF_Init();
}
...
Window::init(SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_TIMER, IMG_INIT_PNG);
I know that a window can be created in a separate class because the first version of this project did that and it worked, I'm just not sure what has changed that is causing the window not to show up.
As said by some programmer dude, you design is not perfect and should be thought again.
Nevertheless, from what we can see on your code : If the Window constructor is called (and the SDL_Init was called before, which I assume so), then the windows should be created.
From there we only can guess what we can't see (as it's not part of what you are displaying) :
is the definition of SDL_WINDOWPOS_UNDEFINED, the same in both context ?
is the screen variable definition the same in both context ?
is the "screen" used in "update", or "draw" method, and, as uninitialized : it fails
... ?
As you probably are new to development, I suggest you adopt this habit very early : your code should check and report everything it does. A good program is easy to debug, as it says what's wrong
For instance, just after :
screen = SDL_CreateWindow(title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
w, h, flags);
you may want to write something like :
if(!screen)
{
std::cout << "Failed to create window\n";
return -1;
}
or better :
if(!screen)
{
throw std::exception("Failed to create window\n");
}
And so on.
For instance, in your function update, you may want to have something like :
if(!screen)
{
throw std::exception("Unable to update the display as it is uninitialized\n");
}
I assume your application would not end without any comment... but that's a guess

Portably exit readline from C++11 thread with SDL2

Consider a simple program: (require -std=c++11 -lpthread -lreadline -lSDL2 when compile with g++ with pthread)
#include <iostream>
#include <thread>
#include <SDL2/SDL.h>
#include <cstdlib>
extern "C" {
#include <readline/readline.h>
}
int main(int argc, char *argv[])
{
SDL_Init(SDL_INIT_VIDEO);
auto window = SDL_CreateWindow("title", 0, 0, 200, 200, SDL_WINDOW_SHOWN);
std::thread console([](){
while (true) {
char *console_input_c_str = readline("> ");
if (console_input_c_str == NULL)
break;
std::cout << "line: " << console_input_c_str << '\n';
std::free(console_input_c_str);
}
});
while (true) {
SDL_Event event;
SDL_WaitEvent(&event);
std::cerr << "received event type "<<event.type<<'\n';
if(event.type == SDL_WINDOWEVENT &&
event.window.event == SDL_WINDOWEVENT_CLOSE)
break;
}
SDL_DestroyWindow(window);
SDL_Quit();
console.join();
}
The main thread creates a window window with SDL2, enter a SDL event loop, while the thread console repeatedly read from the console using readline. When the window is exited, it waits for the console thread to finish then exit.
The program works fine; however, how can I make the console thread to stop when the window is exited?
It's fine if the program only use a single thread. It's also fine if exiting the console readline quits the program.
Exit readline is easy, but there is a problem with all the answers there - pthread_kill or reset -Q is not portable.
Terminating a thread is easy (using std::terminate or .detach()), but the console is left in a bad state because readline don't terminate normally. To reset the console it's possible to use reset -Q, but that's not portable.
Using readline's alternative (async) interface doesn't work because SDL don't listen for events on the console. Using select is also not portable.
Actually, readline is not completely blocking, it calls rl_event_hook every 1/10 seconds (by default). It's possible to check whether the program is stopped inside the event hook, and if so do a longjmp or throw to get out of the function (in this example int is used, although it's possible to define your own exception class):
std::atomic<bool> stopped (false);
rl_event_hook = [](){
if (stopped)
throw 0;
return 0;
};
try {
/* something calls readline() */
} catch (int) {
rl_cleanup_after_signal();
}
return 0;
or by setting rl_done = 1: (this won't work when rl_event_hook is nullptr. Also note that rl_event_hook is not called when a key is held, it's necessary to override rl_getc_function to change the behavior. It's not recommended to modify rl_done from the other thread)
std::atomic<bool> stopped (false);
rl_event_hook = [](){
if (stopped)
rl_done = true;
return 0;
};
/* something calls readline() */
if (stopped) return 0;
/* process */
return 0;
and in the main thread use:
stopped = true;
console.join();

SDL_MOUSEBUTTONUP occurring even before releasing mouse button?

I'm trying to make a video player using SDL and ffmpeg in C++. I've created two separate threads, one that renders the video on the SDL window and one that handles window events. When the user clicks and releases on the video I want it to toggle playback/pause. However, it fires multiple times and the event occurs even before I release the mouse which results in unpredictable behavior.
My code:
SDL_Event event;
while (1)
{
SDL_PollEvent(&event);
switch (event.type)
{
case SDL_QUIT:
SDL_DestroyWindow(screen);
SDL_Quit();
break;
case SDL_MOUSEBUTTONUP:
if (event.button.state == SDL_RELEASED)
{
printf("Mouse released\n");
}
break;
}
}
When I click the window and hold down I would expect it wouldn't print Mouse released until I release the button. However, it prints Mouse released the entire time I hold down the mouse button. I don't know if maybe this has to do with me using a touchpad on my laptop.
SDL_PollEvent has a return value, you are ignoring.
[It] returns 1 if there are any pending events, or 0 if there are none available.
Given your code logic, whenever there is no pending event, you keep handling the previous event over and over again, until a new event arrives. This leads to the observed behavior.
The easiest fix would be to wrap the entire event handling inside an if (SDL_PollEvent(&event)) { /* Event handling */ } conditional.
EDIT: My answer is wrong, check IInspectable's answer.
Your error is that you're not checking all the pending events given by pollEvent, just one. Try this code and tell me how many button ups you get.
#include <SDL2/SDL.h>
#include <iostream>
int main(int argc, char *argv[]) {
if(SDL_Init(SDL_INIT_VIDEO) != 0) {
throw std::runtime_error("SDL failed to initialize.\n");
}
SDL_Window *window = SDL_CreateWindow("App", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 640, 480, NULL);
bool done = false;
while(!done) {
SDL_Event event;
while(SDL_PollEvent(&event)) {
if(event.type == SDL_QUIT) {
done = true;
}
if (event.type == SDL_MOUSEBUTTONUP) {
if (event.button.state == SDL_RELEASED) {
printf("Mouse released\n");
}
}
}
}
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}

XLib: Windows Manager not Sending Client Message after Calling XSetWMProtocols

There are many examples on how to handle the closing of a window using XLib, which can be found on the internet:
http://cboard.cprogramming.com/linux-programming/60466-xwindows-close-window-event.html
https://en.wikibooks.org/wiki/X_Window_Programming/XLib
https://john.nachtimwald.com/2009/11/01/x11-intercept-window-close-event/
There are several more. That said I have tried to implement them in code, as seen below. However when I click on X in the corner of my window I get no event sent to my message loop. Is this because XChcekWindowEvent ignores or does not process Client Messages? If this is not the case what are some other things I should be looking for to get messages from XLib set using SetWMProtocols?
m_impl->m_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", False);
if (!XSetWMProtocols(display, window, &m_impl->m_delete_window, 1)) {
std::cout << "Set Window Protocols Failed" << std::endl;
}
...
while (!terminate) {
while (::XCheckWindowEvent(display, window, events::mask, &x_event)) {
if (x_event.type == ClientMessage) {
std::cout << "Client Message" << std::endl;
if ((Atom)x_event.xclient.data.l[0] == m_impl->m_delete_window) {
terminate = true;
}
}
}
}
The XCheckWindowEvent() will not return ClientMessage. It returns none of the non maskable ones. Work around:
while (XPending(display))
{
XNextEvent(display, &event);
But could create extra work to filter event by window.
BR Pekka
If you want to prevent using XNextEvent (which is not applied in real-time event loops), you may use the following code based on XCheckTypedWindowEvent:
// Globals
Atom wm_protocols;
Atom wm_delete_window;
// Functions
void PreventClose(Display* disp, Window& win)
{
wm_protocols = XInternAtom(disp, "WM_PROTOCOLS", false);
wm_delete_window = XInternAtom(disp, "WM_DELETE_WINDOW", false);
XSetWMProtocols(disp, win, &wm_delete_window, 1);
}
bool IsClosed(Display* disp, Window& win)
{
XEvent e;
if (XCheckTypedWindowEvent(disp, win, ClientMessage, &e))
if (e.xclient.message_type == wm_protocols &&
e.xclient.data.l[0] == wm_delete_window_)
return true;
return false;
}
// Usage
int main()
{
...
PreventClose(disp, win);
do {
if (IsClosed(disp, win))
// break, some actions, etc...
...
} while(true);
return 0;
}
For more info see man 3 XCheckTypedWindowEvent

Is there something wrong with my cleanup code? (OpenGL + SDL)

I think I have a bug in my program. I use SDL and OpenGL to render an animation. The program also measures the average FPS. Tipically, when I run the program, it will run at around 550 FPS.
However, if I start a second instance of the program, the FPS drops for both at around half (220 FPS). The strange thing is that if I close the first instance, the second one will still run at only 220 FPS. This leads me to believe that my cleanup code is somehow flawed.
Sometimes, even if I run a single instance, it will run at only 220 FPS, probably due to a previous instance that failed to clean up properly. Is there something wrong with my approach below?
I use a screen class which has the following *tors:
namespace gfx
{
screen::screen(const settings& vs) : dbl_buf_(false), sdl_surface_(0)
{
if (SDL_Init(SDL_INIT_VIDEO) < 0)
throw util::exception(::std::string("Unable to initialize SDL video: ") + SDL_GetError());
if (!set(vs))
{
SDL_Quit();
throw util::exception("Unable to setup initial video mode.");
}
glewInit();
}
screen::~screen()
{
SDL_Quit();
}
bool screen::set(const settings& vs)
{
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
Uint32 flags = SDL_HWSURFACE | SDL_OPENGL;
if (vs.full_screen) flags |= SDL_FULLSCREEN;
sdl_surface_ = SDL_SetVideoMode(vs.size_x, vs.size_y, vs.bpp, flags);
if (!sdl_surface_) return false;
settings_ = vs;
int db_flag = 0;
SDL_GL_GetAttribute(SDL_GL_DOUBLEBUFFER, &db_flag);
dbl_buf_ = (db_flag == 1);
return true;
}
// ...
}
Also:
int main()
{
try
{
gfx::settings vs = {800, 600, 32, false};
gfx::screen scr(vs);
// main app loop, render animation using OpenGL calls
// loop runs while running_ variable is true (see below)
}
// catch, etc.
return 0;
}
If it makes any difference, I use Linux and an ATI card.
Update: Event handling code:
SDL_Event event;
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_KEYDOWN:
if (event.key.keysym.sym == SDLK_ESCAPE)
running_ = false;
break;
case SDL_QUIT:
running_ = false;
break;
default:
world_.process_event(event);
break;
}
}
When a process terminates all the resources it used are freed automatically. That includes OpenGL. What may happen is, that you don't terminate your process but only hide the window by clicking the close button.