In desktop environments, users can have multiple windows open at once. On most platforms, only a single window can have input focus at a time, which is typically gained by the user clicking on the window:
This is referred to as input focus, and we covered it in more detail in our earlier lesson on keyboard input:
In this lesson, we’ll introduce the related concept of mouse focus. A window has mouse focus if the user’s pointer is currently hovering over it. The window with mouse focus is not necessarily the same as the window with input focus.
Let’s begin with a basic application loop. SDL dispatches window events (SDL_WindowEvent
) when a window gains and loses mouse focus, so we’ll look out for those in our event loop:
#include <SDL.h>
#include "Window.h"
void HandleWindowEvent(SDL_WindowEvent& 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_WINDOWEVENT) {
HandleWindowEvent(E.window);
}
}
GameWindow.Update();
}
SDL_Quit();
return 0;
}
We can check if the event specifically relates to a window gaining or losing focus by comparing the event
property of the SDL_WindowEvent
to SDL_WINDOWEVENT_ENTER
or SDL_WINDOWEVENT_LEAVE
respectively:
#include <SDL.h>
#include <iostream>
#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) {/*...*/}
Mouse Entered Window
Mouse Left Window
SDL_GetWindowFlags()
As with most things in SDL, we don't need to monitor events to retrieve the state of its internally-managed objects, like an SDL_Window
. Instead, we can directly query the state of those objects at any time.
SDL_Window
objects keep track of whether or not they have focus within their window flags. As we covered in the previous chapter, we can access those flags by passing the SDL_Window
pointer to SDL_GetWindowFlags()
:
SDLWindow* Window{SDL_CreateWindow(
"Some Window",
200, 200,
700, 300, 0
)};
SDL_WindowFlags Flags{
SDL_GetWindowFlags(Window)
};
The SDL_WindowFlags
type is a bit mask in the form of a 32-bit unsigned integer. We can examine the state of any flag using the &
operator. SDL provides the SDL_WINDOW_MOUSE_FOCUS
helper to give us easy access to the flag that tracks whether the window has mouse focus:
bool hasFocus{
SDL_GetWindowFlags(Window) &
SDL_WINDOW_MOUSE_FOCUS
};
In practice, this state-checking approach is appropriate when we need to check if the window has focus as part of our reaction to some other event. The following example checks if our window has mouse focus when the user presses a keyboard button:
#include <SDL.h>
#include <iostream>
#include "Window.h"
void LogFocus(SDL_Window* Window) {
std::cout << "\nMouse focus: ";
if (SDL_GetWindowFlags(Window) &
SDL_WINDOW_MOUSE_FOCUS) {
std::cout << "yes";
} else {
std::cout << "no";
}
}
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_KEYDOWN) {
LogFocus(GameWindow.SDLWindow);
}
}
GameWindow.Update();
}
SDL_Quit();
return 0;
}
Mouse focus: no
Mouse focus: no
Mouse focus: yes
In the following example, we create a timer that checks every second (1,000 milliseconds) if our window has mouse focus:
#include <SDL.h>
#include <iostream>
#include "Window.h"
Uint32 MonitorWindow(Uint32 Timer, void* Win) {
auto* Window{static_cast<SDL_Window*>(Win)};
if (SDL_GetWindowFlags(Window) &
SDL_WINDOW_MOUSE_FOCUS) {
std::cout << "Focus\n";
} else {
std::cout << "No Focus\n";
}
return Timer;
}
int main(int argc, char** argv) {/*...*/}
No Focus
Focus
Focus
SDL_GetMouseFocus()
SDL also provides the SDL_GetMouseFocus()
function, which returns the SDL_Window*
with mouse focus, or a nullptr
if no window has focus.
This example is very similar to the previous one, except we’ve updated our implementation to use SDL_GetMouseFocus()
instead of SDL_GetWindowFlags()
.
#include <SDL.h>
#include <iostream>
#include "Window.h"
Uint32 MonitorWindow(Uint32 Timer, void* Win) {
auto* Window{static_cast<SDL_Window*>(Win)};
if (Window == SDL_GetMouseFocus()) {
std::cout << "Focus\n";
} else {
std::cout << "No Focus\n";
}
return Timer;
}
int main(int argc, char** argv) {/*...*/}
No Focus
Focus
Focus
Whilst the SDL_GetWindowFlags()
example lets us determine if a specific window has focus, SDL_GetMouseFocus()
is primarily useful when we want to know which window has focus. This is typically used in applications where we manage multiple windows, including utility windows.
The following program creates two windows and, on every iteration of our application loop, reports which one of them has mouse focus:
#include <SDL.h>
#include <iostream>
#include "Window.h"
int main(int argc, char** argv) {
SDL_Init(SDL_INIT_VIDEO);
Window Window1;
Window Window2;
SDL_Event E;
while (true) {
while (SDL_PollEvent(&E)) {
// ...
}
SDL_Window* Focused{SDL_GetMouseFocus()};
if (Focused == Window1.SDLWindow) {
std::cout << "Window 1 has focus\n";
} else if (Focused == Window2.SDLWindow) {
std::cout << "Window 2 has focus\n";
} else {
std::cout << "Neither has focus\n";
}
Window1.Update();
Window2.Update();
}
SDL_Quit();
return 0;
}
Window 1 has focus
Window 1 has focus
Neither has focus
Window 2 has focus
The most common way a user will switch their input focus is by left-clicking on the window with their mouse. By default, SDL does not report these clicks - you won't get a mouse click event when the user clicks to set input focus to your window.
This is typically a sensible default for games. For example, if we’re working on a first-person shooter, we want players to be able to switch focus to our window by clicking on it, and we won’t want that click to also fire their weapon.
However, we can change this behavior by setting the SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH
hint to "1"
:
#include <SDL.h>
#include <iostream>
#include "Window.h"
int main(int argc, char** argv) {
SDL_SetHint(
SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH,
"1"
);
SDL_Init(SDL_INIT_VIDEO);
Window GameWindow;
SDL_Event E;
while (true) {
while (SDL_PollEvent(&E)) {
switch (E.type) {
case SDL_WINDOWEVENT:
if (E.window.event ==
SDL_WINDOWEVENT_FOCUS_GAINED) {
std::cout << "Input Focus Event\n";
}
break;
case SDL_MOUSEBUTTONDOWN:
if (E.button.button ==
SDL_BUTTON_LEFT) {
std::cout << "Also a Click Event\n";
}
break;
default:
break;
}
}
GameWindow.Update();
}
SDL_Quit();
return 0;
}
Input Focus Event
Also a Click Event
We can set it to "0"
to restore the default behavior:
SDL_SetHint(
SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH,
"0"
);
Mouse focus management is crucial for creating responsive multi-window applications in SDL2, allowing you to track which window the mouse is hovering over and respond appropriately. Here are the key topics covered:
SDL_WindowEvent
to detect when the mouse enters or leaves a windowSDL_GetWindowFlags()
and the SDL_WINDOW_MOUSE_FOCUS
flagSDL_GetMouseFocus()
to determine which window currently has mouse focusSDL_HINT_MOUSE_FOCUS_CLICKTHROUGH
Learn how to track and respond to mouse focus events in SDL2, including handling multiple windows and customizing focus-related click behavior.
Learn C++ and SDL development by creating hands on, practical projects inspired by classic retro games