Managing Window Input Focus

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

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.

In this lesson, we'll learn how to interact with this mechanism using SDL.

Starting Point

This lesson builds on our earlier work. We'll start with a minimal project containing a Window class and a main function with a basic application loop if you want to follow along.

Note that we've added a GetRaw() function to our Window class, which returns the underlying SDL_Window pointer that it manages:

Files

src
Select a file to view its content

Creating a Window with Input Focus

On most platforms, a newly opened window will have input focus by default, but we can explicitly specify this as a requirement if we wish.

The SDL_WindowFlags mask includes a bit dedicated to whether or not the window has input focus. In SDL3, this flag is named SDL_WINDOW_INPUT_FOCUS.

We can specify that our window should have input focus as soon as it is created by providing this flag to SDL_CreateWindow():

src/Window.h

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

class Window {
public:
  Window(){
    SDLWindow = SDL_CreateWindow(
      "Hello Window",
      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", 700, 300,
  SDL_WINDOW_INPUT_FOCUS | SDL_WINDOW_RESIZABLE  
);

You can learn more about this technique in our .

Detecting if a Window has Input Focus

SDL keeps the window flags updated throughout 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.

SDL_GetWindowFlags() returns a bitmask as an SDL_WindowFlags type, which is a 64-bit unsigned integer in SDL3:

src/main.cpp

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

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

  std::cout << "Window Flags: "
    << SDL_GetWindowFlags(GameWindow.GetRaw());

  bool IsRunning = true;
  while (IsRunning) {
    while (SDL_PollEvent(&Event)) {
      if (Event.type == SDL_EVENT_QUIT) {
        IsRunning = false;
      }
    }
    GameWindow.Render();
    GameWindow.Update();
  }

  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:

src/main.cpp

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

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

  SDL_WindowFlags Flags{
    SDL_GetWindowFlags(GameWindow.GetRaw())};

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

  bool IsRunning = true;
  while (IsRunning) {
    while (SDL_PollEvent(&Event)) {
      if (Event.type == SDL_EVENT_QUIT) {
        IsRunning = false;
      }
    }
    GameWindow.Render();
    GameWindow.Update();
  }

  SDL_Quit();
  return 0;
}
Window has input focus

Using SDL_GetKeyboardFocus()

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

src/main.cpp

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

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

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

  bool IsRunning = true;
  while (IsRunning) {
    while (SDL_PollEvent(&Event)) {
      if (Event.type == SDL_EVENT_QUIT) {
        IsRunning = false;
      }
    }
    GameWindow.Render();
    GameWindow.Update();
  }

  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:

src/main.cpp

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

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

  SDL_Window* Focused{SDL_GetKeyboardFocus()};

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

  bool IsRunning = true;
  while (IsRunning) {
    while (SDL_PollEvent(&Event)) {
      if (Event.type == SDL_EVENT_QUIT) {
        IsRunning = false;
      }
    }
    GameWindow.Render();
    GameWindow.Update();
  }

  SDL_Quit();
  return 0;
}
GameWindow has input focus

Using the Event Loop

We can also keep track of whether our window has focus using the event loop. In SDL3, window events are top-level events. When our window gains or loses focus, SDL broadcasts SDL_EVENT_WINDOW_FOCUS_GAINED and SDL_EVENT_WINDOW_FOCUS_LOST events.

We can be on the lookout for these event types in our event loop:

src/main.cpp

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

void HandleWindowEvent(const SDL_WindowEvent& E) {
  if (E.type == SDL_EVENT_WINDOW_FOCUS_GAINED) {
    std::cout << "Window gained input focus\n";
  }
  if (E.type == SDL_EVENT_WINDOW_FOCUS_LOST) {
    std::cout << "Window lost input focus\n";
  }
}

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

  bool IsRunning = true;
  while (IsRunning) {
    while (SDL_PollEvent(&Event)) {
      if (Event.type == SDL_EVENT_WINDOW_FOCUS_GAINED ||
          Event.type == SDL_EVENT_WINDOW_FOCUS_LOST
      ) {
        HandleWindowEvent(Event.window);
      } else if (Event.type == SDL_EVENT_QUIT) {
        IsRunning = false;
      }
    }
    GameWindow.Render();
    GameWindow.Update();
  }

  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.GetRaw()};
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 stops responding for 5 seconds after losing input focus, and then automatically automatically grabs it back using SDL_RaiseWindow().

The SDL_WindowEvent struct contains a windowID, which we can use with SDL_GetWindowFromID() to retrieve the SDL_Window* associated with that event:

src/main.cpp

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

void HandleWindowEvent(const SDL_WindowEvent& E) {
  if (E.type == SDL_EVENT_WINDOW_FOCUS_GAINED) {
    std::cout << "Window gained input focus\n";
  }
  if (E.type == SDL_EVENT_WINDOW_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, char**) {/*...*/}
Window gained input focus
Window lost input focus
Window gained input focus

Flashing Windows

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:

The SDL_FlashWindow() function lets 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.

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.

src/main.cpp

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

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

void HandleWindowEvent(const SDL_WindowEvent& E) {
  if (E.type == SDL_EVENT_WINDOW_FOCUS_GAINED) {
    std::cout << "Window gained input focus\n";
  }
  if (E.type == SDL_EVENT_WINDOW_FOCUS_LOST) {
    std::cout << "Window lost input focus\n";
  }
}

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

  bool IsRunning = true;
  while (IsRunning) {
    while (SDL_PollEvent(&Event)) {
      if (Event.type == SDL_EVENT_WINDOW_FOCUS_GAINED ||
          Event.type == SDL_EVENT_WINDOW_FOCUS_LOST) {
        HandleWindowEvent(Event.window);
      } else if (Event.type == SDL_EVENT_KEY_DOWN) {
        HandleKeyboardEvent(Event.key);
      } else if (Event.type == SDL_EVENT_QUIT) {
        IsRunning = false;
      }
    }
    GameWindow.Render();
    GameWindow.Update();
  }

  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. The key takeaways include:

  • Creating windows with initial input focus using the SDL_WINDOW_KEYBOARD_FOCUS window flag.
  • Detecting if a window has input focus using SDL_GetWindowFlags() and SDL_GetKeyboardFocus().
  • Handling window focus events in the event loop by checking for SDL_EVENT_WINDOW_FOCUS_GAINED and SDL_EVENT_WINDOW_FOCUS_LOST.
  • Using SDL_RaiseWindow() to grab input focus and bring the window to the front.
  • Reacting to keyboard events when the window has input focus.
Next Lesson
Lesson 48 of 52

Understanding Keyboard State

Learn how to detect and handle keyboard input in SDL3 using both event-driven and polling methods. This lesson covers obtaining and interpreting the keyboard state array.

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