Каков наилучший способ создания изменяемого размера окна OpenGL в WinAPI?
За последние годы я много читал о переполнении стека, когда пытался разобраться в странном мире Microsoft Windows - CreateWindowEx() и т. Д.
Я изо всех сил пытался заставить WinAPI создать окно, которое:
- Имеет контекст OpenGL
- Правильно отцентрировано как на мониторах с несколькими мониторами, так и на мониторах с одним монитором в режиме "Оконный" или "Полноэкранный"
- Имеет фиксированный внутренний размер экрана клиента
- Можно плавно изменять размер, но не меняет внутренний "размер клиента" (это означает, что он растягивает содержимое OpenGL, которое имеет фиксированный размер, до нового размера экрана, без обновления внутренних элементов на основе произвольного размера разрешения, установленного пользователем) так как мой фреймворк не обрабатывает события Resized()
- Корректно обрабатывает трансляцию событий мыши из эквивалента screen_size -> client_size через соотношение screen->client
В моей доморощенной платформе приложений я должен установить размер экрана, и даже тогда Windows не дает мне правильного размера окна. Кроме того, недавно, при переходе с 2010 EE (Win32 / Windows 7) на 2015 (win32 / Windows 10), мне пришлось изменить параметры, чтобы перенастроить представление, потому что оно было не по центру на главном дисплее.
Я могу поиграть с этими вещами, предоставив следующие параметры командной строки:
- -ограниченный (по умолчанию, и не имеет никакого эффекта, является оконным режимом по умолчанию)
- -безграничен (кажется, переходит в полноэкранный режим, с приложением вне центра, где win 0,0 фактически находится в центре экрана)
- -оконный (или -оконный)
Если я не предоставлю -window, по умолчанию будет настроено разрешение "на весь экран" (но я полагаю, что только если поддерживается, иначе это может привести к ошибке) .
Во всяком случае, все это очень плохо, потому что а) я должен написать баджиллионные случаи для каждого разрешения, в котором я работаю, а не писать все для 1080p и настроить его на размер экрана, что мне и нужно, б) я не могу изменяйте размер окна плавно 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);
if (CmdLine.Option("-trapdoor")) {
gl.escTrapdoor = true;
OUTPUT("The emergency exit is unlocked.\n");
}
// 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);
В приведенном выше коде я получаю окно, которое не имеет функции изменения размера, скрывает курсор мыши операционной системы и может быть закрыто только с помощью ALT-TAB (или ALT-F4), а при выходе из него появляется в задней части окна. окна Z-порядка. Я всегда открываю свое окно, используя параметр, который устанавливает display.w в 1920 и display.h в 1080, либо в полноэкранном режиме, либо в оконном режиме. Затем вызывается WM_SIZE, чтобы настроить его для клиентской области.
Обратите внимание, что следующий WM_SIZE вызывается во время WinProc сразу после начального времени, которое я установил display.Resized(w,h):
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);
Какие шаги мне нужно предпринять, чтобы сделать окно растягиваемым так, чтобы контекст изменялся к виду, а мышь правильно настраивалась в зависимости от соотношения сторон экрана?