Event Loop with Win32 on Windows

References

This time we'll use our own Window Procedure and handle events.

Build with:

gcc main.c
#define WINVER _WIN32_WINNT_WIN10 #define _WIN32_WINNT WINVER

You can use these macros to define which minimum version of Windows your program is targetting. Here I've selected Windows 10, which enables the gamepad virtual key codes. Feel free to remove these definitions and the keycodes which become unavailable if you're using an older Windows version.

.lpfnWndProc = WindowProc,

In the window class structure, we assign our own window procedure to handle messages. Since you retrieve messages with Peek/GetMessage, you might think you could just call your window procedure directly, without setting this value and using DispatchMessage. However, in some cases, Windows calls your window procedure directly.

while (!quit) { MSG message; while (PeekMessage (&message, NULL, 0, 0, PM_REMOVE)) { if (message.message == WM_QUIT) { puts ("Quitting"); quit = true; } else DispatchMessage (&message); } }

This loop runs continuously until the WM_QUIT message comes in. WM_QUIT is a special message which cannot be send to your window procedure, and should be handled separately. All other messages get forwarded to the window procedure via DispatchMessage.

static LRESULT CALLBACK WindowProc(HWND window_handle, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) {

The window procedure receives the handle to the associated window (in a single-window application, this is always your window), the message, and two parameters which contain extra data. We switch on the message value to handle each one.

case WM_DESTROY: PostQuitMessage (0); break;

WM_DESTROY is called when your window is destroyed. I like to call PostQuitMessage to add a WM_QUIT message to the queue - that way, I can handle "quitting" in a single place.

case WM_PAINT: ValidateRect (window_handle, NULL); break;

This is where you'd put rendering code if you were to use Win32 rendering APIs such as GDI. I'll skip this and go straight to OpenGL, but feel free to look into it. I just call ValidateRect to tell Windows that the window has been redrawn.

case WM_SYSKEYDOWN: case WM_KEYDOWN: case WM_SYSKEYUP: case WM_KEYUP: { bool key_down = ((lParam & (1 << 31)) == 0); if (key_down && (lParam & (1 << 30))) { puts ("Key repeat"); break; } printf ("Key %s %s\n", key_down ? "pressed" : "released", vk_print[wParam & 0xff]); }

WM_KEYDOWN/UP are the normal key messages, but if ALT is being held, you'll receive the SYS versions instead. I like to just internally track the state of keys in my programs so I lump them together. Since I've combined up and down events, I retrieve the up/down state of the key from lParam, and check for key repeats (when holding a key causes more events to be sent). I've prepared the vk_print[] array of strings so we can print out the keys being pressed.

default: return DefWindowProc (window_handle, message, wParam, lParam); } return 0;

For any message we don't handle, we pass it on to DefWindowProc. Otherwise, we return 0 to tell Windows we've handled the message.