Handling Mouse Scrolling
Learn how to detect and handle mouse scroll wheel events in SDL3, including vertical and horizontal scrolling, as well as scroll wheel button events.
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.
Starting Point
This lesson builds on our earlier work. If you want to follow along, a minimal project containing a Window class and a main function with a basic application loop are provided below.
We've re-introduced the GetWidth() and GetHeight() methods to our Window class, which will be helpful for examples later in this lesson.
Files
Our SDL_PollEvent(&Event) statement will update our Event object with any mouse wheel action that the user performs. We'll then detect and react to those actions within the body of the loop.
Mouse Wheel Events
When an event is created by the user interacting with their mouse wheel, the SDL_Event object will have a type of SDL_EVENT_MOUSE_WHEEL:
src/main.cpp
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <iostream>
#include "Window.h"
int main(int, char**) {
SDL_Init(SDL_INIT_VIDEO);
Window GameWindow;
SDL_Event Event;
bool IsRunning{true};
while (IsRunning) {
while (SDL_PollEvent(&Event)) {
if (Event.type == SDL_EVENT_MOUSE_WHEEL) {
std::cout << "Mouse wheel input detected\n";
} else if (Event.type == SDL_EVENT_QUIT) {
IsRunning = false;
}
}
GameWindow.Render();
GameWindow.Update();
}
SDL_Quit();
return 0;
}Mouse wheel input detected
Mouse wheel input detected
Mouse wheel input detectedWithin the wheel object, the y member variable is a float in SDL3 that lets us know how far the wheel was scrolled. By default, positive values mean the user scrolled the wheel forward (away from them), while negative values mean they scrolled backward (toward them).
if (Event.type == SDL_EVENT_MOUSE_WHEEL) {
std::cout << "Scroll amount: "
<< Event.wheel.y << '\n';
}Unlike button presses, mouse scrolling is not a discrete input. Users can scroll for as long as they want, but even a tiny scroll that takes only a fraction of a second is likely to span multiple frames of our application.
As such, a single scroll input from the user will result in many SDL_EVENT_MOUSE_WHEEL events, each with a relatively small y value.
The previous program's 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.
Using the SDL_MouseWheelEvent Type
If we want to store or transfer an SDL_Event that has a type of SDL_EVENT_MOUSE_WHEEL, 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:
src/main.cpp
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <iostream>
#include "Window.h"
void HandleMouseWheel(const SDL_MouseWheelEvent& E) {
float ScrollAmount{E.y};
std::cout << "Scroll amount: " << ScrollAmount << '\n';
}
int main(int, char**) {
SDL_Init(SDL_INIT_VIDEO);
Window GameWindow;
SDL_Event Event;
bool IsRunning = true;
while (IsRunning) {
while (SDL_PollEvent(&Event)) {
if (Event.type == SDL_EVENT_MOUSE_WHEEL) {
HandleMouseWheel(Event.wheel);
} else if (Event.type == SDL_EVENT_QUIT) {
IsRunning = false;
}
}
GameWindow.Render();
GameWindow.Update();
}
SDL_Quit();
return 0;
}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 a .
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:
src/main.cpp
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <iostream>
#include "Window.h"
void HandleMouseButton(const SDL_MouseButtonEvent& E) {
if (E.button == SDL_BUTTON_MIDDLE) {
std::cout << "\nScroll button "
<< (E.down ? "pressed" : "released");
}
}
int main(int, char**) {
SDL_Init(SDL_INIT_VIDEO);
Window GameWindow;
SDL_Event Event;
bool IsRunning = true;
while (IsRunning) {
while (SDL_PollEvent(&Event)) {
if (Event.type == SDL_EVENT_MOUSE_BUTTON_DOWN ||
Event.type == SDL_EVENT_MOUSE_BUTTON_UP) {
HandleMouseButton(Event.button);
} else if (Event.type == SDL_EVENT_QUIT) {
IsRunning = false;
}
}
GameWindow.Render();
GameWindow.Update();
}
SDL_Quit();
return 0;
}Scroll button pressed
Scroll button releasedHorizontal Scrolling
Some mice allow the user to provide horizontal scroll input, typically by tilting their mouse wheel left or right.
These actions also generate SDL_EVENT_MOUSE_WHEEL 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:
src/main.cpp
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <iostream>
#include "Window.h"
void HandleMouseWheel(const SDL_MouseWheelEvent& E) {
float HorizontalScrollAmount{E.x};
if (HorizontalScrollAmount != 0) {
std::cout << "Horizontal scroll detected: "
<< HorizontalScrollAmount << '\n';
}
}
int main(int, char**) {
SDL_Init(SDL_INIT_VIDEO);
Window GameWindow;
SDL_Event Event;
bool IsRunning = true;
while (IsRunning) {
while (SDL_PollEvent(&Event)) {
if (Event.type == SDL_EVENT_MOUSE_WHEEL) {
HandleMouseWheel(Event.wheel);
} else if (Event.type == SDL_EVENT_QUIT) {
IsRunning = false;
}
}
GameWindow.Render();
GameWindow.Update();
}
SDL_Quit();
return 0;
}Horizontal scroll detected: -1
Horizontal scroll detected: 1Flipped Scroll Direction
On some platforms, users can invert their scroll direction (often called "natural scrolling"). SDL detects this and reports it in the direction field of the SDL_MouseWheelEvent. This variable will be equal to either SDL_MOUSEWHEEL_NORMAL or SDL_MOUSEWHEEL_FLIPPED:
void HandleMouseWheel(const SDL_MouseWheelEvent& E) {
if (E.direction == SDL_MOUSEWHEEL_NORMAL) {
std::cout << "Using normal direction\n";
} else if (E.direction == SDL_MOUSEWHEEL_FLIPPED) {
std::cout << "Using flipped direction\n";
}
}Using normal directionThe x, y, integer_x, and integer_y values reported by SDL have already accounted for 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(const SDL_MouseWheelEvent& E) {
float ScrollAmount{E.direction ==
SDL_MOUSEWHEEL_NORMAL ? E.y : E.y * -1};
// ...
}Mouse Position
Sometimes, we need to know where the cursor was when a scroll event occurred. These coordinates are available through the mouse_x and mouse_y variables of the SDL_MouseWheelEvent:
src/main.cpp
// ...
void HandleMouseWheel(const SDL_MouseWheelEvent& E) {
std::cout << "\nScroll event happened at "
<< "x = " << E.mouse_x
<< ", y = " << E.mouse_y;
}
// ...Scroll event happened at x = 446, y = 181Similar to SDL_MouseMotionEvents, the coordinates are relative to the top-left corner of our window. If needed, we can determine the distance from the right and bottom edges by subtracting them from our window's width and height:
src/main.cpp
// ...
void HandleMouseWheel(
const SDL_MouseWheelEvent& E,
const Window& GameWindow
) {
float DistanceFromLeft{E.mouse_x};
float DistanceFromTop{E.mouse_y};
float DistanceFromRight{
GameWindow.GetWidth() - E.mouse_x};
float DistanceFromBottom{
GameWindow.GetHeight() - E.mouse_y};
}
// ...Summary
This lesson covered how to detect and handle mouse scroll wheel events. Key points include:
- Understanding the
SDL_EVENT_MOUSE_WHEELevent and theSDL_MouseWheelEventstructure. - Handling vertical (
y) and horizontal (x) scrolling asfloatvalues. - Detecting and responding to scroll wheel button events.
- Using
preciseXandpreciseYfor high-precision scrolling when supported. - Accounting for flipped scroll directions and retrieving the mouse position (
mouse_x,mouse_y) during scroll events.
Managing Mouse Focus
Learn how to track and respond to mouse focus events in SDL3, including handling multiple windows and customizing focus-related click behavior.