Mouse Input Basics
Discover how to process mouse input, including position tracking and button presses
Building on our basic SDL window setup, this lesson introduces interactivity by focusing on mouse input.
We'll explore how SDL represents mouse actions through its event system. You'll learn to detect when the user moves the mouse, clicks buttons, or even moves the cursor into or out of the application window.
Key topics include:
- Processing
SDL_MOUSEMOTION
events to get cursor coordinates. - Understanding SDL's coordinate system.
- Detecting mouse button presses and releases (
SDL_MOUSEBUTTONDOWN
,SDL_MOUSEBUTTONUP
). - Identifying which mouse button was clicked.
- Handling double clicks.
- Responding to the cursor entering or leaving the windopullw (
SDL_WINDOWEVENT_ENTER
,SDL_WINDOWEVENT_LEAVE
).
Starting Point
We'll start with the basic SDL application structure from our previous lessons. This includes initializing SDL, creating a window using our Window class, and setting up the main event loop. The code below provides this foundation:
As a reminder, our SDL_PollEvent(&Event)
statement will update our Event
object with any mouse action that the user performs. We'll then detect and react to those actions within the body of the event loop.
Mouse Motion Events
Every time the player moves their mouse within our window, an SDL_Event
is pushed onto the event queue.
That event will have a type of SDL_MOUSEMOTION
, and the mouse's new position is available through the x
and y
members of the motion
object:
// main.cpp
#include <SDL.h>
#include "Window.h"
int main(int argc, char** argv) {
SDL_Init(SDL_INIT_VIDEO);
Window GameWindow;
SDL_Event E;
while (true) {
while (SDL_PollEvent(&E)) {
if (E.type == SDL_MOUSEMOTION) {
std::cout << "Mouse Motion Detected - "
<< "x: " << E.motion.x
<< ", y: " << E.motion.y << '\n';
} else if (E.type == SDL_QUIT) {
SDL_Quit();
return 0;
}
}
GameWindow.Render();
GameWindow.Update();
}
return 0;
}
Mouse Motion Detected - x: 2, y: 77
Mouse Motion Detected - x: 6, y: 79
Mouse Motion Detected - x: 8, y: 80
SDL's Coordinate System
When working with SDL, and pixel data in general, the coordinate system that is used tends to be different to what we might expect.
It's common that the origin - that is, the position $(0, 0)$ - represents the top left of the window, surface, or image. Increasing x
values corresponds to moving right, and increasing y
values corresponds to moving down.
So, if our window was 700 units wide and 300 units tall, its coordinate system would look like this:

For example, if we have a mouse motion event with an x
value of 300
, that means the pointer was moved to a position that is 300 units from the left edge of our window. If y
is 200
, that means the pointer is 200 pixels from the top edge of the window.

This is sometimes called a y-down coordinate system. The system we typically use in other contexts, and we learn about in geometry class, is y-up

We cover coordinate systems and moving objects between them in much more detail later in the course.
Using SDL_MouseMotionEvent
When we're dealing with an SDL_Event
that has a type of SDL_MOUSEMOTION
, the motion
struct it contains has the SDL_MouseMotionEvent
type.
When working with mouse motion events, this object is slightly easier to use than the top-level SDL_Event
, as it does not require us to access the intermediate motion
subobject to retrieve the information we care about.
This makes it useful for storing and transferring mouse motion events. The event loop body can get very large, so to mitigate this, we can move our mouse motion handler to a standalone function, and pass the Event.motion
struct to it from our event loop:
// main.cpp
#include <SDL.h>
#include "Window.h"
void HandleMotion(SDL_MouseMotionEvent& E) {
int DistanceFromLeft{E.x};
int DistanceFromTop{E.y};
// ...
}
int main(int argc, char** argv) {
SDL_Init(SDL_INIT_VIDEO);
Window GameWindow;
SDL_Event E;
while (true) {
while (SDL_PollEvent(&E)) {
if (E.type == SDL_MOUSEMOTION) {
HandleMotion(E.motion);
} else if (E.type == SDL_QUIT) {
SDL_Quit();
return 0;
}
}
GameWindow.Render();
GameWindow.Update();
}
return 0;
}
Mouse Enter / Mouse Leave Events
By default, SDL will only report mouse motion events when our window has mouse focus - that is, when the user's cursor is hovering over our window.
When the user's cursor enters or leaves our window, SDL provides events to notify us of this. These events have a type
of SDL_WINDOWEVENT
.
while(SDL_PollEvent(&E)) {
if (E.type == SDL_WINDOWEVENT) {
// ...
}
}
However, a wide range of other events are categorized under this SDL_WINDOWEVENT
type too, which we'll cover in more detail later.
If our event is an SDL_WINDOWEVENT
, we can access more specific information under the window
struct of that SDL_Event
. This subobject has another field called event
, which gives us a more specific categorization.
If the window event represents the user's cursor entering our window, the window.event
member will be equal to SDL_WINDOWEVENT_ENTER
. When the event represents the cursor leaving the window, it will be equal to SDL_WINDOWEVENT_LEAVE
:
if (E.type == SDL_WINDOWEVENT) {
if (E.window.event == SDL_WINDOWEVENT_ENTER) {
std::cout << "Mouse Entered Window\n";
} else if (E.window.event == SDL_WINDOWEVENT_LEAVE) {
std::cout << "Mouse Left Window\n";
}
}
Using SDL_WindowEvent
Similar to SDL_MouseMotionEvent
, a dedicated SDL_WindowEvent
type is available for window events, which is helpful if we want to send that event to some other function.
If the outer SDL_Event
has a type of SDL_WINDOWEVENT
, then the window
variable of that event will contain the SDL_WindowEvent
:
#include <SDL.h>
#include "Window.h"
void HandleWindowEvent(SDL_WindowEvent& E) {
if (E.event == SDL_WINDOWEVENT_ENTER) {
std::cout << "Mouse Entered Window\n";
} else if (E.event == SDL_WINDOWEVENT_LEAVE) {
std::cout << "Mouse Left Window\n";
}
}
int main(int argc, char** argv) {
SDL_Init(SDL_INIT_VIDEO);
Window GameWindow;
SDL_Event E;
while (true) {
while (SDL_PollEvent(&E)) {
if (E.type == SDL_WINDOWEVENT) {
HandleWindowEvent(E.window);
} else if (E.type == SDL_QUIT) {
SDL_Quit();
return 0;
}
}
GameWindow.Render();
GameWindow.Update();
}
return 0;
}
Mouse Click Events
When any mouse button is pressed or released, we get an SDL_Event
whose type
is equal to SDL_MOUSEBUTTONDOWN
or SDL_MOUSEBUTTONUP
respectively:
while (SDL_PollEvent(&E)) {
if (E.type == SDL_MOUSEBUTTONDOWN) {
std::cout << "Button Pressed\n";
} else if (E.type == SDL_MOUSEBUTTONUP) {
std::cout << "Button Released\n";
}
}
Both of these event types include an SDL_MouseButtonEvent
in the button
variable:
#include <SDL.h>
#include "Window.h"
void HandleButtonEvent(SDL_MouseButtonEvent& E) {
// ...
}
int main(int argc, char** argv) {
SDL_Init(SDL_INIT_VIDEO);
Window GameWindow;
SDL_Event E;
while (true) {
while (SDL_PollEvent(&E)) {
if (
E.type == SDL_MOUSEBUTTONDOWN ||
E.type == SDL_MOUSEBUTTONUP
) {
HandleButtonEvent(E.button);
} else if (E.type == SDL_QUIT) {
SDL_Quit();
return 0;
}
}
GameWindow.Render();
GameWindow.Update();
}
return 0;
}
Button Pressed vs Button Released
This SDL_MouseButtonEvent
also includes the type
value, so we can still determine if it corresponds to a button being pressed or released. Alternatively, we can access the state
variable, and compare it to SDL_PRESSED
or SDL_RELEASED
:
#include <SDL.h>
#include "Window.h"
void HandleButtonEvent(SDL_MouseButtonEvent& E) {
if (E.type == SDL_MOUSEBUTTONDOWN) {
// Button Pressed
} else if (E.type == SDL_MOUSEBUTTONUP) {
// Button Released
}
// Alternatively:
if (E.state == SDL_PRESSED) {
// Button Pressed
} else if (E.state == SDL_RELEASED) {
// Button Released
}
}
int main(int argc, char** argv) {/*...*/}
Which Button was Pressed or Released?
To understand which button was pressed or released, the SDL_MouseButtonEvent
includes a button
member, storing a numeric identifier for the mouse button that triggered the event.
SDL provides some helper values to compare this identifier to the common mouse buttons:
SDL_BUTTON_LEFT
- The left mouse buttonSDL_BUTTON_RIGHT
- The right mouse buttonSDL_BUTTON_MIDDLE
- The middle mouse buttonSDL_BUTTON_X1
- The first extra mouse button, which is on the side of some miceSDL_BUTTON_X2
- The second extra mouse button, which is on the side of some mice
Using these values in code might look something like this:
#include <SDL.h>
#include "Window.h"
void HandleButtonEvent(SDL_MouseButtonEvent& E) {
if (E.button == SDL_BUTTON_LEFT) {
// The left button was pressed or released
} else if (E.button == SDL_BUTTON_RIGHT) {
// The right button was pressed or released
}
if (
E.button == SDL_BUTTON_LEFT &&
E.state == SDL_PRESSED
) {
// The left button was pressed
}
}
int main(int argc, char** argv) {/*...*/}
Double Clicks
If our application needs to detect double clicks, we can check the clicks
member variable of the SDL_MouseButtonEvent
. This represents the number of times the user has clicked that same button in quick succession:
void HandleButtonEvent(const SDL_MouseButtonEvent& E) {
if (E.clicks >= 2) {
// Double Click Detected
}
}
Note that the clicks
variable will also be set on the SDL_MOUSEBUTTONUP
event, so the code in this if
block will be executed twice:
- On the
SDL_MOUSEBUTTONDOWN
event of the second click - On the
SDL_MOUSEBUTTONUP
event of the second click
It will also detect double-clicks of any button. We'll often want to filter on the specific user action we care about:
#include <SDL.h>
#include "Window.h"
void HandleButtonEvent(SDL_MouseButtonEvent& E) {
if (E.button == SDL_BUTTON_LEFT &&
E.state == SDL_PRESSED &&
E.clicks >= 2
) {
// The left button was double-clicked
}
}
int main(int argc, char** argv) {/*...*/}
Using the clicks
variable tends to be preferred over trying to create double-click detection from scratch, as it's more complex than it first appears.
Additionally, most operating systems allow users to change their double-click sensitivity and, by using SDL's solution, our application can respect those settings.
Complete Code
Here's the complete code incorporating all the mouse event handling techniques discussed in this lesson. It demonstrates how to detect motion, window enter/leave events, button clicks, and double clicks using simple std::cout
statements for illustration.
While the specific output functions like HandleMotionEvent()
won't be carried forward into subsequent lessons, the fundamental principles - checking event types (Event.type
), accessing specific event data (Event.motion
, Event.window
, Event.button
), and processing input within the event loop - are crucial concepts that we will build upon throughout the rest of the course.
Mouse Motion Detected - x: 200, y: 124
Distance from Right: 500
Distance from Bottom: 176
Left Double Click
Right Click or Release
Right Click or Release
Summary
We've explored how to make SDL applications responsive to mouse input. By examining the SDL_Event
structure within the main loop, we can detect and react to mouse movements, button clicks (including double clicks), and window boundary crossings by the cursor. This forms the basis for implementing mouse controls.
Key Takeaways:
- The core of input handling is the
while(SDL_PollEvent(&E))
loop. - Filter events based on
E.type
(e.g.,SDL_MOUSEMOTION
,SDL_MOUSEBUTTONDOWN
). - Access event-specific data using union members (e.g.,
E.motion
,E.button
). - Mouse position is given by
E.motion.x
andE.motion.y
(y-down). - Button identity uses constants like
SDL_BUTTON_LEFT
,SDL_BUTTON_RIGHT
. - Button state is checked via
E.button.state
(SDL_PRESSED
,SDL_RELEASED
). - Double clicks are identified using
E.button.clicks
. - Window enter/leave events are a subtype of
SDL_WINDOWEVENT
. - Dedicated structs like
SDL_MouseMotionEvent
exist for specific event types.
Rectangles and SDL_Rect
Learn to create, render, and interact with basic rectangles using the SDL_Rect
and SDL_Color
types.