This article was originally posted as “SDL2: Keyboard and Mouse Movement (Events)” at Programmer’s Ranch on 12th February 2014. It is slightly updated here. The source code and spaceship bitmap are available at the Gigi Labs BitBucket repository.
Hi there! 🙂
In this article, we’ll learn how to handle keyboard and mouse events, and we’ll use them to move an object around the window. Hooray! 🙂
We’ll start with an image. I made this 64×64 bitmap:
As you can see, I can’t draw to save my life. But since this is a bitmap, we don’t need the SDL_image extension.
Once you have an image, you’ll want to create a new Visual Studio project, wire it up to work with SDL2, and then add some code to display a window and the spaceship in it. If you don’t remember how, these past articles should help:
- “Setting up SDL2 with Visual Studio 2015“
- “Showing an Empty Window in SDL2“
- “Displaying an Image in an SDL2 Window“
You should end up with code that looks something like this:
#include <SDL.h> int main(int argc, char ** argv) { // variables bool quit = false; SDL_Event event; int x = 288; int y = 208; // init SDL SDL_Init(SDL_INIT_VIDEO); SDL_Window * window = SDL_CreateWindow("SDL2 Keyboard/Mouse events", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, 0); SDL_Renderer * renderer = SDL_CreateRenderer(window, -1, 0); SDL_Surface * image = SDL_LoadBMP("spaceship.bmp"); SDL_Texture * texture = SDL_CreateTextureFromSurface(renderer, image); SDL_FreeSurface(image); SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); // handle events while (!quit) { SDL_WaitEvent(&event); switch (event.type) { case SDL_QUIT: quit = true; break; } SDL_Rect dstrect = { x, y, 64, 64 }; SDL_RenderClear(renderer); SDL_RenderCopy(renderer, texture, NULL, &dstrect); SDL_RenderPresent(renderer); } // cleanup SDL SDL_DestroyTexture(texture); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); return 0; }
You’ll also want to place spaceship.bmp into your Debug folder (along with SDL2.dll) so that the program can find the files it needs..
Once you actually run the program, you should see this:
I set the window’s background to white to match the spaceship’s white background by setting the clear colour using SDL_SetRenderDrawColor(), and then calling SDL_RenderClear() to clear the window to that colour.
In order to handle keyboard and mouse events, we need an SDL_Event. Well, what do you know: we have been using one all along, in order to take action when the window is closed. What we need now is to handle a different event type. So let’s add a new case
 statement after the one that handles SDL_QUIT
:
case SDL_KEYDOWN: break;
Within this case
 statement, let us now determine which key was actually pressed, and move the spaceship accordingly:
case SDL_KEYDOWN: switch (event.key.keysym.sym) { case SDLK_LEFT: x--; break; case SDLK_RIGHT: x++; break; case SDLK_UP: y--; break; case SDLK_DOWN: y++; break; } break;
If you now run the program, you’ll find that you can move the spaceship using the arrow keys:
You’ll notice that it seems to move pretty slowly, and you have to keep pressing for quite a while to make any significant movement. Now, in your code, replace SDL_WaitEvent with SDL_PollEvent:
SDL_PollEvent(&event);
Now, try running it again:
Swoosh! In less than half a second, the spaceship hits the edge of the window. It’s actually too fast. To get this down to something manageable, add a small delay at the beginning of your while
 loop:
SDL_Delay(20);
SDL_PollEvent() is better than SDL_WaitEvent() when you want to continuously check (i.e. poll) for events, but it consumes more CPU power (you can see this if you open Task Manager). SDL_WaitEvent() is okay when your window is mostly sitting idle so you don’t need to check for events that often.
Handling mouse events is also very easy. All you need to do is handle the appropriate event. Let’s see how to handle a mouse click:
case SDL_MOUSEBUTTONDOWN: switch (event.button.button) { case SDL_BUTTON_LEFT: SDL_ShowSimpleMessageBox(0, "Mouse", "Left button was pressed!", window); break; case SDL_BUTTON_RIGHT: SDL_ShowSimpleMessageBox(0, "Mouse", "Right button was pressed!", window); break; default: SDL_ShowSimpleMessageBox(0, "Mouse", "Some other button was pressed!", window); break; } break;
And this is what happens when you run it, and click somewhere in the window:
You can also track mouse motion and obtain the current coordinates of the mouse pointer. This is useful when moving things with the mouse (e.g. moving an object by mouse-dragging it). The following code obtains the mouse coordinates and displays them in the window title:
case SDL_MOUSEMOTION: int mouseX = event.motion.x; int mouseY = event.motion.y; std::stringstream ss; ss << "X: " << mouseX << " Y: " << mouseY; SDL_SetWindowTitle(window, ss.str().c_str()); break;
Note that you’ll need to add the following at the top of your main.cpp to make the above code work:
#include <sstream>
You will now notice the mouse coordinates in the window title:
Wonderful! You should now have an idea of how to capture and handle keyboard and mouse events in SDL2. We will see more of these in an upcoming article dealing with drawing pixels in the window, so stay tuned to learn more! 🙂
Awesome 😀 now I feel just like a child who got a new LEGO set for Christmas
Beautiful, and, even more important: very useful!
What is ‘sstream’ and is there an alternative not using this library? I don’t think I have it unless its included in C and I don’t know how to link it, either way, an explanation on how to use sstream would be useful.
sstream is a String Stream and its only available in C++. I think its only used to log output in this case so using C you could just use printf.
How do you get around “Expected expression” on the int declarations?
I tried it with wasd and only a and s are working
This was really helpful.
This was really helpful.