Managing Window Position

Learn how to control and monitor the position of SDL windows on screen
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 positioning capabilities, from setting initial window locations to handling dynamic repositioning. We’ll cover

  • How to set window positions explicitly, or use special values that dynamically position our window based on the environment our program is run on
  • Retrieving the current location of our window
  • Moving windows programmatically, including how to shift a window based on its current position
  • Detecting when the user moves our window, so we can respond appropriately
  • A preview of window IDs, which let us manage events in applications that have multiple windows

Window Position

As we’ve seen, the primary way we create windows within an SDL application is by using the SDL_CreateWindow() function. The second and third arguments to SDL_CreateWindow() define where we want the window to be positioned within the user’s screen.

These arguments represent the horizontal and vertical positions respectively. Below, we open a window 100 points from the left edge of the screen, and 200 points from the top edge:

SDL_CreateWindow(
  "Hello Window",
  100, 200, 
  700, 300, 0);

SDL_WINDOWPOS_UNDEFINED

In most use cases, we have no particular need to be this specific with the window position. Instead, we can let the platform decide the best place to open it.

To do this, we can pass SDL_WINDOWPOS_UNDEFINED to either or both of these parameters: which we can use to let the platform decide where to open the window:

SDL_CreateWindow(
  "Hello Window",
  SDL_WINDOWPOS_UNDEFINED, 
  SDL_WINDOWPOS_UNDEFINED, 
  700, 300, 0);

This is a sensible default choice, as the platform developers will have put some thought into where to open windows such that the user experience is pleasant.

For example, they might open the window near where the user clicked the icon to run the program, or they might open the program in the same location where the user placed it the last time they ran it.

SDL_WINDOWPOS_CENTERED

Alternatively, we can pass SDL_WINDOWPOS_CENTERED to have SDL determine what position to use that would cause our window to be centered along either dimension. SDL examines the size of the monitor and the size of the window we’re creating (700x300 in this example) to determine what value would cause that window to be centered.

Below, we open a window that is centered both horizontally and vertically:

SDL_CreateWindow(
  "Hello Window",
  SDL_WINDOWPOS_CENTERED, 
  SDL_WINDOWPOS_CENTERED, 
  700, 300, 0);

Getting the Window Position

Whilst we can specify where we want our window to be initially created, many platforms allow users to move the window elsewhere on their screen. This is typically done by, for example, dragging on the title bar.

We can get the current position of a window by passing its SDL_Window pointer to the SDL_GetWindowPosition() function. We pass two additional arguments, which should be pointers to integers:

int x, y;
SDL_GetWindowPosition(Window, &x, &y);

SDL_GetWindowPosition() updates these integers with the window’s horizontal and vertical position respectively:

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

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

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

  int x, y;
  SDL_GetWindowPosition(Window, &x, &y);
  std::cout << "Position: " << x << ", " << y;

  SDL_Quit();
  return 0;
}
Position: 930, 570

If we only care about the window’s position in one dimension, we can pass a nullptr to the other:

// We only care about horizontal position
int x;
SDL_GetWindowPosition(Window, &x, nullptr);

// We only care about vertical position
int y;
SDL_GetWindowPosition(Window, nullptr, &y);

SDL_WINDOWEVENT_MOVED Events

Whilst SDL_GetWindowPosition() allows us to retrieve the position of a window at any time, when our specific need is to react to a window being moved, the event loop is usually the better approach.

SDL emits an SDL_WindowEvent with an event value of SDL_WINDOWEVENT_MOVED when a window is moved. We can react to this event in the usual way:

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

void HandleWindowEvent(SDL_WindowEvent& E) {
  if (E.event == SDL_WINDOWEVENT_MOVED) {
    std::cout << "Window Moved\n";
  }
}

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);
      } else if (E.type == SDL_QUIT) {
        SDL_Quit();
        return 0;
      }
    }
    GameWindow.Update();
    GameWindow.Render();
  }
}
Window Moved

The data1 and data2 members of this event will contain the new horizontal and vertical position of the window respectively:

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

void HandleWindowEvent(SDL_WindowEvent& E) {
  if (E.event == SDL_WINDOWEVENT_MOVED) {
    std::cout << "Window Moved: "
      << E.data1 << ", " << E.data2 << '\n';
  }
}

int main(int argc, char** argv) {/*...*/}
Window Moved: 506, 281

Programmatically Moving a Window

We can update the position of an SDL_Window* at any time using the SDL_SetWindowPosition() function. This function accepts the SDL_Window* as the first argument, and the window’s new horizontal and vertical positions as the second and third arguments respectively.

Let’s add this capability to our custom Window class by introducing a Move() method:

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

class Window {
public:
  // ...
  void Move(int x, int y) {
    SDL_SetWindowPosition(SDLWindow, x, y);
  }

  SDL_Window* SDLWindow;
};

Just like when we initially created the window, we can pass SDL_WINDOWPOS_UNDEFINED to the positional arguments of SDL_SetWindowPosition() to let the platform decide the best values:

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

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

  SDL_Quit();
  return 0;
}

We can also use SDL_WINDOWPOS_CENTERED to let SDL calculate the value that would cause the window to be centered along either dimension. Below, we move the window to the center of the screen if the user presses their spacebar whilst our window has input focus:

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

void HandleKeydownEvent(
  SDL_KeyboardEvent& E, Window& Window
) {
  if (E.keysym.sym == SDLK_SPACE) {
    Window.Move(
      SDL_WINDOWPOS_CENTERED,
      SDL_WINDOWPOS_CENTERED);
  }
}

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) {
        HandleKeydownEvent(E.key, GameWindow);
      } else if (E.type == SDL_QUIT) {
        SDL_Quit();
        return 0;
      }
    }
    GameWindow.Update();
    GameWindow.Render();
  }
}

SDL_GetWindowFromID()

The event handler in the previous example requires access to a pointer to the SDL_Window that it needs to resize. We accomplished this by providing an additional argument - a reference to our GameWindow object that includes the SDL_Window* as a member variable:

HandleKeydownEvent(E.key, GameWindow);

This is a valid approach for the simple example above, but it is not always possible. For example, if our program contains multiple windows, we won’t necessarily know which one caused the event to show up in our event loop.

An alternative approach involves retrieving the SDL_Window* from the event itself. Most event types, including SDL_KeyboardEvent, include a windowID that identifies the window associated with the event.

By passing this value to SDL_GetWindowFromID(), we can retrieve the corresponding SDL_Window pointer:

void HandleKeydownEvent(SDL_KeyboardEvent& E) {
  if (E.keysym.sym == SDLK_SPACE) {
    SDL_SetWindowPosition(
      SDL_GetWindowFromID(E.windowID),
      SDL_WINDOWPOS_CENTERED,
      SDL_WINDOWPOS_CENTERED);
  }
}

Relative Moving

When our program needs to move a window, the position we want to move it to will often depend on its current position. Rather than setting a position relative to the top left of the screen, we want to set a position relative to where the window currently is.

To do this, we can combine the SDL_GetWindowPosition() and SDL_SetWindowPosition() functions.

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

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

  int x, y;
  SDL_GetWindowPosition(
    GameWindow.SDLWindow, &x, &y);

  // Move the window 10 pixels right
  SDL_SetWindowPosition(
    GameWindow.SDLWindow, x + 10, y);

  SDL_Quit();
  return 0;
}

Let’s update our Window class with a generalized form of this capability, in the form of a method called MoveRelative():

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

class Window {
public:
  void MoveRelative(int DeltaX, int DeltaY) {
    int CurrentX, CurrentY;
    SDL_GetWindowPosition(
      SDLWindow, &CurrentX, &CurrentY);

    SDL_SetWindowPosition(
      SDLWindow,
      CurrentX + DeltaX,
      CurrentY + DeltaY);
  }

  SDL_Window* SDLWindow;
};

In the following example, we use this to let the user move their window with their arrow keys:

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

void HandleKeydownEvent(
  SDL_KeyboardEvent& E, Window& GameWindow) {
  if (E.keysym.sym == SDLK_LEFT) {
    GameWindow.MoveRelative(-10, 0);
  } else if (E.keysym.sym == SDLK_RIGHT) {
    GameWindow.MoveRelative(10, 0);
  } else if (E.keysym.sym == SDLK_UP) {
    GameWindow.MoveRelative(0, -10);
  } else if (E.keysym.sym == SDLK_DOWN) {
    GameWindow.MoveRelative(0, 10);
  }
}

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) {
        HandleKeydownEvent(E.key, GameWindow);
      } else if (E.type == SDL_QUIT) {
        SDL_Quit();
        return 0;
      }
    }
    GameWindow.Update();
    GameWindow.Render();
  }
}

Summary

In this lesson, we've covered the fundamentals of window positioning in SDL2. We've learned how to control window placement both at creation and during runtime, respond to window movement events, and implement relative movement. Key takeaways:

  • Window positions can be specified explicitly or using special values like SDL_WINDOWPOS_UNDEFINED and SDL_WINDOWPOS_CENTERED.
  • SDL_GetWindowPosition() lets us query a window's current location.
  • SDL_SetWindowPosition() allows programmatic window movement.
  • Window movement events (SDL_WINDOWEVENT_MOVED) help us track user-initiated window changes.
  • Relative movement can be implemented by combining position getting and setting.
  • Window IDs provide a reliable way to track and manage multiple windows.

Was this lesson useful?

Next Lesson

Window Sizing

Learn how to resize, constrain, and manage SDL2 windows
Abstract art representing computer programming
Ryan McCombe
Ryan McCombe
Updated
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 Sizing

Learn how to resize, constrain, and manage SDL2 windows
Abstract art representing computer programming
Contact|Privacy Policy|Terms of Use
Copyright © 2024 - All Rights Reserved