3
votes

I've got some odd issue with fullscreen behaviour on Ubuntu. Windowed mode is working fine, but both "fake" (SDL_WINDOW_FULLSCREEN) and "real" (SDL_WINDOW_FULLSCREEN_DESKTOP) fullscreen modes are not.

On Windows, "fake" fullscreen mode does a videomode change and we see window's content stretched to a whole display. "Real" fullscreen mode takes the size of the desktop. In this mode I draw window's content in the top-left corner and leave extra area empty.

On Ubuntu, "fake" fullscreen mode does a videomode change and window's content is stretched to a whole display, but only part of it is drawn. It's either top part (more than 90% of the display) or bottom part (less than 10% of the display). The part that is not drawn is black or contains something that was drawn on the screen before the application was statred. Sometimes application does not change videomode back on exit. Cursor is locked inside top part even if only bottom part is drawn.

"Real" fullscreen mode is either completely black or shows window's contents in the center of the display. Contents are shifted to the top, and everything around that area is black (though background color is not). In this mode cursor is locked inside that area.

Behaviour differs a bit if I change fullscreen mode in run-time - drawn area (which is also area where cursor is locked) can be anywhere, not only top/bottom in "fake" mode or center in "real".

  • "Fake" fullscreen mode on Windows: (800x600 videomode)
  • "Real" fullscreen mode on Windows: real windows (800x600 window's content is drawn in the corner, other area is empty)
  • "Fake" fullscreen mode on Ubuntu: fake ubuntu (800x600 videomode, but not whole area is visible. You can see a part of gedit though.)
  • "Real" fullscreen mode on Ubuntu: real ubuntu (800x600 area in center, but image is shifted to the top (red square meant to be in the corner))

I'm manually using OpenGL to draw on screen instead of SDL. I've wrote a small example that shows the problem. It creates a 800x600 window and draws a blue triangle in it's center. Green square is drawn in the corner of the screen and red square is drawn in the corner of the window (those can be in the same place, so green square is bigger and red square is drawn on top of it). One can enter windowed, "fake" or "real" fullscreen modes using '1', '2' or '3' key respectively. Escape key closes the application.

#include <SDL.h>
#include <GL/gl.h>

#define FULLSCREEN 0
#define WIDTH 800
#define HEIGHT 600

//fullscreen:
// 0 - windowed mode,
// 1 - "fake" fullscreen mode,
// 2 - "real" fullscreen mode

int real_width;
int real_height;

//screen width & height - to draw green square that shows us actual screen size

void set_viewport(SDL_Window* window);
void draw(SDL_Window* window);

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

 int position;
 #if FULLSCREEN == 0
  position = SDL_WINDOWPOS_CENTERED;
 #else
  position = 0;
 #endif

 //the only thing I've found - some guy said it works
 //if window is in (0,0) while entering fullscreen
 //well, it's not, but I've kept it

 int flags = SDL_WINDOW_OPENGL;
 #if FULLSCREEN == 1
  flags |= SDL_WINDOW_FULLSCREEN;
 #elif FULLSCREEN == 2
  flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
 #endif

 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

 SDL_Window* window = SDL_CreateWindow(
  "",
  position, position, /* centered/(0,0) */
  WIDTH, HEIGHT, /* request 800x600 window */
  flags /* needed mode */
 );

 //setup GL
 SDL_GLContext glcontext = SDL_GL_CreateContext(window);

 glShadeModel(GL_SMOOTH);
 glCullFace(GL_BACK);
 glFrontFace(GL_CCW);
 glEnable(GL_CULL_FACE);

 //viewport and projection
 set_viewport(window);

 draw(window);

 bool done = false;
 while(!done) {
  draw(window);

  SDL_Event event;
  while(SDL_PollEvent(&event)) {
   switch(event.type) {
    case SDL_QUIT: done = true; break;
    case SDL_KEYDOWN:
     if(event.key.keysym.scancode == SDL_SCANCODE_ESCAPE)
      done = true;
     else if(event.key.keysym.scancode == SDL_SCANCODE_1) {
      SDL_SetWindowFullscreen(window, 0);
      set_viewport(window);
     } else if(event.key.keysym.scancode == SDL_SCANCODE_2) {
      SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN);
      set_viewport(window);
     } else if(event.key.keysym.scancode == SDL_SCANCODE_3) {
      SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP);
      set_viewport(window);
     }
    break;
   }
  }
 }

 SDL_GL_DeleteContext(glcontext);
 SDL_DestroyWindow(window);
 SDL_Quit();
 return 0;
}

void set_viewport(SDL_Window* window) {
 SDL_GetWindowSize(window, &real_width, &real_height);

 glClearColor(1, 1, 1, 1);
 glViewport(0, 0, real_width, real_height);
 glMatrixMode(GL_PROJECTION);
 glLoadIdentity();
 glOrtho(0, real_width, real_height, 0, -1, 0);
}

void draw_triangle();
void draw_square(int x, int y, float side);

void draw(SDL_Window* window) {
 glClear(GL_COLOR_BUFFER_BIT);
 glEnable(GL_BLEND);
 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

 //triangle on while background
 glClearColor(1, 1, 1, 1);
 draw_triangle();

 //green square at screen corner
 glColor3f(0, 1, 0);
 draw_square(real_width, real_height, 20);

 //red square at window corner
 glColor3f(1, 0, 0);
 draw_square(WIDTH, HEIGHT, 10);

 SDL_GL_SwapWindow(window);
}

void draw_triangle() {
 const float w = 460;
 const float h = 400;

 float colorBuffer[9] = {
  0, 0.43f, 0.85f /*#006dd9*/,
  0, 0.22f, 0.43f /*#00376e*/,
  0, 0.43f, 0.85f /*#006dd9*/
 };
 float vertexBuffer[9] = {
  0, 0, 0,
  w/2, h, 0,
  w, 0, 0
 };

 glEnableClientState(GL_VERTEX_ARRAY);
 glEnableClientState(GL_COLOR_ARRAY);

 glTranslatef((WIDTH-w)/2,(HEIGHT-h)/2,0);

 glColorPointer(3, GL_FLOAT, 0, colorBuffer);
 glVertexPointer(3, GL_FLOAT, 0, vertexBuffer);
 glDrawArrays(GL_TRIANGLE_FAN, 0, 3);

 glTranslatef(-(WIDTH-w)/2,-(HEIGHT-h)/2,0);

 glDisableClientState(GL_COLOR_ARRAY);
 glDisableClientState(GL_VERTEX_ARRAY);
}


void draw_square(int x, int y, float side) {
 float vertexBuffer[12] = {
  0, 0, 0, /*top left*/
  0, side, 0, /*bottom left*/
  side, side, 0, /*bottom right*/
  side, 0, 0 /*top right*/
 };

 glEnableClientState(GL_VERTEX_ARRAY);

 glTranslatef(x-side/2, y-side/2, 0);

 glVertexPointer(3, GL_FLOAT, 0, vertexBuffer);
 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

 glTranslatef(-x+side/2, -y+side/2, 0);

 glDisableClientState(GL_VERTEX_ARRAY);
}
1
I don't understand what you mean by fake and real here, SDL_WINDOW_FULLSCREEN means "go fullscreen with the closest matching video mode available to what the user requested", while SDL_WINDOW_FULLSCREEN_DESKTOP means "go fullscreen at the current desktop resolution". Changing modes on Linux may or may not work correctly depending on the window manager, graphics card driver, number of monitors, xrandr support, etc (it works fine on my system with KDE+nVidia blob+Twinview for example). If your system does not behave as expected, please fill a bug with as much detail as possible. - gabomdq

1 Answers

3
votes

Modechanges are eeeeevil, especially in multi-display environments.

Try borderless, desktop-sized windows instead:

#include <GL/glew.h>
#include <SDL2/SDL.h>

// use border state as proxy for fullscreenedness
SDL_Rect ToggleFakeFullscreen( SDL_Window* window, const SDL_Rect& oldBounds )
{
    if( SDL_GetWindowFlags( window ) & SDL_WINDOW_BORDERLESS )
    {
        SDL_SetWindowBordered( window, SDL_TRUE );
        SDL_SetWindowSize( window, oldBounds.w, oldBounds.h );
        SDL_SetWindowPosition( window, oldBounds.x, oldBounds.y );
        return oldBounds;
    }
    else
    {
        SDL_Rect curBounds;
        SDL_GetWindowPosition( window, &curBounds.x, &curBounds.y );
        SDL_GetWindowSize( window, &curBounds.w, &curBounds.h );

        int idx = SDL_GetWindowDisplayIndex( window );
        SDL_Rect bounds;
        SDL_GetDisplayBounds( idx, &bounds );
        SDL_SetWindowBordered( window, SDL_FALSE );
        SDL_SetWindowPosition( window, bounds.x, bounds.y );
        SDL_SetWindowSize( window, bounds.w, bounds.h );

        return curBounds;
    }
}

int main( int argc, char **argv )
{
    if( SDL_Init( SDL_INIT_EVERYTHING ) < 0 )
        return -1;

    SDL_Window* window = SDL_CreateWindow
        (
        "Test",
        SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
        640, 480,
        SDL_WINDOW_RESIZABLE | SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL
        );
    if( NULL == window )
        return -1;

    SDL_GLContext ctx = SDL_GL_CreateContext( window );
    if( GLEW_OK != glewInit() )
        return -1;

    SDL_Rect curBounds;

    bool running = true;
    while( running )
    {
        SDL_Event ev;
        while( SDL_WaitEventTimeout( &ev, 16 ) )
        {
            if( ev.type == SDL_QUIT )  
                running = false;
            if( ev.type == SDL_KEYUP &&
                ev.key.keysym.sym == SDLK_ESCAPE ) 
                running = false;

            if( ev.type == SDL_KEYUP && 
                ev.key.keysym.sym == SDLK_f )
                curBounds = ToggleFakeFullscreen( window, curBounds );
        }

        int w, h;
        SDL_GetWindowSize( window, &w, &h );
        glViewport( 0, 0, w, h );

        glClearColor( 0, 0, 0, 1 );
        glClear( GL_COLOR_BUFFER_BIT );

        glMatrixMode( GL_PROJECTION );
        glLoadIdentity();

        glMatrixMode( GL_MODELVIEW );
        glLoadIdentity();

        glColor3ub( 255, 0, 0 );
        glBegin( GL_TRIANGLES );
        glVertex2i( -1, -1 );
        glVertex2i(  1, -1 );
        glVertex2i(  0,  1 );
        glEnd();

        SDL_GL_SwapWindow( window );
    }

    SDL_GL_DeleteContext( ctx );
    SDL_DestroyWindow( window );
    SDL_Quit();

    return 0;
}

Hit f to toggle "fullscreen".