SFML Event Handling
Welcome to SFML tutorial 3. In this lesson, we discuss event handling, and see how we can handle input using events.
The Video
The video was split into two parts due to length considerations. Here they are, and I suggest you head over to YouTube to watch them in HD for the best viewing experience.
Read on for the textual tutorial!
Recap of Initial Program
Let’s recall how we handled events in our basic application. We had the following code inside our main loop.
sf::Event Event;
while (Window.PollEvent(Event))
{
switch (Event.Type)
{
case sf::Event::Closed:
Window.Close();
break;
default:
break;
}
}
We create an sf::Event structure to describe events, and then load event data into it with the sf::Window::PollEvent function, which accepts a reference to the event structure as its single argument. This function returns bool: true if there are more events in the window’s queue and false otherwise. This means we can use the while loop to loop through all the queued events.
We then use a switch statement to test the value of sf::Event::Type, which describes the type of event. We had one case block, for the value sf::Event::Closed (dispatched when the user clicks the red cross in the window’s corner, or presses ALT+F4, etc). If this event was received, we closed the window with the sf::Window::Close member function.
Handling Other Events
All other events are currently handled by the empty default block. Clearly, to add handlers for extra events, we need only add additional case blocks.
Preliminaries
In this tutorial, we shall be using console I/O, so please include the iostream header in your program.
#include <iostream>
Let’s add one extra line to the sf::Event::Closed handler:
std::cout << "The window was closed." << std::endl;
Now it’s time to handle some other events. So what other events are there? Well you can see them all in the official SFML 2.0 documentation, but we’ll discuss a good few now.
Lost and Gained Focus
Firstly, we have the sf::Event::LostFocus and sf::Event::GainedFocus events, which signify lost and gained focus of your window. You window loses focus when it is active and the user clicks outside the window; it gains focus when it is inactive and the user clicks inside the window. Let’s add some simple handlers for these events – insert the following additional case blocks into your switch statement.
case sf::Event::GainedFocus: std::cout << "The window gained focus." << std::endl; break; case sf::Event::LostFocus: std::cout << "The window lost focus." << std::endl; break;
If the window gains, or loses, focus, we simply output a notification of this to the standard output. Obviously, in a real world program, this wouldn’t be a useful handler, but it does show you how to catch the events.
Resizing
Next, consider the sf::Event::Resized event, dispatched when the window is resized. We shall proceed in a similar manner to the focus events – by outputting a simple notification to the console.
case sf::Event::Resized: std::cout << "The window was resized to X." << std::endl; break;
Keyboard and Mouse
Easy enough to understand I think, in fact exactly the same as before. But what about keyboard and mouse events? These are some of the most important, especially for a game.
Keyboard Events
First we consider the keyboard, and two events sf::Event::KeyPressed and sf::Event::KeyReleased. The first is dispatched when the user depresses any key (i.e. pushes it down, not necessarily let’s go again), and the second when the user releases any key. Here’s the relevant code.
case sf::Event::KeyPressed: std::cout << "A key was pressed." << std::endl; break; case sf::Event::KeyReleased: std::cout << "A key was released." << std::endl; break;
Mouse Events
Again, it is just as before. Now for the mouse. We shall consider three events, the two of which are analogous to the key events – sf::Event::MouseButtonPressed and sf::Event::MouseButtonReleased. The first is dispatched when the user depresses any mouse button, and the second when the user releases one. This time, however, there is one additional event: sf::Event::MouseMoved, which is dispatched whenever the mouse is moved.
case sf::Event::MouseButtonPressed: std::cout << "A mouse button was pressed." << std::endl; break; case sf::Event::MouseButtonReleased: std::cout << "A mouse button was released." << std::endl; break; case sf::Event::MouseMoved: std::cout << "The mouse was moved." << std::endl; break;
Again, everything is the same in each case – we catch and event and display a notification on the console. Well we’re half done now – our program doesn’t really do anything useful, but it will illustrate the capture of event signals, and the execution of code depending on the received event.
Events in Action
Go ahead and run your program now. Play around: give the window focus, remove focus from it, resize it, click and move the mouse, press keyboard buttons. You should get all the desired notifications. Note that when holding a keyboard key, one gets many notifications. There are also many, incremental notifications for the resize event and the mouse move event. Finally, there will be a console notification when you close the window.
Event Data
At this stage, you may reasonably have a question. Lost focus, closed and gained focus events are easy to understand – no additional information is needed to fully describe them. But what about keypresses, mouseclicks, resize events? Well the SFML sf::Event structure has more than one member.
As well as the sf::Event::Type member, there are many structures for describing different events, and a union of these structures, to provide this data when an event signal is received. Let’s now go through our handlers again, adding output lines which use this information.
Resizing
When we resize a window, it would be nice to know what the new size is, right? Well the structure sf::Event::SizeEvent has the answer. It contains two variables X and Y, which provide the new width and height (respectively) of the window. The union member of type sf::Event::SizeEvent is called Size, so to get at this data, if our sf::Event is called Event, we would access the members like this: Event.Size.X and Event.Size.Y.
Change your sf::Event::Resized case block to the following:
case sf::Event::Resized: std::cout << "The window was resized to X=" << Event.Size.Width << " Y=" << Event.Size.Height << "." << std::endl; std::cout << "The width/height from the window get functions: " << Window.GetWidth() << "/" << Window.GetHeight() << std::endl; break;
We access the new width and height of the window and display them to the screen as part of the resize notification. There is, however, a second line of output, so you may wonder what this is to demonstrate. Well, as you will see, in this second line, we get the width and height using the window’s get functions rather than the event structure.
This is merely to illustrate that it is possible, but I recommend you use the event structure in this context, as there is a slight discrepancy between the value given by the structure, and the value given by the window’s get functions.
Keyboard and Mouse
Detecting Particular Keys
Obviously, we need to know more than just that a key was pressed or released. It is important to know which key as well! For this, there is the sf::Event::KeyEvent structure, and the union member sf::Event::Key. This structure has five members. One, called Code is an enumeration type sf::Key::Key, describing the key pressed itself; the other four are booleans Control, Alt, Shift and System. They signify whether those keys were pressed along with the key represented by the code.
Catching Backspace Press
Let’s now change our key pressed handler to notice in particular if the backspace key was pressed.
case sf::Event::KeyPressed: std::cout << "A key was pressed." << std::endl; if (Event.Key.Code == sf::Key::Back) std::cout << "Backspace was pressed." << std::endl; break;
We check the key code – sf::Event::Key::Code - to see if it equals sf::Key::Back, which represents backspace. If it does, we output an additional notification. Note that all key codes are inside the sf::Key namespace.
Catching Control + S Release
Now let’s change our key released handler to notice if control + S is pressed.
case sf::Event::KeyReleased: std::cout << "A key was released." << std::endl; if ((Event.Key.Code == sf::Key::S) && (Event.Key.Control == true)) std::cout << "Control+S was released." << std::endl; break;
We check the key code to see if the S key was release. If so, we further check sf::Event::Key::Control to see if the control key was active when S was released (or perhaps just before…). If so we display a specialised notification as before.
Managing Mouse Events
We also want additional data for mouse events. In this case, we still want to know which mouse button has been pressed or released, but perhaps more significantly, we need to know the location of the mouse cursor, in the event of a mouse click or otherwise.
Handling Movement
First, we’ll consider the handling of mouse movement, irrespective of button clicks and/or releases. We’ll be building on our handler for the sf::Event::MouseMoved event. The structure describing the mouse position is called sf::Event::MouseMoveEvent, and the union member is called sf::Event::MouseMove. It has two members: X and Y to provide the new X and Y coordinates of the mouse.
Let’s change our case block for sf::Event::MouseMoved as follows.
case sf::Event::MouseMoved: std::cout << "The mouse was moved to X=" << Event.MouseMove.X << " Y=" << Event.MouseMove.Y << "." << std::endl; break;
We now output the new position of the mouse cursor. Everything should be fairly simple to follow given what we’ve already discussed above.
Catching Left Mouse Button Press
As well as mouse movement, we want to catch specific mouse keys just as we would catch specific keyboard keys. The structure describing a mouse button press is called sf::Event::MouseButtonEvent, and the union member is called sf::Event::MouseButton. This structure still has the members X and Y to describe the mouse cursor position. It also has a member named Button which specifies the mouse button pressed. All the mouse buttons are contained within the sf::Mouse namespace.
Change the sf::MouseButtonPressed case block to the following.
case sf::Event::MouseButtonPressed: std::cout << "A mouse button was pressed." << std::endl; if (Event.MouseButton.Button == sf::Mouse::Left) std::cout << "The left mouse button was pressed at X=" << Event.MouseButton.X << "Y=" << Event.MouseButton.Y << "." << std::endl; break;
We check the pressed button – provided by Event.MouseButton.Button - and see if it is the left mouse button – sf::Mouse::Left. If so we output a notification which includes the mouse cursor X & Y positions at the time of the press – given by Event.MouseButton.X and Event.MouseButton.Y.
Catching Middle Mouse Button Release
We shall now similarly handle the release of the middle mouse button. The structure which describes a mouse button released event is the same as the structure describing a mouse button pressed event.
case sf::Event::MouseButtonReleased: std::cout << "A mouse button was released." << std::endl; if (Event.MouseButton.Button == sf::Mouse::Middle) std::cout << "The middle mouse button was released at" << "X=" << Event.MouseButton.X << "Y=" << Event.MouseButton.Y << "." << std::endl;
This is much the same as last time, but with sf::Mouse::Middle instead of sf::Mouse::Left. Clearly you could do something similar for any other mouse button such as sf::Mouse::Right or sf::Mouse::XButton1.
A Better Approach to Keyboard and Mouse Events – Switch Statements
There is one final consideration for handling key presses and mouse clicks. Once you are handling lots of different buttons, using if statement will become very messy. Thus you should really use switch statements to test Event.MouseButton.Button and Event.Key.Code instead.
Let’s replace the code we have in the case block for sf::Event::MouseButtonPressed to show how we could easily handle multiple buttons with a switch statement.
case sf::Event::MouseButtonPressed:
std::cout << "A mouse button was pressed." << std::endl;
switch (Event.MouseButton.Button)
{
case sf::Mouse::Left:
std::cout << "LMB" << std::endl;
break;
case sf::Mouse::Right:
std::cout << "RMB" << std::endl;
break;
case sf::Mouse::Middle:
std::cout << "MMB" << std::endl;
break;
}
break;
This time we handle three buttons – left, middle and right mouse button. Naturally, this is easier than using lots of if and if else statements! One final note – this current code might yield a warning about some enumeration values not being handled. To silence this, just add an empty default case block to handle them.
default: break;
Summing Up
And there we are. All done. Not only have we handled events, but also accessed event specific data to react more specifically to events. Next time I’ll discuss windowing and video modes, and after that: real time input. Until then.
Complete Source Code
The following source code was used in this tutorial.
#include <SFML/Graphics.hpp>
#include <iostream>
int main()
{
sf::VideoMode VMode(800, 600, 32);
sf::RenderWindow Window(VMode, "SFMLCoder Tutorial - Events");
while (Window.IsOpened())
{
sf::Event Event;
while (Window.PollEvent(Event))
{
switch (Event.Type)
{
case sf::Event::Closed:
std::cout << "The window was closed." << std::endl;
Window.Close();
break;
case sf::Event::GainedFocus:
std::cout << "The window gained focus." << std::endl;
break;
case sf::Event::LostFocus:
std::cout << "The window lost focus." << std::endl;
break;
case sf::Event::Resized:
std::cout << "The window was resized to X=" << Event.Size.Width << " Y=" << Event.Size.Height << "." << std::endl;
std::cout << "The width/height from the window get functions: " << Window.GetWidth() << "/" << Window.GetHeight() << std::endl;
break;
case sf::Event::KeyPressed:
std::cout << "A key was pressed." << std::endl;
if (Event.Key.Code == sf::Key::Back)
std::cout << "Backspace was pressed." << std::endl;
break;
case sf::Event::KeyReleased:
std::cout << "A key was released." << std::endl;
if ((Event.Key.Code == sf::Key::S) && (Event.Key.Control == true))
std::cout << "Control+S was released." << std::endl;
break;
case sf::Event::MouseButtonPressed:
std::cout << "A mouse button was pressed." << std::endl;
switch (Event.MouseButton.Button)
{
case sf::Mouse::Left:
std::cout << "LMB" << std::endl;
break;
case sf::Mouse::Right:
std::cout << "RMB" << std::endl;
break;
case sf::Mouse::Middle:
std::cout << "MMB" << std::endl;
break;
default:
break;
}
break;
case sf::Event::MouseButtonReleased:
std::cout << "A mouse button was released." << std::endl;
if (Event.MouseButton.Button == sf::Mouse::Left)
std::cout << "The left mouse button was pressed at X=" << Event.MouseButton.X
<< "Y=" << Event.MouseButton.Y << "." << std::endl;
if (Event.MouseButton.Button == sf::Mouse::Middle)
std::cout << "The middle mouse button was released at"
<< "X=" << Event.MouseButton.X
<< "Y=" << Event.MouseButton.Y
<< "." << std::endl;
break;
case sf::Event::MouseMoved:
std::cout << "The mouse was moved to X=" << Event.MouseMove.X << " Y=" << Event.MouseMove.Y << "." << std::endl;
break;
default:
break;
}
}
Window.Clear(sf::Color(0, 255, 255));
Window.Display();
}
return 0;
}
