1 // Copyright 2012 - 2014 Intel Corporation
2 //
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are met:
7 //
8 // - Redistributions of source code must retain the above copyright notice, this
9 //   list of conditions and the following disclaimer.
10 //
11 // - Redistributions in binary form must reproduce the above copyright notice,
12 //   this list of conditions and the following disclaimer in the documentation
13 //   and/or other materials provided with the distribution.
14 //
15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 
26 /// @file
27 /// @brief Do some basic OpenGL rendering using Waffle.
28 ///
29 /// This example does the following:
30 ///     1. Dynamically choose the platform and OpenGL API according to
31 ///        command line arguments.
32 ///     2. Create a window and OpenGL context.
33 ///     3. Fill the window with red, then green, then blue, sleeping between
34 ///        each buffer swap.
35 
36 #define _POSIX_C_SOURCE 199309L // glibc feature macro for nanosleep.
37 #define WAFFLE_API_VERSION 0x0106
38 #define WAFFLE_API_EXPERIMENTAL
39 
40 #include <assert.h>
41 #include <getopt.h>
42 #include <stdarg.h>
43 #include <stdbool.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #if !defined(_WIN32)
48 #include <time.h>
49 #else
50 #include <windows.h>
51 #endif
52 
53 #ifdef __APPLE__
54 #    import <Foundation/NSAutoreleasePool.h>
55 #    import <Appkit/NSApplication.h>
56 
57 static void
58 removeXcodeArgs(int *argc, char **argv);
59 #endif
60 
61 #include "waffle.h"
62 
63 static const char *usage_message =
64     "usage:\n"
65     "    gl_basic --platform=android|cgl|gbm|glx|wayland|wgl|x11_egl\n"
66     "             --api=gl|gles1|gles2|gles3\n"
67     "             [--version=MAJOR.MINOR]\n"
68     "             [--profile=core|compat|none]\n"
69     "             [--forward-compatible]\n"
70     "             [--debug]\n"
71     "             [--robust]\n"
72     "             [--resize-window]\n"
73     "             [--window-size=WIDTHxHEIGHT | --fullscreen]\n"
74     "\n"
75     "examples:\n"
76     "    gl_basic --platform=glx --api=gl\n"
77     "    gl_basic --platform=x11_egl --api=gl --version=3.2 --profile=core\n"
78     "    gl_basic --platform=wayland --api=gles3\n"
79     "\n"
80     "description:\n"
81     "    Create a window. Fill it with red, green, then blue.\n"
82     "\n"
83     "options:\n"
84     "    --forward-compatible\n"
85     "        Create a forward-compatible context.\n"
86     "\n"
87     "    --debug\n"
88     "        Create a debug context.\n"
89     "\n"
90     "    --robust\n"
91     "        Create a robust context.\n"
92     "\n"
93     "    --resize-window\n"
94     "        Resize the window between each draw call.\n"
95     "\n"
96     "    --fullscreen\n"
97     "        Create a fullscreen window.\n"
98     ;
99 
100 enum {
101     OPT_PLATFORM = 1,
102     OPT_API,
103     OPT_VERSION,
104     OPT_PROFILE,
105     OPT_DEBUG,
106     OPT_FORWARD_COMPATIBLE,
107     OPT_ROBUST,
108     OPT_RESIZE_WINDOW,
109     OPT_WINDOW_SIZE,
110     OPT_FULLSCREEN,
111 };
112 
113 static const struct option get_opts[] = {
114     { .name = "platform",       .has_arg = required_argument,     .val = OPT_PLATFORM },
115     { .name = "api",            .has_arg = required_argument,     .val = OPT_API },
116     { .name = "version",        .has_arg = required_argument,     .val = OPT_VERSION },
117     { .name = "profile",        .has_arg = required_argument,     .val = OPT_PROFILE },
118     { .name = "debug",          .has_arg = no_argument,           .val = OPT_DEBUG },
119     { .name = "forward-compatible", .has_arg = no_argument,       .val = OPT_FORWARD_COMPATIBLE },
120     { .name = "robust",         .has_arg = no_argument,           .val = OPT_ROBUST },
121     { .name = "resize-window",  .has_arg = no_argument,           .val = OPT_RESIZE_WINDOW },
122     { .name = "window-size",    .has_arg = required_argument,     .val = OPT_WINDOW_SIZE },
123     { .name = "fullscreen",     .has_arg = no_argument,           .val = OPT_FULLSCREEN },
124     { 0 },
125 };
126 
127 #if defined(__GNUC__)
128 #define NORETURN __attribute__((noreturn))
129 #elif defined(_MSC_VER)
130 #define NORETURN __declspec(noreturn)
131 #else
132 #define NORETURN
133 #endif
134 
135 static void NORETURN
error_printf(const char * fmt,...)136 error_printf(const char *fmt, ...)
137 {
138     va_list ap;
139 
140     fflush(stdout);
141 
142     va_start(ap, fmt);
143     fprintf(stderr, "gl_basic: error: ");
144     vfprintf(stderr, fmt, ap);
145     fprintf(stderr, "\n");
146     va_end(ap);
147 
148     exit(EXIT_FAILURE);
149 }
150 
151 static void NORETURN
usage_error_printf(const char * fmt,...)152 usage_error_printf(const char *fmt, ...)
153 {
154     fflush(stdout);
155     fprintf(stderr, "gl_basic: usage error");
156 
157     if (fmt) {
158         va_list ap;
159         va_start(ap, fmt);
160         fprintf(stderr, ": ");
161         vfprintf(stderr, fmt, ap);
162         va_end(ap);
163     }
164 
165     fprintf(stderr, "\n");
166     fprintf(stderr, "\n");
167     fprintf(stderr, "%s", usage_message);
168 
169     exit(EXIT_FAILURE);
170 }
171 
172 static void
error_waffle(void)173 error_waffle(void)
174 {
175     const struct waffle_error_info *info = waffle_error_get_info();
176     const char *code = waffle_error_to_string(info->code);
177 
178     if (info->message_length > 0)
179         error_printf("%s: %s", code, info->message);
180     else
181         error_printf("%s", code);
182 }
183 
184 static void
error_get_gl_symbol(const char * name)185 error_get_gl_symbol(const char *name)
186 {
187     error_printf("failed to get function pointer for %s", name);
188 }
189 
190 typedef float GLclampf;
191 typedef unsigned int GLbitfield;
192 typedef unsigned int GLint;
193 typedef int GLsizei;
194 typedef unsigned int GLenum;
195 typedef void GLvoid;
196 
197 enum {
198     // Copied from <GL/gl*.h>.
199     GL_UNSIGNED_BYTE =    0x00001401,
200     GL_RGBA =             0x00001908,
201     GL_COLOR_BUFFER_BIT = 0x00004000,
202 
203     GL_CONTEXT_FLAGS = 0x821e,
204     GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT = 0x00000001,
205     GL_CONTEXT_FLAG_DEBUG_BIT              = 0x00000002,
206     GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT      = 0x00000004 ,
207 };
208 
209 static int window_width = 320;
210 static int window_height = 240;
211 
212 #ifndef _WIN32
213 #define APIENTRY
214 #else
215 #ifndef APIENTRY
216 #define APIENTRY __stdcall
217 #endif
218 #endif
219 
220 static void (APIENTRY *glClearColor)(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);
221 static void (APIENTRY *glClear)(GLbitfield mask);
222 static GLenum (APIENTRY *glGetError)(void);
223 static void (APIENTRY *glGetIntegerv)(GLenum pname, GLint *params);
224 static void (APIENTRY *glReadPixels)(GLint x, GLint y, GLsizei width, GLsizei height,
225                                      GLenum format, GLenum type, GLvoid* data);
226 static void (APIENTRY *glViewport)(GLint x, GLint y, GLsizei width, GLsizei height);
227 
228 /// @brief Command line options.
229 struct options {
230     /// @brief One of `WAFFLE_PLATFORM_*`.
231     int platform;
232 
233     /// @brief One of `WAFFLE_CONTEXT_OPENGL_*`.
234     int context_api;
235 
236     /// @brief One of `WAFFLE_CONTEXT_PROFILE_*` or `WAFFLE_NONE`.
237     int context_profile;
238 
239     int context_version;
240 
241     bool context_forward_compatible;
242     bool context_debug;
243     bool context_robust;
244 
245     bool resize_window;
246 
247     bool fullscreen;
248 
249     /// @brief One of `WAFFLE_DL_*`.
250     int dl;
251 };
252 
253 struct enum_map {
254     int i;
255     const char *s;
256 };
257 
258 static const struct enum_map platform_map[] = {
259     {WAFFLE_PLATFORM_ANDROID,   "android"       },
260     {WAFFLE_PLATFORM_CGL,       "cgl",          },
261     {WAFFLE_PLATFORM_GBM,       "gbm"           },
262     {WAFFLE_PLATFORM_GLX,       "glx"           },
263     {WAFFLE_PLATFORM_WAYLAND,   "wayland"       },
264     {WAFFLE_PLATFORM_WGL,       "wgl"           },
265     {WAFFLE_PLATFORM_X11_EGL,   "x11_egl"       },
266     {0,                         0               },
267 };
268 
269 static const struct enum_map context_api_map[] = {
270     {WAFFLE_CONTEXT_OPENGL,         "gl"        },
271     {WAFFLE_CONTEXT_OPENGL_ES1,     "gles1"     },
272     {WAFFLE_CONTEXT_OPENGL_ES2,     "gles2"     },
273     {WAFFLE_CONTEXT_OPENGL_ES3,     "gles3"     },
274     {0,                             0           },
275 };
276 
277 /// @brief Translate string to `enum waffle_enum`.
278 ///
279 /// @param self is a list of map items. The last item must be zero-filled.
280 /// @param result is altered only if @a s if found.
281 /// @return true if @a s was found in @a map.
282 static bool
enum_map_translate_str(const struct enum_map * self,const char * s,int * result)283 enum_map_translate_str(
284         const struct enum_map *self,
285         const char *s,
286         int *result)
287 {
288     for (const struct enum_map *i = self; i->i != 0; ++i) {
289         if (!strncmp(s, i->s, strlen(i->s) + 1)) {
290             *result = i->i;
291             return true;
292         }
293     }
294 
295     return false;
296 }
297 
298 /// @return true on success.
299 static bool
parse_args(int argc,char * argv[],struct options * opts)300 parse_args(int argc, char *argv[], struct options *opts)
301 {
302     bool ok;
303     bool loop_get_opt = true;
304     bool found_window_size = false;
305 
306 #ifdef __APPLE__
307     removeXcodeArgs(&argc, argv);
308 #endif
309 
310     // Set some context attrs to invalid values.
311     opts->context_profile = -1;
312     opts->context_version = -1;
313 
314     while (loop_get_opt) {
315         int opt = getopt_long(argc, argv, "", get_opts, NULL);
316         switch (opt) {
317             case -1:
318                 loop_get_opt = false;
319                 break;
320             case '?':
321                 goto error_unrecognized_arg;
322             case OPT_PLATFORM:
323                 ok = enum_map_translate_str(platform_map, optarg,
324                                             &opts->platform);
325                 if (!ok) {
326                     usage_error_printf("'%s' is not a valid platform",
327                                        optarg);
328                 }
329                 break;
330             case OPT_API:
331                 ok = enum_map_translate_str(context_api_map, optarg,
332                                             &opts->context_api);
333                 if (!ok) {
334                     usage_error_printf("'%s' is not a valid API for an OpenGL "
335                                        "context", optarg);
336                 }
337                 break;
338             case OPT_VERSION: {
339                 int major;
340                 int minor;
341                 int match_count;
342 
343                 match_count = sscanf(optarg, "%d.%d", &major, &minor);
344                 if (match_count != 2) {
345                     usage_error_printf("'%s' is not a valid OpenGL version",
346                                        optarg);
347                 }
348                 opts->context_version = 10 * major + minor;
349                 break;
350             }
351             case OPT_PROFILE:
352                 if (strcmp(optarg, "none") == 0) {
353                     opts->context_profile = WAFFLE_NONE;
354                 } else if (strcmp(optarg, "core") == 0) {
355                     opts->context_profile = WAFFLE_CONTEXT_CORE_PROFILE;
356                 } else if (strcmp(optarg, "compat") == 0) {
357                     opts->context_profile = WAFFLE_CONTEXT_COMPATIBILITY_PROFILE;
358                 } else {
359                     usage_error_printf("'%s' is not a valid OpenGL profile",
360                                        optarg);
361                 }
362                 break;
363             case OPT_FORWARD_COMPATIBLE:
364                 opts->context_forward_compatible = true;
365                 break;
366             case OPT_DEBUG:
367                 opts->context_debug = true;
368                 break;
369             case OPT_ROBUST:
370                 opts->context_robust = true;
371                 break;
372             case OPT_RESIZE_WINDOW:
373                 opts->resize_window = true;
374                 break;
375             case OPT_WINDOW_SIZE: {
376                 int match_count;
377                 match_count = sscanf(optarg, "%dx%d", &window_width, &window_height);
378                 if (match_count != 2) {
379                     usage_error_printf("'%s' is not a valid window geometry",
380                                        optarg);
381                 }
382                 found_window_size = true;
383                 break;
384             }
385             case OPT_FULLSCREEN:
386                 opts->fullscreen = true;
387                 break;
388             default:
389                 abort();
390                 loop_get_opt = false;
391                 break;
392         }
393     }
394 
395     if (optind < argc) {
396         goto error_unrecognized_arg;
397     }
398 
399     if (!opts->platform) {
400         usage_error_printf("--platform is required");
401     }
402 
403     if (!opts->context_api) {
404         usage_error_printf("--api is required");
405     }
406 
407     if (opts->fullscreen && found_window_size) {
408         usage_error_printf("--fullscreen and --window-size are mutually "
409                            "exclusive options");
410     }
411 
412     // Set dl.
413     switch (opts->context_api) {
414         case WAFFLE_CONTEXT_OPENGL:     opts->dl = WAFFLE_DL_OPENGL;      break;
415         case WAFFLE_CONTEXT_OPENGL_ES1: opts->dl = WAFFLE_DL_OPENGL_ES1;  break;
416         case WAFFLE_CONTEXT_OPENGL_ES2: opts->dl = WAFFLE_DL_OPENGL_ES2;  break;
417         case WAFFLE_CONTEXT_OPENGL_ES3: opts->dl = WAFFLE_DL_OPENGL_ES3;  break;
418         default:
419             abort();
420             break;
421     }
422 
423     return true;
424 
425 error_unrecognized_arg:
426     usage_error_printf("unrecognized option '%s'", optarg);
427 }
428 
429 // The rules that dictate how to properly query a GL symbol are complex. The
430 // rules depend on the OS, on the winsys API, and even on the particular driver
431 // being used. The rules differ between EGL 1.4 and EGL 1.5; differ between
432 // Linux, Windows, and Mac; and differ between Mesa and Mali.
433 //
434 // This function hides that complexity with a naive heuristic: try, then try
435 // again.
436 static void *
get_gl_symbol(const struct options * opts,const char * name)437 get_gl_symbol(const struct options *opts, const char *name)
438 {
439     void *sym = NULL;
440 
441     if (waffle_dl_can_open(opts->dl)) {
442         sym = waffle_dl_sym(opts->dl, name);
443     }
444 
445     if (!sym) {
446         sym = waffle_get_proc_address(name);
447     }
448 
449     return sym;
450 }
451 
452 static bool
draw(struct waffle_window * window,bool resize)453 draw(struct waffle_window *window, bool resize)
454 {
455     bool ok;
456     unsigned char *colors;
457     int width = window_width;
458     int height = window_height;
459 
460 #if !defined(_WIN32)
461     static const struct timespec sleep_time = {
462          // 0.5 sec
463         .tv_sec = 0,
464         .tv_nsec = 500000000,
465     };
466 #endif
467 
468     for (int i = 0; i < 3; ++i) {
469         switch (i) {
470             case 0: glClearColor(1, 0, 0, 1); break;
471             case 1: glClearColor(0, 1, 0, 1); break;
472             case 2: glClearColor(0, 0, 1, 1); break;
473             case 3: abort(); break;
474         }
475 
476         if (resize) {
477             width = (i + 2) * 40;
478             height = width;
479             waffle_window_resize(window, width, height);
480             glViewport(0, 0, width, height);
481         }
482 
483         glClear(GL_COLOR_BUFFER_BIT);
484 
485         colors = calloc(width * height * 4, sizeof(*colors));
486         glReadPixels(0, 0,
487                      width, height,
488                      GL_RGBA, GL_UNSIGNED_BYTE,
489                      colors);
490         for (int j = 0; j < width * height * 4; j += 4) {
491            if ((colors[j]   != (i == 0 ? 0xff : 0)) ||
492                (colors[j+1] != (i == 1 ? 0xff : 0)) ||
493                (colors[j+2] != (i == 2 ? 0xff : 0)) ||
494                (colors[j+3] != 0xff)) {
495               fprintf(stderr, "glReadPixels returned unexpected result\n");
496               break;
497            }
498         }
499         free(colors);
500 
501         if (i == 0) {
502             ok = waffle_window_show(window);
503             if (!ok)
504                 return false;
505         }
506 
507         ok = waffle_window_swap_buffers(window);
508         if (!ok)
509             return false;
510 
511 #if !defined(_WIN32)
512         nanosleep(&sleep_time, NULL);
513 #else
514         Sleep(500);
515 #endif
516     }
517 
518     return true;
519 }
520 
521 #ifdef __APPLE__
522 
523 static NSAutoreleasePool *pool;
524 
525 static void
cocoa_init(void)526 cocoa_init(void)
527 {
528     // From the NSApplication Class Reference:
529     //     [...] if you do need to use Cocoa classes within the main()
530     //     function itself (other than to load nib files or to instantiate
531     //     NSApplication), you should create an autorelease pool before using
532     //     the classes and then release the pool when you’re done.
533     pool = [[NSAutoreleasePool alloc] init];
534 
535     // From the NSApplication Class Reference:
536     //     The sharedApplication class method initializes the display
537     //     environment and connects your program to the window server and the
538     //     display server.
539     //
540     // It also creates the singleton NSApp if it does not yet exist.
541     [NSApplication sharedApplication];
542 }
543 
544 static void
cocoa_finish(void)545 cocoa_finish(void)
546 {
547     [pool drain];
548 }
549 
550 static void
removeArg(int index,int * argc,char ** argv)551 removeArg(int index, int *argc, char **argv)
552 {
553     --*argc;
554     for (; index < *argc; ++index)
555         argv[index] = argv[index + 1];
556 }
557 
558 static void
removeXcodeArgs(int * argc,char ** argv)559 removeXcodeArgs(int *argc, char **argv)
560 {
561     // Xcode sometimes adds additional arguments.
562     for (int i = 1; i < *argc; )
563     {
564         if (strcmp(argv[i], "-NSDocumentRevisionsDebugMode") == 0 ||
565             strcmp(argv[i], "-ApplePersistenceIgnoreState" ) == 0)
566         {
567             removeArg(i, argc, argv);
568             removeArg(i, argc, argv);
569         } else
570             ++i;
571     }
572 }
573 
574 #endif // __APPLE__
575 
576 #ifdef __native_client__
577 #include "ppapi_simple/ps_main.h"
578 //
579 // We need to rename main() for native client
580 // because ppapi_simple already defines main().
581 //
582 int basic_test_main(int argc, char **argv);
PPAPI_SIMPLE_REGISTER_MAIN(basic_test_main)583 PPAPI_SIMPLE_REGISTER_MAIN(basic_test_main)
584 int
585 basic_test_main(int argc, char **argv)
586 #else
587 int
588 main(int argc, char **argv)
589 #endif
590 {
591     bool ok;
592     int i;
593 
594     struct options opts = {0};
595 
596     int32_t init_attrib_list[3];
597     int32_t config_attrib_list[64];
598     intptr_t window_attrib_list[5];
599 
600     struct waffle_display *dpy;
601     struct waffle_config *config;
602     struct waffle_context *ctx;
603     struct waffle_window *window;
604 
605     GLint context_flags = 0;
606 
607     #ifdef __APPLE__
608         cocoa_init();
609     #endif
610 
611     #ifdef __native_client__
612         // Fixed arguments for native client.
613         opts.context_api = WAFFLE_CONTEXT_OPENGL_ES2;
614         opts.platform = WAFFLE_PLATFORM_NACL;
615         opts.dl = WAFFLE_DL_OPENGL_ES2;
616         opts.context_profile = WAFFLE_NONE;
617         opts.context_version = -1;
618     #else
619     ok = parse_args(argc, argv, &opts);
620     if (!ok)
621         exit(EXIT_FAILURE);
622     #endif
623 
624     i = 0;
625     init_attrib_list[i++] = WAFFLE_PLATFORM;
626     init_attrib_list[i++] = opts.platform;
627     init_attrib_list[i++] = WAFFLE_NONE;
628 
629     ok = waffle_init(init_attrib_list);
630     if (!ok)
631         error_waffle();
632 
633     dpy = waffle_display_connect(NULL);
634     if (!dpy)
635         error_waffle();
636 
637     if (!waffle_display_supports_context_api(dpy, opts.context_api)) {
638         error_printf("Display does not support %s",
639                      waffle_enum_to_string(opts.context_api));
640     }
641 
642     glClear = get_gl_symbol(&opts, "glClear");
643     if (!glClear)
644         error_get_gl_symbol("glClear");
645 
646     glClearColor = get_gl_symbol(&opts, "glClearColor");
647     if (!glClearColor)
648         error_get_gl_symbol("glClearColor");
649 
650     glGetError = get_gl_symbol(&opts, "glGetError");
651     if (!glGetError)
652         error_get_gl_symbol("glGetError");
653 
654     glGetIntegerv = get_gl_symbol(&opts, "glGetIntegerv");
655     if (!glGetIntegerv)
656         error_get_gl_symbol("glGetIntegerv");
657 
658     glReadPixels = get_gl_symbol(&opts, "glReadPixels");
659     if (!glReadPixels)
660         error_get_gl_symbol("glReadPixels");
661 
662     glViewport = get_gl_symbol(&opts, "glViewport");
663     if (!glViewport)
664         error_get_gl_symbol("glViewport");
665 
666     i = 0;
667     config_attrib_list[i++] = WAFFLE_CONTEXT_API;
668     config_attrib_list[i++] = opts.context_api;
669 
670     if (opts.context_profile != -1) {
671         config_attrib_list[i++] = WAFFLE_CONTEXT_PROFILE;
672         config_attrib_list[i++] = opts.context_profile;
673     }
674 
675     if (opts.context_version != -1) {
676         config_attrib_list[i++] = WAFFLE_CONTEXT_MAJOR_VERSION;
677         config_attrib_list[i++] = opts.context_version / 10;
678         config_attrib_list[i++] = WAFFLE_CONTEXT_MINOR_VERSION;
679         config_attrib_list[i++] = opts.context_version % 10;
680     }
681 
682     if (opts.context_forward_compatible) {
683         config_attrib_list[i++] = WAFFLE_CONTEXT_FORWARD_COMPATIBLE;
684         config_attrib_list[i++] = true;
685     }
686 
687     if (opts.context_debug) {
688         config_attrib_list[i++] = WAFFLE_CONTEXT_DEBUG;
689         config_attrib_list[i++] = true;
690     }
691 
692     if (opts.context_robust) {
693         config_attrib_list[i++] = WAFFLE_CONTEXT_ROBUST_ACCESS,
694         config_attrib_list[i++] = true;
695     }
696 
697     config_attrib_list[i++] = WAFFLE_RED_SIZE;
698     config_attrib_list[i++] = 8;
699     config_attrib_list[i++] = WAFFLE_GREEN_SIZE;
700     config_attrib_list[i++] = 8;
701     config_attrib_list[i++] = WAFFLE_BLUE_SIZE;
702     config_attrib_list[i++] = 8;
703     config_attrib_list[i++] = WAFFLE_ALPHA_SIZE;
704     config_attrib_list[i++] = 8;
705     config_attrib_list[i++] = WAFFLE_DOUBLE_BUFFERED;
706     config_attrib_list[i++] = true;
707     config_attrib_list[i++] = 0;
708 
709     config = waffle_config_choose(dpy, config_attrib_list);
710     if (!config)
711         error_waffle();
712 
713     ctx = waffle_context_create(config, NULL);
714     if (!ctx)
715         error_waffle();
716 
717 
718     i = 0;
719     if (opts.fullscreen) {
720         window_attrib_list[i++] = WAFFLE_WINDOW_FULLSCREEN;
721         window_attrib_list[i++] = true;
722         window_attrib_list[i++] = 0;
723     } else {
724         window_attrib_list[i++] = WAFFLE_WINDOW_WIDTH;
725         window_attrib_list[i++] = window_width;
726         window_attrib_list[i++] = WAFFLE_WINDOW_HEIGHT;
727         window_attrib_list[i++] = window_height;
728         window_attrib_list[i++] = 0;
729     }
730 
731     window = waffle_window_create2(config, window_attrib_list);
732     if (!window)
733         error_waffle();
734 
735     ok = waffle_make_current(dpy, window, ctx);
736     if (!ok)
737         error_waffle();
738 
739     if (opts.context_forward_compatible || opts.context_debug) {
740         glGetIntegerv(GL_CONTEXT_FLAGS, &context_flags);
741     }
742 
743     if (glGetError())
744         error_printf("glGetIntegerv(GL_CONTEXT_FLAGS) failed");
745 
746     if (opts.context_forward_compatible
747         && !(context_flags & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT)) {
748         error_printf("context is not forward-compatible");
749     }
750 
751     if (opts.context_debug
752         && !(context_flags & GL_CONTEXT_FLAG_DEBUG_BIT)) {
753         error_printf("context is not a debug context");
754     }
755 
756     // Don't verify if the context is robust because that pollutes this simple
757     // example program with hairy GL logic. The method of verifying if
758     // a context is robust varies on the combination of context profile,
759     // context version, and supported extensions.
760 
761     ok = draw(window, opts.resize_window);
762     if (!ok)
763         error_waffle();
764 
765     ok = waffle_make_current(dpy, NULL, NULL);
766     if (!ok)
767         error_waffle();
768 
769     ok = waffle_window_destroy(window);
770     if (!ok)
771         error_waffle();
772 
773     ok = waffle_context_destroy(ctx);
774     if (!ok)
775         error_waffle();
776 
777     ok = waffle_config_destroy(config);
778     if (!ok)
779         error_waffle();
780 
781     ok = waffle_display_disconnect(dpy);
782     if (!ok)
783         error_waffle();
784 
785     ok = waffle_teardown();
786     if (!ok)
787         error_waffle();
788 
789     #ifdef __APPLE__
790         cocoa_finish();
791     #endif
792 
793     printf("gl_basic: run was successful\n");
794     return EXIT_SUCCESS;
795 }
796