Window Events and Window IDs
Discover how to monitor and respond to window state changes in SDL3 applications
Previously, we've seen how we can retrieve aspects of our window's configuration by checking its window flags. However, we don't need to continuously monitor our SDL_Window
to understand its state.
For most use cases, monitoring the event loop makes more sense. SDL dispatches a wide variety of events reporting actions performed on our window. In this lesson, we'll explore this in a bit more detail.
Starting Point
This lesson builds on the concepts from the previous lesson. To focus on window events, we will start with a minimal project containing a Window
class and a main
function with a basic application loop.
Files
Window Events in SDL3
A major change from SDL2 to SDL3 is how window events are handled. In SDL2, all window-related events were bundled under a single SDL_WINDOWEVENT
type. You had to first check for this general type, and then inspect a sub-event to find out what specifically happened.
In SDL3, this has been simplified. Window events are now top-level events, just like SDL_EVENT_KEY_DOWN
or SDL_EVENT_QUIT
. This means we can check for them directly in our event loop.
For example, to detect if a window has been minimized, we now check for SDL_EVENT_WINDOW_MINIMIZED
directly on the SDL_Event
's type
member:
while (SDL_PollEvent(&E)) {
if (E.type == SDL_EVENT_WINDOW_MINIMIZED) {
// The window was minimized
}
}
Although the event type is now top-level, the detailed information about the event is still stored in the window
member of the SDL_Event
union, which is an SDL_WindowEvent
struct.
Detecting Any Window Event
Behind the scenes, the type
value of an SDL_Event
is just an integer, and SDL3 has arranged these integers such that all window events fall within a specific range.
This gives us a way to detect any window event, if we prefer a design that closer matches the SDL2 pattern. To do this, SDL3 provides the SDL_EVENT_WINDOW_FIRST
and SDL_EVENT_WINDOW_LAST
variables, giving us visibility of where the range of window events begins and ends.
Therefore, we can check if the event was any window even by checking if its type
falls within this range:
if (E.type >= SDL_EVENT_WINDOW_FIRST &&
E.type <= SDL_EVENT_WINDOW_LAST) {
// This is a window event
}
Let's create a handler function that takes an SDL_WindowEvent
and logs a message. In our main loop, we'll check if an event is a window event and, if so, pass E.window
to our handler.
src/main.cpp
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <iostream>
#include "Window.h"
void HandleWindowEvent(const SDL_WindowEvent& E) {
std::cout << "Detected Window Event\n";
}
int main(int, char**) {
SDL_Init(SDL_INIT_VIDEO);
Window GameWindow;
SDL_Event E;
bool IsRunning = true;
while (IsRunning) {
while (SDL_PollEvent(&E)) {
if (E.type >= SDL_EVENT_WINDOW_FIRST &&
E.type <= SDL_EVENT_WINDOW_LAST
) {
HandleWindowEvent(E.window);
} else if (E.type == SDL_EVENT_QUIT) {
IsRunning = false;
}
}
GameWindow.Render();
GameWindow.Update();
}
SDL_Quit();
return 0;
}
Detected Window Event
Detected Window Event
Detected Window Event
Reacting to Specific Window Events
Within our window event handler, we can still check the exact nature of the event with a more specific type
check. For example, here's how we can react to a few common window events:
src/main.cpp
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <iostream>
#include "Window.h"
void HandleWindowEvent(const SDL_WindowEvent& E) {
if (E.type == SDL_EVENT_WINDOW_MINIMIZED) {
std::cout << "Window Minimized\n";
} else if (E.type == SDL_EVENT_WINDOW_MOVED) {
std::cout << "Window Moved\n";
} else if (E.type == SDL_EVENT_WINDOW_MOUSE_ENTER) {
std::cout << "Cursor Entered the Window\n";
}
}
int main(int, char**) {/*...*/}
Cursor Entered the Window
Window Moved
Window Minimized
Example: Mouse Entering and Leaving Window
In this example, we'll react to the user's cursor entering and leaving our window by using the SDL_SetWindowTitle()
function to change the title based on these events:
src/main.cpp
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <iostream>
#include "Window.h"
void HandleWindowEvent(
const SDL_WindowEvent& E, SDL_Window* Window
) {
if (E.type == SDL_EVENT_WINDOW_MOUSE_ENTER) {
SDL_SetWindowTitle(
Window, "Mouse Inside the Window"
);
} else if (E.type == SDL_EVENT_WINDOW_MOUSE_LEAVE) {
SDL_SetWindowTitle(
Window, "Mouse Outside the Window"
);
}
}
int main(int, char**) {
SDL_Init(SDL_INIT_VIDEO);
Window GameWindow;
SDL_Event E;
bool IsRunning = true;
while (IsRunning) {
while (SDL_PollEvent(&E)) {
if (E.type >= SDL_EVENT_WINDOW_FIRST &&
E.type <= SDL_EVENT_WINDOW_LAST
) {
HandleWindowEvent(
E.window, GameWindow.GetRaw()
);
} else if (E.type == SDL_EVENT_QUIT) {
IsRunning = false;
}
}
GameWindow.Render();
GameWindow.Update();
}
SDL_Quit();
return 0;
}
Window IDs
In the previous example, our HandleWindowEvent()
logic requires a pointer to the SDL_Window
it needs to change the title of. We passed it as a second argument, but it's also possible to retrieve the SDL_Window
directly from the SDL_WindowEvent
.
Internally, SDL assigns a unique identifier to each window it creates. This ID is available on the windowID
member of the SDL_WindowEvent
struct. The type for this ID is SDL_WindowID
, which is an alias for a Uint32
.
void HandleWindowEvent(const SDL_WindowEvent& E) {
SDL_WindowID id = E.windowID;
// ...
}
The windowID
variable exists on every event type that relates to a specific window. This includes types like SDL_KeyboardEvent
, where the windowID
will be the window with input focus, and SDL_MouseMotionEvent
, where the ID will be the window the mouse is hovering over.
Getting an SDL_Window*
Using SDL_GetWindowFromID()
By passing this window ID to SDL_GetWindowFromID()
, we get the corresponding SDL_Window
pointer:
SDL_Window* Win{SDL_GetWindowFromID(E.windowID)};
Below, we update our program to use this technique, removing the need to pass our GameWindow
pointer to the event handler. This makes the handler more self-contained.
src/main.cpp
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <iostream>
#include "Window.h"
void HandleWindowEvent(const SDL_WindowEvent& E) {
SDL_Window* Window{
SDL_GetWindowFromID(E.windowID)
};
if (!Window) return;
if (E.type == SDL_EVENT_WINDOW_MOUSE_ENTER) {
SDL_SetWindowTitle(
Window, "Mouse Inside the Window"
);
} else if (E.type == SDL_EVENT_WINDOW_MOUSE_LEAVE) {
SDL_SetWindowTitle(
Window, "Mouse Outside the Window"
);
}
}
int main(int, char**) {/*...*/}
While not strictly necessary for a single-window application, this technique becomes essential in multi-window applications, where you need to know which specific window an event belongs to.
Getting a Window ID Using SDL_GetWindowID()
We can perform this conversion in the opposite direction using SDL_GetWindowID()
. We pass it the SDL_Window
pointer, and it returns the SDL_WindowID
associated with that SDL_Window
:
src/main.cpp
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <iostream>
int main(int, char**) {
SDL_Init(SDL_INIT_VIDEO);
SDL_Window* Window = SDL_CreateWindow(
"Window ID Example", 600, 300, 0
);
SDL_WindowID WindowID{SDL_GetWindowID(Window)};
std::cout << "The ID of the created window is: "
<< WindowID << '\n';
SDL_Event E;
bool IsRunning = true;
while (IsRunning) {
while (SDL_PollEvent(&E)) {
if (E.type == SDL_EVENT_QUIT) {
IsRunning = false;
}
}
GameWindow.Render();
GameWindow.Update();
}
SDL_DestroyWindow(Window);
SDL_Quit();
return 0;
}
The ID of the created window is: 4
One scenario where this might be useful is when we're using SDL's event system for custom events within our game. SDL provides the SDL_UserEvent
type for this purpose, and it includes a windowID
member which we can provide if it would be useful to consumers:
Uint32 GAME_OVER{SDL_RegisterEvents(1)};
SDL_Event MyEvent{};
MyEvent.type = GAME_OVER;
MyEvent.user.windowID = SDL_GetWindowID(Window);
SDL_PushEvent(&MyEvent);
Consumers can then retrieve the SDL_Window
associated with the event in the usual way:
void HandleGameOverEvent(const SDL_UserEvent& E){
SDL_Window* Window{
SDL_GetWindowFromID(E.windowID)
};
// ...
}
We covered user events in a dedicated lesson earlier in the course:
Summary
In this lesson, we explored more of SDL3's window management systems, focusing on window events and unique identifiers. Key takeaways:
- In SDL3, window events like
SDL_EVENT_WINDOW_MINIMIZED
are top-level events, simplifying event handling. SDL_WindowEvent
still provides detailed information about the event viaevent.window
.- Window IDs uniquely identify SDL windows, which is crucial for multi-window applications.
- Use
SDL_GetWindowFromID()
to retrieve anSDL_Window*
from a window ID andSDL_GetWindowID()
to find the ID associated with anSDL_Window*
.
Managing Window Position
Learn how to control and monitor the position of SDL windows on screen