1 //========================================================================
2 // Event linter (event spewer)
3 // Copyright (c) Camilla Löwy <elmindreda@glfw.org>
4 //
5 // This software is provided 'as-is', without any express or implied
6 // warranty. In no event will the authors be held liable for any damages
7 // arising from the use of this software.
8 //
9 // Permission is granted to anyone to use this software for any purpose,
10 // including commercial applications, and to alter it and redistribute it
11 // freely, subject to the following restrictions:
12 //
13 // 1. The origin of this software must not be misrepresented; you must not
14 //    claim that you wrote the original software. If you use this software
15 //    in a product, an acknowledgment in the product documentation would
16 //    be appreciated but is not required.
17 //
18 // 2. Altered source versions must be plainly marked as such, and must not
19 //    be misrepresented as being the original software.
20 //
21 // 3. This notice may not be removed or altered from any source
22 //    distribution.
23 //
24 //========================================================================
25 //
26 // This test hooks every available callback and outputs their arguments
27 //
28 // Log messages go to stdout, error messages to stderr
29 //
30 // Every event also gets a (sequential) number to aid discussion of logs
31 //
32 //========================================================================
33 
34 #include <glad/gl.h>
35 #define GLFW_INCLUDE_NONE
36 #include <GLFW/glfw3.h>
37 
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <ctype.h>
41 #include <string.h>
42 #include <locale.h>
43 
44 #include "getopt.h"
45 
46 // Event index
47 static unsigned int counter = 0;
48 
49 typedef struct
50 {
51     GLFWwindow* window;
52     int number;
53     int closeable;
54 } Slot;
55 
usage(void)56 static void usage(void)
57 {
58     printf("Usage: events [-f] [-h] [-n WINDOWS]\n");
59     printf("Options:\n");
60     printf("  -f use full screen\n");
61     printf("  -h show this help\n");
62     printf("  -n the number of windows to create\n");
63 }
64 
get_key_name(int key)65 static const char* get_key_name(int key)
66 {
67     switch (key)
68     {
69         // Printable keys
70         case GLFW_KEY_A:            return "A";
71         case GLFW_KEY_B:            return "B";
72         case GLFW_KEY_C:            return "C";
73         case GLFW_KEY_D:            return "D";
74         case GLFW_KEY_E:            return "E";
75         case GLFW_KEY_F:            return "F";
76         case GLFW_KEY_G:            return "G";
77         case GLFW_KEY_H:            return "H";
78         case GLFW_KEY_I:            return "I";
79         case GLFW_KEY_J:            return "J";
80         case GLFW_KEY_K:            return "K";
81         case GLFW_KEY_L:            return "L";
82         case GLFW_KEY_M:            return "M";
83         case GLFW_KEY_N:            return "N";
84         case GLFW_KEY_O:            return "O";
85         case GLFW_KEY_P:            return "P";
86         case GLFW_KEY_Q:            return "Q";
87         case GLFW_KEY_R:            return "R";
88         case GLFW_KEY_S:            return "S";
89         case GLFW_KEY_T:            return "T";
90         case GLFW_KEY_U:            return "U";
91         case GLFW_KEY_V:            return "V";
92         case GLFW_KEY_W:            return "W";
93         case GLFW_KEY_X:            return "X";
94         case GLFW_KEY_Y:            return "Y";
95         case GLFW_KEY_Z:            return "Z";
96         case GLFW_KEY_1:            return "1";
97         case GLFW_KEY_2:            return "2";
98         case GLFW_KEY_3:            return "3";
99         case GLFW_KEY_4:            return "4";
100         case GLFW_KEY_5:            return "5";
101         case GLFW_KEY_6:            return "6";
102         case GLFW_KEY_7:            return "7";
103         case GLFW_KEY_8:            return "8";
104         case GLFW_KEY_9:            return "9";
105         case GLFW_KEY_0:            return "0";
106         case GLFW_KEY_SPACE:        return "SPACE";
107         case GLFW_KEY_MINUS:        return "MINUS";
108         case GLFW_KEY_EQUAL:        return "EQUAL";
109         case GLFW_KEY_LEFT_BRACKET: return "LEFT BRACKET";
110         case GLFW_KEY_RIGHT_BRACKET: return "RIGHT BRACKET";
111         case GLFW_KEY_BACKSLASH:    return "BACKSLASH";
112         case GLFW_KEY_SEMICOLON:    return "SEMICOLON";
113         case GLFW_KEY_APOSTROPHE:   return "APOSTROPHE";
114         case GLFW_KEY_GRAVE_ACCENT: return "GRAVE ACCENT";
115         case GLFW_KEY_COMMA:        return "COMMA";
116         case GLFW_KEY_PERIOD:       return "PERIOD";
117         case GLFW_KEY_SLASH:        return "SLASH";
118         case GLFW_KEY_WORLD_1:      return "WORLD 1";
119         case GLFW_KEY_WORLD_2:      return "WORLD 2";
120 
121         // Function keys
122         case GLFW_KEY_ESCAPE:       return "ESCAPE";
123         case GLFW_KEY_F1:           return "F1";
124         case GLFW_KEY_F2:           return "F2";
125         case GLFW_KEY_F3:           return "F3";
126         case GLFW_KEY_F4:           return "F4";
127         case GLFW_KEY_F5:           return "F5";
128         case GLFW_KEY_F6:           return "F6";
129         case GLFW_KEY_F7:           return "F7";
130         case GLFW_KEY_F8:           return "F8";
131         case GLFW_KEY_F9:           return "F9";
132         case GLFW_KEY_F10:          return "F10";
133         case GLFW_KEY_F11:          return "F11";
134         case GLFW_KEY_F12:          return "F12";
135         case GLFW_KEY_F13:          return "F13";
136         case GLFW_KEY_F14:          return "F14";
137         case GLFW_KEY_F15:          return "F15";
138         case GLFW_KEY_F16:          return "F16";
139         case GLFW_KEY_F17:          return "F17";
140         case GLFW_KEY_F18:          return "F18";
141         case GLFW_KEY_F19:          return "F19";
142         case GLFW_KEY_F20:          return "F20";
143         case GLFW_KEY_F21:          return "F21";
144         case GLFW_KEY_F22:          return "F22";
145         case GLFW_KEY_F23:          return "F23";
146         case GLFW_KEY_F24:          return "F24";
147         case GLFW_KEY_F25:          return "F25";
148         case GLFW_KEY_UP:           return "UP";
149         case GLFW_KEY_DOWN:         return "DOWN";
150         case GLFW_KEY_LEFT:         return "LEFT";
151         case GLFW_KEY_RIGHT:        return "RIGHT";
152         case GLFW_KEY_LEFT_SHIFT:   return "LEFT SHIFT";
153         case GLFW_KEY_RIGHT_SHIFT:  return "RIGHT SHIFT";
154         case GLFW_KEY_LEFT_CONTROL: return "LEFT CONTROL";
155         case GLFW_KEY_RIGHT_CONTROL: return "RIGHT CONTROL";
156         case GLFW_KEY_LEFT_ALT:     return "LEFT ALT";
157         case GLFW_KEY_RIGHT_ALT:    return "RIGHT ALT";
158         case GLFW_KEY_TAB:          return "TAB";
159         case GLFW_KEY_ENTER:        return "ENTER";
160         case GLFW_KEY_BACKSPACE:    return "BACKSPACE";
161         case GLFW_KEY_INSERT:       return "INSERT";
162         case GLFW_KEY_DELETE:       return "DELETE";
163         case GLFW_KEY_PAGE_UP:      return "PAGE UP";
164         case GLFW_KEY_PAGE_DOWN:    return "PAGE DOWN";
165         case GLFW_KEY_HOME:         return "HOME";
166         case GLFW_KEY_END:          return "END";
167         case GLFW_KEY_KP_0:         return "KEYPAD 0";
168         case GLFW_KEY_KP_1:         return "KEYPAD 1";
169         case GLFW_KEY_KP_2:         return "KEYPAD 2";
170         case GLFW_KEY_KP_3:         return "KEYPAD 3";
171         case GLFW_KEY_KP_4:         return "KEYPAD 4";
172         case GLFW_KEY_KP_5:         return "KEYPAD 5";
173         case GLFW_KEY_KP_6:         return "KEYPAD 6";
174         case GLFW_KEY_KP_7:         return "KEYPAD 7";
175         case GLFW_KEY_KP_8:         return "KEYPAD 8";
176         case GLFW_KEY_KP_9:         return "KEYPAD 9";
177         case GLFW_KEY_KP_DIVIDE:    return "KEYPAD DIVIDE";
178         case GLFW_KEY_KP_MULTIPLY:  return "KEYPAD MULTIPLY";
179         case GLFW_KEY_KP_SUBTRACT:  return "KEYPAD SUBTRACT";
180         case GLFW_KEY_KP_ADD:       return "KEYPAD ADD";
181         case GLFW_KEY_KP_DECIMAL:   return "KEYPAD DECIMAL";
182         case GLFW_KEY_KP_EQUAL:     return "KEYPAD EQUAL";
183         case GLFW_KEY_KP_ENTER:     return "KEYPAD ENTER";
184         case GLFW_KEY_PRINT_SCREEN: return "PRINT SCREEN";
185         case GLFW_KEY_NUM_LOCK:     return "NUM LOCK";
186         case GLFW_KEY_CAPS_LOCK:    return "CAPS LOCK";
187         case GLFW_KEY_SCROLL_LOCK:  return "SCROLL LOCK";
188         case GLFW_KEY_PAUSE:        return "PAUSE";
189         case GLFW_KEY_LEFT_SUPER:   return "LEFT SUPER";
190         case GLFW_KEY_RIGHT_SUPER:  return "RIGHT SUPER";
191         case GLFW_KEY_MENU:         return "MENU";
192 
193         default:                    return "UNKNOWN";
194     }
195 }
196 
get_action_name(int action)197 static const char* get_action_name(int action)
198 {
199     switch (action)
200     {
201         case GLFW_PRESS:
202             return "pressed";
203         case GLFW_RELEASE:
204             return "released";
205         case GLFW_REPEAT:
206             return "repeated";
207     }
208 
209     return "caused unknown action";
210 }
211 
get_button_name(int button)212 static const char* get_button_name(int button)
213 {
214     switch (button)
215     {
216         case GLFW_MOUSE_BUTTON_LEFT:
217             return "left";
218         case GLFW_MOUSE_BUTTON_RIGHT:
219             return "right";
220         case GLFW_MOUSE_BUTTON_MIDDLE:
221             return "middle";
222         default:
223         {
224             static char name[16];
225             snprintf(name, sizeof(name), "%i", button);
226             return name;
227         }
228     }
229 }
230 
get_mods_name(int mods)231 static const char* get_mods_name(int mods)
232 {
233     static char name[512];
234 
235     if (mods == 0)
236         return " no mods";
237 
238     name[0] = '\0';
239 
240     if (mods & GLFW_MOD_SHIFT)
241         strcat(name, " shift");
242     if (mods & GLFW_MOD_CONTROL)
243         strcat(name, " control");
244     if (mods & GLFW_MOD_ALT)
245         strcat(name, " alt");
246     if (mods & GLFW_MOD_SUPER)
247         strcat(name, " super");
248     if (mods & GLFW_MOD_CAPS_LOCK)
249         strcat(name, " capslock-on");
250     if (mods & GLFW_MOD_NUM_LOCK)
251         strcat(name, " numlock-on");
252 
253     return name;
254 }
255 
encode_utf8(char * s,unsigned int ch)256 static size_t encode_utf8(char* s, unsigned int ch)
257 {
258     size_t count = 0;
259 
260     if (ch < 0x80)
261         s[count++] = (char) ch;
262     else if (ch < 0x800)
263     {
264         s[count++] = (ch >> 6) | 0xc0;
265         s[count++] = (ch & 0x3f) | 0x80;
266     }
267     else if (ch < 0x10000)
268     {
269         s[count++] = (ch >> 12) | 0xe0;
270         s[count++] = ((ch >> 6) & 0x3f) | 0x80;
271         s[count++] = (ch & 0x3f) | 0x80;
272     }
273     else if (ch < 0x110000)
274     {
275         s[count++] = (ch >> 18) | 0xf0;
276         s[count++] = ((ch >> 12) & 0x3f) | 0x80;
277         s[count++] = ((ch >> 6) & 0x3f) | 0x80;
278         s[count++] = (ch & 0x3f) | 0x80;
279     }
280 
281     return count;
282 }
283 
error_callback(int error,const char * description)284 static void error_callback(int error, const char* description)
285 {
286     fprintf(stderr, "Error: %s\n", description);
287 }
288 
window_pos_callback(GLFWwindow * window,int x,int y)289 static void window_pos_callback(GLFWwindow* window, int x, int y)
290 {
291     Slot* slot = glfwGetWindowUserPointer(window);
292     printf("%08x to %i at %0.3f: Window position: %i %i\n",
293            counter++, slot->number, glfwGetTime(), x, y);
294 }
295 
window_size_callback(GLFWwindow * window,int width,int height)296 static void window_size_callback(GLFWwindow* window, int width, int height)
297 {
298     Slot* slot = glfwGetWindowUserPointer(window);
299     printf("%08x to %i at %0.3f: Window size: %i %i\n",
300            counter++, slot->number, glfwGetTime(), width, height);
301 }
302 
framebuffer_size_callback(GLFWwindow * window,int width,int height)303 static void framebuffer_size_callback(GLFWwindow* window, int width, int height)
304 {
305     Slot* slot = glfwGetWindowUserPointer(window);
306     printf("%08x to %i at %0.3f: Framebuffer size: %i %i\n",
307            counter++, slot->number, glfwGetTime(), width, height);
308 }
309 
window_content_scale_callback(GLFWwindow * window,float xscale,float yscale)310 static void window_content_scale_callback(GLFWwindow* window, float xscale, float yscale)
311 {
312     Slot* slot = glfwGetWindowUserPointer(window);
313     printf("%08x to %i at %0.3f: Window content scale: %0.3f %0.3f\n",
314            counter++, slot->number, glfwGetTime(), xscale, yscale);
315 }
316 
window_close_callback(GLFWwindow * window)317 static void window_close_callback(GLFWwindow* window)
318 {
319     Slot* slot = glfwGetWindowUserPointer(window);
320     printf("%08x to %i at %0.3f: Window close\n",
321            counter++, slot->number, glfwGetTime());
322 
323     glfwSetWindowShouldClose(window, slot->closeable);
324 }
325 
window_refresh_callback(GLFWwindow * window)326 static void window_refresh_callback(GLFWwindow* window)
327 {
328     Slot* slot = glfwGetWindowUserPointer(window);
329     printf("%08x to %i at %0.3f: Window refresh\n",
330            counter++, slot->number, glfwGetTime());
331 
332     glfwMakeContextCurrent(window);
333     glClear(GL_COLOR_BUFFER_BIT);
334     glfwSwapBuffers(window);
335 }
336 
window_focus_callback(GLFWwindow * window,int focused)337 static void window_focus_callback(GLFWwindow* window, int focused)
338 {
339     Slot* slot = glfwGetWindowUserPointer(window);
340     printf("%08x to %i at %0.3f: Window %s\n",
341            counter++, slot->number, glfwGetTime(),
342            focused ? "focused" : "defocused");
343 }
344 
window_iconify_callback(GLFWwindow * window,int iconified)345 static void window_iconify_callback(GLFWwindow* window, int iconified)
346 {
347     Slot* slot = glfwGetWindowUserPointer(window);
348     printf("%08x to %i at %0.3f: Window was %s\n",
349            counter++, slot->number, glfwGetTime(),
350            iconified ? "iconified" : "uniconified");
351 }
352 
window_maximize_callback(GLFWwindow * window,int maximized)353 static void window_maximize_callback(GLFWwindow* window, int maximized)
354 {
355     Slot* slot = glfwGetWindowUserPointer(window);
356     printf("%08x to %i at %0.3f: Window was %s\n",
357            counter++, slot->number, glfwGetTime(),
358            maximized ? "maximized" : "unmaximized");
359 }
360 
mouse_button_callback(GLFWwindow * window,int button,int action,int mods)361 static void mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
362 {
363     Slot* slot = glfwGetWindowUserPointer(window);
364     printf("%08x to %i at %0.3f: Mouse button %i (%s) (with%s) was %s\n",
365            counter++, slot->number, glfwGetTime(), button,
366            get_button_name(button),
367            get_mods_name(mods),
368            get_action_name(action));
369 }
370 
cursor_position_callback(GLFWwindow * window,double x,double y)371 static void cursor_position_callback(GLFWwindow* window, double x, double y)
372 {
373     Slot* slot = glfwGetWindowUserPointer(window);
374     printf("%08x to %i at %0.3f: Cursor position: %f %f\n",
375            counter++, slot->number, glfwGetTime(), x, y);
376 }
377 
cursor_enter_callback(GLFWwindow * window,int entered)378 static void cursor_enter_callback(GLFWwindow* window, int entered)
379 {
380     Slot* slot = glfwGetWindowUserPointer(window);
381     printf("%08x to %i at %0.3f: Cursor %s window\n",
382            counter++, slot->number, glfwGetTime(),
383            entered ? "entered" : "left");
384 }
385 
scroll_callback(GLFWwindow * window,double x,double y)386 static void scroll_callback(GLFWwindow* window, double x, double y)
387 {
388     Slot* slot = glfwGetWindowUserPointer(window);
389     printf("%08x to %i at %0.3f: Scroll: %0.3f %0.3f\n",
390            counter++, slot->number, glfwGetTime(), x, y);
391 }
392 
key_callback(GLFWwindow * window,int key,int scancode,int action,int mods)393 static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
394 {
395     Slot* slot = glfwGetWindowUserPointer(window);
396     const char* name = glfwGetKeyName(key, scancode);
397 
398     if (name)
399     {
400         printf("%08x to %i at %0.3f: Key 0x%04x Scancode 0x%04x (%s) (%s) (with%s) was %s\n",
401                counter++, slot->number, glfwGetTime(), key, scancode,
402                get_key_name(key),
403                name,
404                get_mods_name(mods),
405                get_action_name(action));
406     }
407     else
408     {
409         printf("%08x to %i at %0.3f: Key 0x%04x Scancode 0x%04x (%s) (with%s) was %s\n",
410                counter++, slot->number, glfwGetTime(), key, scancode,
411                get_key_name(key),
412                get_mods_name(mods),
413                get_action_name(action));
414     }
415 
416     if (action != GLFW_PRESS)
417         return;
418 
419     switch (key)
420     {
421         case GLFW_KEY_C:
422         {
423             slot->closeable = !slot->closeable;
424 
425             printf("(( closing %s ))\n", slot->closeable ? "enabled" : "disabled");
426             break;
427         }
428 
429         case GLFW_KEY_L:
430         {
431             const int state = glfwGetInputMode(window, GLFW_LOCK_KEY_MODS);
432             glfwSetInputMode(window, GLFW_LOCK_KEY_MODS, !state);
433 
434             printf("(( lock key mods %s ))\n", !state ? "enabled" : "disabled");
435             break;
436         }
437     }
438 }
439 
char_callback(GLFWwindow * window,unsigned int codepoint)440 static void char_callback(GLFWwindow* window, unsigned int codepoint)
441 {
442     Slot* slot = glfwGetWindowUserPointer(window);
443     char string[5] = "";
444 
445     encode_utf8(string, codepoint);
446     printf("%08x to %i at %0.3f: Character 0x%08x (%s) input\n",
447            counter++, slot->number, glfwGetTime(), codepoint, string);
448 }
449 
drop_callback(GLFWwindow * window,int count,const char * paths[])450 static void drop_callback(GLFWwindow* window, int count, const char* paths[])
451 {
452     int i;
453     Slot* slot = glfwGetWindowUserPointer(window);
454 
455     printf("%08x to %i at %0.3f: Drop input\n",
456            counter++, slot->number, glfwGetTime());
457 
458     for (i = 0;  i < count;  i++)
459         printf("  %i: \"%s\"\n", i, paths[i]);
460 }
461 
monitor_callback(GLFWmonitor * monitor,int event)462 static void monitor_callback(GLFWmonitor* monitor, int event)
463 {
464     if (event == GLFW_CONNECTED)
465     {
466         int x, y, widthMM, heightMM;
467         const GLFWvidmode* mode = glfwGetVideoMode(monitor);
468 
469         glfwGetMonitorPos(monitor, &x, &y);
470         glfwGetMonitorPhysicalSize(monitor, &widthMM, &heightMM);
471 
472         printf("%08x at %0.3f: Monitor %s (%ix%i at %ix%i, %ix%i mm) was connected\n",
473                counter++,
474                glfwGetTime(),
475                glfwGetMonitorName(monitor),
476                mode->width, mode->height,
477                x, y,
478                widthMM, heightMM);
479     }
480     else if (event == GLFW_DISCONNECTED)
481     {
482         printf("%08x at %0.3f: Monitor %s was disconnected\n",
483                counter++,
484                glfwGetTime(),
485                glfwGetMonitorName(monitor));
486     }
487 }
488 
joystick_callback(int jid,int event)489 static void joystick_callback(int jid, int event)
490 {
491     if (event == GLFW_CONNECTED)
492     {
493         int axisCount, buttonCount, hatCount;
494 
495         glfwGetJoystickAxes(jid, &axisCount);
496         glfwGetJoystickButtons(jid, &buttonCount);
497         glfwGetJoystickHats(jid, &hatCount);
498 
499         printf("%08x at %0.3f: Joystick %i (%s) was connected with %i axes, %i buttons, and %i hats\n",
500                counter++, glfwGetTime(),
501                jid,
502                glfwGetJoystickName(jid),
503                axisCount,
504                buttonCount,
505                hatCount);
506     }
507     else
508     {
509         printf("%08x at %0.3f: Joystick %i was disconnected\n",
510                counter++, glfwGetTime(), jid);
511     }
512 }
513 
main(int argc,char ** argv)514 int main(int argc, char** argv)
515 {
516     Slot* slots;
517     GLFWmonitor* monitor = NULL;
518     int ch, i, width, height, count = 1;
519 
520     glfwSetErrorCallback(error_callback);
521 
522     if (!glfwInit())
523         exit(EXIT_FAILURE);
524 
525     printf("Library initialized\n");
526 
527     glfwSetMonitorCallback(monitor_callback);
528     glfwSetJoystickCallback(joystick_callback);
529 
530     while ((ch = getopt(argc, argv, "hfn:")) != -1)
531     {
532         switch (ch)
533         {
534             case 'h':
535                 usage();
536                 exit(EXIT_SUCCESS);
537 
538             case 'f':
539                 monitor = glfwGetPrimaryMonitor();
540                 break;
541 
542             case 'n':
543                 count = (int) strtoul(optarg, NULL, 10);
544                 break;
545 
546             default:
547                 usage();
548                 exit(EXIT_FAILURE);
549         }
550     }
551 
552     if (monitor)
553     {
554         const GLFWvidmode* mode = glfwGetVideoMode(monitor);
555 
556         glfwWindowHint(GLFW_REFRESH_RATE, mode->refreshRate);
557         glfwWindowHint(GLFW_RED_BITS, mode->redBits);
558         glfwWindowHint(GLFW_GREEN_BITS, mode->greenBits);
559         glfwWindowHint(GLFW_BLUE_BITS, mode->blueBits);
560 
561         width = mode->width;
562         height = mode->height;
563     }
564     else
565     {
566         width  = 640;
567         height = 480;
568     }
569 
570     slots = calloc(count, sizeof(Slot));
571 
572     for (i = 0;  i < count;  i++)
573     {
574         char title[128];
575 
576         slots[i].closeable = GLFW_TRUE;
577         slots[i].number = i + 1;
578 
579         snprintf(title, sizeof(title), "Event Linter (Window %i)", slots[i].number);
580 
581         if (monitor)
582         {
583             printf("Creating full screen window %i (%ix%i on %s)\n",
584                    slots[i].number,
585                    width, height,
586                    glfwGetMonitorName(monitor));
587         }
588         else
589         {
590             printf("Creating windowed mode window %i (%ix%i)\n",
591                    slots[i].number,
592                    width, height);
593         }
594 
595         slots[i].window = glfwCreateWindow(width, height, title, monitor, NULL);
596         if (!slots[i].window)
597         {
598             free(slots);
599             glfwTerminate();
600             exit(EXIT_FAILURE);
601         }
602 
603         glfwSetWindowUserPointer(slots[i].window, slots + i);
604 
605         glfwSetWindowPosCallback(slots[i].window, window_pos_callback);
606         glfwSetWindowSizeCallback(slots[i].window, window_size_callback);
607         glfwSetFramebufferSizeCallback(slots[i].window, framebuffer_size_callback);
608         glfwSetWindowContentScaleCallback(slots[i].window, window_content_scale_callback);
609         glfwSetWindowCloseCallback(slots[i].window, window_close_callback);
610         glfwSetWindowRefreshCallback(slots[i].window, window_refresh_callback);
611         glfwSetWindowFocusCallback(slots[i].window, window_focus_callback);
612         glfwSetWindowIconifyCallback(slots[i].window, window_iconify_callback);
613         glfwSetWindowMaximizeCallback(slots[i].window, window_maximize_callback);
614         glfwSetMouseButtonCallback(slots[i].window, mouse_button_callback);
615         glfwSetCursorPosCallback(slots[i].window, cursor_position_callback);
616         glfwSetCursorEnterCallback(slots[i].window, cursor_enter_callback);
617         glfwSetScrollCallback(slots[i].window, scroll_callback);
618         glfwSetKeyCallback(slots[i].window, key_callback);
619         glfwSetCharCallback(slots[i].window, char_callback);
620         glfwSetDropCallback(slots[i].window, drop_callback);
621 
622         glfwMakeContextCurrent(slots[i].window);
623         gladLoadGL(glfwGetProcAddress);
624         glfwSwapInterval(1);
625     }
626 
627     printf("Main loop starting\n");
628 
629     for (;;)
630     {
631         for (i = 0;  i < count;  i++)
632         {
633             if (glfwWindowShouldClose(slots[i].window))
634                 break;
635         }
636 
637         if (i < count)
638             break;
639 
640         glfwWaitEvents();
641 
642         // Workaround for an issue with msvcrt and mintty
643         fflush(stdout);
644     }
645 
646     free(slots);
647     glfwTerminate();
648     exit(EXIT_SUCCESS);
649 }
650 
651