Managing Mouse Focus with SDL2

Learn how to track and respond to mouse focus events in SDL2, including handling multiple windows and customizing focus-related click behavior.
This lesson is part of the course:

Game Dev with SDL2

Learn C++ and SDL development by creating hands on, practical projects inspired by classic retro games

Free, Unlimited Access
Abstract art representing computer programming
Ryan McCombe
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:

Screenshot showing windows with and without focus

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.

Tracking Mouse Focus Using the Event Loop

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

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":

#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"
);

Summary

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:

  • Using SDL_WindowEvent to detect when the mouse enters or leaves a window
  • Checking window focus state using SDL_GetWindowFlags() and the SDL_WINDOW_MOUSE_FOCUS flag
  • Using SDL_GetMouseFocus() to determine which window currently has mouse focus
  • Customizing click-through behavior during focus changes with SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH

Was this lesson useful?

Next Lesson

Mouse Input Constraints

Implement mouse constraints in SDL2 to control cursor movement using window grabs and rectangular bounds
Abstract art representing computer programming
Ryan McCombe
Ryan McCombe
Updated
Lesson Contents

Managing Mouse Focus with SDL2

Learn how to track and respond to mouse focus events in SDL2, including handling multiple windows and customizing focus-related click behavior.

sdl2-promo.jpg
This lesson is part of the course:

Game Dev with SDL2

Learn C++ and SDL development by creating hands on, practical projects inspired by classic retro games

Free, Unlimited Access
Mouse Input
sdl2-promo.jpg
This lesson is part of the course:

Game Dev with SDL2

Learn C++ and SDL development by creating hands on, practical projects inspired by classic retro games

Free, unlimited access

This course includes:

  • 67 Lessons
  • 100+ Code Samples
  • 91% Positive Reviews
  • Regularly Updated
  • Help and FAQ
Next Lesson

Mouse Input Constraints

Implement mouse constraints in SDL2 to control cursor movement using window grabs and rectangular bounds
Abstract art representing computer programming
Contact|Privacy Policy|Terms of Use
Copyright © 2024 - All Rights Reserved