Erik Yuzwa

Creating a Windows Game Loop With C and the Win32 API (part 2)

July 28, 2021 | 4 Minute Read

Back in Part 1 of this tutorial, we took a look at putting together a game loop for the Windows desktop using C and the Win32 API.

We put the basics together, and so in this episode we’ll look at adding an ability to create your application in either a windowed frame, or as a fullscreen program.

While we can “force” which presentation mode we want to unveil our creation to the player, as a player I must admit I enjoy it when I get the choice; there’s times when I’m playing a game that I want it in a smaller window vs. taking over fullscreen, and vice-versa.

Updating initialization

Lets start by taking a look at how we’re initializing our main window handle (aka. the CreateWindowEx function call):

// attempt to register the win32 application class
    if (!RegisterClassEx(&winclass)) {
      return -1;
    }

    // create the actual visual window handle - 640x480
    if (!(hWnd = CreateWindowEx(NULL,
       className,
       appTitle,
       WS_OVERLAPPEDWINDOW | WS_VISIBLE,
       CW_USEDEFAULT,
       CW_USEDEFAULT,
       640,
       480,
       NULL,
       NULL,
       hInstance,
       NULL)))
    return -2;

What we need to do is to keep track of our desired window size, and if we want to work in a windowed or fullscreen mode.

At the top of the same file, let’s add some variables:

int width = 640;
int height = 480;
bool windowed = true;

Feel free to make the initial width and height to whatever size you want: 800x600 or 1024x768 for example.

Usually I find it a better experience (as a player) if the game starts in a windowed mode then I have the option available to go fullscreen; but that’s just me. Again, feel free to set the initial windowed boolean to your own preference.

Now in the same file just before we make the call to CreateWindowEx, we’ll add a bit of logic to help setup our window with our given size and windowed mode.

Just after the RegisterClassEx call, let’s add some new code, and update some parameters to CreateWindowEx:

DWORD style = WS_OVERLAPPEDWINDOW;
    RECT gameWindow = {0, 0, width, height};

    if (!windowed) {
      style = WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
    }

    AdjustWindowRect(&rectWindow, style, FALSE);

    // create the actual visual window handle
    if (!(hWnd = CreateWindowEx(NULL,
       className,
       appTitle,
       style | WS_VISIBLE,
       CW_USEDEFAULT,
       CW_USEDEFAULT,
       gameWindow.right - gameWindow.left,
       gameWindow.bottom - gameWindow.top,
       NULL,
       NULL,
       hInstance,
       NULL)))
    return -2;

Toggle Windowed Mode During Runtime

With our window initialization updated, we can turn to focus on being able to support the ability to switch our application between windowed and fullscreen modes during runtime.

As mentioned in the Part 1 tutorial, Windows programs operate by responding to events received by the WindowProc; such as a keyboard keypress for our purposes.

The event we’ll need to listen for is called WM_KEYDOWN, which is triggered whenever a keyboard key is pressed.

switch(uMsg) {
        case WM_KEYDOWN:
            switch(wParam) {
                case VK_F1:
                  // toggle our windowed mode
                break;
                default:
                break;
            }
            break;
        case WM_PAINT:
            hdc = BeginPaint(hWnd, &ps);
            EndPaint(hWnd, &ps);
            return 0;
        break;
        case WM_DESTROY:
            PostQuitMessage(0);
        break;
        default:
        break;
    }