多年来,当我在努力理解 Microsoft Windows 的奇怪世界时,我已经阅读了很多 Stack OverflowCreateWindowEx()
等等。这个问题最初被问到“在 WinAPI 中创建可流畅调整大小的 OpenGL 窗口的最佳方法是什么? "
我一直在努力让 WinAPI 制作一个窗口:
- 具有 OpenGL 上下文
- 在“窗口”模式或“全屏”模式下,在多显示器和单显示器显示中正确居中于主显示器(或由命令行信号确定的任何显示器)
- 具有固定的内部客户端屏幕尺寸(视口 2d)
- 不允许您单击外部导致它在错误的时间或在多显示器的特殊情况下失去焦点
- 可以流畅地调整大小,但不会改变内部“客户端大小”(意味着它将固定大小的 OpenGL 内容拉伸到新的屏幕大小)......这里的想法是添加一层虚拟化,以便所有像素都在相同的 1920x1080 (1080p) 坐标系中表示。这部分对我来说没有问题。
- 通过 screen->client ratio 正确处理从 screen_size -> client_size 等效的鼠标事件转换
在我自己开发的 App 框架中,我必须设置显示大小,即使这样,Windows 也没有给我正确大小的窗口。(例如,有时会减去标题栏,有时会减去滚动条,但上下文会绘制在标题栏下方。)
此外,最近从 2010 EE (Win32 / Windows 7) 迁移到 2015 (win32 / Windows 10) 时,我不得不更改参数以使视图居中,因为它在主显示屏上偏离中心。现在,只有有时这些值是正确的或不正确的。例如,如果我去“全屏”,相同的值将绘制在屏幕顶部上方,以便屏幕底部有一个区域显示“gl clear color”(在我的情况下,橙色)
我可以通过提供以下命令行参数来玩这些东西:
- -bordered(默认,实际上没有效果,是带有标题栏等的默认窗口模式)
- -borderless(似乎进入全屏模式,应用偏离中心,win 0,0 实际上位于屏幕中心)
- -windowed(或-window)
如果我不提供 -window,它默认为“全屏”分辨率调整(但前提是我假设支持,否则可能会引发错误)。
无论如何,所有这一切都非常糟糕,因为a)我必须为我正在使用的每个分辨率编写一个 bajillion 案例,而不是为 1080p 编写所有内容并让它调整到显示大小,这是我想要的,因为它可以处理大多数笔记本电脑和台式机上的新显示器(记住这是 Windows)(并且在那些角落情况下只会稍微挤压东西) b)我无法流畅地调整窗口大小,而且我必须将鼠标困在中心并重新计算鼠标位置,所以我记录只有增量 - 这是为了避免鼠标离开窗口并单击桌面,或者从显示器上浮动到其他显示器,即使它被隐藏。我还必须使鼠标光标不可见,以便用户看不到这一点,然后显示模拟鼠标光标。c) 不专门支持 1920x1080 的用户将不会
有人在另一个问题中指出了这篇文章(Win32 中的窗口边框宽度和高度 - 我如何得到它?): https ://web.archive.org/web/20120716062211/http://suite101.com/article/client -area-size-with-movewindow-a17846
我已经通读了这个,了解到 AdjustWindowRectEx() 有一些问题: AdjustWindowRectEx() 和 GetWindowRect() 给 WS_OVERLAPPED 错误的大小
我不使用 WS_OVERLAPPED,所以这只是适度的帮助: AdjustWindowRectEx() 和 GetWindowRect() 使用 WS_OVERLAPPED 给出错误的大小
这是我现在的做法:
display.Resized(display.w,display.h);
// Fill in the window class structure for testing display type.
winclass.cbSize = sizeof(WNDCLASSEX);
winclass.style = CS_DBLCLKS | CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
winclass.lpfnWndProc = WinProc;
winclass.cbClsExtra = 0;
winclass.cbWndExtra = 0;
winclass.hInstance = hinstance;
winclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
winclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
winclass.hCursor = LoadCursor(NULL, IDC_ARROW);
winclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
winclass.lpszMenuName = NULL;
winclass.lpszClassName = WINDOW_CLASS_NAME;
// Save the game instance handle
display.hinstance = game_instance = hinstance;
// Register the window class
if (!RegisterClassEx(&winclass)) return(0);
if (!gl.Init(hinstance, display.bits)) {
return(0);
}
// Detect the display size and create the final display profile
DWORD winStyle=
WS_EX_APPWINDOW |
WS_EX_TOPMOST /*|
WS_EX_ACCEPTFILES*/ ;
// Adjust Window, Account For Window Borders
int xPos = GetSystemMetrics(SM_CXSCREEN) - display.w;
int yPos = GetSystemMetrics(SM_CYSCREEN) - display.h;
RECT windowRect = {0, 0, display.w, display.h}; // Define Our Window Coordinates
AdjustWindowRectEx (&windowRect, WS_POPUP, 0, winStyle );
// Create the window
if (!(hwnd = CreateWindowEx(
winStyle, // extended style
WINDOW_CLASS_NAME, // class
gl.winTitle.c_str(), // title
( gl.borderless || CmdLine.Option("-borderless") ) ? (WS_POPUPWINDOW | WS_VISIBLE)
: (gl.noFullscreen ? ((CmdLine.Option("-bordered") ? WS_BORDER : 0) | WS_VISIBLE)
: (WS_POPUP | WS_VISIBLE)), // use POPUP for full screen
gl.noFullscreen && !CmdLine.Option("-recenter") ? xPos / 2 : 0,
gl.noFullscreen && !CmdLine.Option("-recenter") ? yPos / 2 : 0, // initial game window x,y
display.w, // initial game width
display.h, // initial game height
HWND_DESKTOP, // handle to parent
NULL, // handle to menu
hinstance, // instance of this application
NULL)
) // extra creation parms
) {
OUTPUT("WinAPI ERROR: Could not open window.\n");
return(0);
}
if (gl.borderless || CmdLine.Option("-borderless") ) {
LONG lStyle = GetWindowLong(hwnd, GWL_STYLE);
lStyle &= ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZE | WS_MAXIMIZE | WS_SYSMENU);
SetWindowLong(hwnd, GWL_STYLE, lStyle);
LONG lExStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
lExStyle &= ~(WS_EX_DLGMODALFRAME | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE);
SetWindowLong(hwnd, GWL_EXSTYLE, lExStyle);
SetWindowPos(hwnd, NULL, 0, 0, display.w, display.h, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
}
// Temporary change to full screen mode
ZeroMemory(&game_screen, sizeof(game_screen)); // clear out size of DEVMODE struct
game_screen.dmSize = sizeof(game_screen);
game_screen.dmPelsWidth = display.w;
game_screen.dmPelsHeight = display.h;
game_screen.dmBitsPerPel = display.bits;
game_screen.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL;
ChangeDisplaySettings(&game_screen, CDS_FULLSCREEN);
// save the game window handle
display.hwnd = game_window = hwnd;
display.hdc = game_dc = GetDC(display.hwnd = game_window); // get the GDI device context
// set up the pixel format desc struct
pfd = {
sizeof(PIXELFORMATDESCRIPTOR), // size of this PFD
1, // version number
PFD_DRAW_TO_WINDOW | // supports window
PFD_SUPPORT_OPENGL | // supports OpenGL
PFD_DOUBLEBUFFER, // support double buff
PFD_TYPE_RGBA, // request RGBA format
(BYTE)display.bits, // select color depth
0, 0, 0, 0, 0, 0, // color bits ignored
0, // no alpha buff
0, // shift bit ignored
0, // no accum buff
0, 0, 0, 0, // accum bits ignored
16, // 16-bit Z-buff (depth buff)
0, // no stencil buff
0, // no aux buff
PFD_MAIN_PLANE, // main drawing layer
0, // reserved
0, 0, 0 // layer masks ignored
};
int pf; // pixel format
if (!gl.arbMultisampleSupported) {
if (!(pf = ChoosePixelFormat(game_dc, &pfd))) // match the pixel format
{
MessageBox(game_window, "OpenGL could not be initialized -- ChoosePixelFormat Error ; report this to program authors for help!", "OpenGL Error", MB_OK);
return FALSE; // error returned
}
} else {
pf = gl.arbMultisampleFormat;
}
if (!SetPixelFormat(game_dc, pf, &pfd)) // set the pixel format
{
MessageBox(game_window, "OpenGL could not be initialized -- SetPixelFormat Error ; report this to program authors for help!", "OpenGL Error", MB_OK);
return FALSE; // error returned
}
if (!(game_rc = wglCreateContext(game_dc))) // create the rendering context
{
MessageBox(game_window, "OpenGL could not be initialized -- CreateContext Error ; report this to program authors for help!", "OpenGL Error", MB_OK);
return FALSE; // error returned
}
if (!(upload_rc = wglCreateContext(game_dc))) // create the rendering context
{
MessageBox(game_window, "Multiple OpenGL contexts could not be initialized -- CreateContext Error ; report this to program authors for help!", "OpenGL Error", MB_OK);
return FALSE; // error returned
} else { // Share as much as you can between two contexts
if (!wglShareLists(game_rc, upload_rc)) {
// could use GetLastError here
MessageBox(game_window, "wglShareLists -- Error ; report this to program authors for help!", "OpenGL Error", MB_OK);
return FALSE; // error returned
}
}
if (!wglMakeCurrent(game_dc, display.hglrc = game_rc)) // make it current
{
MessageBox(game_window, "OpenGL could not be initialized -- MakeCurrent Error ; report this to program authors for help!", "OpenGL Error", MB_OK);
return FALSE; // error returned
}
ShowCursor(false);
ShowWindow(game_window, SW_SHOWNORMAL);
SetForegroundWindow(game_window);
在上面的代码中,我得到的是一个没有resize功能的窗口,隐藏了OS鼠标光标,只能用ALT-TAB(或ALT-F4)退出,退出时出现在后面窗口 Z 顺序。我总是使用将 display.w 设置为 1920 并将 display.h 设置为 1080 的参数打开我的窗口,无论是全屏还是窗口模式。然后调用 WM_SIZE 将其调整到客户区。
请注意,在我设置 display.Resized(w,h) 的初始时间之后,在 WinProc 期间调用了以下 WM_SIZE:
case WM_SIZE:
{
display.Resized(LOWORD(lparam), HIWORD(lparam));
return (0);
}
break;
这在应用程序加载期间只执行一次,在第一种情况下,它的值看起来像:1918,1078
更新:如果我在这里使用 GetWindowRect() 或 GetClientRect() 的结果,如下所示,窗口会神秘地移动到屏幕的 Center-X,Center-Y!是什么赋予了??
// RECT rect;
// if ( GetClientRect(hwnd,&rect) ) {
// display.Resized((int)rect.right,(int)rect.bottom);
// }
//if ( GetWindowRect( hwnd, &rect ) ) {
// display.Resized((int)ADIFF(rect.left,rect.right),(int)ADIFF(rect.top,rect.bottom));
//}
display.Resized(LOWORD(lparam), HIWORD(lparam));
return (0);
我需要采取哪些步骤来使窗口可拉伸,以便将上下文大小调整为视图,并根据屏幕比例正确调整鼠标?
基本上,有太多的边缘案例无法理解所有这些。自两年前我问这个问题以来,随着时间的推移,我在全屏和窗口出现之间出现了其他不一致。
据我了解,基本上有 3 种类型的窗口:
- 用于窗口 GUI 的正常屏幕上可移动/可调整大小的窗口,例如此浏览器窗口(如果您不在移动设备上)
- 一个与显示器的分辨率支持相匹配(包括小于其本机的分辨率)——我们称之为“全屏”(或全屏,甚至不是一个词)
- 一个普通的屏幕窗口,但缺少标题栏、边框和滚动条,并且显示与屏幕一样大。被称为“街上”的“无边界之窗”
我想掌握所有这些,但以一种使它们都可以访问并且不需要特殊情况的方式。我基本上已经放弃了使用 WinAPI 这样做,但显然有多家公司这样做。遵循 Microsoft 的文档并不是很有帮助,而且我已经尝试了很多不同的 CreateWindow CreateWindowEx——顺便说一句,这些功能中的许多功能已被弃用,或者根本不起作用。
(也许最好的问题是,微软什么时候会清理这个垃圾?但我想我们都知道答案。).. 任何帮助让它工作将不胜感激。
我现在工作的领域:C++、Windows API、OpenGL 3.x / 4.x、Windows 10。