trying to setup my classes to programm faster with SDL2 - so far evrything worked fine.
But in the end of my last coding session my SDL_PollEvent does no longer catch SDL_QUIT.
I´m using my own SDL_Window class to handle events and other SDL relevant Vars.
In my Window class i have the function loadEvent(), its my gameloop function, and in it i call SDL_PollEvent and right after i check if it is SDL_QUIT and if so it returns a false and ends the gameloop, otherwise it returns true. SDL_PollEvent saves into a private SDL_Event varriable of my class so i can later ask for keys pressed by looking into the event of my window class.
Any keydown event works just fine, i can end my game with esc if i want to, but i never seem to catch SDL_QUIT.
There is no error, it just does not get any SDL_QUIT back.
Any ideas what could be the problem?
Code:
class My_SDL_Window{
private:
SDL_Window* window;
SDL_Surface* surface;
SDL_Renderer* renderer;
SDL_Event sEvent;
My_Object_Node* allObj;
public:
…
bool loadEvent()
{
if(SDL_PollEvent( &sEvent ) != 0)
{
if(sEvent.type == SDL_QUIT)
{
destroy();
return false ;
}
}
return true;
}
You're only checking whether there's an event once every time the program loops.
Replace:
if(SDL_PollEvent( &sEvent ) != 0) , which only checks whether there are any events at all, where you then only act on the first event in that list, with:
while (SDL_PollEvent(&sEvent)) , which loops through every event in the event queue, where you then act on every single event that was performed since the last time you checked.
You need to change how you handle the SDL_PoolEvent() instead a if use while, with this method you will catch all events on pool not only the first one.
For example:
bool quit = false;
SDL_Event event;
while(!quit) {
// Process input
while(SDL_PollEvent(&event) > 0) {
switch(event.type) {
case SDL_QUIT: quit = true; break;
case SDL_KEYDOWN:
switch(event.key.keysym.sym) {
case SDLK_UP: go_up(); break;
case SDLK_DOWN: go_dowm(); break;
// ... other keyboard handling
}
break;
}
}
// Update state
do_game_state_update();
// Render
do_screen_render();
}
Related
I am using sfml library for my graphics. During paused I want to avoid drawing on the screen. I want to intercept a spacebar press using sf::Event::MouseButtonPressed. In the debug mode, the event is successfully intercepted but failed in normal execution, why? The bool variable 'paused' don't seemed to update.
while (window.isOpen()) {
Event event;
while (window.pollEvent(event)) {
/*..*/
if (event.type = Event::KeyPressed) {
if (event.key.code == Keyboard::Space) {
if (!paused) {
paused = true;// UDATE HERE
}
else
if (paused)
{
paused = false; // UDATE HERE
}
}
}
}
if (!paused) //// UDATE DONT SEEM TO AFFECT HERE
{
/* draw here */
}
}
Edited: Even tried by declaring the variable paused as atomic, but still don't work.
Use the Event::KeyReleased instead of the Event::KeyPressed as the key press gets called every loop that a key is down leading to many enters while the Release event is only called once per click.
I'm experiencing some problem to getting working touch events with CEF in offscreen rendering using SDL2 in some situation:
Im writing an SDL-CEF application that need to execute some online game into a videolottery. so I start the application from tty using startx and not in DE. Morever, I can't use the mouse but is only possible use touchscreen to interact with the machine.The touch event work pretty well in the most os cases, but with some game the click event is not recognized by the web application.
For pass the correct event to the CEF browser I have written some specialized handler functions that catchs the SDL_Event and converts their to CEF_Event. Fo example for processing tauch events I have a function called handleFingerEvent(SDL_Event, CEF_Browser*). This specialized function is called from a generic handleEvents(SDL_Event, VEF_Browser*) handler function of highter level .
// The Touch Event Hendler function
void handleFingerEvent(SDL_Event &e, CefBrowser* browser) {
CefMouseEvent event;
switch (e.type) {
case SDL_FINGERDOWN:
// W_SIZE and H_SIZE are the dimension of the screen in px
if (e.tfinger.pressure > 0.9) {
event.x = static_cast<int>(e.tfinger.x*W_SIZE);
event.y = static_cast<int>(e.tfinger.y*H_SIZE);
browser->GetHost()->SendMouseClickEvent(event,
MBT_LEFT,
false, 1);
}
break;
case SDL_FINGERUP:
event.x = static_cast<int>(e.tfinger.x*W_SIZE);
event.y = static_cast<int>(e.tfinger.y*H_SIZE);
browser->GetHost()->SendMouseClickEvent(event, MBT_LEFT, true, 1);
break;
case SDL_FINGERMOTION: {
int scrollX = static_cast<int>(e.tfinger.dx);
int scrollY = static_cast<int>(e.tfinger.dy);
event.x = static_cast<int>(e.tfinger.x*W_SIZE);
event.y = static_cast<int>(e.tfinger.y*H_SIZE);
browser->GetHost()->SendMouseWheelEvent(event, scrollX, scrollY);
break;
}
default:
break;
}
}
void handleEvents(SDL_Event &e, CefBrowser *browser) {
switch (e.type) {
// Key events
case SDL_KEYDOWN:
case SDL_KEYUP:
handleKeyEvent(e, browser);
break;
// Window events
case SDL_WINDOWEVENT:
handleWindowEvent(e, browser);
break;
//Mouse events
case SDL_MOUSEMOTION:
case SDL_MOUSEBUTTONUP:
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEWHEEL:
handleMouseEvent(e, browser);
break;
// Touch events
case SDL_FINGERDOWN:
case SDL_FINGERMOTION:
case SDL_FINGERUP:
handleFingerEvent(e, browser);
break;
default:
break;
}
}
I call it from a SDL_PollEvent() while loop like this:
browser = CefBrowserHost::CreateBrowserSync(window_info,
browserClient, //My BrowserClient implementation
"https://www.something.sometother",
browserSettings, // The cef setting
nullptr, nullptr);
while(!browserClient->closeAllowed()) {
//Send events to the browser
SDL_Event e;
while(SDL_PollEvent(&e) != 0){
//Quit and window resize
if(e.type == SDL_QUIT) {
browser->GetHost()->CloseBrowser(false);
}
else if (e.type == SDL_WINDOWEVENT && e.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
renderHandler->resize(e.window.data1, e.window.data2);
browser->GetHost()->WasResized();
}
else {
handleEvents(e, browser.get());
}
}
// Cef main work loop
cefApp->doCefWork();
//Set background color
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
//Clear window/renderer
SDL_RenderClear(renderer);
renderHandler->render();
//Update screen contents
SDL_RenderPresent(renderer);
}
Now I really don't know why in some game the event isn't caught by the browser. Morever if I open the game in a machine with mouse connected and a DM, and so I use the mouse pointer for interact with the game, it works without any problem. Maybe is there a better approach to send a click event to CefBrowser?
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;
}
I have written the follow (typical) code snippet for handling key events in SDL2:
#include <SDL.h>
#include <iostream>
using std::cout;
using std::endl;
// Custom key types
typedef enum Keys {
Back,
Reset
} KeyType;
// Structure that holds the type and pressed state of custom keys
typedef struct Button {
KeyType type;
bool pressed;
} Button;
int main(int argc, char ** argv)
{
// Variables
bool quit = false;
SDL_Event event;
Button buttons[] = {
{Back, false},
{Reset, false}
};
// Initialize SDL sub-systems, window, renderer and texture
SDL_Init(SDL_INIT_VIDEO);
SDL_Window * window = SDL_CreateWindow("SDL2 Keyboard/Mouse events",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 640, 0);
SDL_Renderer * renderer = SDL_CreateRenderer(window, -1, 0);
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
// Loop while quit not disabled
while (!quit)
{
// Pool events
while(SDL_PollEvent(&event))
{
// Filter events
switch (event.type)
{
case SDL_QUIT: // Window closed
quit = true;
break;
case SDL_KEYDOWN: // Key pressed
switch (event.key.keysym.sym)
{
case SDLK_ESCAPE:
quit = true;
break;
case SDLK_LEFT:
buttons[Back].pressed = true; // Toggle Back button to pressed
cout << "Back held" << endl;
// TODO Measure elapsed time and artificially toggle the pressed state to false if > 5s
break;
case SDLK_DOWN:
buttons[Reset].pressed = true; // Toggle Reset button to pressed
break;
}
break;
case SDL_KEYUP: // Key released
switch (event.key.keysym.sym)
{
case SDLK_LEFT:
if (buttons[Back].pressed) {
// TODO Measure elapsed time and print "Back emitted" if less than 5s
buttons[Back].pressed = false; // Toggle Back button to unpressed and emit Back event
}
break;
case SDLK_DOWN:
buttons[Reset].pressed = false; // Toggle Reset button to unpressed
cout << "Reset emitted" << endl;
break;
}
break;
case SDL_USEREVENT:
break;
}
SDL_RenderClear(renderer);
SDL_RenderPresent(renderer);
}
}
// Cleanup resources
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
// Stop all SDL sub-systems
SDL_Quit();
return 0;
}
I'm trying to figure out a way to measure the time between the moment when buttons[Back].pressed is set to true (even when holding the key down only the exact begin of the "holding" is what I'm interested in) and then to false. The "key released" part is easy since this a single event anyway.
I want to distinguish between a short (single "key pressed" event received or holding the key for less than let's say 5s) and long (more than 5s holding the key down) strokes which is basically mapping two actions to the same key based on the time a key is held by the user.
I can't figure out how to do that. Any ideas? I need to handle the time event in two location (imho):
Given key is released - if elapsed time is less/equal to 5s detect a short key stroke else long key stroke
While given key is in being pressed - if the timeout kicks in, the pressed state needs to be artificially toggled to false in order to prevent a short key stroke to be detected when the user actually releases the key. In this case a long key stroke is detected
I expanded upon #keltar 's suggestion and it works although not that precise (but in my case it's not required.
I added elapsedTime in the Button struct to hold the time information and initialized it to 0:
typedef struct Button {
KeyType type;
bool pressed;
uint32_t elapsedTime;
} Button;
Whenever the given key is pressed down I check if the elapsedTime == 0 and if so I call SDL_GetTicks(). Upon releasing the key I get the currentTime which is just another call of SDL_GetTicks() at that point in time. I calculate the difference between elapsedTime and currenTime and check if a specific time interval has elapsed and then make a decision what function is called.
This solution can be expanded by making the calculation above during every loop iteration and making the exact same check. This makes it possible to check the elapsed time not only upon releasing the given key and it is a more accurate answer to my initial question. However the first solution (without the extra check) works fine. I just had to adapt my scenario a little bit.
First of all I'd like to say that I'm not sure if the title is appropriate but it's the only logical explanation I could find.
What I'm trying to do is move my window by clicking a sprite (sort of like simulating a titlebar).
All working fine until I let go of the mouse button the rate new mouse events are emitted is way lower and with huge 1.5~2s pauses between them.
Is it possible that sf::Mouse::isBUttonPressed is filling the queue or is it another issue?
Edit: The window class has a sf::Event object and passes it to each object's event handler.
The sprite class has an event handler of this form:
bool object::handleEvents(sf::Event& event)
{
switch(event.type)
{
case sf::Event::MouseMoved:
case sf::Event::MouseButtonPressed:
case sf::Event::MouseButtonReleased:
{
auto mouse_pos = sf::Mouse::getPosition(*this->parent);
if(this->isPointInside(mouse_pos))
{
if(event.type == sf::Event::MouseMoved)
{
this->hovering = true;
if(this->callback["onHover"])
this->callback["onHover"](this, nullptr);
return true;
}
else if(event.type == sf::Event::MouseButtonPressed)
{
this->clicked = true;
this->focused = true;
if(event.mouseButton.button == sf::Mouse::Left)
if(this->callback["onLClick"])
this->callback["onLClick"](this, ref(mouse_pos));
if(event.mouseButton.button == sf::Mouse::Right)
if(this->callback["onRClick"])
this->callback["onRClick"](this, ref(mouse_pos));
return true;
}
else if(event.type == sf::Event::MouseButtonReleased && this->clicked)
{
this->clicked = false;
if(event.mouseButton.button == sf::Mouse::Left)
if(this->callback["onLClickReleased"])
this->callback["onLClickReleased"](this, ref(mouse_pos));
if(event.mouseButton.button == sf::Mouse::Right)
if(this->callback["onRClickReleased"])
this->callback["onRClickReleased"](this, ref(mouse_pos));
return true;
}
}
else
{
if(this->hovering)
{
if(this->callback["onHoverLost"])
this->callback["onHoverLost"](this, nullptr);
this->hovering = false;
}
}
}break;
default: ;
}
return false;
}
and the code responsible for moving the window:
titlebar->callback["onLClick"] = [&](object* obj, void* data)
{
sf::Vector2i* relpos = (sf::Vector2i*)(data);
while(sf::Mouse::isButtonPressed(sf::Mouse::Left))
{
sf::Vector2i abspos = sf::Mouse::getPosition();
window.setPosition(sf::Vector2i((abspos.x - relpos->x),(abspos.y - relpos->y)));
}
titlebar->clicked = false;
};
The sf::Mouse class as well as sf::Keyboard and sf::Joystick aren't connected to the event system, but are completely independent, thus it's impossible that isButtonPressed would've any influence on the events.
The real problem in your code is the 'infinite' loop when the left mouse button is pressed. If the left mouse button is pressed, everything that happens in your application is, that the window gets moved around. There won't be any event dispatches (= processing) and every event that happens within that time, will get pilled in the event queue. Thus when you return to process the events, you'll have a longer queue than usual and will start dispatching with the oldest event.
So if you now move your window around for 2 seconds, you'll get a filled queue worth 2 seconds which can delay the further processing.
To solve this problem, you'll most probably have to dispatch all the events while you're moving the window.