【问题标题】:Is there a graceful way to handle toggling between fullscreen and windowed mode in a Windows OpenGL application?是否有一种优雅的方式来处理 Windows OpenGL 应用程序中全屏和窗口模式之间的切换?
【发布时间】:2011-11-03 19:48:33
【问题描述】:

我想知道是否可以在 OpenGL 窗口中在全屏模式和窗口模式之间来回切换(我正在使用 C++ 和 win32 为 Windows 编写),而不会破坏 OpenGL 上下文,因此必须重新加载资产(纹理、VBO 等)在处理中?

这是不可取的,因为它会在全屏和窗口模式之间的切换中引入延迟,可能会很长,并且更容易因为忘记重新初始化而搞砸事情。

作为后续行动,是否有某些视觉效果因设法做到这一点而被破坏?

在过去的几天里,我进行了大量的搜索和阅读,尽管 SDL 和其他框架因有同样的问题而受到抨击(我反正没有使用它们,但是......) ,我设法找到的最好的方法是在后台打开一个 1x1 窗口以保留上下文,而辅助窗口被破坏或随心所欲地创建。从我发现的有关它的 cmets 来看,这似乎不可靠,而且无论如何看起来都很笨拙。

是否有正确的方法来做到这一点,或者是破坏窗口并重新创建它的经常给出的示例方法,包括破坏您的 OpenGL 上下文并重新创建它的正确方法?

【问题讨论】:

    标签: c++ c winapi opengl fullscreen


    【解决方案1】:

    基本上它只是调整窗口大小并指定边框不可见的标志。

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

    重新设置:

    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);
    

    或者对于不可调整大小的窗口:

    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);
    

    然后只需调整 OpenGL 视口设置的大小。

    如果你也想设置显示模式,使用这个:

    // 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;
    

    【讨论】:

    • 这几乎完美地回答了我的问题,谢谢。但是,我遇到了另一个奇怪的问题......一旦我返回窗口模式,我的全屏绘图中的剩余数据仍然存在。我尝试过的任何方法(InvalidateRect()、RedrawWindow()、ChangeDisplaySettings())似乎都无法使屏幕自行重绘。 (我最终使用了 ChangeDisplaySettings(&saved, 0) 和一个在 ChangeDisplaySettings() 之前使用 ShowWindow(hwnd, SW_HIDE) 的解决方法,除了窗口暂时失去焦点,因此它的输入可以被其他一些应用程序抓取。有吗更好的选择?
    • 我发现了一些额外的信息。显然,在使用 SetWindowLongPtr() 时,您想使用 SetWindowPos() 而不是 MoveWindow()。此外,在调整窗口大小后使用 ChangeDisplaySettings(NULL, 0) 似乎可以解决脏背景问题。
    • 这真的是全屏还是只是一个屏幕大小的无边框窗口?有很大的不同。
    【解决方案2】:

    这是我使用的代码,它使用 SetWindowPos() 而不是 MoveWindow(),正如另一个答案的 cmets 中所讨论的那样。

    void enter_fullscreen(application* App)
    {
      POINT Point = {0};
      HMONITOR Monitor = MonitorFromPoint(Point, MONITOR_DEFAULTTONEAREST);
      MONITORINFO MonitorInfo = { sizeof(MonitorInfo) };
      if (GetMonitorInfo(Monitor, &MonitorInfo)) {
        DWORD Style = WS_POPUP | WS_VISIBLE;
        SetWindowLongPtr(App->Window, GWL_STYLE, Style);
        SetWindowPos(App->Window, 0, MonitorInfo.rcMonitor.left, MonitorInfo.rcMonitor.top,
            MonitorInfo.rcMonitor.right - MonitorInfo.rcMonitor.left, MonitorInfo.rcMonitor.bottom - MonitorInfo.rcMonitor.top,
            SWP_FRAMECHANGED | SWP_SHOWWINDOW);
      }
      App->IsFullscreen = true;
    }
    
    void exit_fullscreen(application* App)
    {
      bool WasMaximized = App->IsMaximized;
      DWORD Style = WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN;
      if (WasMaximized) {
        Style = Style | WS_MAXIMIZE;
      }
      ivec2 WindowPosition = WasMaximized ? App->WindowPosition : App->NormalWindowPosition;
      ivec2 WindowSize = WasMaximized ? App->WindowSize : App->NormalWindowSize;
      SetWindowLongPtr(App->Window, GWL_STYLE, Style);
      SetWindowPos(App->Window, 0,
          WindowPosition.X, WindowPosition.Y, WindowSize.X, WindowSize.Y,
          SWP_FRAMECHANGED | SWP_SHOWWINDOW);
      App->IsFullscreen = false;
    }
    

    我在 F11 上调用它,但也在 WM_ACTIVATE 上调用它。否则窗口有时会在 Windows 7 上保持渲染,即使另一个应用程序会收到所有消息,包括鼠标和键盘。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-02
      • 1970-01-01
      • 2015-07-10
      • 1970-01-01
      • 2019-12-04
      相关资源
      最近更新 更多