简体   繁体   English

是否有一种优雅的方式来处理Windows OpenGL应用程序中的全屏模式和窗口模式之间的切换?

[英]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? 我想知道是否可以在OpenGL窗口中使用全屏模式和窗口模式之间来回切换(我使用C ++和win32编写Windows),而不会破坏OpenGL上下文,因此不得不重新加载资源(纹理, VBO等)在这个过程中?

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. 在过去的几天里,我已经做了很多搜索和阅读,尽管SDL和其他框架有很多问题导致出现同样的问题(我还没有使用它们,但是......),最好的我设法找到一个可能的领导者在后台打开1x1窗口以保留上下文,同时销毁或创建一个辅助窗口。 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? 有没有一种正确的方法来执行此操作,或者是通常给定的示例方法来破坏窗口并重新创建它,包括破坏OpenGL上下文并重新创建它?

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. 然后只调整OpenGL视口设置的大小。

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;

Here's the code I use, which uses SetWindowPos() rather than MoveWindow() , as discussed in the comments of the other answer. 这是我使用的代码,它使用SetWindowPos()而不是MoveWindow() ,正如另一个答案的注释中所讨论的那样。

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

I call it on F11, but also on WM_ACTIVATE . 我在F11上调用它,但也在WM_ACTIVATE上调用它。 Otherwise the window would sometimes keep rendering on top on Windows 7, even if another application would receive all messages, including mouse and keyboard. 否则窗口有时会在Windows 7上呈现在顶部,即使另一个应用程序将接收所有消息,包括鼠标和键盘。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM