This page was generated from a source code file. Click here to download the file this page was generated from.
Include The system windows.h header for constants and functions used below. This pulls in the system OpenGL headers automatically.
Also include mini.h, a header with some program-specific constants we'll also be using.
1 | #include |
2 | #include "mini.h" |
Define a window style which we will use later in our calls to AdjustWindowRect() and CreateWindow().
3 | #define WIN_STYLE (WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX & ~WS_THICKFRAME) |
Define an arbitrary timer ID to use later in our call to SetTimer(). Please see the note before the WM_TIMER handler code in WndProc(), below, for more information.
4 | #define WIN_TIMER_ID 1000 |
Setup the window message processing callback. This will be called for each Win32 message received by the application window and defines how the window will respond.
5 | LRESULT CALLBACK WndProc( |
6 | HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam |
7 | ) { |
Define the hGLRC statically so that we can create it in one call and dispose of it later on in a different call when it is no longer needed.
8 | static HGLRC hrc = 0; |
9 | int pixel_fmt_int = 0; |
10 | HDC hdc = 0; |
Provide some parameters for Windows to pass to the driver and query what available pixel formats match. In a more robust program, many of these parameters would be defined from a configuration file or registry settings, but for now, we're going to define them statically.
11 | PIXELFORMATDESCRIPTOR pixel_fmt = { |
A struct size field and version, as is common in Win32-related structs.
12 | sizeof( PIXELFORMATDESCRIPTOR ), |
13 | 1, |
This buffer will be drawn to a window (hWnd), support OpenGL, and will use double-buffering with SwapBuffers().
14 | PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, |
This buffer will use non-indexed colors specified by RGB values.
15 | PFD_TYPE_RGBA, |
This will be a 16-bit color buffer.
16 | 16, |
These values will not be used for now, and can be ignored.
17 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
This is the size of the "cells" in the depth buffer. See GL_DEPTH_TEST in opengl01.c for more information.
18 | 8, |
These values will not be used for now, and can be ignored.
19 | 0, 0, |
Specify the main plane.
20 | PFD_MAIN_PLANE, |
These values will not be used for now, and can be ignored.
21 | 0, 0, 0, 0 |
22 | }; |
Now that our variables are declared, let's handle the window message we were called with this time!
23 | switch( message ) { |
The WM_CREATE message is received by the window when it is created, so we'll act on it to setup an OpenGL context to render into for the rest of the program.
24 | case WM_CREATE: |
First, get the device context (hDC) associated with this window (hWnd).
25 | hdc = GetDC( hWnd ); |
Get the index of the pixel format which matches the descriptor provided.
26 | pixel_fmt_int = ChoosePixelFormat( hdc, &pixel_fmt ); |
27 | if( 0 == pixel_fmt_int ) { |
28 | MessageBox( 0, "Unable to choose pixel format!", "OpenGL Error", 0 ); |
29 | break; |
30 | } |
Set this window to use the pixel format found by ChoosePixelFormat().
31 | if( !SetPixelFormat( hdc, pixel_fmt_int, &pixel_fmt ) ) { |
32 | MessageBox( 0, "Unable to set pixel format!", "OpenGL Error", 0 ); |
33 | break; |
34 | } |
Create an OpenGL context (hGLRC) associated with this window's device context and make it the current OpenGL context for this window.
35 | hrc = wglCreateContext( hdc ); |
36 | if( 0 == hrc ) { |
37 | MessageBox( 0, "Invalid hGLRC!", "OpenGL Error", 0 ); |
38 | } |
39 | if( FALSE == wglMakeCurrent( hdc, hrc ) ) { |
40 | MessageBox( 0, "Unable to set HGLRC!", "OpenGL Error", 0 ); |
41 | break; |
42 | } |
43 | break; |
The WM_TIMER message is received when the timer we create later with SetTimer() reaches its interval. Some programs might have multiple timers, in which case they would need to check the wParam parameter to see which timer is sending the message and act accordingly. We only create one timer in this program, though.
44 | case WM_TIMER: |
45 | if( mini_opengl_frame() ) { |
46 | MessageBox( 0, "Error", "OpenGL frame error.", 0 ); |
47 | PostQuitMessage( 1 ); |
48 | } else { |
As no error was returned by mini_opengl_frame(), swap the double buffers so that the one we've been rasterizing to is visible.
49 | hdc = GetDC( hWnd ); |
50 | SwapBuffers( hdc ); |
51 | } |
52 | break; |
The WM_DESTROY message is received when the window is closing (either because the user clicked the close button or the application was terminated in some other way).
53 | case WM_DESTROY: |
Reset this window back to the NULL OpenGL context and destroy the context we created earlier.
54 | hdc = GetDC( hWnd ); |
55 | wglMakeCurrent( hdc, NULL ); |
56 | wglDeleteContext( hrc ); |
Make sure we terminate the program, since the main window is closing.
57 | PostQuitMessage( 0 ); |
58 | break; |
DefWindowProc() will handle all messages not specifically enumerated here.
59 | default: |
60 | return DefWindowProc( hWnd, message, wParam, lParam ); |
61 | } |
62 | } |
This is the application entry point. In traditional C, this would be defined as main(), and most modern Windows compilers can also accept main() as an entry point, but this gives us our hInstance and nCmdShow simply, so we will use it for our demonstration.
63 | int WINAPI WinMain( |
64 | HINSTANCE hInstance, HINSTANCE hPrevInstance, |
65 | LPSTR lpCmdLine, int nCmdShow |
66 | ) { |
67 | int retval = 0; |
68 | int msg_retval = 0; |
69 | WNDCLASS wc = { 0 }; |
70 | RECT wr = { 0, 0, 0, 0 }; |
71 | MSG msg; |
72 | HWND hWnd = 0; |
Get the *real* dimensions of the window, including titlebar.
Reminder that the MINI_SCREEN_W and MINI_SCREEN_H constants are defined in mini.h!
73 | wr.right = MINI_SCREEN_W; |
74 | wr.bottom = MINI_SCREEN_H; |
75 | AdjustWindowRect( &wr, WIN_STYLE, FALSE ); |
Create and register the window class, using WndProc(), which we defined above, as the message processor. Use a generic arrow cursor defined by IDC_ARROW and (by default) gray background defined by COLOR_BTNFACE. The class name is arbitrary, but we'll use it when we create the actual window to associate it with this class.
76 | memset( &wc, '\0', sizeof( WNDCLASS ) ); |
77 | wc.lpfnWndProc = (WNDPROC)&WndProc; |
78 | wc.hInstance = hInstance; |
79 | wc.hCursor = LoadCursor( 0, IDC_ARROW ); |
80 | wc.hbrBackground = (HBRUSH)( COLOR_BTNFACE + 1 ); |
81 | wc.lpszClassName = "MiniTestGLClass"; |
82 | if( !RegisterClass( &wc ) ) { |
83 | MessageBox( 0, "Error", "Could not register window class!", 0 ); |
84 | retval = 1; |
85 | goto cleanup; |
86 | } |
Create the actual window.
87 | hWnd = CreateWindow( |
Notice that we use the window class we defined above.
88 | "MiniTestGLClass", |
Set the window title visible to the user to a generic string.
89 | "Mini OpenGL Test", |
Use the window style we defined at the beginning of the source file.
90 | WIN_STYLE, |
Use the default window position on the center of the active screen, or thereabouts.
91 | CW_USEDEFAULT, CW_USEDEFAULT, |
Use the window size adjusted by AdjustWindowRect() above to give the pixel dimensions we requested while accounting for the title bar and decorations.
92 | wr.right - wr.left, wr.bottom - wr.top, |
No menus or parent window, and use the hInstance provided to the initial call to WinMain() by the OS.
93 | 0, 0, hInstance, 0 |
94 | ); |
95 | if( 0 == hWnd ) { |
96 | MessageBox( 0, "Error", "Could not create window!", 0 ); |
97 | } |
Show the window we just created on-screen.
98 | ShowWindow( hWnd, nCmdShow ); |
Create a timer to refresh the screen. This will continually send the WM_TIMER message we're setup to handle in WndProc().
99 | if( !SetTimer( hWnd, WIN_TIMER_ID, (int)(1000 / MINI_FPS), NULL ) ) { |
100 | MessageBox( 0, "Error", "Could not create graphics timer!", 0 ); |
101 | retval = 1; |
102 | goto cleanup; |
103 | } |
Call our generic OpenGL setup function defined in openglxx.c.
104 | if( mini_opengl_setup() ) { |
105 | MessageBox( 0, "Error", "OpenGL setup error.", 0 ); |
106 | PostQuitMessage( 1 ); |
107 | } |
Run the message-processing loop which will continually pass messages to WndProc
This will effectively call opengl_frame() repeatedly via the WM_TIMER message generated by the timer we created earlier.
108 | do { |
109 | msg_retval = GetMessage( &msg, 0, 0, 0 ); |
110 | TranslateMessage( &msg ); |
111 | DispatchMessage( &msg ); |
112 | } while( 0 < msg_retval ); |
Handle generic cleanup. There isn't much to cleanup in this minimal example program, but putting things like calls to free() after the cleanup label will allow them to still be called in an error state by using goto in the error handler, as opposed to if the handler just returned directly.
Modern operating systems generally handle cleanup pretty well on their own, but older versions of Windows tended to leak resources (particularly GDI objects) for the whole operating system if those resources were not cleaned up in every program that used them.
113 | cleanup: |
114 | return retval; |
115 | } |