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.

Ryan McCombe
Updated

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.

Starting Point

This lesson builds upon our previous work. To keep the focus on mouse focus, the starting point below uses a minimal main.cpp and Window.h. We've removed the scroll-specific event handling from the previous lesson.

Files

src
Select a file to view its content

Tracking Mouse Focus Using the Event Loop

Let's begin with a basic application loop. When a window gains or loses mouse focus, SDL dispatches top-level events that we can detect in our event loop.

We can check if an event is related to a window gaining or losing focus by comparing the type of the SDL_Event to SDL_EVENT_WINDOW_MOUSE_ENTER or SDL_EVENT_WINDOW_MOUSE_LEAVE respectively:

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 E;

  bool IsRunning = true;
  while (IsRunning) {
    while (SDL_PollEvent(&E)) {
      if (E.type == SDL_EVENT_WINDOW_MOUSE_ENTER) {
        std::cout << "Mouse Entered Window\n";
      } else if (E.type == SDL_EVENT_WINDOW_MOUSE_LEAVE) {
        std::cout << "Mouse Left Window\n";
      } else if (E.type == SDL_EVENT_QUIT) {
        IsRunning = false;
      }
    }
    GameWindow.Render();
    GameWindow.Update();
  }

  SDL_Quit();
  return 0;
}
Mouse Entered Window
Mouse Left Window

Using 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():

SDL_Window* Window{SDL_CreateWindow(
  "Some Window",
  700, 300, 0
)};

SDL_WindowFlags Flags{
  SDL_GetWindowFlags(Window)
};

The SDL_WindowFlags type is a bit mask. 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) != 0
};

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:

src/main.cpp

#include <SDL3/SDL.h>
#include <SDL3/SDL_main.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, 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_KEY_DOWN) {
        LogFocus(GameWindow.GetRaw());
      } else if (E.type == SDL_EVENT_QUIT) {
        IsRunning = false;
      }
    }
    GameWindow.Render();
    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:

src/main.cpp

#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <iostream>
#include "Window.h"

Uint32 MonitorWindow(
  void* Win, SDL_TimerID timerID, Uint32 interval
) {
  auto* Window{static_cast<SDL_Window*>(Win)};

  if ((SDL_GetWindowFlags(Window) &
    SDL_WINDOW_MOUSE_FOCUS) != 0) {
    std::cout << "Focus\n";
  } else {
    std::cout << "No Focus\n";
  }

  return interval;
}

int main(int, char**) {
  SDL_Init(SDL_INIT_VIDEO);
  Window GameWindow;

  SDL_AddTimer(1000, MonitorWindow,
               GameWindow.GetRaw());

  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_Quit();
  return 0;
}
No Focus
Focus
Focus

Using 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().

src/main.cpp

#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <iostream>
#include "Window.h"

Uint32 MonitorWindow(
  void* Win, SDL_TimerID timerID, Uint32 interval
) {
  auto* Window{static_cast<SDL_Window*>(Win)};

  if (Window == SDL_GetMouseFocus()) {
    std::cout << "Focus\n";
  } else {
    std::cout << "No Focus\n";
  }
  return interval;
}

int main(int, char**) {
  SDL_Init(SDL_INIT_VIDEO);
  Window GameWindow;
  SDL_AddTimer(1000, MonitorWindow,
               GameWindow.GetRaw());

  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_Quit();
  return 0;
}
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:

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 Window1;
  Window Window2;

  SDL_Event E;
  bool IsRunning = true;
  while (IsRunning) {
    while (SDL_PollEvent(&E)) {
      if (E.type == SDL_EVENT_QUIT) {
        IsRunning = false;
      }
    }

    SDL_Window* Focused{SDL_GetMouseFocus()};

    if (Focused == Window1.GetRaw()) {
      std::cout << "Window 1 has focus\n";
    } else if (Focused == Window2.GetRaw()) {
      std::cout << "Window 2 has focus\n";
    } else {
      std::cout << "Neither has focus\n";
    }
    
    Window1.Render();
    Window2.Render();

    Window1.Update();
    Window2.Update();
  }

  SDL_Quit();
  return 0;
}
Window 1 has focus
Window 1 has focus
Neither has focus
Window 2 has focus

Click Events when Switching Input 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":

src/main.cpp

#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <iostream>
#include "Window.h"

int main(int, char**) {
  SDL_SetHint(
    SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH,
    "1"
  );
  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_FOCUS_GAINED) {
        std::cout << "Got an Input Focus Event\n";
      } else if (E.type == SDL_EVENT_MOUSE_BUTTON_DOWN) {
        if (E.button.button == SDL_BUTTON_LEFT) {
          std::cout << "Also got a Click Event\n";
        }
      } else if (E.type == SDL_EVENT_QUIT) {
        IsRunning = false;
      }
    }
    GameWindow.Render();
    GameWindow.Update();
  }

  SDL_Quit();
  return 0;
}
Got an Input Focus Event
Also got a Click Event

We can set it to "0" to restore the default behavior:

SDL_SetHint(
  SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH,
  "0"
);

Summary

Mouse focus management is important for handling mouse interaction, particularly multi-window applications. Here are the key topics covered:

  • Use SDL_EVENT_WINDOW_MOUSE_ENTER and SDL_EVENT_WINDOW_MOUSE_LEAVE to detect when the mouse enters or leaves a window.
  • Check window focus state using SDL_GetWindowFlags() and the SDL_WINDOW_MOUSE_FOCUS flag.
  • Use SDL_GetMouseFocus() to determine which window currently has mouse focus.
  • Customize click-through behavior during focus changes with SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH.
Next Lesson
Lesson 72 of 73

Mouse Input Constraints

Implement mouse constraints in SDL3 to control cursor movement using window grabs and rectangular bounds

Have a question about this lesson?
Answers are generated by AI models and may not be accurate