Skip to content

Commit 097f654

Browse files
Update chap04/chap04-windows.md
1 parent 8efeea4 commit 097f654

File tree

1 file changed

+134
-45
lines changed

1 file changed

+134
-45
lines changed

chap04/chap04-windows.md

Lines changed: 134 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
## Creating a Window on Microsoft Windows
22

3-
In order to create a window, we're going to be using platform specific code. Please note that this tutorial is for Windows **only**. None of this will apply to Linux, Android, GLFW, etc.
3+
In order to create a window, we're going to be using platform specific code. Please note that this tutorial is for Windows **only**. None of this will apply to Linux, Android, GLFW, etc. I'll make the warning ahead of time: this chapter makes use of a lot of functions. Their definitions are not super relevant to you as a Vulkan developer because this will be written once.
44

55
### Including Headers
66

@@ -14,75 +14,102 @@ If you're targeting Linux as well, you should surround both by the `#if defined(
1414

1515
### Setting Up a Console Window
1616

17-
Because we're now switching from a **Windows Console Application** to a **Windows Application**, we'll need to make sure we have a console to view the output of `stdout` and `stderr`. In addition, because we're exiting right after we encounter an error, we should:
17+
Because we're now switching from a **Windows Console Application** to a **Windows Application**, we'll need to make sure we have a console to view the output of `stdout` and `stderr`. Also, because we're exiting right after we encounter an error, we should:
1818

1919
- Show a message box
2020
- Wait for user input (keypress)
2121
- Close after the user has acknowledged the error
2222

23-
We'll be using four methods to do this work:
23+
We'll be using four methods to do this work.
24+
25+
**Definition for `AllocConsole`**:
2426

2527
```cpp
2628
BOOL WINAPI AllocConsole(void);
2729
```
2830
29-
- [Documentation](https://goo.gl/8k36tq)
31+
**[Documentation](https://goo.gl/8k36tq) for `AllocConsole`**:
32+
3033
- This function takes no arguments
3134
35+
**Usage for `AllocConsole`**:
36+
37+
```cpp
38+
AllocConsole();
39+
```
40+
41+
**Definition for `AttachConsole`**:
42+
3243
```cpp
3344
BOOL WINAPI AttachConsole(
3445
_In_ DWORD dwProcessId
3546
);
3647
```
3748

38-
- [Documentation](https://goo.gl/EeSrhh)
49+
**[Documentation](https://goo.gl/EeSrhh) for `AttachConsole`**:
50+
3951
- `dwProcessId` is the identifier of the process whose console is to be used.
4052

53+
**Usage for `AttachConsole`**:
54+
55+
```cpp
56+
AttachConsole(GetCurrentProcessId());
57+
```
58+
59+
**Definition for `freopen`**:
60+
4161
```cpp
4262
FILE * freopen (
4363
const char * filename,
4464
const char * mode,
4565
FILE * stream );
4666
```
4767

48-
- [Documentation](http://www.cplusplus.com/reference/cstdio/freopen/)
68+
**[Documentation](http://www.cplusplus.com/reference/cstdio/freopen/) for `freopen`**:
69+
4970
- `fileName` is a C string containing the name of the file to be opened.
5071
- `mode` is a C string containing a file access mode. It can be:
5172
- `"r"`
5273
- `"w"`
5374
- `"a"`
54-
- `"r+"`
55-
- `"w+"`
56-
- `"a+"`
75+
- etc.
5776
- `stream` is a pointer to a `FILE` object that identifies the stream to be reopened.
5877

78+
**Usage for `freopen`**:
79+
80+
```cpp
81+
freopen("CON", "w", stdout);
82+
freopen("CON", "w", stderr);
83+
```
84+
85+
**Definition for `SetConsoleTitle`**:
86+
5987
```cpp
6088
BOOL WINAPI SetConsoleTitle(
6189
_In_ LPCTSTR lpConsoleTitle
6290
);
6391
```
6492

65-
- [Documentation](https://goo.gl/HAIfMd)
93+
**[Documentation](https://goo.gl/HAIfMd) for `SetConsoleTitle`**:
94+
6695
- `lpConsoleTitle` is the string to be displayed in the title bar of the console window. The total size must be less than 64K.
6796

97+
**Usage for `SetConsoleTitle`**:
98+
99+
```cpp
100+
SetConsoleTitle(TEXT(applicationName));
101+
```
102+
68103
If you put these methods together you can:
69104
70105
- Allocate a console
71106
- Attach the console to the current process
72107
- Redirect `stdout` and `stderr` to said console
73108
- Set the title of the console window
74109
75-
Let's look at the code:
110+
Now, let's modify our `exitOnError` method to show a error message box. We'll need to use the `MessageBox` method.
76111
77-
```cpp
78-
AllocConsole();
79-
AttachConsole(GetCurrentProcessId());
80-
freopen("CON", "w", stdout);
81-
freopen("CON", "w", stderr);
82-
SetConsoleTitle(TEXT(applicationName));
83-
```
84-
85-
Now, let's modify our `exitOnError` method to show a error message box. We'll need to use the `MessageBox` method:
112+
**Definition for `MessageBox`**:
86113
87114
```cpp
88115
int WINAPI MessageBox(
@@ -93,12 +120,15 @@ int WINAPI MessageBox(
93120
);
94121
```
95122

96-
- [Documentation](https://goo.gl/7tAVnv)
123+
**[Documentation](https://goo.gl/7tAVnv) for `MessageBox`**:
124+
97125
- `hWnd` is a handle to the owner window of the message box to be created. If this parameter is `NULL`, the message box has no owner window.
98126
- `lpText` is the message to be displayed. If the string consists of more than one line, you can separate the lines using a carriage return and/or linefeed character between each line.
99127
- `lpCaption` is the dialog box title. If this parameter is `NULL`, the default title is "Error".
100128
- `uType` is the contents and behavior of the dialog box. This parameter can be a combination of flags.
101129

130+
**Usage for `MessageBox`**:
131+
102132
```cpp
103133
MessageBox(NULL, msg, applicationName, MB_ICONERROR);
104134
```
@@ -121,18 +151,9 @@ In this section we'll be writing the body this method:
121151
void createWindow(HINSTANCE hInstance) {}
122152
```
123153
124-
Don't worry about `hInstance` for now. It is simply passed from the `WinMain` method we'll write later on. To setup our window, we'll need to register it with Windows. You can find the documentation for the `RegisterClassEx` method [here](https://goo.gl/m3WViB). The definition looks like this:
125-
126-
```cpp
127-
ATOM WINAPI RegisterClassEx(
128-
_In_ const WNDCLASSEX *lpwcx
129-
);
130-
```
131-
132-
As you see, we'll need to call it with a `WNDCLASSEX` object. You can find the documentation [here](https://goo.gl/1M92FX). The definition and valid usage look like this:
154+
Don't worry about `hInstance` for now. It is passed from the `WinMain` method we'll write later on. To setup our window, we'll need to register it with Windows, but first, we need to create a `WNDCLASSEX` object to pass during registration.
133155
134156
```cpp
135-
// Definition
136157
typedef struct WNDCLASSEX {
137158
UINT cbSize;
138159
UINT style;
@@ -147,8 +168,26 @@ typedef struct WNDCLASSEX {
147168
LPCTSTR lpszClassName;
148169
HICON hIconSm;
149170
} WNDCLASSEX, *PWNDCLASSEX;
171+
```
172+
173+
**[Documentation](https://goo.gl/1M92FX) for `WNDCLASSEX`**:
150174

151-
// Usage
175+
- `cbSize` is the size, in bytes, of this structure. Set this member to `sizeof(WNDCLASSEX)`. Be sure to set this member before calling the `GetClassInfoEx` function.
176+
- `style` is the class style(s). This member can be any combination of the Class Styles.
177+
- `lpfnWndProc` is a pointer to the window procedure. You must use the `CallWindowProc` function to call the window procedure.
178+
- `cbClsExtra` is the number of extra bytes to allocate following the window-class structure.
179+
- `cbWndExtra` is the number of extra bytes to allocate following the window instance.
180+
- `hInstance` is a handle to the instance that contains the window procedure for the class.
181+
- `hIcon` is a handle to the class icon. This member must be a handle to an icon resource. If this member is `NULL`, the system provides a default icon.
182+
- `hCursor` is a handle to the class cursor.
183+
- `hbrBackground` A handle to the class background brush.
184+
- `lpszMenuName` is a pointer to a null-terminated character string that specifies the resource name of the class menu, as the name appears in the resource file. If you use an integer to identify the menu, use the `MAKEINTRESOURCE` macro.
185+
- `lpszClassName` is a pointer to a null-terminated string or is an atom.
186+
- `hIconSm` is a handle to a small icon that is associated with the window class.
187+
188+
**Usage for `WNDCLASSEX`**:
189+
190+
```cpp
152191
WNDCLASSEX wcex;
153192

154193
wcex.cbSize = sizeof(WNDCLASSEX);
@@ -165,10 +204,26 @@ wcex.lpszClassName = applicationName;
165204
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
166205
```
167206

168-
As I said, I'm not going to go too much into detail on what each field means so we'll move on to registering the window. Calling `RegisterClassEx` returns `NULL` upon failure so we should make sure we check for that.
207+
Now, we can make a call to `RegisterClassEx` to get Windowss to register the Window class.
208+
209+
**Definition for `RegisterClassEx`**:
169210

170211
```cpp
171-
if (!RegisterClassEx(&wcex))
212+
ATOM WINAPI RegisterClassEx(
213+
_In_ const WNDCLASSEX *lpwcx
214+
);
215+
```
216+
217+
**[Documentation](https://goo.gl/m3WViB) for `RegisterClassEx`**:
218+
219+
- `lpwcx` is a pointer to a `WNDCLASSEX` structure. You must fill the structure with the appropriate class attributes before passing it to the function.
220+
221+
**Usage for `RegisterClassEx`**:
222+
223+
Calling `RegisterClassEx` returns `NULL` upon failure so we should make sure we check for that.
224+
225+
```cpp
226+
if (!RegisterClassEx(&wcex))
172227
exitOnError("Failed to register window");
173228
```
174229
@@ -189,7 +244,7 @@ $$W_{left} = S_{width} / 2 - W_{width} / 2$$
189244

190245
$$W_{top} = S_{height} / 2 - W_{height} / 2$$
191246

192-
To get all of this information and compute the window's left and top positions, we can write the following code:
247+
To get this information and compute the window's left and top positions, we can write the following code:
193248

194249
```cpp
195250
int screenWidth = GetSystemMetrics(SM_CXSCREEN);
@@ -198,10 +253,11 @@ int windowLeft = screenWidth / 2 - windowWidth / 2;
198253
int windowTop = screenHeight / 2 - windowHeight / 2;
199254
```
200255

201-
Finally, we can call Window's `CreateWindow` method. You can find documentation [here](https://goo.gl/dmHFfS). The definition and valid usage look like this:
256+
Finally, we can call Window's `CreateWindow` method. This will, as the name suggests, create the window like we want. We'll specify dimensions, location, and other parameters.
257+
258+
**Definition for `CreateWindow`**:
202259

203260
```cpp
204-
// Definition
205261
HWND WINAPI CreateWindow(
206262
_In_opt_ LPCTSTR lpClassName,
207263
_In_opt_ LPCTSTR lpWindowName,
@@ -215,8 +271,25 @@ HWND WINAPI CreateWindow(
215271
_In_opt_ HINSTANCE hInstance,
216272
_In_opt_ LPVOID lpParam
217273
);
274+
```
275+
276+
**[Documentation](https://goo.gl/dmHFfS) for `CreateWindow`**:
277+
278+
- `lpClassName` is a null-terminated string or a class atom created by a previous call to the `RegisterClass` or `RegisterClassEx` function. The atom must be in the low-order word of `lpClassName`; the high-order word must be zero. If `lpClassName` is a string, it specifies the window class name.
279+
- `lpWindowName` is the window name. If the window style specifies a title bar, the window title pointed to by lpWindowName is displayed in the title bar.
280+
- `dwStyle` is the style of the window being created. This parameter can be a combination of the window style values.
281+
- `x` is the initial horizontal position of the window.
282+
- `y` is the initial vertical position of the window.
283+
- `nWidth` is the width, in device units, of the window.
284+
- `nHeight` is the height, in device units, of the window.
285+
- `hWndParent` is a handle to the parent or owner window of the window being created or `NULL` in our case.
286+
- `hMenu` is a handle to a menu, or specifies a child-window identifier depending on the window style or `NULL` in our case.
287+
- `hInstance` is a handle to the instance of the module to be associated with the window.
288+
- `lpParam` is a pointer to a value to be passed to the window through the `CREATESTRUCT` structure (`lpCreateParams` member) pointed to by the `lParam` param of the `WM_CREATE` message or `NULL` in our case.
218289

219-
// Usage
290+
**Usage for `CreateWindow`**:
291+
292+
```
220293
window = CreateWindow(
221294
applicationName,
222295
applicationName,
@@ -234,10 +307,11 @@ window = CreateWindow(
234307
The `CreateWindow` method also returns `NULL` upon failure. Let's deal with that possibility before we move on:
235308

236309
```cpp
237-
if (!window) exitOnError("Failed to create window");
310+
if (!window)
311+
exitOnError("Failed to create window");
238312
```
239313
240-
Last, but not least, we should show the window, set it in the foreground, and focus it. Windows provides three methods that do exactly that:
314+
Last, but not least, we should show the window, set it in the foreground, and focus it. Windows provides three methods that do exactly that. These are very self explanatory:
241315
242316
```cpp
243317
ShowWindow(window, SW_SHOW);
@@ -253,7 +327,7 @@ For this section, we'll be writing the body of this method:
253327
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {}
254328
```
255329
256-
We need to destroy the window and tell Windows we quit if the user attempted to close the window. If we're told we need to paint, we'll simply update the window. If neither of those cases we're met, we'll simply call the window's default procedure to handle events we didn't process. You can do this like so:
330+
We need to destroy the window and tell Windows we quit if the user attempted to close the window. If we're told we need to paint, we'll simply update the window. If neither of those cases we're met, we'll the default procedure to handle events we didn't process. You can do this like so:
257331
258332
```cpp
259333
switch (message) {
@@ -278,7 +352,13 @@ For this section, we'll write the body of this method:
278352
void VulkanExample::renderLoop() {}
279353
```
280354

281-
We're calling it `renderLoop` because later we'll make calls to rendering functions within it. For now, however, we're going to create a message, loop while we have Windows set it, Windows translate it into a character message then add it to the thread queue, and dispatch the message to the windows procedure. While that sounds complicated, it can be done with just a few lines of code:
355+
We're calling it `renderLoop` because later we'll make calls to rendering functions within it. For now, however, we're going to:
356+
357+
- Create a message, loop while we have Windows set it
358+
- Windows translate it into a character message then add it to the thread queue
359+
- Dispatch the message to the windows procedure.
360+
361+
While that sounds complicated, it can be done with just a few lines of code:
282362

283363
```cpp
284364
MSG message;
@@ -297,18 +377,27 @@ This is our application's new entry-point. We will **not** be using your typical
297377
- Call our `initWindow` method
298378
- Call our `renderLoop`
299379

300-
You can find documentation on the `WinMain` entry-point [here](https://goo.gl/uToSOo). The definition and our usage are:
380+
**Definition for `WinMain`**:
301381

302382
```cpp
303-
// Definition
304383
int CALLBACK WinMain(
305384
_In_ HINSTANCE hInstance,
306385
_In_ HINSTANCE hPrevInstance,
307386
_In_ LPSTR lpCmdLine,
308387
_In_ int nCmdShow
309388
);
389+
```
310390

311-
// Our implementation
391+
**[Documentation](https://goo.gl/uToSOo) for `WinMain`**:
392+
393+
- `hInstance` is a handle to the current instance of the application.
394+
- `hPrevInstance` is a handle to the previous instance of the application. This parameter is always `NULL`.
395+
- `lpCmdLine` is the command line for the application, excluding the program name.
396+
- `nCmdShow` controls how the window is to be shown.
397+
398+
**Usage for `WinMain`**:
399+
400+
```cpp
312401
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
313402
LPSTR lpCmdLine, int nCmdShow) {
314403
VulkanExample ve = VulkanExample();

0 commit comments

Comments
 (0)