Complete sample: A windowed vsync application using OpenGL ES

This code sample uses Screen Graphics Subsystem with OpenGL ES 1.X as the rendering API.

This sample application uses the components of a grey hourglass, a moving blue vertical bar, and a yellow background. It aims to demonstrate how to integrate the use of OpenGL ES 1.X and Screen in one application.


Figure 1. OpenGL Application
/**
 ** gles1-vsync
 ** Windowed vsync that uses OpenGL ES 1.X for rendering API.
 **
 ** Features:
 **  - configurable window size through the -size=(width)x(height) option
 **  - configurable window position through the -pos=(x),(y) option
 **  - adjustable swap interval through the -interval=(n) option;
 **    a swap interval of 0 lets the app run as fast as possible
 **    numbers of 1 or more limit the rate to the number of vsync periods
 **  - application responds to size changes from window manager or delegate
 **  - rendering is suspended if window is not visible
 **
 ** Copyright 2010, QNX Software Systems Ltd. All Rights Reserved
 **
 ** Permission to use, copy, modify, and distribute this software and its
 ** documentation for any purpose and without fee is hereby granted,
 ** provided that the above copyright notice appear in all copies and that
 ** both that copyright notice and this permission notice appear in
 ** supporting documentation.
 **
 ** This file is provided AS IS with no warranties of any kind.  The author
 ** shall have no liability with respect to the infringement of copyrights,
 ** trade secrets or any patents by this file or any part thereof.  In no
 ** event will the author be liable for any lost revenue or profits or
 ** other special, indirect and consequential damages.
 */

/**
 ** Include the header files for libraries we are using.
 **/

#include <ctype.h>         /* Header file for isdigit */
#include <stdio.h>         /* Header file for fprintf */
#include <stdlib.h>        /* Header file for EXIT_FAILURE, EXIT_SUCCESS, atoi */
#include <string.h>        /* Header file for strncmp */
#include <sys/keycodes.h>  /* Header file for KEYCODE_ESCAPE */
#include <screen/screen.h> /* Header file for the native screen API */
#include <EGL/egl.h>       /* Header file for EGL */
#include <GLES/gl.h>       /* Header file for OpenGL ES 1.X */

/**
 ** Here is the list of attributes that will be passed to EGL to get us
 ** a pixel format configuration. An EGL configuration is required by
 ** EGL when creating surfaces and rendering contexts. Since we will
 ** modify certain values in this list when certain command line arguments
 ** are provided, we will organize our attributes as an aggregate of named
 ** key/value pairs of EGLint's. This way we won't have to track the
 ** index locations various attributes in a one-dimensional array.
 **/
struct {
	EGLint surface_type;
	EGLint red_size;
	EGLint green_size;
	EGLint blue_size;
	EGLint alpha_size;
	EGLint samples;
	EGLint config_id;
} egl_conf_attr = {
	.surface_type = EGL_WINDOW_BIT,   /* Ask for displayable and pbuffer surfaces */
	.red_size = EGL_DONT_CARE,        /* Minimum number of red bits per pixel */
	.green_size = EGL_DONT_CARE,      /* Minimum number of green bits per pixel */
	.blue_size = EGL_DONT_CARE,       /* Minimum number of blue bits per pixel */
	.alpha_size = EGL_DONT_CARE,      /* Minimum number of alpha bits per pixel */
	.samples = EGL_DONT_CARE,         /* Minimum number of samples per pixel */
	.config_id = EGL_DONT_CARE,       /* used to get a specific EGL config */
};

/* This function will convert EGL error codes into more meaningful messages. */
static void
egl_perror(const char *msg) {
    static const char *errmsg[] = {
        "function succeeded",
        "EGL is not initialized, or could not be initialized, for the specified display",
        "cannot access a requested resource",
        "failed to allocate resources for the requested operation",
        "an unrecognized attribute or attribute value was passed in an attribute list",
        "an EGLConfig argument does not name a valid EGLConfig",
        "an EGLContext argument does not name a valid EGLContext",
        "the current surface of the calling thread is no longer valid",
        "an EGLDisplay argument does not name a valid EGLDisplay",
        "arguments are inconsistent",
        "an EGLNativePixmapType argument does not refer to a valid native pixmap",
        "an EGLNativeWindowType argument does not refer to a valid native window",
        "one or more argument values are invalid",
        "an EGLSurface argument does not name a valid surface configured for rendering",
        "a power management event has occurred",
    };

    fprintf(stderr, "%s: %s\n", msg, errmsg[eglGetError() - EGL_SUCCESS]);
}

/* This function will parse the configuration string and/or select an appropriate
 * configuration based on configuration attributes.*/
EGLConfig choose_config(EGLDisplay egl_disp, const char* str)
{
	EGLConfig egl_conf = (EGLConfig)0;  /* the resulting EGL config */
	EGLConfig *egl_configs;             /* describes the color and ancillary buffers */
	EGLint egl_num_configs;             /* number of configs that match our attributes */
	EGLint val;                         /* an EGL integer value */
	EGLBoolean rc;                      /* the return value of EGL functions */
	const char *tok;                    /* a pointer that will traverse the string */
	EGLint i;                           /* variable used to loop on matching configs */

	/**
	 ** We start by parsing the config string, which is a comma-separated list
	 ** of specifiers. We don't have to use strtok because the syntax is quite
	 ** simple. All we will need is strncmp and atoi. We start by skipping any
	 ** whitespace or separators. The str argument might be null, indicating
	 ** that we must find a config that matches the default criteria. In this
	 ** case, we must skip any processing of the str and make sure that there
	 ** is a default value for all configuration attributes (usually
	 ** EGL_DONT_CARE.)
	 **/

	if (str != NULL) {
		tok = str;
		while (*tok == ' ' || *tok == ',') {
			tok++;
		}

		/**
		 ** Loop as long as there are tokens to be processed.
		 **/

		while (*tok != '\0') {
			if (strncmp(tok, "rgba8888", strlen("rgba8888")) == 0) {
				egl_conf_attr.red_size = 8;
				egl_conf_attr.green_size = 8;
				egl_conf_attr.blue_size = 8;
				egl_conf_attr.alpha_size = 8;
				tok += strlen("rgba8888");
			} else if (strncmp(tok, "rgba5551", strlen("rgba5551")) == 0) {
				egl_conf_attr.red_size = 5;
				egl_conf_attr.green_size = 5;
				egl_conf_attr.blue_size = 5;
				egl_conf_attr.alpha_size = 1;
				tok += strlen("rgba5551");
			} else if (strncmp(tok, "rgba4444", strlen("rgba4444")) == 0) {
				egl_conf_attr.red_size = 4;
				egl_conf_attr.green_size = 4;
				egl_conf_attr.blue_size = 4;
				egl_conf_attr.alpha_size = 4;
				tok += strlen("rgba4444");
			} else if (strncmp(tok, "rgb565", strlen("rgb565")) == 0) {
				egl_conf_attr.red_size = 5;
				egl_conf_attr.green_size = 6;
				egl_conf_attr.blue_size = 5;
				egl_conf_attr.alpha_size = 0;
				tok += strlen("rgb565");
			} else if (isdigit(*tok)) {
				/**
				 ** An integer value could either be an EGL configuration id or
				 ** a multi-sampling count. An 'x' immediately after the integer
				 ** indicates that it is a multi-sampling specifier.
				 **/
				val = atoi(tok);
				while (isdigit(*(++tok)));
				if (*tok == 'x') {
					egl_conf_attr.samples = val;
					tok++;
				} else {
					/**
					 ** Using the EGL_CONFIG_ID attribute allows us to get a
					 ** configuration by its id without a typecast,
					 ** i.e. egl_conf = (EGLConfig)val. Note that the EGL spec says
					 ** that when the EGL_CONFIG_ID attribute is specified, all
					 ** other attributes are ignored, so we don't have to shorten
					 ** the list ourselves in this case.
					 **/
					egl_conf_attr.config_id = val;
				}
			} else {
				/**
				 ** Print a message on the console if we encounter something we
				 ** don't know. This way, the user will know that he might not get
				 ** the config he was expecting.
				 **/
				fprintf(stderr, "Invalid configuration specifier: ");
				while (*tok != ' ' && *tok != ',' && *tok != '\0') {
					fputc(*tok++, stderr);
				}
				fputc('\n', stderr);
			}

			/**
			 ** Skip any spaces and separators between this token and the next one.
			 **/

			while (*tok == ' ' || *tok == ',') {
				tok++;
			}
		}
	}

	/* Use eglGetConfigs to retrieve EGL configurations */

	rc = eglGetConfigs(egl_disp, NULL, 0, &egl_num_configs);
	if (rc != EGL_TRUE) {
		egl_perror("eglGetConfigs");
		return egl_conf;
	}
	if (egl_num_configs == 0) {
		fprintf(stderr, "eglGetConfigs: could not find a configuration\n");
		return egl_conf;
	}

	/**
	 ** Now the total number of configs has been stored in egl_num_configs.
	 ** We will malloc enough memory to hold all the configs. We need this to
	 ** traverse the configs to find a perfect match.
	 **/

	egl_configs = malloc(egl_num_configs * sizeof(*egl_configs));
	if (egl_configs == NULL) {
		fprintf(stderr, "could not allocate memory for %d EGL configs\n", egl_num_configs);
		return egl_conf;
	}

	/**
	 ** The second time we call eglGetConfigs, it is with an array of configs
	 ** big enough to store all the results. The list of EGL configs is
	 ** expected to be static, which means our list of matching configs should
	 ** be static as well. As long as the call succeeds, we don't have to check
	 ** for egl_num_configs again.
	 **/

	rc = eglGetConfigs(egl_disp, egl_configs,
		egl_num_configs, &egl_num_configs);
	if (rc != EGL_TRUE) {
		egl_perror("eglGetConfigs");
		free(egl_configs);
		return egl_conf;
	}

	/**
	 ** Now we just have to go through the list of configs and find one that
	 ** has all the attributes we are looking for. Some attributes like the
	 ** surface type or the renderable type are masks. All the others are just
	 ** integers that we need to match unless we don't care about the value.
	 **/

	for (i = 0; i < egl_num_configs; i++) {
		/**
		 ** Make sure the config id matches what we asked for.
		 **/

		if (egl_conf_attr.config_id != EGL_DONT_CARE) {
			eglGetConfigAttrib(egl_disp, egl_configs[i], EGL_CONFIG_ID, &val);
			if (val == egl_conf_attr.config_id) {
				egl_conf = egl_configs[i];
				break;
			} else {
				continue;
			}
		}

		/**
		 ** Make sure the surface type matches what we asked for.
		 **/

		eglGetConfigAttrib(egl_disp, egl_configs[i], EGL_SURFACE_TYPE, &val);
		if ((val & egl_conf_attr.surface_type) != egl_conf_attr.surface_type) {
			continue;
		}

		/**
		 ** Make sure the renderable type has the OpenGL ES bit.
		 **/

		eglGetConfigAttrib(egl_disp, egl_configs[i], EGL_RENDERABLE_TYPE, &val);
		if (!(val & EGL_OPENGL_ES_BIT)) {
			continue;
		}

		/**
		 ** Make sure the number of red bits matches what we asked for.
		 **/

		if (egl_conf_attr.red_size != EGL_DONT_CARE) {
			eglGetConfigAttrib(egl_disp, egl_configs[i], EGL_RED_SIZE, &val);
			if (val != egl_conf_attr.red_size) {
				continue;
			}
		}

		/**
		 ** Make sure the number of green bits matches what we asked for.
		 **/

		if (egl_conf_attr.green_size != EGL_DONT_CARE) {
			eglGetConfigAttrib(egl_disp, egl_configs[i], EGL_GREEN_SIZE, &val);
			if (val != egl_conf_attr.green_size) {
				continue;
			}
		}

		/**
		 ** Make sure the number of blue bits matches what we asked for.
		 **/

		if (egl_conf_attr.blue_size != EGL_DONT_CARE) {
			eglGetConfigAttrib(egl_disp, egl_configs[i], EGL_BLUE_SIZE, &val);
			if (val != egl_conf_attr.blue_size) {
				continue;
			}
		}

		/**
		 ** Make sure the number of alpha bits matches what we asked for.
		 **/

		if (egl_conf_attr.alpha_size != EGL_DONT_CARE) {
			eglGetConfigAttrib(egl_disp, egl_configs[i], EGL_ALPHA_SIZE, &val);
			if (val != egl_conf_attr.alpha_size) {
				continue;
			}
		}

		/**
		 ** Make sure the number of samples matches what we asked for.
		 **/

		if (egl_conf_attr.samples != EGL_DONT_CARE) {
			eglGetConfigAttrib(egl_disp, egl_configs[i], EGL_SAMPLES, &val);
			if (val != egl_conf_attr.samples) {
				continue;
			}
		}

		/**
		 ** This config has the pixel format we asked for, so we can keep it
		 ** and stop looking.
		 **/

		egl_conf = egl_configs[i];
		break;
	}

	/**
	 ** At this point, it is important to remember to free the array allocated
	 ** to hold all configs before returning.
	 **/

	free(egl_configs);

	if (egl_conf == (EGLConfig)0) {
		/**
		 ** The value of egl_conf will be that of a matching config if one was
		 ** found or (EGLConfig)0 if there were no exact matches. The calling
		 ** function can decide to do what it wants with the result. For example,
		 ** it could ask for a different config if no matches were found, or it can
		 ** simply fail and report the error.
		 **/

		fprintf(stderr, "eglChooseConfig: could not find a matching configuration\n");
	}

	return egl_conf;
}

int choose_format(EGLDisplay egl_disp, EGLConfig egl_conf)
{
	EGLint buffer_bit_depth, alpha_bit_depth;

	eglGetConfigAttrib(egl_disp, egl_conf, EGL_BUFFER_SIZE, &buffer_bit_depth);
	eglGetConfigAttrib(egl_disp, egl_conf, EGL_ALPHA_SIZE, &alpha_bit_depth);
	switch (buffer_bit_depth) {
		case 32: {
			return SCREEN_FORMAT_RGBA8888;
		}
		case 24: {
			return SCREEN_FORMAT_RGB888;
		}
		case 16: {
			switch (alpha_bit_depth) {
				case 4: {
					return SCREEN_FORMAT_RGBA4444;
				}
				case 1: {
					return SCREEN_FORMAT_RGBA5551;
				}
				default: {
					return SCREEN_FORMAT_RGB565;
				}
			}
			break;
		}
		default: {
			return SCREEN_FORMAT_BYTE;
		}
	}
}

/**
 ** The function is used to initialize the OpenGL ES viewport and
 ** projection matrix. It also computes the position of vertices that will be
 ** used to do rendering. We compute the position of those vertices based on
 ** the window's dimensions instead of using scale and translation matrices.
 ** Because it is unlikely that the size will change very often, this is more
 ** efficient than applying transformations every time a frame is rendered.
 **/

static void resize(GLshort *points, GLint width, GLint height, GLint barwidth)
{
	/**
	 ** The first four vertices take up 8 shorts. These vertices define a
	 ** rectangle that goes from (0,0) to (barwidth,height). A translation
	 ** matrix will be used to slide this rectangle across the viewport.
	 **/

	points[0] = 0;
	points[1] = height;
	points[2] = barwidth;
	points[3] = height;
	points[4] = 0;
	points[5] = 0;
	points[6] = barwidth;
	points[7] = 0;

	/**
	 ** The last six vertices take up 12 shorts. These vertices define two
	 ** triangles that share a vertex. Because the OpenGL ES coordinate system
	 ** starts at the bottom left instead of the top left corner, all y values
	 ** need to be inverted. In other words, the hourglass needs to be
	 ** translated up and down as the window height increases and decreases
	 ** respectively.
	 **/

	points[8] = 10;
	points[9] = height - 10;
	points[10] = 110;
	points[11] = height - 10;
	points[12] = 60;
	points[13] = height - 60;
	points[14] = 60;
	points[15] = height - 60;
	points[16] = 110;
	points[17] = height - 110;
	points[18] = 10;
	points[19] = height - 110;

	/* Update the viewport and projection matrix */
	glViewport(0, 0, width, height);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrthof(0.0f, (GLfloat)width, 0.0f, (GLfloat)height, -1.0f, 1.0f);
	glMatrixMode(GL_MODELVIEW);
}

/**
 ** This is the entry point of our native application. This is where we will
 ** parse command line arguments, create our window, handle events, and do our
 ** rendering.
 **/

int main(int argc, char **argv)
{
	/**
	 ** This is the size for an invisible exit button. We choose a value that's
	 ** big enough to be useable with touchscreens and pointer devices.
	 **/

	const int exit_area_size = 20;

	/**
	 ** Make the sliding vertical blue bar 32 pixels wide. Any number would be
	 ** fine here, as long as it is no larger than the width of the window.
	 **/

	const int barwidth = 32;

	/**
	 ** We will use the surface attributes to choose between single-buffered
	 ** and double-buffered rendering. Again, to avoid having to keep track of
	 ** indexes in a one-dimensional array of attribute/value pairs, we use
	 ** an aggregate of named attribute/value pairs of EGLint's.
	 **/

	struct {
		EGLint render_buffer[2];
		EGLint none;
	} egl_surf_attr = {
		.render_buffer = { EGL_RENDER_BUFFER, EGL_BACK_BUFFER }, /* Ask for double-buffering */
		.none = EGL_NONE                                         /* End of list */
	};

	screen_context_t screen_ctx;         /* connection to screen windowing system */
	screen_window_t screen_win;          /* native handle for our window */
	screen_event_t screen_ev;            /* handle used to pop events from our queue */
	EGLDisplay egl_disp;                 /* abstract display on which graphics are drawn */
	EGLConfig egl_conf;                  /* describes the color and ancillary buffers */
	EGLSurface egl_surf;                 /* refers to our window's rendering surface */
	EGLContext egl_ctx;                  /* a handle to a rendering context */
	int usage = SCREEN_USAGE_OPENGL_ES1; /* we will use OpenGL ES 1.X to do our rendering */
	int size[2] = { -1, -1 };            /* width and height of our window */
	int pos[2] = { 0, 0 };               /* x,y position of our window */
	int nbuffers = 2;                    /* number of buffers backing the window */
	int format;                          /* native visual type / screen format */
	int val;                             /* a generic variable used to set/get window properties */
	EGLint interval = 1;                 /* EGL swap interval */
	int verbose = EGL_FALSE;             /* EGL_TRUE if the verbose option was set */
	int vis = 1;                         /* boolean that indicates if our window is visible */
	int pause = 0;                       /* EGL_TRUE if rendering is frozen */
	const char *conf_str = NULL;         /* configuration string */
	const char *tok;                     /* used to process command-line arguments */
	int rval = EXIT_FAILURE;             /* application exits with value stored here */
	int rc;                              /* return value from functions */
	int i;                               /* loop/frame counter */
	GLshort points[20];                  /* we'll store the vertices in this array */

	/**
	 ** We start by processing the command line arguments. The first argument
	 ** is skipped because it contains the name of the program. Arguments
	 ** follow the syntax -(option)=(value).
	 **/

	for (i = 1; i < argc; i++) {
		if (strncmp(argv[i], "-config=", strlen("-config=")) == 0) {
			/**
			 ** The syntax of the EGL configuration option is
			 ** -config=[option][,[option]...]. All we need is to do is pass
			 ** the string after the '=' to choose_config, which will parse the
			 ** options and find the right EGL config.
			 **/

			conf_str = argv[i] + strlen("-config=");
		} else if (strncmp(argv[i], "-size=", strlen("-size=")) == 0) {
			/**
			 ** The syntax of the size option is -size=(width)x(height).
			 ** Because atoi stops processing at the first non-digit character,
			 ** we can simply call atoi on the string after the '=' to get the
			 ** width, and call atoi again on the string after the 'x' to get
			 ** the height.
			 **/

			tok = argv[i] + strlen("-size=");
			size[0] = atoi(tok);
			while (*tok >= '0' && *tok <= '9') {
				tok++;
			}
			size[1] = atoi(tok+1);
		} else if (strncmp(argv[i], "-pos=", strlen("-pos=")) == 0) {
			/**
			 ** The syntax of the pos option is -pos=(x),(y).
			 ** Because atoi stops processing at the first non-digit character,
			 ** we can simply call atoi on the string after the '=' to get the
			 ** x offset, and call atoi again on the string after the ',' to
			 ** get the y offset.
			 **/

			tok = argv[i] + strlen("-pos=");
			pos[0] = atoi(tok);
			while (*tok >= '0' && *tok <= '9') {
				tok++;
			}
			pos[1] = atoi(tok+1);
		} else if (strncmp(argv[i], "-interval=", strlen("-interval=")) == 0) {
			/**
			 ** The syntax of the interval option is -interval=(number). All
			 ** we need is to convert the number that starts after the '='.
			 **/

			interval = atoi(argv[i] + strlen("-interval="));
		} else if (strcmp(argv[i], "-single-buffer") == 0) {
			/**
			 ** The -single-buffer option on the command line will cause the
			 ** rendering to be done to a single on-screen buffer. There are
			 ** typically artifacts associated with single-buffered rendering
			 ** caused by rendering to a visible surface.
			 **/

			nbuffers = 1;
		} else if (strcmp(argv[i], "-double-buffer") == 0) {
			/**
			 ** The -double-buffer option on the command line will cause the
			 ** rendering to be on alternating back buffers. This eliminates
			 ** artifacts associated with single-buffered rendering.
			 **/

			nbuffers = 2;
		} else if (strncmp(argv[i], "-verbose", strlen("-verbose")) == 0) {
			/**
			 ** The verbose option has no special syntax. It just has to be
			 ** present on the command line to cause the verbose messages to
			 ** be printed.
			 **/

			verbose = EGL_TRUE;
		} else {
			/**
			 ** Make sure we say something instead of silently ignoring a
			 ** command line option.
			 **/

			fprintf(stderr, "Invalid command-line option: %s\n", argv[i]);
		}
	}

	/**
	 ** Before we can do any kind of rendering, we must establish a connection
	 ** to a display. We don't have any particular preference, so we'll just
	 ** ask for the default display, unless a display id is specified on the
	 ** command line.
	 **/

	egl_disp = eglGetDisplay(EGL_DEFAULT_DISPLAY);
	if (egl_disp == EGL_NO_DISPLAY) {
		egl_perror("eglGetDisplay");
		goto fail1;
	}

	/**
	 ** Now we initialize EGL on the display. We can't do anything with this
	 ** EGL display until EGL has been initialized. OpenGL ES 1.X is supported
	 ** by all versions of EGL, so it is not necessary to check for the major
	 ** and minor version numbers.
	 **/

	rc = eglInitialize(egl_disp, NULL, NULL);
	if (rc != EGL_TRUE) {
		egl_perror("eglInitialize");
		goto fail2;
	}

	/**
	 ** Choosing an EGL configuration can be a tedious process. Here, we call
	 ** choose_config which will do all the necessary work. In this case, this
	 ** includes parsing a configuration string and/or select an appropriate
	 ** configuration based on some configuration attributes.
	 **/

	egl_conf = choose_config(egl_disp, conf_str);
	if (egl_conf == (EGLConfig)0) {
		goto fail2;
	}

	/**
	 ** Now we need to create an OpenGL ES rendering context. The context will
	 ** keep track of the OpenGL ES 1.X state among other things. We don't have
	 ** to specify the current rendering API with an eglBindApi call because
	 ** OpenGL ES is the default rendering API. The third argument to
	 ** eglCreateContext is another EGL rendering context that we wish to share
	 ** data with. We pass EGL_NO_CONTEXT to indicate that we won't need any
	 ** of the textures or vertex buffer objects created in another EGL render
	 ** context. The last argument is an attribute list that can be used to
	 ** specify an API version number. We would use it to override the
	 ** EGL CONTEXT CLIENT VERSION default value of 1 to 2 if we were writing
	 ** an OpenGL ES 2.X application.
	 **/

	egl_ctx = eglCreateContext(egl_disp, egl_conf, EGL_NO_CONTEXT, NULL);
	if (egl_ctx == EGL_NO_CONTEXT) {
		egl_perror("eglCreateContext");
		goto fail2;
	}

	/**
	 ** If the application was started with the -verbose command line argument,
	 ** we will print a few information strings about the EGL configuration we
	 ** end-up using. This might be useful if the -config option was not
	 ** provided, or if the user doesn't know the particular details of a given
	 ** pixel format. We will get the information by using the
	 ** eglGetConfigAttrib with several interesting attribute names.
	 **/

	if (verbose) {
		printf("EGL_VENDOR = %s\n", eglQueryString(egl_disp, EGL_VENDOR));
		printf("EGL_VERSION = %s\n", eglQueryString(egl_disp, EGL_VERSION));
		printf("EGL_CLIENT_APIS = %s\n", eglQueryString(egl_disp, EGL_CLIENT_APIS));
		printf("EGL_EXTENSIONS = %s\n\n", eglQueryString(egl_disp, EGL_EXTENSIONS));

		i = -1;
		eglGetConfigAttrib(egl_disp, egl_conf, EGL_CONFIG_ID, &i);
		printf("EGL_CONFIG_ID = %d\n", i);

		i = 0;
		eglGetConfigAttrib(egl_disp, egl_conf, EGL_RED_SIZE, &i);
		printf("EGL_RED_SIZE = %d\n", i);

		i = 0;
		eglGetConfigAttrib(egl_disp, egl_conf, EGL_GREEN_SIZE, &i);
		printf("EGL_GREEN_SIZE = %d\n", i);

		i = 0;
		eglGetConfigAttrib(egl_disp, egl_conf, EGL_BLUE_SIZE, &i);
		printf("EGL_BLUE_SIZE = %d\n", i);

		i = 0;
		eglGetConfigAttrib(egl_disp, egl_conf, EGL_ALPHA_SIZE, &i);
		printf("EGL_ALPHA_SIZE = %d\n", i);

		i = 0;
		eglGetConfigAttrib(egl_disp, egl_conf, EGL_DEPTH_SIZE, &i);
		printf("EGL_DEPTH_SIZE = %d\n", i);

		i = 0;
		eglGetConfigAttrib(egl_disp, egl_conf, EGL_LEVEL, &i);
		printf("EGL_LEVEL = %d\n", i);

		i = 0;
		eglGetConfigAttrib(egl_disp, egl_conf, EGL_NATIVE_RENDERABLE, &i);
		printf("EGL_NATIVE_RENDERABLE = %s\n", i ? "EGL_TRUE" : "EGL_FALSE");

		i = 0;
		eglGetConfigAttrib(egl_disp, egl_conf, EGL_NATIVE_VISUAL_TYPE, &i);
		printf("EGL_NATIVE_VISUAL_TYPE = %d\n", i);

		i = 0;
		eglGetConfigAttrib(egl_disp, egl_conf, EGL_RENDERABLE_TYPE, &i);
		printf("EGL_RENDERABLE_TYPE = 0x%04x\n", i);

		i = 0;
		eglGetConfigAttrib(egl_disp, egl_conf, EGL_SURFACE_TYPE, &i);
		printf("EGL_SURFACE_TYPE = 0x%04x\n", i);

		i = 0;
		eglGetConfigAttrib(egl_disp, egl_conf, EGL_TRANSPARENT_TYPE, &i);
		if (i == EGL_TRANSPARENT_RGB) {
			printf("EGL_TRANSPARENT_TYPE = EGL_TRANSPARENT_RGB\n");

			i = 0;
			eglGetConfigAttrib(egl_disp, egl_conf, EGL_TRANSPARENT_RED_VALUE, &i);
			printf("EGL_TRANSPARENT_RED = 0x%02x\n", i);

			i = 0;
			eglGetConfigAttrib(egl_disp, egl_conf, EGL_TRANSPARENT_GREEN_VALUE, &i);
			printf("EGL_TRANSPARENT_GREEN = 0x%02x\n", i);

			i = 0;
			eglGetConfigAttrib(egl_disp, egl_conf, EGL_TRANSPARENT_BLUE_VALUE, &i);
			printf("EGL_TRANSPARENT_BLUE = 0x%02x\n\n", i);
		} else {
			printf("EGL_TRANSPARENT_TYPE = EGL_NONE\n\n");
		}
	}

	rc = screen_create_context(&screen_ctx, 0);
	if (rc) {
		perror("screen_context_create");
		goto fail3;
	}

	rc = screen_create_window(&screen_win, screen_ctx);
	if (rc) {
		perror("screen_create_window");
		goto fail4;
	}

	format = choose_format(egl_disp, egl_conf);
	rc = screen_set_window_property_iv(screen_win, SCREEN_PROPERTY_FORMAT, &format);
	if (rc) {
		perror("screen_set_window_property_iv(SCREEN_PROPERTY_FORMAT)");
		goto fail5;
	}

	rc = screen_set_window_property_iv(screen_win, SCREEN_PROPERTY_USAGE, &usage);
	if (rc) {
		perror("screen_set_window_property_iv(SCREEN_PROPERTY_USAGE)");
		goto fail5;
	}

	rc = screen_set_window_property_iv(screen_win, SCREEN_PROPERTY_SWAP_INTERVAL, &interval);
	if (rc) {
		perror("screen_set_window_property_iv(SCREEN_PROPERTY_SWAP_INTERVAL)");
		goto fail5;
	}

	if (size[0] > 0 && size[1] > 0) {
		rc = screen_set_window_property_iv(screen_win, SCREEN_PROPERTY_SIZE, size);
		if (rc) {
			perror("screen_set_window_property_iv(SCREEN_PROPERTY_SIZE)");
			goto fail5;
		}
	} else {
		rc = screen_get_window_property_iv(screen_win, SCREEN_PROPERTY_SIZE, size);
		if (rc) {
			perror("screen_get_window_property_iv(SCREEN_PROPERTY_SIZE)");
			goto fail5;
		}
	}

	if (pos[0] != 0 || pos[1] != 0) {
		rc = screen_set_window_property_iv(screen_win, SCREEN_PROPERTY_POSITION, pos);
		if (rc) {
			perror("screen_set_window_property_iv(SCREEN_PROPERTY_POSITION)");
			goto fail5;
		}
	}

	rc = screen_create_window_buffers(screen_win, nbuffers);
	if (rc) {
		perror("screen_create_window_buffers");
		goto fail5;
	}

	rc = screen_create_event(&screen_ev);
	if (rc) {
		perror("screen_create_event");
		goto fail5;
	}

	/**
	 ** Now that we have created a native platform window we can use it to
	 ** create an EGL on-screen rendering surface. We'll be able to use this
	 ** surface as the target of our OpenGL ES 1.X rendering. We'll use the
	 ** same EGL display and config to create the EGL surface as the ones we
	 ** used to create our native window. The EGL config just needs to be
	 ** compatible with the one used to create the window.
	 **/

	egl_surf = eglCreateWindowSurface(egl_disp, egl_conf,
		screen_win, (EGLint*)&egl_surf_attr);
	if (egl_surf == EGL_NO_SURFACE) {
		egl_perror("eglCreateWindowSurface");
		goto fail6;
	}

	/**
	 ** eglMakeCurrent binds ctx to the current rendering thread and to a draw
	 ** and a read surface. In our case, we want to draw to our EGL surface and
	 ** don't really care about where we read from. EGL does not allow
	 ** specifying EGL_NO_SURFACE for the read surface only, so we will simply
	 ** use egl_surf for both reading and writing. Once eglMakeCurrent
	 ** completes successfully, all OpenGL ES 1.X calls will be executed on
	 ** the context and surface provided as arguments.
	 **/

	rc = eglMakeCurrent(egl_disp, egl_surf, egl_surf, egl_ctx);
	if (rc != EGL_TRUE) {
		egl_perror("eglMakeCurrent");
		goto fail7;
	}

	/**
	 ** The eglSwapInterval function specifies the minimum number of video
	 ** frame periods per buffer swap for the window associated with the
	 ** current context. If the interval is 0, the application renders as
	 ** fast as it can. Interval values of 1 or more limit the rendering
	 ** to fractions of the display's refresh rate, i.e. 60, 30, 20, 15, etc
	 ** fps in the case of a display with a refresh rate of 60 Hz.
	 **/

	rc = eglSwapInterval(egl_disp, interval);
	if (rc != EGL_TRUE) {
		egl_perror("eglSwapInterval");
		goto fail8;
	}

	/**
	 ** At this point, we can start doing OpenGL ES stuff. Our application is
	 ** quite simple, so we'll just do the initialization here followed by
	 ** some basic rendering in our application loop.
	 **/

	if (verbose) {
		printf("GL_VENDOR = %s\n", (char *)glGetString(GL_VENDOR));
		printf("GL_RENDERER = %s\n", (char *)glGetString(GL_RENDERER));
		printf("GL_VERSION = %s\n", (char *)glGetString(GL_VERSION));
		printf("GL_EXTENSIONS = %s\n", (char *)glGetString(GL_EXTENSIONS));
	}

	/* The resize function initializes the viewport and geometry */
	resize(points, size[0], size[1], barwidth);

	/* Set the clear color to yellow */
	glClearColor(1.0f, 1.0f, 0.0f, 1.0f);

	/* We will use one vertex array for all of our rendering */
	glVertexPointer(2, GL_SHORT, 0, (const GLvoid *)points);
	glEnableClientState(GL_VERTEX_ARRAY);

	/**
	 ** This is our main application loop. It keeps on running unless an error
	 ** occurs or we receive a close event from the windowing system. The
	 ** application loop consists of two parts. The first part processes any
	 ** events that have been put in our queue. The second part does the
	 ** rendering. When the window is visible, we don't wait if the event queue
	 ** is empty and move on to the rendering part immediately. When the window
	 ** is not visible we skip the rendering part.
	 **/

	i = 0;
	while (1) {
		/**
		 ** We start the loop by processing any events that might be in our
		 ** queue. The only event that is of interest to us are the resize
		 ** and close events. The timeout variable is set to 0 (no wait) or
		 ** forever depending if the window is visible or invisible.
		 **/

		while (!screen_get_event(screen_ctx, screen_ev, vis ? 0 : ~0)) {
			rc = screen_get_event_property_iv(screen_ev, SCREEN_PROPERTY_TYPE, &val);
			if (rc || val == SCREEN_EVENT_NONE) {
				break;
			}
			switch (val) {
				case SCREEN_EVENT_CLOSE:
					/**
					 ** All we have to do when we receive the close event is
					 ** exit the application loop. Because we have a loop
					 ** within a loop, a simple break won't work. We'll just
					 ** use a goto to take us out of here.
					 **/
					goto end;
				case SCREEN_EVENT_PROPERTY:
					/**
					 ** We are interested in visibility changes so we can pause
					 ** or unpause the rendering.
					 **/

					screen_get_event_property_iv(screen_ev, SCREEN_PROPERTY_NAME, &val);
					switch (val) {
						case SCREEN_PROPERTY_VISIBLE:
							/**
							 ** The new visibility status is not included in the
							 ** event, so we must get it ourselves.
							 **/
							screen_get_window_property_iv(screen_win, SCREEN_PROPERTY_VISIBLE, &vis);
							break;
					}
					break;
				case SCREEN_EVENT_POINTER:
					/**
					 ** To provide a way of gracefully terminating our application,
					 ** we will exit if there is a pointer select event in the upper
					 ** right corner of our window. This should happen if the mouse's
					 ** left button is clicked or if a touch screen display is pressed.
					 ** The event will come as a screen pointer event, with an (x,y)
					 ** coordinate relative to the window's upper left corner and a
					 ** select value. We have to verify ourselves that the coordinates
					 ** of the pointer are in the upper right hand area.
					 **/
					screen_get_event_property_iv(screen_ev, SCREEN_PROPERTY_BUTTONS, &val);
					if (val) {
						screen_get_event_property_iv(screen_ev, SCREEN_PROPERTY_POSITION, pos);
						if (pos[0] >= size[0] - exit_area_size &&
							pos[1] < exit_area_size) {
							goto end;
						}
					}
					break;
				case SCREEN_EVENT_KEYBOARD:
					screen_get_event_property_iv(screen_ev, SCREEN_PROPERTY_KEY_FLAGS, &val);
					if (val & KEY_DOWN) {
						screen_get_event_property_iv(screen_ev, SCREEN_PROPERTY_KEY_SYM, &val);
						switch (val) {
							case KEYCODE_ESCAPE:
								goto end;
							case KEYCODE_F:
								pause = !pause;
								break;
							default:
								break;
						}
					}
					break;
			}
		}

		/**
		 ** The second part of the application loop is the rendering. We want
		 ** to skip the rendering part if our window is not visible. This will
		 ** leave the CPU and GPU to other applications and make the system a
		 ** little bit more responsive while we are invisible.
		 **/

		if (vis && !pause) {
			/* Start by clearing the window */
			glClear(GL_COLOR_BUFFER_BIT);

			/**
			 ** We could use glLoadIdentity or glPushMatrix here. If we used
			 ** glLoadIdentity, we would have to call glLoadIdentity again when
			 ** we draw the hourglass. We assume that glPushMatrix,
			 ** glTranslatef, glPopMatrix is more efficient than
			 ** glLoadIdentity, glTranslatef, and glLoadIdentity.
			 **/
			glPushMatrix();

			/* Use translation to animate the vertical bar */
			glTranslatef((GLfloat)(i++ % (size[0] - barwidth)), 0.0f, 0.0f);

			/* We want the animated vertical bar to be drawn in blue */
			glColor4f(0.0f, 0.0f, 1.0f, 1.0f);

			/* Render the vertical bar */
			glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

			/* We don't want the hourglass to be translated */
			glPopMatrix();

			/* We want the hourglass to be drawn in gray */
			glColor4f(0.62745f, 0.62745f, 0.62745f, 1.0f);

			/* Render the hourglass */
			glDrawArrays(GL_TRIANGLES, 4, 6);

			/**
			 ** Posting of the new frame requires a call to eglSwapBuffers.
			 ** For now, this is true even when using single buffering. If an
			 ** event has occured that invalidates the surface we are currently
			 ** using, eglSwapBuffers will return EGL_FALSE and set the error
			 ** code to EGL_BAD_NATIVE_WINDOW. At this point, we could destroy
			 ** our EGL surface, close our OpenKODE window, and start again.
			 ** This application will simply exit when any errors occur.
			 **/

			rc = eglSwapBuffers(egl_disp, egl_surf);
			if (rc != EGL_TRUE) {
				egl_perror("eglSwapBuffers");
				break;
			}
		}
	}

	/**
	 ** If we made it here, it means everything ran successfully. We'll thus
	 ** change the exit return value to indicate success.
	 **/

end:
	rval = EXIT_SUCCESS;

	/**
	 ** Before we can destroy any of the resources we have created for this
	 ** application, we must deactivate the rendering context that we were
	 ** using and release the surfaces we were drawing to and reading from.
	 ** This is done by calling eglMakeCurrent with EGL_NO_SURFACE and
	 ** EGL_NO_CONTEXT for arguments. Note that the call to eglMakeCurrent
	 ** will generate an error unless all arguments are EGL_NO_SURFACE and
	 ** EGL_NO_CONTEXT, or all arguments are valid EGLSurface and EGLContext
	 ** objects.
	 **/

fail8:
	eglMakeCurrent(egl_disp, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);

	/**
	 ** Destroy the EGL surface if one was created.
	 **/

fail7:
	eglDestroySurface(egl_disp, egl_surf);

fail6:
	screen_destroy_event(screen_ev);
fail5:
	screen_destroy_window(screen_win);
fail4:
	screen_destroy_context(screen_ctx);

	/**
	 ** Destroy the EGL render context if one was created.
	 **/

fail3:
	eglDestroyContext(egl_disp, egl_ctx);

	/**
	 ** Terminate our connection to the EGL display. Since we are just about to
	 ** exit, we can also release any resources that were allocated for this
	 ** thread. This is done by calling eglReleaseThread. On most systems,
	 ** those resources would probably be released automatically when the
	 ** program exists.
	 **/

fail2:
	eglTerminate(egl_disp);
	eglReleaseThread();

	/**
	 ** Return with EXIT_SUCCESS or EXIT_FAILURE.
	 **/

fail1:
	return rval;
}