Here’s another fairly trivial code snippet. I’ve stumbled across some
borked attempts at initializing and maintaining rendering windows for
games lately. Most failed to properly respond to window messages, either
ignoring WM_CLOSE
outright or letting DefWindowProc()
call DestroyWindow()
when WM_CLOSE
was received, thereby not giving the rest of
the game’s code any time to cleanly shut down before the window handle
becomes invalid.
So I’ll provide a clean and well-behaved window class here. It doesn’t
use any global variables – in fact, you could create any number of windows
from any number of threads. WM_CLOSE
simply causes the class’
WasCloseRequested()
method to return true, so by polling this
method you can first shut down graphics and input devices and then destroy
the window in an orderly fashion.
For your convenience I also added some helper methods: one resizes the window in a way that ensures the client area will actually end up with the exact pixel size requested. Another will center the window on the screen without messing up if the user has extended his desktop over multiple monitors.
Usage example:
/// <summary>Main entry point for the application</summary>
/// <param name="instanceHandle">Instance handle of the new process</param>
/// <param name="previousInstanceHandle">Instance handle of the previous process</param>
/// <param name="commandLine">arguments provided to the application on the command line</param>
/// <param name="showWindow">Desired initial state of the application's window</param>
/// <returns>Zero on success</returns>
int WINAPI WinMain(
HINSTANCE instanceHandle, HINSTANCE previousInstanceHandle,
LPSTR commandLine, int showWindow
) {
// This creates a new window, using the default window size and position.
// It starts invisible, so you can tweak the window before showing it.
Window mainWindow(instanceHandle, L"My Game");
// You can resize the window and it will end up with its drawable region
// having the exact size you requested
mainWindow.ResizeViewRectangle(800, 600);
// Centering the window can be done with a single call and it will for once
// not mess up if your game is run on a workstation with multiple monitors.
mainWindow.CenterOnPrimaryDisplay();
// After you're done tweaking the window, you show it
mainWindow.Show();
// Run the message loop. When the user tries to close the window by clicking
// on 'X' or pressing Alt+F4, your game is informed via WasCloseRequested()
// and can perform an orderly shutdown.
while(!mainWindow.WasCloseRequested()) {
MSG message;
while(::PeekMessage(&message, NULL, 0, 0, PM_REMOVE)) {
if(message.message == WM_QUIT) {
return message.wParam;
}
::TranslateMessage(&message);
::DispatchMessage(&message);
}
}
// As the window goes out of scope, it gets destroyed and everything will
// be cleaned up.
return 0;
}