Window Configuration

Explore window creation, configuration, and event handling using SDL's windowing system
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 this lesson, we'll explore SDL's window management system, learning how to create, configure, and respond to window events.

We'll cover essential concepts like window creation, error handling, and event processing, providing the foundation needed to build robust windowed applications.

SDL_CreateWindow()

As we’ve seen in previous lessons, the main way we create a window using SDL is the SDL_CreateWindow() function:

SDLWindow* Window{SDL_CreateWindow(
  "My Program", // Title
  100, // Horizontal Position
  200, // Vertical Position
  600, // Width
  300, // Height
  0 // Configuration
)};

This function accepts 6 arguments:

  1. The window title
  2. The window’s horizontal position - 100 points from the left of the screen, in this example
  3. The window’s vertical position - 200 points from the top of the screen, in this example
  4. The window’s horizontal size - 600 points wide, in this example
  5. The window’s vertical size - 300 points tall, in this example
  6. A collection of window flags representing configuration options. In this example, we’ve passed 0, indicating we just want the default configuration.

We’ll cover window titles, positioning, and size in detail later in this chapter. In this lesson, we’ll cover the 6th argument - the window flags that control how the window behaves.

Pixels, Points, and Screen Coordinates

The screens that our programs run on can have different pixel densities. A higher-quality screen will have more pixels-per-inch, that is, more pixels in the same physical area when compared to a lower-quality display:

Comparison between two screens with different pixel densities

This has implications for how we size and position windows and other elements. For example, if we size our elements on the assumption that the display will have 100 pixels per inch, but it is being run on a display with 200 pixels per inch, the physical size of our element will be much smaller than we intended.

This can impair the usability of our program on newer hardware. By default, operating systems managing that hardware assume our programs have not considered this. They will therefore intervene and reinterpret our requested size to be something more appropriate for the pixel density of the screen it is being displayed on.

The net effect is that we do not necessarily know the units we’re using to size and position windows. It may be in pixels, or it may be in some other unit (sometimes referred to as points or screen coordinates) that will be converted to pixel values appropriate for the screen our content is displayed on.

We cover pixel density in much more detail in the next chapter. This includes preventing the platform from rescaling our content, thereby giving us full control to take advantage of higher quality displays.

SDL_Window

Windows are internally managed by SDL, using a type called SDL_Window. The SDL_CreateWindow() function returns a pointer to the SDL_Window that it created. We should save this pointer, as we’ll need to use it later:

SDLWindow* Window{SDL_CreateWindow(
  "My Program",
  100, 200, 600, 300, 0
)};

However, we should consider SDL_Window objects to be read-only.

While it is possible to modify the properties and behavior of windows in our application, this should only be done through specific functions provided by SDL. Direct modification of the SDL_Window structure is not recommended.

We’ll cover most of these functions throughout this chapter.

SDL_DestroyWindow()

The first function used with an SDL_Window* is SDL_DestroyWindow() which, as we might guess, deletes the window when we no longer need it.

We should remember to call SDL_DestroyWindow() when we’re done with any window. Window creation requires SDL to allocate quite a lot of memory so, to prevent memory leaks, we should tell SDL when it’s safe to release those resources:

// Create the Window
SDLWindow* Window{SDL_CreateWindow(
  "My Program",
  100, 200, 600, 300, 0
)};

// Use the Window
// ...

// Destroy the Window when we're done
SDL_DestroyWindow(Window);

In most programs, our primary window will be open for the entire duration of our program, so this might not seem useful. However, as we’ll see later in this chapter, a program can contain multiple windows.

This can include temporary "utility windows" used to implement UI designs like tooltips and right-click menus.

To manage this, we’ll typically have a custom Window type that controls an SDL_Window in the context of our application. This involves creating the SDL_Window in its constructor, and destroying it in the destructor.

We don’t want objects of this type to be copyable, so we’ll also delete the copy constructor and copy assignment operator:

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

class Window {
public:
  Window() {
    SDLWindow = SDL_CreateWindow(
      "My Program",
      100, 200, 600, 300, 0
    );
  }

  ~Window() { SDL_DestroyWindow(SDLWindow); }
  
  Window(const Window&) = delete;
  Window& operator=(const Window&) = delete;
  
  SDL_Window* SDLWindow;
};

Error Handling

The SDL_CreateWindow() function can fail. When this happens, it returns a nullptr, which we can test for using an if statement. We can also call SDL_GetError() to receive an explanation of why window creation failed.

Below, we attempt to create a window using Metal (an API used on Apple systems) on a platform that doesn’t support it:

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

class Window {
public:
  Window() {
    SDLWindow = SDL_CreateWindow(
      "My Program",
      100, 200, 600, 300,
      SDL_WINDOW_METAL 
    );
    
    if (!SDLWindow) {
      std::cout << SDL_GetError();
    }
  }

  ~Window() { SDL_DestroyWindow(SDLWindow); }
  
  Window(const Window&) = delete;
  Window& operator=(const Window&) = delete;

  SDL_Window* SDLWindow;
};
// main.cpp
#include <SDL.h>
#include "Window.h"

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

  SDL_Quit();
  return 0;
}
Metal support is either not configured in SDL or not available in current SDL video driver (windows) or platform

SDL_WindowFlags

The 6th argument to SDL_CreateWindow() controlling our window configuration has a type of SDL_WindowFlags. This type is a bit set, where each individual bit in the type corresponds to whether a specific setting should be enabled or disabled.

A value of 0 corresponds to no flags being enabled:

SDL_WindowFlags Flags{0};

SDL_CreateWindow(
  "My Program",
  100, 200, 600, 300,
  Flags 
);

SDL provides a collection of bit masks, helping us select specific bits within the SDL_WindowFlags bit set. We’ll cover the available window flags throughout the rest of this chapter, but some examples include SDL_WINDOW_FULLSCREEN, SDL_WINDOW_BORDERLESS, and SDL_WINDOW_RESIZABLE:

SDL_WindowFlags A{SDL_WINDOW_FULLSCREEN};
SDL_WindowFlags B{SDL_WINDOW_BORDERLESS};
SDL_WindowFlags C{SDL_WINDOW_RESIZABLE};

One of the benefits of a bit set is that we can easily select multiple flags at once using the | operator. Below, we create a set of flags that will make the window both borderless and resizable:

SDL_WindowFlags Flags{
  SDL_WINDOW_BORDERLESS | SDL_WINDOW_RESIZABLE
};

Note that the bitwise OR operator uses a single vertical bar - | . It is not the same as the more common logical OR operator, which uses two: ||.

We cover bitwise operators and bit flags in much more detail in our introductory course:

SDL_GetWindowFlags()

When our program runs, the state of our window can be changed by the code we write or actions performed by the user. For example, whilst we may configure our window to be initially maximized, the user may later resize it.

SDL keeps the SDL_WindowFlags associated with a window up to date through these interactions. We can get the current state of a window’s flags by passing the SDL_Window pointer to SDL_GetWindowFlags():

SDL_Window* Window{SDL_CreateWindow(
  "My Program",
  100, 200, 600, 300,
  SDL_WINDOW_RESIZABLE | SDL_WINDOW_MAXIMIZED
)};

SDL_WindowFlags Flags{
  SDL_GetWindowFlags(Window)
};

We can examine the state of a specific flag by using the same masks we introduced earlier. To test if a flag is enabled, we use the bitwise AND operator, &.

This will return 0 if the flag is disabled, and a non-zero number otherwise:

SDL_Window* Window{SDL_CreateWindow(
  "My Program",
  100, 200, 600, 300,
  SDL_WINDOW_RESIZABLE | SDL_WINDOW_MAXIMIZED
)};

SDL_WindowFlags Flags{
  SDL_GetWindowFlags(Window)
};

bool isMaximized{Flags & SDL_WINDOW_MAXIMIZED != 0};

Let’s update our Window class to add some of these capabilities. For example, we’ll update our constructor to accept an SDL_WindowFlags argument and a public method that can be used to determine if our window is currently maximized:

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

class Window {
public:
  Window(SDL_WindowFlags Flags = 0) {
    SDLWindow = SDL_CreateWindow(
      "My Program",
      100, 200, 600, 300,
      Flags 
    );
    
    if (!SDLWindow) {
      std::cout << SDL_GetError();
    }
  }
  
  bool GetIsMaximized() {
    return SDL_GetWindowFlags(SDLWindow) & SDL_WINDOW_MAXIMIZED;
  }

  ~Window() { SDL_DestroyWindow(SDLWindow); }
  
  Window(const Window&) = delete;
  Window& operator=(const Window&) = delete;

  SDL_Window* SDLWindow;
};

Summary

In this lesson, we've explored the fundamentals of window management in SDL. We've learned how to create windows, handle errors, configure window behavior using flags, and respond to window events.

Key takeaways:

  • Windows are created using SDL_CreateWindow() with customizable position, size, and behavior
  • We should consider the possibility that window creation can fail, and use SDL_GetError() to understand why
  • Window management requires proper cleanup using SDL_DestroyWindow()
  • SDL_WindowFlags control window behavior through bitwise operations
  • Window state can be monitored through SDL_GetWindowFlags() and event handling

Was this lesson useful?

Next Lesson

Window Events and Window IDs

Discover how to monitor and respond to window state changes in SDL applications
Abstract art representing computer programming
Ryan McCombe
Ryan McCombe
Updated
Lesson Contents

Window Configuration

Explore window creation, configuration, and event handling using SDL's windowing system

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
Window Management
  • 60.GPUs and Rasterization
  • 61.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:

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

Window Events and Window IDs

Discover how to monitor and respond to window state changes in SDL applications
Abstract art representing computer programming
Contact|Privacy Policy|Terms of Use
Copyright © 2024 - All Rights Reserved