# SDL - Getting a single keypress event instead of a keystate?

Right now I'm working on a simple SDL project, but I've hit an issue when trying to get a single keypress event to skip past a splash screen. Right now, there are 4 start-up splash screens that I would like to be able to skip with a single keypress (of any key). My issue is that, as of now, if I hold down a key, it skips through each splash screen to the very last one immediately.

The splash screens are stored as an array of SDL surfaces which are all loaded at the initialization of the state. I have an variable called currentSplashImage that controls which element of the array is being rendered on the screen. I've set it up so that whenever there's a SDL_KEYDOWN event, it triggers a single incrementation of the currentSplashImage variable.

So, I'm really not sure why my code isn't working correctly. For some reason, when I hold down a button, it seems to be treating the held button as a new key press event every time it ticks through the code. Does anyone know how I can go about fixing this issue?

[Here's a snippet of code that I've been using...]

void SplashScreenState::handleEvents()
{
SDL_PollEvent( &localEvent );

if ( localEvent.type == SDL_KEYDOWN )
{
if ( currentSplashImage < 3 && currentSplashImage >= 0)
{ currentSplashImage++; }
}
else if ( localEvent.type == SDL_QUIT )
{
smgaEngine.setRunning(false);
}
}


I should also mention that the SDL_Event 'localEvent' is part of the GameState parent class, while this event handling code is part of a SplashScreenState subclass.

If anyone knows why this is happening, or if there is any way to improve my code, It'd be helpful to me! :D I'm still a very new programmer, trying to learn.

UPDATE:

I added a std::cout line to that the code runs multiple times with a single KEYDOWN event. I also tried disabling SDL_EnableKeyRepeat, but it didn't fix the issue.

void SplashScreenState::handleEvents()
{
SDL_PollEvent( &localEvent );

if ( localEvent.type == SDL_KEYDOWN )
{
if ( currentSplashImage < 3 && currentSplashImage >= 0)
{
currentSplashImage++;
std::cout << "KEYDOWN.."; //<---- test cout line
}
}
else if ( localEvent.type == SDL_QUIT )
{
smgaEngine.setRunning(false);
}
}


This prints out "KEYDOWN..KEYDOWN..KEYDOWN.." in the cout stream when a button is held.

SDL_PollEvent will not change the content of the event you pass through if there is no new event to report. This means that type == SDL_KEYDOWN will stay true until some other event arrives.

You need to check the value returned from SDL_PollEvent and only continue on to your event handling code when there is a new event to handle.

eg.

if (SDL_PollEvent( &localEvent ) == 1)
{
if ( localEvent.type == SDL_KEYDOWN )
{
if ( currentSplashImage < 3 && currentSplashImage >= 0)
{
currentSplashImage++;
}
}
else if ( localEvent.type == SDL_QUIT )
{
smgaEngine.setRunning(false);
}
}


You may also consider using SDL_WaitEvent instead, which pauses the app until another event is available.

• +1. After reading the SDL documentation about how to handle the keyboard (specifically here) this seems to be the problem. – llonesmiz Sep 10 '12 at 12:09
• I see! That's gotta be the issue. Thanks so much for your insight! :D – MrKatSwordfish Sep 10 '12 at 12:35
• I'm curious.. Would it be possible (or smart) to create an SDL_UserEvent that essentially adds an empty event to the queue using SDL_PushEvent? That way, I could simply push an empty event to the queue after each keypress is registered? -- Also, would SDL_WaitEvent pause the program? I'd like to have the ability to use animated screens also, but maybe I shouldn't be looking for a one-size-fits-all solution... – MrKatSwordfish Sep 10 '12 at 23:23
• Why would you need an empty event? And yes, SDL_WaitEvent paused the program - but you're in a splash screen state, so that doesn't sound like it would be a problem. – Kylotan Sep 10 '12 at 23:40
• All you need to do, is wrap all your code that looks at localEvent in an if statement, where the if statement only executes that code if SDL_PollEvent returns 1. If it doesn't return 1, you don't run your event handling code, as there is no event to handle. I edited the answer to try and make this clear. – Kylotan Sep 11 '12 at 11:58

Does simply checking for an SDL_KEYUP not work? You can't have an SDL_KEYUP before an SDL_KEYDOWN.

If SDL_EnableKeyRepeat(0, 0); is not working for you, then you should assume it is not reliable and not use it. Build your own key-repeat handling logic like lionesmiz hinted at.

If I were you I'd build an abstraction layer on top of this. Polling for SDL events is an app-wide thing, and you should be asking an input module (something like Input::WasKeyReleasedThisFrame(SDLKey somekey);) about the state of the keyboard. Keep a std::map that maps SDLKey values to some sort of KeyState value so you poll once every frame, then all the rest of your code just goes to this input module for this information.

But however you do it, just provide some sort of sensible API for your state classes to use. Why keep it wrapped up inside this SDL_Event nonsense? You want INPUT dammit!

Something like this should work:

void SplashScreenState::handleEvents()
{
SDL_PollEvent( &localEvent );

if ( localEvent.type == SDL_KEYDOWN )
{
if(!keyIsAlreadyPressed)//member variable--should be initialized to false
{
if ( currentSplashImage < 3 && currentSplashImage >= 0)
{ currentSplashImage++; }
}
}
else if ( localEvent.type == SDL_KEYUP )
{