Introduction
Welcome back! In our last lesson, we established the high-level architecture of OpenGL. We learned that it's a specification implemented by graphics drivers, and that our application acts as a "client" sending commands to the GPU "server". The server's state is encapsulated in an object called the OpenGL context, which is essentially a large state machine.
Today, we take our first practical step in building our game engine. Before we can send any rendering commands, we need a window to draw in and, crucially, an OpenGL context to send commands to. This lesson directly addresses that need. Our learning outcome is to use the GLFW library to create and manage a window and its context.
We will cover the entire lifecycle of a window in a C application, from initialization to termination, using a small, powerful library called GLFW.
Why Use a Library for Windowing?
Creating and managing a window is an operating system-specific task. The code to create a window on Windows (using the Win32 API) is completely different from the code for macOS (Cocoa) or Linux (X11/Wayland). Writing and maintaining code for each platform would be a significant distraction from our main goal of learning 3D graphics.
This is where GLFW comes in. GLFW is a lightweight, open-source, cross-platform library written in C. Its primary purpose is to provide a simple API for creating windows, managing OpenGL contexts, and handling user input. By using GLFW, we can write a single piece of code that will work on Windows, macOS, and Linux, allowing us to focus on what matters: the graphics.
The GLFW Workflow: From Init to Terminate
Getting a window to appear on screen involves a clear sequence of steps. We will walk through each one, culminating in a complete C program that you can run.
We will primarily follow the process shown in the video below. While the video uses C++, the functions and the overall structure are identical to what we will use in C.
This video from Victor Gordan provides a concise and clear demonstration of the entire process of creating a window with GLFW. Watch it to get a complete overview of the functions we'll be discussing.
Watch the video from the beginning to the end (00:00 - 04:24). Pay attention to the sequence of function calls: initializing GLFW, setting hints, creating the window, making the context current, and running the main loop.
Now, let's break down the steps from the video with more detail.
1. Initialization and Cleanup
Before any GLFW functions can be used, the library itself must be initialized. This is done with a single call.
glfwInit(): This function initializes the GLFW library. It should be the very first GLFW function you call. It returnsGL_TRUE(which is just1in C) on success andGL_FALSE(0) on failure. You should always check the return value.
Equally important is cleaning up when your application is finished.
glfwTerminate(): This function frees any allocated resources, destroys any remaining windows, and restores the system state. It should be the last GLFW function you call before your program exits.
A robust program structure always pairs initialization with termination:
#include <stdio.h>
#include <GLFW/glfw3.h>
int main(void) {
if (!glfwInit()) {
printf("Failed to initialize GLFW\n");
return -1;
}
// ... create window and run main loop ...
glfwTerminate();
return 0;
}
2. Configuring the Window with Hints
Before creating the window, we can give GLFW "hints" about the properties we want the window and its OpenGL context to have. This is how we request a specific version of OpenGL.
glfwWindowHint(int hint, int value): This function sets various options. The two most important hints for us are:GLFW_CONTEXT_VERSION_MAJORandGLFW_CONTEXT_VERSION_MINOR: We'll set these to3and3respectively to request OpenGL 3.3.GLFW_OPENGL_PROFILE: We'll set this toGLFW_OPENGL_CORE_PROFILEto get access to modern OpenGL functions without the clutter of old, deprecated features.
// Place these calls after glfwInit() but before glfwCreateWindow()
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
3. Creating the Window and Context
With the hints set, we can now ask GLFW to create the window. This single function also creates the associated OpenGL context for us.
glfwCreateWindow(int width, int height, const char* title, GLFWmonitor* monitor, GLFWwindow* share):width,height,title: The dimensions and title of the window.monitor: Used for creating a fullscreen window (we'll useNULLfor windowed mode).share: Used to share resources between windows (we'll useNULL).- The function returns a pointer to a
GLFWwindowobject, orNULLif creation failed. This pointer is our handle to the window.
You must always check if the window creation was successful.
GLFWwindow* window = glfwCreateWindow(800, 600, "3D Game Engine", NULL, NULL);
if (window == NULL) {
printf("Failed to create GLFW window\n");
glfwTerminate();
return -1;
}
4. Making the Context Current
We now have a window and an OpenGL context. As we discussed in the last lesson, the context is the state machine on the GPU. But our program needs to tell the operating system which context our OpenGL commands should be sent to.
glfwMakeContextCurrent(GLFWwindow* window): This function tells GLFW to make the context of the specified window the current one on the calling thread.
This is a critical step. No OpenGL calls will work until you have a current context.

After creating the window, we immediately make its context current:
glfwMakeContextCurrent(window);
5. The Render Loop
If our main function were to end here, the program would exit and the window would disappear instantly. We need a loop that keeps the program running, processes user input, and draws to the screen, until the user decides to close the window. This is commonly called the render loop or game loop.
while (!glfwWindowShouldClose(window)) {
// --- Game logic and rendering will go here ---
// Swap buffers to display the new frame
glfwSwapBuffers(window);
// Process events like keyboard input or window close requests
glfwPollEvents();
}
Let's break down the three key functions here:
glfwWindowShouldClose(window): This function returnstrueif the user has attempted to close the window (e.g., by clicking the 'X' button). Our loop continues as long as this isfalse.glfwPollEvents(): This function processes any pending events (like mouse movement, key presses, or the window close event) and calls any registered callback functions. Without this, the window would become unresponsive.glfwSwapBuffers(window): This function is essential for smooth rendering. To avoid flickering, applications use a technique called double buffering. All rendering commands draw to a hidden back buffer. When drawing is complete for a frame,glfwSwapBuffersswaps the back buffer with the visible front buffer, displaying the newly rendered image all at once.
To learn more about these core loop components, you can refer to the official documentation.
GLFW and LearnOpenGL Documentation
The official GLFW 'Getting started' guide and the 'Hello Window' chapter from LearnOpenGL provide excellent written explanations for the render loop and its components.
From the GLFW 'Getting started' guide, read the sections 'Checking the window close flag', 'Swapping buffers', and 'Processing events'. This will give you the official definitions for the functions that make up our main loop.
A Complete C Example
Let's put all the pieces together into a single, minimal C program. This program will initialize GLFW, create an 800x600 window, and run a loop until the window is closed. For now, the window will just be black, but it's a monumental first step!
// main.c
#include <stdio.h>
#include <GLFW/glfw3.h>
int main(void) {
// 1. Initialize GLFW
if (!glfwInit()) {
printf("Failed to initialize GLFW\n");
return -1;
}
// 2. Set window hints for OpenGL 3.3 Core Profile
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// 3. Create the window
GLFWwindow* window = glfwCreateWindow(800, 600, "My First Window", NULL, NULL);
if (window == NULL) {
printf("Failed to create GLFW window\n");
glfwTerminate();
return -1;
}
// 4. Make the window's context current
glfwMakeContextCurrent(window);
// 5. The Render Loop
while (!glfwWindowShouldClose(window)) {
// Our rendering code will go here in the future
// Swap front and back buffers
glfwSwapBuffers(window);
// Poll for and process events
glfwPollEvents();
}
// 6. Clean up and terminate
glfwDestroyWindow(window); // Optional here, as glfwTerminate does this
glfwTerminate();
return 0;
}
A Note on Compiling: To compile this code, your compiler needs to know where to find the GLFW header files (-I flag) and which library file to link against (-l flag). This process is covered in the course outcome on build systems. For now, the focus is on understanding the C code itself.
Conclusion
Congratulations! You now understand the complete process of creating and managing a window and an OpenGL context using GLFW. This is the foundational piece of code for any graphics application we will build.
Key Takeaways:
- GLFW is a C library that abstracts away OS-specific details for windowing, context creation, and input.
- The lifecycle of a GLFW application follows a strict order:
glfwInit, set hints withglfwWindowHint,glfwCreateWindow,glfwMakeContextCurrent. - The
glfwMakeContextCurrentfunction is the bridge that connects our application thread to the OpenGL state machine (the context). - A
whileloop usingglfwWindowShouldClosekeeps the application alive. - Inside the loop,
glfwPollEventskeeps the window responsive, andglfwSwapBuffersdisplays the rendered frame using double buffering. - Finally,
glfwTerminatemust be called to release all resources.
Preview of the Next Lesson:
We have a window and a context, but we still can't call any modern OpenGL functions like glGenBuffers. Why? Because the locations of these functions in the graphics driver are not known at compile time. In the next lesson, we will use the GLAD library to load pointers to OpenGL functions at runtime, finally unlocking the full power of the graphics API.