Search code examples
c++cwinapiopenglfullscreen

Is there a graceful way to handle toggling between fullscreen and windowed mode in a Windows OpenGL application?


I'm wondering if it's possible to toggle back and forth between fullscreen mode and windowed mode in an OpenGL window(I'm writing for Windows using C++ and win32), without destroying the OpenGL context, and thus having to reload assets(Textures, VBOs, etc) in the process?

This is undesirable because it introduces a delay in switching between fullscreen and windowed mode, potentially a long one, as well as making it easier to screw things up by forgetting to reinitialize something.

As a followup to that, are there certain visual effects that are broken by managing to do this?

I've done a fair bit of searching and reading for the past few days, and despite a lot of flaming of SDL and other frameworks for having the same problem(I'm not using them anyway, but...), the best I've managed to find is a possible lead on opening a 1x1 window in the background to retain the context while a secondary window is destroyed or created at whim. And that's seeming unreliable from the comments I found regarding it, and seems very kludgey regardless.

Is there a proper way to do this, or is the proper way the often-given-as-an-example method of destroying your window, and recreating it, including destroying your OpenGL context and recreating it?


Solution

  • Basically it's just resizing the window and specifying flags that the border is invisible.

    SetWindowLongPtr(hWnd, GWL_STYLE, 
        WS_SYSMENU | WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE);
    MoveWindow(hWnd, 0, 0, width, height, TRUE);
    

    to set it back:

    RECT rect;
    rect.left = 0;
    rect.top = 0;
    rect.right = width;
    rect.bottom = height;
    SetWindowLongPtr(hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW | WS_VISIBLE);
    AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE);
    MoveWindow(hWnd, 0, 0, rect.right-rect.left, rect.bottom-rect.top, TRUE);
    

    or for a not-resizable window:

    SetWindowLongPtr(hWnd, GWL_STYLE, WS_CAPTION | WS_POPUPWINDOW | WS_VISIBLE);
    AdjustWindowRect(&rect, WS_CAPTION | WS_POPUPWINDOW, FALSE);
    MoveWindow(hWnd, 0, 0, rect.right-rect.left, rect.bottom-rect.top, TRUE);
    

    and then just resize your OpenGL viewport settings.

    If you want to set the display mode too, use this:

    // change display mode if destination mode is fullscreen
    if (fullscreen) {
        DEVMODE dm;
        dm.dmSize = sizeof(DEVMODE);
        dm.dmPelsWidth = width;
        dm.dmPelsHeight = height;
        dm.dmBitsPerPel = bitsPerPixel;
        dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL;
        success = ChangeDisplaySettings(&dm, 0) == DISP_CHANGE_SUCCESSFUL;
    }
    
    // reset display mode if destination mode is windowed
    if (!fullscreen)
        success = ChangeDisplaySettings(0, 0) == DISP_CHANGE_SUCCESSFUL;