Using SDL_BORDERLESS for Seamless Windowed‑Fullscreen Transitions
Learn how SDL’s borderless window flag lets you render a true full‑screen image while keeping the window managed by the OS, enabling instant alt‑tabbing and avoiding mode‑switch flicker.
29 Jun 2026, 20:14 UTC

The problem: jarring fullscreen switches
Many games switch to exclusive fullscreen to remove window decorations and gain direct access to the graphics hardware. The mode change forces the compositor to reset the display, which can cause a brief flicker, delay alt‑tabbing, and hide other desktop applications. On laptops or multi‑monitor setups this interruption feels especially disruptive.
Thesis: a borderless window sized to the desktop gives the same visual result without the mode switch
By creating a regular SDL window with the SDL_WINDOW_BORDERLESS flag and matching its size to the current desktop resolution, the application draws directly to the client area while remaining a normal window. The user sees a full‑screen image, but the OS never changes the display mode, so alt‑tabbing is instant and other apps stay accessible.
How SDL_BORDERLESS works under the hood
When SDL_CreateWindow is called with SDL_WINDOW_BORDERLESS, SDL asks the window manager to create a window without a title bar or borders. The window still has a client area where OpenGL, Direct3D, Vulkan, or SDL’s 2D renderer can draw. Because the window is not in exclusive mode, the underlying swap chain and rendering context created with SDL_CreateRenderer stay valid, and SDL_SetSwapInterval continues to control VSync exactly as in a windowed mode.
Worked example: minimal C program
The following code creates a borderless window, clears it to a solid colour, and reports whether the flag is set. Compile it on any platform with SDL2 development libraries installed.
#include <SDL.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
fprintf(stderr, "SDL_Init failed: %s\n", SDL_GetError());
return 1;
}
/* Get the current desktop resolution */
SDL_DisplayMode desktop;
if (SDL_GetDesktopDisplayMode(0, &desktop) != 0) {
fprintf(stderr, "SDL_GetDesktopDisplayMode failed: %s\n", SDL_GetError());
SDL_Quit();
return 1;
}
/* Create a borderless window sized to the desktop */
SDL_Window *win = SDL_CreateWindow(
"Borderless fullscreen example",
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
desktop.w, desktop.h,
SDL_WINDOW_BORDERLESS | SDL_WINDOW_SHOWN
);
if (!win) {
fprintf(stderr, "SDL_CreateWindow failed: %s\n", SDL_GetError());
SDL_Quit();
return 1;
}
/* Verify the flag is present */
Uint32 flags = SDL_GetWindowFlags(win);
if (flags & SDL_WINDOW_BORDERLESS) {
puts("SDL_WINDOW_BORDERLESS flag is set.");
} else {
puts("Warning: SDL_WINDOW_BORDERLESS flag not set.");
}
/* Create a renderer and clear to cornflower blue */
SDL_Renderer *ren = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED);
if (!ren) {
fprintf(stderr, "SDL_CreateRenderer failed: %s\n", SDL_GetError());
SDL_DestroyWindow(win);
SDL_Quit();
return 1;
}
SDL_SetRenderDrawColor(ren, 100, 149, 237, 255); /* cornflower blue */
SDL_RenderClear(ren);
SDL_RenderPresent(ren);
/* Simple event loop – close on ESC or window close */
SDL_Event e;
int running = 1;
while (running) {
while (SDL_PollEvent(&e)) {
if (e.type == SDL_QUIT ||
(e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE)) {
running = 0;
}
}
SDL_Delay(16);
}
SDL_DestroyRenderer(ren);
SDL_DestroyWindow(win);
SDL_Quit();
return 0;
}
To build and run:
- Install SDL2 development headers (e.g.,
sudo apt-get install libsdl2-devon Ubuntu, or use vcpkg/Homebrew on other platforms). - Save the code to
borderless.c. - Compile:
gcc borderless.c -o borderless `sdl2-config --cflags --libs`. - Execute:
./borderless.
Expected checks:
- The window appears without a title bar and stretches edge‑to‑edge of the monitor.
- The console prints “SDL_WINDOW_BORDERLESS flag is set.”
- The rendered cornflower blue fills the entire client area.
- Pressing Alt+Tab switches to other applications instantly, confirming the window remains managed by the compositor.
Trade‑offs and limitations
While borderless windowed mode eliminates mode‑switch latency, it is not a perfect substitute for exclusive fullscreen on every system:
- Window manager interference: Some Linux compositors or Windows themes may still reserve space for a panel or taskbar, causing a thin uncovered strip. Adjusting the window position or size (e.g., subtracting the taskbar height) can mitigate this.
- Input latency: Exclusive fullscreen can bypass certain compositor queues, yielding marginally lower latency on some GPUs. If sub‑millisecond response is critical (e.g., competitive gaming), measure latency on the target hardware and compare.
- Resolution changes: Because the desktop mode stays fixed, you cannot dynamically switch to a different refresh rate without recreating the window. For applications that need to support multiple refresh rates, a hybrid approach (borderless for most cases, exclusive when a specific mode is requested) may be required.
Actionable closing
If your goal is to provide a smooth, alt‑tab‑friendly fullscreen experience without sacrificing rendering flexibility, start with SDL_WINDOW_BORDERLESS and size the window to the desktop resolution. Verify the flag and dimensions at runtime, test on your primary development OS and any target platforms, and measure input latency if it matters for your genre. When the trade‑offs are acceptable, you’ll have a reliable, cross‑platform path to seamless windowed‑fullscreen transitions.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.