SDL fullscreen no longer stretching by default.

Hi Everyone,

I recently noticed that my program is no longer stretching the output by default. On windows I’m running SDL 2.0.12 and the stretching is happening automatically as expected. But in Linux I’m running SDL 2.26.3 and the screen is no longer stretching when I switch to full screen. It seems the default in SDL changed at some point. What can I do to have my surface automatically stretch when switching to full screen?

This is how I create a window:

void ley::Video::createWindow() {

    /* create a window to render to */
    if(SDL_InitSubSystem(SDL_INIT_VIDEO) >= 0) {
        // if all good
        window = SDL_CreateWindow(
            APPLICATION_STRING.c_str(),
            SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
            SCREEN_WIDTH, SCREEN_HEIGHT,
            SDL_WINDOW_SHOWN /*SDL_WINDOW_FULLSCREEN*/);

        SDL_StopTextInput();
    } else {
        printf("Can't Initialize SDL2 Video");
        video_ready = 0;
    }
}

This is how I switch to fullscreen:

void ley::Video::setFullScreen(bool f) {

    if(f) {
        SDL_SetWindowFullscreen(window,SDL_WINDOW_FULLSCREEN);
    } else { SDL_SetWindowFullscreen(window,SDL_WINDOW_SHOWN); }
    fs = f;
}

Thanks for your time,
Electrosys

This is kind of a known issue, but it seems to be more Xorg related. At least it doesn’t occur on my computer in Wayland, but it does in X.
The best term I’ve heard so far is “Letterboxing”, and the idea is that it’s meant to preserve the “intended” aspect ratio of the originally requested window.
I don’t know exactly where it occurs, but I was assuming it’s happening at X11/Xorg or the window manager level. It’s strange that it doesn’t occur on older versions, so perhaps it is being triggered in the SDL source code somewhere.

Some work-arounds for it are:

  • Use SDL_WINDOW_FULLSCREEN_DESKTOP instead. (Things aren’t stretched to fit, just more of the image/screen is visible)
  • Set SCREEN_WIDTH and SCREEN_HEIGHT to a 16:9 aspect ratio. (640, 360), (1280, 720), (1920, 1080), etc.

Here’s a program that changes the requested window size to match the closest available SDL_DisplayMode that is available. It seems that Wayland actually does not stretch the pixels of the window to a true match of the screen, but instead does something similar as this program behind the scenes (I did a red rectangle test on Wayland and there were about 100 extra pixels to the right of the rectangle).

#include <SDL2/SDL.h>

int SCREEN_WIDTH = 500;
int SCREEN_HEIGHT = 500;

int main()
{
	SDL_Init(SDL_INIT_EVERYTHING);

	// redRectangle will represent your originally requested size;
	SDL_Rect redRectangle = {0, 0, SCREEN_WIDTH, SCREEN_HEIGHT};

	int modeCount = SDL_GetNumDisplayModes(0);
	SDL_Log("Mode count: %d", modeCount);
	SDL_DisplayMode mode;
	// A list of the display sizes available on your device, 
	// these can often be determined by your OS and Window Manager instead of
	// your actual hardware
	for(int i = 0; i < modeCount; i ++)
	{
		SDL_GetDisplayMode(0, i, &mode);
		SDL_Log("Mode %d: (w, h) = (%d, %d)", i, mode.w, mode.h);
	}

	// Fetching the closest mode to your requested size
	mode.w = SCREEN_WIDTH;
	mode.h = SCREEN_HEIGHT;
	SDL_DisplayMode modeRecieved;
	SDL_GetClosestDisplayMode(0, &mode, &modeRecieved);
	SCREEN_WIDTH = modeRecieved.w;
	SCREEN_HEIGHT = modeRecieved.h;

	// Creating the window with closest available screen ratio
	SDL_Window * win = SDL_CreateWindow("title", 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
	SDL_Renderer * screen = SDL_CreateRenderer(win, -1, SDL_RENDERER_PRESENTVSYNC);

	int modeIndex = 0;

	uint32_t fsFlag = 0;
	bool run = true;
	while(run)
	{
		SDL_Event ev;
		while(SDL_PollEvent(&ev))
		{
			switch(ev.type)
			{
				case SDL_KEYDOWN:
					switch(ev.key.keysym.sym)
					{
						case SDLK_1:
							// hit 1 to enter full screen.
							fsFlag = SDL_WINDOW_FULLSCREEN;
							SDL_SetWindowFullscreen(win, fsFlag);
							break;
						case SDLK_ESCAPE:
							// hit escape to exit fullscreen
							fsFlag = 0;
							SDL_SetWindowFullscreen(win, fsFlag);
							break;

						case SDLK_f:
							// toggle fullscreen
							fsFlag ^= SDL_WINDOW_FULLSCREEN;
							SDL_SetWindowFullscreen(win, fsFlag);
							break;

							// this block is for if you want to try scrolling through different display modes, 
							// It worked fine on Xorg, but was not very stable on Wayland.
							/*
						case SDLK_UP:
							// while in true fullscreen, hit up or down to try out different display modes.
							// These are not aware of best fit, so it starts at modeIndex = 0
							if(modeIndex > 0)
							{
								modeIndex --;
							}
							SDL_GetDisplayMode(0, modeIndex, &mode);
							SDL_SetWindowDisplayMode(win, &mode);
							SDL_Log("- Current Mode %d: (w, h) = (%d, %d)", modeIndex, mode.w, mode.h);
							break;
						case SDLK_DOWN:
							if(modeIndex < modeCount)
							{
								modeIndex ++;
							}
							SDL_GetDisplayMode(0, modeIndex, &mode);
							SDL_SetWindowDisplayMode(win, &mode);
							SDL_Log("- Current Mode %d: (w, h) = (%d, %d)", modeIndex, mode.w, mode.h);
							break;
							*/
					}
					break;
				case SDL_QUIT:
					run = false;
					break;
			}
		}

		SDL_SetRenderDrawColor(screen, 100, 100, 255, 255);
		SDL_RenderClear(screen);
		SDL_SetRenderDrawColor(screen, 255, 100, 100, 255);
		// drawing redRectangle, we don't have a perfect match, but maybe it's close enough?
		// I don't own a Windows computer, does it do the stretch correctly for the rectangle?
		SDL_RenderDrawRect(screen, &redRectangle);
		SDL_RenderPresent(screen);
	}
	SDL_DestroyWindow(win);
	SDL_Quit();
}

I guess one of my questions to you since I don’t have a Windows machine is this:
On Windows, If you draw a rectangle the size that you expect the screen to be, does it outline the screen correctly in fullscreen mode? Are you instead getting extra pixels or missing some that you weren’t expecting (like Wayland and the above code)?

Sorry to throw so much out there, but I just found this hint in include/SDL_hints.h:

/**
 *  \brief  A variable controlling the scaling policy for SDL_RenderSetLogicalSize.
 *
 *  This variable can be set to the following values:
 *    "0" or "letterbox" - Uses letterbox/sidebars to fit the entire rendering on screen
 *    "1" or "overscan"  - Will zoom the rendering so it fills the entire screen, allowing edges to be drawn offscreen
 *
 *  By default letterbox is used
 */
#define SDL_HINT_RENDER_LOGICAL_SIZE_MODE       "SDL_RENDER_LOGICAL_SIZE_MODE"

So, by that definition, it’s actually strange that letterboxing does not happen on all devices…
Also I think the “overscan” option still has a 16:9 display size, though I haven’t tried using this hint yet.

[Edit: I don’t know if anyone has had luck with this hint, but it seems to be broken. I don’t use hints very often, so I’m quite possibly trying to use it wrong…]

This is all really great info. Thank you! Let me take a look a little closer and see what I can come up with.

The solution was as simple as adding the SDL_RenderSetLogicalSize() method. I’ll have to play around with this some more as I think my game will likely need more advanced resolution handling but this will at least scale when switching between fullscreen and windowed so its a good start.

const auto SCREEN_WIDTH = 1280;
const auto SCREEN_HEIGHT = 720;
void ley::Video::setFullScreen(bool f) {

    if(f) {
        SDL_SetWindowFullscreen(window,SDL_WINDOW_FULLSCREEN_DESKTOP);
        SDL_RenderSetLogicalSize(renderer, SCREEN_WIDTH, SCREEN_HEIGHT);
    } else { 
        SDL_SetWindowFullscreen(window,SDL_WINDOW_SHOWN);
        SDL_RenderSetLogicalSize(renderer, SCREEN_WIDTH, SCREEN_HEIGHT);
    }

    fs = f;
}
1 Like

When running in windows with my modification the rect is visible on the edge of both windowed and fullscreen, likely because the window is 1280 x 720 and the screen is 1920 x 1080, same ratio. I suspect this would have also been true before the slight modification as well.

1 Like