In this lesson, we cover how to detect and react to our user providing input through their mouse scroll wheel. Similar to other forms of input, when these interactions are detected, SDL pushes events into the event queue. Accordingly, we receive these events within our event loop, and can react to them as needed.
This lesson builds on our earlier work, where we have a Window
class that initializes SDL and creates a window, and an application loop set up in our main
function. We receive and handle mouse wheel events within the SDL_PollEvent
loop, highlighted below:
#include <SDL.h>
class Window {/*...*/};
int main(int argc, char** argv) {
Window GameWindow;
SDL_Event Event;
while(true) {
while(SDL_PollEvent(&Event)) {
// Detect and handle input events
// ...
}
GameWindow.RenderFrame();
}
SDL_Quit();
return 0;
}
When an event is created by the user interacting with their mouse wheel, the SDL_Event
object will have a type
of SDL_MOUSEWHEEL
:
while (SDL_PollEvent(&Event)) {
if (Event.type == SDL_MOUSEWHEEL) {
std::cout << "Mouse wheel input detected\n";
}
}
Mouse wheel input detected
Mouse wheel input detected
Mouse wheel input detected
Within the wheel
object, the y
member variable is an integer that lets us know how far the wheel was scrolled. By default, positive values mean the user scrolled the wheel forward, while negative means they scrolled backward.
while (SDL_PollEvent(&Event)) {
if (Event.type == SDL_MOUSEWHEEL) {
std::cout << "Scroll amount: "
<< Event.wheel.y << '\n';
}
}
Unlike button presses, mouse scrolling is not discreet. It typically takes some amount of time for the user to provide their full scroll input - that is, how far they want to move their wheel.
Even if they're performing a small scroll that may only take a fraction of a second, it is still likely to span multiple frames of our application. To handle this, a typical scroll from the user will result in several SDL_MOUSEWHEEL
events, each with a relatively small y
value.
The previous program output will be something like this:
Scroll amount: 1
Scroll amount: 2
Scroll amount: 1
Scroll amount: 1
This implementation helps us make scroll input feel responsive and smooth. As soon as the user starts their input, we get an event we can react to immediately even though the user may still be scrolling. The full extent of the scroll (5, in the previous example) is spread across multiple frames.
SDL_MouseWheelEvent
If we want to store or transfer an SDL_Event
that has a type of SDL_MOUSEWHEEL
, we can use the more descriptive SDL_MouseWheelEvent
subtype. This type is slightly easier to use as it does not require us to access the intermediate wheel
subobject to retrieve the information we care about:
// Event Loop
while (SDL_PollEvent(&Event)) {
if (Event.type == SDL_MOUSEWHEEL) {
HandleMouseWheel(Event)
}
}
// Handler
void HandleMouseWheel(const SDL_MouseWheelEvent& E) {
int ScrollAmount { Event.y };
// ...
}
On many mice, the scroll wheel is also a button that can be pressed. This interaction is handled like any other mouse button event, which we covered in the previous lesson:
The scroll mouse button is typically represented by the SDL_BUTTON_MIDDLE
variable, so we can detect scroll button keydown and keyup events like this:
// Event loop
while (SDL_PollEvent(&Event)) {
if (Event.type == SDL_MOUSEBUTTONDOWN ||
Event.type == SDL_MOUSEBUTTONUP) {
HandleMouseButton(Event.button);
}
}
// Handler
void HandleMouseButton(SDL_MouseButtonEvent& E) {
if (E.button == SDL_BUTTON_MIDDLE) {
std::cout << "\nScroll button "
<< (E.type == SDL_MOUSEBUTTONDOWN
? "pressed" : "released");
}
}
Scroll button pressed
Scroll button released
Some mice allow the user to provide horizontal scroll input, typically by tilting their mouse wheel left or right.
These actions also generate SDL_MouseWheelEvent
events. The amount of horizontal scrolling is represented by the x
member variable, where negative values indicate scrolling left and positive values indicate scrolling right:
// Handler
void HandleMouseWheel(SDL_MouseWheelEvent& E) {
int HorizontalScrollAmount{E.x};
if (HorizontalScrollAmount != 0) {
std::cout << "Horizontal scroll detected: "
<< HorizontalScrollAmount << '\n';
}
}
Horizontal scroll detected: -1
Horizontal scroll detected: 1
On some devices, it is possible to get more precise scroll information than the integers reported in the x
and y
fields of the SDL_MouseWheelEvent
.
If we want to support those devices, we can access the preciseX
and preciseY
fields instead. These will be floating point numbers:
// Handler
void HandleMouseWheel(SDL_MouseWheelEvent& E) {
std::cout << "preciseX: " << E.preciseX
<< ", preciseY: " << E.preciseY << '\n';
}
preciseX: 0, preciseY: 1.023
preciseX: 0, preciseY: 0.853
preciseX: 0, preciseY: 1.911
When precise scroll distance isn’t supported, the preciseX
and preciseY
values will fall back to match the regular x
and y
values, albeit in float
form.
On some platforms, users can invert their scroll direction. SDL detects this, and reports whether the user has inverted their scrolling in the direction
field of the SDL_MouseWheelEvent
. This variable will be an integer that is equal to either SDL_MOUSEWHEEL_NORMAL
or SDL_MOUSEWHEEL_FLIPPED
:
// Handler
void HandleMouseWheel(SDL_MouseWheelEvent& E) {
if (E.direction == SDL_MOUSEWHEEL_NORMAL) {
std::cout << "Using normal direction\n";
}
if (E.direction == SDL_MOUSEWHEEL_FLIPPED) {
std::cout << "Using flipped direction\n";
}
}
Using normal direction
The x
, y
, preciseX
, and preciseY
values reported by SDL have already considered the user’s preferred direction
so, in most cases, we can ignore this.
However, if we wanted to override the user’s individual preferences and ensure scrolling works the same for everyone, we can conditionally multiply the scroll values by -1
before using them:
// Handler
void HandleMouseWheel(SDL_MouseWheelEvent& E) {
int ScrollAmount{E.direction ==
SDL_MOUSEWHEEL_NORMAL ? E.y : E.y * -1};
}
Sometimes, we need to understand where the cursor was when a scroll event occurred. These coordinates are available through the mouseX
and mouseY
variables of the SDL_MouseWheelEvent
:
// Handler
void HandleMouseWheel(SDL_MouseWheelEvent& E) {
std::cout << "\nScroll event happened at "
<< "x = " << E.mouseX
<< ", y = " << E.mouseY;
}
Scroll event happened at x = 446, y = 181
Similar to SDL_MouseMotionEvent
s, the x
and y
coordinates are relative to the left edge and top edge of our window by default. If needed, we can determine the distance from the right and bottom edges by subtracting them from our window’s width and height:
// Handler
void HandleMouseWheel(SDL_MouseWheelEvent& E) {
int DistanceFromLeft{E.mouseX};
int DistanceFromTop{E.mouseY};
int DistanceFromRight{WindowWidth - E.mouseX};
int DistanceFromBottom{WindowHeight - E.mouseY};
}
This lesson covered how to detect and handle mouse scroll wheel events in SDL2. Key points include:
SDL_MouseWheelEvent
structure and its member variables.Learn how to detect and handle mouse scroll wheel events in SDL2, including vertical and horizontal scrolling, as well as scroll wheel button events.
Learn C++ and SDL development by creating hands on, practical projects inspired by classic retro games