whenever I press a key nothing happens, there are no errors either, rather odd this is.
Here is the init function:
bool Game::init(int resx, int resy, bool fullscreen)
{
tm = new TextureManager();
int flags = 0;
if(fullscreen)
{
flags = SDL_WINDOW_FULLSCREEN;
}
if(SDL_Init(SDL_INIT_EVERYTHING) >= 0)
{
g_pWindow = SDL_CreateWindow("Pong", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, resx, resy, flags);
if(g_pWindow != 0)
{
g_pRenderer = SDL_CreateRenderer(g_pWindow, -1, 0);
if(g_pRenderer != 0)
{
running = true;
tm->load("/Users/WilsonKoder/C++P/projects/Pong/Pong/Images/bg.png", "bg", g_pRenderer);
tm->load("/Users/WilsonKoder/C++P/projects/Pong/Pong/Images/paddle.png", "player1", g_pRenderer);
tm->load("/Users/WilsonKoder/C++P/projects/Pong/Pong/Images/paddle.png", "player2", g_pRenderer);
tm->load("/Users/WilsonKoder/C++P/projects/Pong/Pong/Images/ball.png", "ball", g_pRenderer);
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
return false;
}
Here is my event handler:
void Game::handleEvents()
{
SDL_Event event;
switch (SDL_PollEvent(&event)) {
case SDL_KEYDOWN:
p1MoveUp = true;
break;
case SDL_KEYUP:
p1MoveUp = false;
break;
default:
break;
}
}
Here is my update function:
void Game::update()
{
if (p1MoveUp)
{
std::cout << "asfasfasf" << std::endl; //for debug purposes :)
p1YPos -= 5;
}
}
and last but not least, here is main.cpp (excluding includes...)
Game *g_game = 0;
int main(int argc, const char * argv[])
{
g_game = new Game();
g_game->init(800, 600, false);
while(g_game->isRunning())
{
g_game->handleEvents();
g_game->update();
g_game->render();
}
g_game->clean();
SDL_Quit();
return 0;
}
You are using the SDL_PollEvent incorrectly. The function does not return the type of event, but rather 1 if there are still items in the event queue, or 0 if the queue is empty.
So you'd want to poll events as long as there are things in the queue and THEN do a switch over the event.type as such:
void Game::handleEvents()
{
SDL_Event event;
// Poll events until the queue is empty...
while(SDL_PollEvent(&event)) {
// ...then check the event.
switch (event.type) {
case SDL_KEYDOWN:
p1MoveUp = true;
break;
case SDL_KEYUP:
p1MoveUp = false;
break;
default:
break;
}
}
}
EDIT: Note that currently ALL key down and up events of each key on the keyboard would cause the p1MoveUp flag to switch. Within your SDL_KEYDOWN and SDL_KEYUP you normally would also check which key has been pressed and act accordingly. You can do this like this:
if(event.key.keysym.sym == SDLK_UP) ...
This would check if the keycode for the "Up Cursor" has been pressed or not. There is a difference between a key scancode and a keycode, for which I would advise you to check the latter as shown above. You can find all these SDL defined keycodes here (look at the rightmost column): https://wiki.libsdl.org/SDL_Keycode
Please note that the event handler can grow quite rapidly if you are checking for different keys and different key events, so I'd suggest that you implement an extra class or other means of encapsulation once you are comfortable with the event mechanism of SDL.
SDL_PollEvent returns true or false. https://wiki.libsdl.org/SDL_PollEvent
If SDL_PollEvent returns true, then 'event' is set. Then do your switch on event.type instead.
Related
Pressing CTRL+C while in the terminal sometimes fails to end the program:
int main(int argc, char *argv[]) {
DrawingWindow window = DrawingWindow(WIDTH, HEIGHT, false);
SDL_Event event;
while(true) {
if (window.pollForInputEvents(event)) handleEvent(event);
draw();
window.renderFrame();
}
}
}
bool DrawingWindow::pollForInputEvents(SDL_Event &event) {
if (SDL_PollEvent(&event)) {
if ((event.type == SDL_QUIT) || ((event.type == SDL_KEYDOWN) && (event.key.keysym.sym == SDLK_ESCAPE))) {
SDL_DestroyTexture(texture);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
printMessageAndQuit("Exiting", nullptr);
}
SDL_Event dummy;
// Clear the event queue by getting all available events
// This seems like bad practice (because it will skip some events) however preventing backlog is paramount !
while (SDL_PollEvent(&dummy));
return true;
}
return false;
}
void printMessageAndQuit(const std::string &message, const char *error) {
if (error == nullptr) {
std::cout << message << std::endl;
exit(0);
} else {
std::cout << message << " " << error << std::endl;
exit(1);
}
}
When CTRL+C succeeds (which is most of the time), it does print "Exiting".
I don't know what handleEvent supposed to do, and your code have braces mismatch so looks like it isn't even your actual code. As written, pollForInputEvents fetches one event, checks if it could be considered a quit condition, and then discards all other events in the queue regardless. If event you're looking for happens to be non-first (e.g. as first event you may have keydown of 'ctrl' or mouse move, or window close event, ...), then it never gets processed. handleEvent has the same problem - it gets only first event in frame.
Generally speaking, event processing have to happen in a fast non-blocking loop, which handles events faster than new events are getting in. Processing only one event per frame is a not fast enough, and there is absolutely no reason to process only one event. Events gets accumulated in a queue, you process all events, draw your image frame (potentially slow), present on screen (potentially blocking on vsync), then repeat.
It looks counterintuitive that your event processing is split into two functions.
As I don't see the rest of your code, with "minimal" changes it should be something like:
#include <SDL2/SDL.h>
#include <iostream>
#define WIDTH 640
#define HEIGHT 480
static void handleEvent(SDL_Event &event) {}
static void draw(SDL_Renderer *renderer) {
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
}
class DrawingWindow {
public:
DrawingWindow(int width, int height, bool fs) {
SDL_Init(SDL_INIT_VIDEO);
window = SDL_CreateWindow("test",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
width, height, 0);
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC);
}
bool pollForInputEvents(SDL_Event &event);
void renderFrame() {
SDL_RenderPresent(renderer);
}
SDL_Window *window;
SDL_Renderer *renderer;
};
void printMessageAndQuit(const std::string &message, const char *error) {
if (error == nullptr) {
std::cout << message << std::endl;
exit(0);
} else {
std::cout << message << " " << error << std::endl;
exit(1);
}
}
bool DrawingWindow::pollForInputEvents(SDL_Event &event) {
if (SDL_PollEvent(&event)) {
if ((event.type == SDL_QUIT) || ((event.type == SDL_KEYDOWN) && (event.key.keysym.sym == SDLK_ESCAPE))) {
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
printMessageAndQuit("Exiting", nullptr);
}
return true;
}
return false;
}
int main(int argc, char *argv[]) {
DrawingWindow window = DrawingWindow(WIDTH, HEIGHT, false);
SDL_Event event;
while(true) {
while(window.pollForInputEvents(event)) handleEvent(event);
draw(window.renderer);
window.renderFrame();
}
}
I have a event handler class that adds all events to a vector. This vector is cleared after each frame. However if it is cleared after each frame the events cannot be detected outside of the handle function of the event handler class. If I don't clear the vector at the end of each frame. The program becomes unresponsive. Although I can check that my for loop is reading the vector of events for a while until the vector becomes for to clogged up for the program to make sense of it.
My questions are:
Is the use of a for loop to iterate over the events a good way to check for these events? Or would it be more useful to use a while loop to check this vector for events much like you would using while(SDL_PollEvents(&e))?
Why would my program become unresponsive when I am checking the events within the vector when I am not clearing the vector?
How can I efficently check my vector eventList outside of the class. Where I could check for keydown events or similar using the vector and also clearing the vector after I have checked for the events without the program becoming unresponsive?
This is my main function where I am trying to read these events without the above mentioned problems.
int main(int argc, char* argv[]){
EventHandler handler;
SDL_SetRenderDrawColor(handler.render, 49, 49, 49, 255);
SDL_RenderClear(handler.render);
SDL_RenderPresent(handler.render);
if(handler.eventList.size() >= 1){
std::cout << "Detected an event: " << handler.eventList.size() << "\n";
}
for(std::vector<SDL_Event>::const_iterator i = handler.get().begin(); i != handler.get().end(); i++){
if(i->type == SDL_KEYDOWN){
if (i->key.keysym.sym == SDLK_w){
std::cout << "You pressed the W key\n";
}
}
}
handler.eventList.clear();
handler.handle();
}
return 0;
}
This is where I want to be able to check the events thats in the eventList
for(std::vector<SDL_Event>::const_iterator i = handler.get().begin(); i != handler.get().end(); i++){
if(i->type == SDL_KEYDOWN){
if (i->key.keysym.sym == SDLK_w){
std::cout << "You pressed the W key\n";
}
}
}
This is a simplified version of my eventHandler class
class EventHandler {
public:
bool quit;
SDL_Event event;
SDL_Window* window;
SDL_Renderer* render;
int width;
int height;
std::vector<SDL_Event> eventList;
EventHandler()
{
//Iniliaze SDL
if (SDL_Init(SDL_INIT_EVERYTHING) != 0){
std::cout << "SDL Error: " << SDL_GetError();
}
window = SDL_CreateWindow("Fallen Planets", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1024, 1080, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
if (window == NULL){
std::cout << "SDL Error: " << SDL_GetError();
SDL_Quit();
quit = true;
}
render = SDL_CreateRenderer(window, -1, 0);
if (render == NULL){
SDL_DestroyWindow(window);
std::cout << "SDL Error: " << SDL_GetError();
SDL_Quit();
}
//Initialize quit bool
quit = false;
}
void handle(){
while (SDL_PollEvent(&event)){
eventList.push_back(event); // This adds the events to the eventList
if (event.type == SDL_QUIT){
SDL_DestroyWindow(window);
SDL_DestroyRenderer(render);
TTF_Quit();
SDL_Quit();
quit = true;
}
if (event.window.event == SDL_WINDOWEVENT_RESIZED){
width = SDL_GetWindowSurface(window)->w;
height = SDL_GetWindowSurface(window)->h;
}
//Update Mouse State
SDL_GetMouseState(&mouseX, &mouseY);
for (std::vector<SDL_Event>::const_iterator i = eventList.begin(); i != eventList.end(); ++i){
if (i->type == SDL_KEYDOWN){
if (i->key.keysym.sym == SDLK_w){
std::cout << "w key pressed\n";
}
}
}
}
std::vector<SDL_Event> get(){
return eventList;
}
void flush_events(){
eventList.clear();
}
};
The most effective way is to have some functions that are called for each event, whether that be mouse movement, mouse clicks, window resizes ect, and those should be handled in your main event loop by calling while (SDL_PollEvent(&event)) then have some if statements for each event and call the corresponding functions when those if statements are reached. SDL does this because you may have more then 1 event at the same time, ie, moving the mouse while pressing 'w' like any fps game. You can (im guessing by your example) just appending them to a vector within that event polling loop but then that defeats the purpose of having it loop through the events as youll just have to end up doing that to your vector anyway. You should just make some API variables that are set to some value when a specific event occurs, for example, instead of having functions, just have static variables such as static unsigned int mousePosition[2] and set [0] to the x mouse position an [1] to the y mouse position and then use that somewhere in the program when you need the mouse coordinates, or just even static SDL_Point mousePosition = {x, y} in that example.
Eg: (with a singleton class)
#pragma once
#ifndef EVENTS_H
#define EVENTS_H
#include <iostream>
#include <string>
#include <SDL2/SDL.h>
class Events ///Singleton
{
public:
Events(const Events&) = delete;
Events(Events&&) = delete;
Events& operator=(const Events&) = delete;
Events& operator=(Events&&) = delete;
static const bool& Display_Changed_Size();
///Mouse
static const SDL_Point& Mouse_Pos();
static const bool& Scrolled_Down();
static const bool& Scrolled_Up();
///Keyboard
static const std::string& Get_Text_Input();
static const bool& Pasted_Text();
static const bool& Copied_Text();
static const bool& Backspace();
private:
Events();
static Events& Get_Instance();
///Allow Main to access private members. Works well, one instance, only called once for those functions too. in Main
friend class Main;
///For event handling
static void Event_Loop();
///For event handling
static void Reset_Events();
///For quitting, used main only
static const bool& Quit_Application();
///For Event_Loop()
int eventLoopCounter = 0; ///To ensure Event_Loop() doesn't get used twice in the same loop
SDL_Event event;
bool m_quit = false;
bool m_Display_Changed_Size = false;
///Mouse
SDL_Point m_Mouse_Pos = {0,0};
bool m_Scrolled_Up = false;
bool m_Scrolled_Down = false;
///Keyboard
std::string m_Text_Input = "";
bool m_Copied_Text = false;
bool m_Pasted_Text = false;
bool m_Backspace = false;
};
#endif // EVENTS_H
and the .cpp
#include "events.h"
Events::Events()
{
std::cout << "Events constructor called\n";
}
Events& Events::Get_Instance()
{
static Events instance;
return instance;
}
void Events::Event_Loop()
{
if (Get_Instance().eventLoopCounter == 0)
{
Get_Instance().eventLoopCounter += 1;
while (SDL_PollEvent(&Get_Instance().event) != 0)
{
if (Get_Instance().event.type == SDL_QUIT)
{
Get_Instance().m_quit = true;
break;
}
if (Get_Instance().event.type == SDL_WINDOWEVENT){
if(Get_Instance().event.window.event == SDL_WINDOWEVENT_RESIZED) {
Get_Instance().m_Display_Changed_Size = true;
}
}
///Mouse
if (Get_Instance().event.type == SDL_MOUSEMOTION)
{
Get_Instance().m_Mouse_Pos = {Get_Instance().event.motion.x, Get_Instance().event.motion.y};
}
if (Get_Instance().event.type == SDL_MOUSEWHEEL){
if (Get_Instance().event.wheel.y > 0){ ///Scrolling up here
Get_Instance().m_Scrolled_Up = true;
}
if (Get_Instance().event.wheel.y < 0){ ///Scrolling down here
Get_Instance().m_Scrolled_Down = true;
}
}
///Keyboard
if (Get_Instance().event.type == SDL_TEXTINPUT)
{
Get_Instance().m_Text_Input = Get_Instance().event.text.text;
break; ///Break here for multiple key presses registered at once
}
///Keydown
if (Get_Instance().event.type == SDL_KEYDOWN)
{
///Handle copy
if( Get_Instance().event.key.keysym.sym == SDLK_c && SDL_GetModState() & KMOD_CTRL )
{
Get_Instance().m_Copied_Text = true;
}
///Handle paste
if( Get_Instance().event.key.keysym.sym == SDLK_v && SDL_GetModState() & KMOD_CTRL )
{
Get_Instance().m_Pasted_Text = true;
}
if (Get_Instance().event.key.keysym.sym == SDLK_BACKSPACE)
{
Get_Instance().m_Backspace = true;
}
}
}
}
else
{
std::cout << "Called Events::Event_Loop(); more than once\n";
}
}
void Events::Reset_Events()
{
Get_Instance().eventLoopCounter = 0;
Get_Instance().m_quit = false;
Get_Instance().m_Display_Changed_Size = false;
///Mouse
Get_Instance().m_Scrolled_Down = false;
Get_Instance().m_Scrolled_Up = false;
///Keyboard
Get_Instance().m_Text_Input = "";
Get_Instance().m_Pasted_Text = false;
Get_Instance().m_Copied_Text = false;
Get_Instance().m_Backspace = false;
}
const bool& Events::Quit_Application()
{
return Get_Instance().m_quit;
}
const bool& Events::Display_Changed_Size()
{
return Get_Instance().m_Display_Changed_Size;
}
///Mouse
const SDL_Point& Events::Mouse_Pos()
{
return Get_Instance().m_Mouse_Pos;
}
const bool& Events::Scrolled_Down()
{
return Get_Instance().m_Scrolled_Down;
}
const bool& Events::Scrolled_Up()
{
return Get_Instance().m_Scrolled_Up;
}
///Keyboard
const std::string& Events::Get_Text_Input()
{
return Get_Instance().m_Text_Input;
}
const bool& Events::Pasted_Text()
{
return Get_Instance().m_Pasted_Text;
}
const bool& Events::Copied_Text()
{
return Get_Instance().m_Copied_Text;
}
const bool& Events::Backspace()
{
return Get_Instance().m_Backspace;
}
I understand its alot of code but this type of implementation is what I use when I use SDL2, its not prone to any errors as its just a singleton, noone can instantiate and hence modify members. I would modify the line of friend class Main since its there so my main loop class can call the private functions. Even something like friend int main(int argc, char* argv[]); so the int main() can call it or something as such. In order to use it, just include the header "events.h" in anywhere you need events to be called
Usage in mainloop, assuming Mainloop is a function or class and is a friend of Events, just change the existing friend code in the header
Mainloop:
Events::Event_Loop();
.. Code
Events::Reset_Events();
In other files that need events:
if ( SDL_PointInRect( &Events::Mouse_Pos(), &rct) ) {} //example
My program generates an image. I want to close the window by clicking on any key or by clicking with my pointer on the window cross.
It works only by clicking on the cross (Use of SDL_QUIT) but not with SDL_KEYDOWN. I tried also with SDL_SPACE, SDL_KEYUP but the result is the same.
So what am I missing in my code ?
I am using xcode.
int main(int argc, char *argv[])
{
SDL_Surface *ecran = NULL, *imageDeFond = NULL, *zozor = NULL;
SDL_Rect positionFond, positionZozor;
SDL_Event event;
int continuer = 1;
SDL_Init(SDL_INIT_VIDEO);
ecran = SDL_SetVideoMode(800, 600, 32, SDL_HWSURFACE);
imageDeFond = SDL_LoadBMP("lac_en_montagne.bmp");
SDL_BlitSurface(imageDeFond, NULL, ecran, &positionFond);
SDL_Flip(ecran);
while (continuer)
{
SDL_WaitEvent(&event);
switch(event.type)
{
case SDL_KEYDOWN:
{
printf("use of SDL_KEYDOWN");
continuer=0;
break;
}
case SDL_QUIT:
{
printf("use of SDL_QUIT");
continuer = 0;
break;
}
}
}
SDL_FreeSurface(imageDeFond);
SDL_Quit();
return EXIT_SUCCESS;
}
I am trying to catch the event for F1 key in SDL in C++.
But,somehow i cannot see any changes after pressing the F1 key.But,when i wnat to toggle my animation objects on the screen i make use SDL_t case to do so,and it is executing fine.
Just not geting why is it not taking the F1 key.
Here is the switch case for handling key press events:
SDL_Event event;
bool done = false;
bool keyPress = false;
while ( not done ) {
draw();
SDL_Flip(screen);
SDL_PollEvent(&event);
if (event.type == SDL_QUIT) { break; }
if(event.type == SDL_KEYUP) { keyCatch = false; }
if(event.type == SDL_KEYDOWN) {
switch ( event.key.keysym.sym ) {
case SDLK_ESCAPE : done = true; break;
case SDLK_q : done = true; break;
case SDLK_F1 : {
if(!keyCatch){
keyPress=true;
//this method is from another class.
io.printMessageAt("F1 is selected:",50,10);
currentOrb = (currentOrb+1) % orbs.size();
}
break;
}
case SDLK_t : case SDLK_SPACE :
if ( !keyPress ) {
//something
}
break;
case SDLK_p : {
if (!keyPress) {
keyPress = true;
//something
}
break;
}
default : break;
}
}
}
NOTE: i cannot give the entire code here.The above is my part what i am trying to do.
Double-check that your window manager or a hotkey daemon isn't consuming your F1's before they hit your process.
This works fine (F1 closes the program) on my system:
#include <SDL.h>
int main( int, char** )
{
SDL_Init(SDL_INIT_EVERYTHING);
SDL_Surface* screen = SDL_SetVideoMode(640, 480, 32, SDL_ANYFORMAT);
SDL_Event event;
bool done = false;
bool keyPress = false;
while ( !done )
{
SDL_Flip(screen);
SDL_PollEvent(&event);
if(event.type == SDL_QUIT)
{
break;
}
if(event.type == SDL_KEYUP)
{
keyPress = false;
}
if(event.type == SDL_KEYDOWN)
{
switch ( event.key.keysym.sym )
{
case SDLK_ESCAPE:
case SDLK_q:
done = true;
break;
case SDLK_F1:
if(!keyPress)
{
keyPress=true;
done = true;
}
break;
case SDLK_t:
case SDLK_SPACE:
if ( !keyPress )
{
//something
}
break;
case SDLK_p:
if (!keyPress)
{
keyPress = true;
//something
}
break;
default:
break;
}
}
}
SDL_Quit();
return 0;
}
I did notice that keyCatch was undefined. I replaced it with keyPress.
I am completely stumped as to why this code does not get any SDL keypress events. The other SDL events (removed for clarity) work fine. It does not work on my XP or Vista machines. No compile/link errors, just never recieve a keydown event.
#include "SDL/SDL.h"
// Yes SDL.lib and SDLmain.lib are linked
Uint32 TimeLeft(void)
{
static Uint32 next_time = 0;
Uint32 now;
now = SDL_GetTicks();
if ( next_time <= now ) {
next_time = now + tickInterval;
return 0;
}
return(next_time-now);
}
int main( int argc, char **argv )
{
if( -1 == SDL_Init( SDL_INIT_EVERYTHING ) )
{
cerr << "Error: SDL_Init failed" << endl;
return -1;
}
SDL_Event event;
bool quit = false;
while( !quit )
{
while( SDL_PollEvent( &event ) )
{
switch( event.type )
{
case SDL_KEYDOWN:
switch( event.key.keysym.sym )
{
case SDLK_ESCAPE:
case SDLK_q:
quit = true;
break;
default:
break;
}
break;
case SDL_JOYAXISMOTION:
// stuff removed
break;
case SDL_QUIT:
quit = true;
break;
default:
break;
}
}
SDL_Delay( TimeLeft() );
}
SDL_Quit();
return 0;
}
You'll need to create a window with SDL_SetVideoMode to get mouse and keyboard events.
I don't think you'll have luck trying to SDL_WM_GrabInput the mouse and keyboard without a window. It may also raise security alerts the first time on moderm Windows machines.