Microsoft Windows の奇妙な世界を理解するのに苦労しているときに、何年にもわたって多くのスタック オーバーフローを読んできましたCreateWindowEx()
.. など。 "
WinAPI で次のようなウィンドウを作成するのに苦労しています。
- OpenGL コンテキストを持っている
- 「ウィンドウ」モードまたは「フルスクリーン」モードの場合、マルチモニターとシングルモニターの両方のディスプレイで、メインモニター (またはコマンドライン信号によって決定される任意のモニター) の中央に正しく配置されます。
- クライアントの内部画面サイズが固定されている (viewport 2d)
- 間違ったタイミングで、またはマルチモニターの特殊なケースでフォーカスを失う原因となる外側をクリックすることはできません
- 流動的にサイズ変更できますが、内部の「クライアント サイズ」は変更しません (つまり、固定サイズの OpenGL コンテンツを新しい画面サイズに引き伸ばすことを意味します) ... ここでのアイデアは、仮想化のレイヤーを追加することです。すべてのピクセルは、同じ 1920x1080 (1080p) 座標系で表されます。この部分は私にとっては問題ありません。
- screen->client 比率を介して、screen_size -> client_size と同等のマウス イベント変換を正しく処理します。
自作のアプリ フレームワークでは、表示サイズを設定する必要があります。それでも、Windows は適切なサイズのウィンドウを提供しません。(タイトル バーが差し引かれたり、スクロールバーが差し引かれたりすることがありますが、たとえば、コンテキストはタイトル バーの下に描画されます。)
また、最近 2010 EE (Win32 / Windows 7) から 2015 (win32 / Windows 10) に移行する際、メイン ディスプレイの中心からずれていたため、パラメータを変更してビューを中心に戻す必要がありました。現在、これらの値が正しいか正しくない場合があります。たとえば、「フルスクリーン」にすると、同じ値が画面の上部に描画され、画面の下部に「gl clear color」(私の場合はオレンジ) を示す領域ができます。
次のコマンド ライン パラメーターを指定することで、これらの操作を行うことができます。
- -bordered (デフォルトであり、実際には何の効果もありません。タイトル バーなどを備えたデフォルトのウィンドウ モードです)
- -borderless (win 0,0 が実際には画面中央にあるアプリが中心から外れて、フルスクリーン モードになっているように見えます)
- -windowed (または -window)
-window を指定しない場合、デフォルトで「フルスクリーン」の解像度が調整されます (ただし、サポートされている場合のみ、サポートされていると想定します。サポートされていない場合は、エラーが発生する可能性があります)。
とにかく、これはすべて非常に悪いです.a)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);
上記のコードで得られるのは、サイズ変更機能を持たず、OS マウス カーソルを非表示にし、ALT-TAB (または ALT-F4) でのみ終了でき、終了するとウィンドウの後ろに表示されるウィンドウです。 Windows Z オーダー。全画面表示またはウィンドウ モードで、display.w を 1920 に、display.h を 1080 に設定するパラメータを使用して、常にウィンドウを開きます。次に WM_SIZE が呼び出され、クライアント領域に合わせて調整されます。
次の WM_SIZE は、最初に display.Resized(w,h) を設定した直後の WinProc 中に呼び出されることに注意してください。
case WM_SIZE:
{
display.Resized(LOWORD(lparam), HIWORD(lparam));
return (0);
}
break;
これは、アプリのロード中に 1 回だけ実行されます。最初のケースでは、値は次のようになります: 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);
コンテキストがビューに合わせてサイズ変更され、画面の比率に基づいてマウスが適切に調整されるようにウィンドウを伸縮可能にするには、どのような手順を実行する必要がありますか?
基本的に、これらすべてを理解するにはエッジ ケースが多すぎます。この質問をした 2 年前から時間が経つにつれ、全画面表示とウィンドウ表示の間に別の矛盾が生じてきました。
私が理解していることから、基本的に3種類のウィンドウがあります。
- このブラウザ ウィンドウのような、GUI をウィンドウ操作するための通常の画面上の移動可能/サイズ変更可能なウィンドウ (モバイルを使用していない場合)
- ディスプレイの解像度サポート (ネイティブよりも小さい解像度を含む) に一致するもの - これを「フルスクリーン」(またはフルスクリーン、言葉ではありません) と呼びます。
- 通常の画面上のウィンドウですが、タイトル バー、境界線、およびスクロール バーがなく、画面と同じ大きさで表示されます。「オン・ザ・ストリート」を「ボーダレス・ウィンドウ」と呼ぶ
これらすべてを習得したいのですが、それらすべてにアクセスできるようにし、特別なケースを必要としない方法で。基本的にWinAPIでやるのは諦めていたのですが、明らかに複数の会社がやっています。Microsoft のドキュメントに従うことはあまり役に立ちません。私はさまざまな CreateWindow CreateWindowEx を試しましたが、これらの機能の多くは推奨されていないか、まったく機能しません。
(おそらく、Microsoft はいつこのがらくたを片付けるのだろうか?というのが一番の質問かもしれませんが、私たちは皆、答えを知っていると思います。)
私は現在、C++、Windows API、OpenGL 3.x / 4.x、Windows 10 で作業しています。