Managing Window Input Focus

Learn how to manage and control window input focus in SDL applications, including how to create, detect, and manipulate window focus states.
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

On most platforms, multiple applications can be open at once. However, when the user is providing input on their keyboard, they typically only intend to provide that input to one of the open applications.

Users can select which application is active by, for example, clicking within its window.

Operating systems typically apply more vibrant styling to the active window. In the following example, the left window is inactive, while the right is active.

Accordingly, the user will not expect the left window to react to keyboard events, as their input is likely intended for the right window instead:

Screenshot showing windows with and without focus

Creating a Window with Input Focus

The SDL_WindowFlags mask includes a bit dedicated to whether or not the window has input focus. The SDL_WINDOW_INPUT_FOCUS variable is available to represent this bit.

Accordingly, we can specify that our window should have input focus as soon as it is created by using this variable with SDL_CreateWindow():

// Window.h
#pragma once
#include <SDL.h>

class Window {
public:
  Window(){
    SDLWindow = SDL_CreateWindow(
      "Hello Window",
      100, 100, 700, 300,
      SDL_WINDOW_INPUT_FOCUS  
    );
  }
  // ...
private:
  SDL_Window* SDLWindow{nullptr};
  // ...
};

We can combine SDL_WINDOW_INPUT_FOCUS with other window flags using the | operator. For example, to create a window that is both resizable and grabs focus when created, we could use this:

SDL_CreateWindow(
  "Hello Window", 100, 100, 700, 300,
  SDL_WINDOW_INPUT_FOCUS | SDL_WINDOW_RESIZABLE  
);

Detecting if a Window has Input Focus

SDL keeps the window flags bitset up to date through the lifecycle of our program. We can get the current window flags at any time using the SDL_GetWindowFlags() function. We pass the pointer to our window - that is, the SDL_Window* value returned from SDL_CreateWindow():

SDL_GetWindowFlags() returns a bit mask in the form of a Uint32 (a 32-bit unsigned integer):

#include <iostream>
#include <SDL.h>
#include "Window.h"

int main(int argc, char** argv) {
  SDL_Init(SDL_INIT_VIDEO);
  Window GameWindow;
  SDL_Event Event;
  
  std::cout << "Window Flags: "
    << SDL_GetWindowFlags(GameWindow.SDLWindow);

while (true) {/*...*/} SDL_Quit(); return 0; }
Window Flags: 516

We can use the value returned by this function with the & operator to determine if any flag is currently set, Below, we use this technique to determine if the window currently has input focus:

#include <iostream>
#include <SDL.h>
#include "Window.h"

int main(int argc, char** argv) {
  SDL_Init(SDL_INIT_VIDEO);
  Window GameWindow;
  SDL_Event Event;

  Uint32 Flags{
    SDL_GetWindowFlags(GameWindow.SDLWindow)};

  if (Flags & SDL_WINDOW_INPUT_FOCUS) {
    std::cout << "Window has input focus";
  }

while (true) {/*...*/} SDL_Quit(); return 0; }
Window has input focus

Using SDL_GetKeyboardFocus()

Alternatively, we can determine which window currently has focus using the GetKeyboardFocus() function:

#include <iostream>
#include <SDL.h>
#include "Window.h"

int main(int argc, char** argv) {
  SDL_Init(SDL_INIT_VIDEO);
  Window GameWindow;
  SDL_Event Event;

  std::cout << "Window with focus: "
    << SDL_GetKeyboardFocus();

while (true) {/*...*/} SDL_Quit(); return 0; }
Window with focus: 0000022391741BF8

This returns a SDL_Window*, which we can compare to any other SDL_Window* to understand whether or not it has focus:

#include <iostream>
#include <SDL.h>
#include "Window.h"

int main(int argc, char** argv) {
  SDL_Init(SDL_INIT_VIDEO);
  Window GameWindow;
  SDL_Event Event;

  SDL_Window* Focused{SDL_GetKeyboardFocus()};

  if (Focused == GameWindow.SDLWindow) {  
    std::cout << "GameWindow has input focus";
  }

while (true) {/*...*/} SDL_Quit(); return 0; }
GameWindow has input focus

Using the Event Loop

We can also keep track of whether or not our window has focus using the event loop. When our window gains or loses focus, SDL broadcasts an SDL_WINDOWEVENT.

To determine whether the event was caused by a window gaining or losing input focus, we can compare the Event.window.event property to SDL_WINDOWEVENT_FOCUS_GAINED and SDL_WINDOWEVENT_FOCUS_LOST constants respectively:

#include <iostream>
#include <SDL.h>
#include "Window.h"

void HandleWindowEvent(SDL_WindowEvent& E) {
  if (E.event == SDL_WINDOWEVENT_FOCUS_GAINED) {
    std::cout << "Window gained input focus\n";
  }
  if (E.event == SDL_WINDOWEVENT_FOCUS_LOST) {
    std::cout << "Window lost input focus\n";
  }
}

int main(int argc, char** argv) {
  SDL_Init(SDL_INIT_VIDEO);
  Window GameWindow;
  SDL_Event Event;

  while (true) {
    while (SDL_PollEvent(&Event)) {
      if (Event.type == SDL_WINDOWEVENT)
        HandleWindowEvent(Event.window);
    }
  }

  SDL_Quit();
  return 0;
}
Window gained input focus
Window lost input focus
Window gained input focus

Grabbing Input Focus

When we want input focus to shift to our application, we can use the SDL_RaiseWindow() function, passing a pointer to our SDL_Window:

SDL_Window* MyWindow{GameWindow.SDLWindow};
SDL_RaiseWindow(MyWindow);

In addition to grabbing focus, SDL_RaiseWindow() will also move the window to be in front of any other windows that might be obscuring it. This ensures the user is aware that it has taken focus, and will now be handling their input.

In the following example, our window automatically grabs input focus 5 seconds after it loses it. Window events have a windowID property, representing which window caused the event. We can pass this ID to the SDL_GetWindowFromID() function to retrieve the SDL_Window* associated with that ID:

#include <iostream>
#include <SDL.h>
#include <chrono>
#include <thread>
#include "Window.h"

void HandleWindowEvent(SDL_WindowEvent& E) {
  if (E.event == SDL_WINDOWEVENT_FOCUS_GAINED) {
    std::cout << "Window gained input focus\n";
  }
  if (E.event == SDL_WINDOWEVENT_FOCUS_LOST) {
    std::cout << "Window lost input focus\n";

    using namespace std::chrono_literals;
    std::this_thread::sleep_for(5s);
    SDL_RaiseWindow(SDL_GetWindowFromID(E.windowID));
  }
}

int main(int argc, char** argv) {/*...*/}
Window gained input focus
Window lost input focus
Window gained input focus

Flashing Windows

Naturally, we should be cautious with grabbing input focus. Having our application jump to the top and grab focus is disruptive for users and is rarely warranted.

Most platforms provide a mechanism for windows to request the user’s attention in less disruptive ways. This is called "flashing" the window, but the specific visual effect that "flashing" has varies from platform to platform.

On Windows, for example, flashing a window means it will be highlighted with a colored blinking animation in the user’s task bar:

Screenshot of a program flashing on the Windows taskbar

The SDL_FlashWindow() function let’s us access these attention-grabbing mechanisms:

SDL_FlashWindow(Window, SDL_FLASH_BRIEFLY);

Our first argument is the SDL_Window* we want to attract attention, and the second argument is one of three possible values:

  • SDL_FLASH_BRIEFLY - Briefly flash the window to attract attention
  • SDL_FLASH_UNTIL_FOCUSED - Attract attention continuously until the user focuses our window, or until we cancel the flashing
  • SDL_FLASH_CANCEL - Stop flashing

We cover window management in more detail in a dedicated chapter later in the course.

SDL_SetWindowInputFocus()

SDL includes the SDL_SetWindowInputFocus() function, which can be a bit confusing. In principle, we can use it the same way as SDL_RaiseWindow():

#include <iostream>
#include <SDL.h>
#include <chrono>
#include <thread>
#include "Window.h"

void HandleWindowEvent(SDL_WindowEvent& E) {
  if (E.event == SDL_WINDOWEVENT_FOCUS_GAINED) {
    std::cout << "Window gained input focus\n";
  }
  if (E.event == SDL_WINDOWEVENT_FOCUS_LOST) {
    std::cout << "Window lost input focus\n";

    using namespace std::chrono_literals;
    std::this_thread::sleep_for(5s);
    SDL_SetWindowInputFocus(
      SDL_GetWindowFromID(E.windowID));
  }
}

int main(int argc, char** argv) {/*...*/}

However, in practice, this is rarely appropriate for two reasons:

  • It is not as widely supported across different platforms and will often have no effect
  • On platforms where it is supported, it does not bring the window into view if it is obscured by other applications. This can mean our user’s keyboard input is being sent to a window they cannot see, which can be confusing.

The SDL_RaiseWindow() function alleviates both of these problems so, if we need to grab focus, we should prefer it over SDL_SetWindowInputFocus().

Keyboard Events and Input Focus

SDL only creates keyboard events if they happen when our application has focus. As such, when we’re reacting to input using the event loop, we do not need to check if our window has input focus.

If we received the event, we can assume our application had focus when it happened, so the input was intended for our application.

#include <iostream>
#include <SDL.h>
#include <chrono>
#include <thread>
#include "Window.h"

void HandleKeyboardEvent(SDL_KeyboardEvent& E) {
  std::cout << "Keyboard input detected\n";
}

void HandleWindowEvent(SDL_WindowEvent& E) {
  if (E.event == SDL_WINDOWEVENT_FOCUS_GAINED) {
    std::cout << "Window gained input focus\n";
  }
  if (E.event == SDL_WINDOWEVENT_FOCUS_LOST) {
    std::cout << "Window lost input focus\n";
  }
}

int main(int argc, char** argv) {
  SDL_Init(SDL_INIT_VIDEO);
  Window GameWindow;
  SDL_Event Event;

  while (true) {
    while (SDL_PollEvent(&Event)) {
      if (Event.type == SDL_WINDOWEVENT)
        HandleWindowEvent(Event.window);
      if (Event.type == SDL_KEYDOWN)
        HandleKeyboardEvent(Event.key);
    }
  }

  SDL_Quit();
  return 0;
}
Window gained input focus
Keyboard input detected
Keyboard input detected
Keyboard input detected
Window lost input focus
Window gained input focus
Keyboard input detected

Summary

In this lesson, we explored how to manage window input focus using SDL. The key takeaways include:

  • Creating windows with input focus using SDL_CreateWindow().
  • Detecting if a window has input focus using SDL_GetWindowFlags() and SDL_GetKeyboardFocus().
  • Handling window focus events in the event loop.
  • Using SDL_RaiseWindow() to grab input focus and bring the window to the front.
  • Understanding the limitations of SDL_SetWindowInputFocus().
  • Reacting to keyboard events when the window has input focus.

Was this lesson useful?

Next Lesson

Understanding Keyboard State

Learn how to detect and handle keyboard input in SDL2 using both event-driven and polling methods. This lesson covers obtaining and interpreting the keyboard state array.
Abstract art representing computer programming
Ryan McCombe
Ryan McCombe
Updated
Lesson Contents

Managing Window Input Focus

Learn how to manage and control window input focus in SDL applications, including how to create, detect, and manipulate window focus states.

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
Keyboard Input
  • 53.GPUs and Rasterization
  • 54.SDL Renderers
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:

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

Understanding Keyboard State

Learn how to detect and handle keyboard input in SDL2 using both event-driven and polling methods. This lesson covers obtaining and interpreting the keyboard state array.
Abstract art representing computer programming
Contact|Privacy Policy|Terms of Use
Copyright © 2024 - All Rights Reserved