This code sample uses Screen and Windowing with OpenGL ES 1.X as the rendering API.
/** ** 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; }