initial commit
authorRob Clark <rob@ti.com>
Tue, 4 Sep 2012 00:28:10 +0000 (19:28 -0500)
committerRob Clark <rob@ti.com>
Tue, 4 Sep 2012 00:36:50 +0000 (19:36 -0500)
.gitignore [new file with mode: 0644]
Makefile.am [new file with mode: 0644]
autogen.sh [new file with mode: 0755]
configure.ac [new file with mode: 0644]
esTransform.c [new file with mode: 0644]
esUtil.h [new file with mode: 0644]
kmscube.c [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..52edbca
--- /dev/null
@@ -0,0 +1,30 @@
+aclocal.m4
+autom4te.cache
+Makefile.in
+Makefile
+.deps
+.libs
+*.o
+*.lo
+*.la
+libtool
+*.pc
+config.log
+config.status
+config.guess
+config.h
+config.h.in
+config.sub
+config
+configure
+install-sh
+ltmain.sh
+missing
+stamp-h1
+depcomp
+.cproject
+.project
+.settings
+kmscube
+tags
+TAGS
diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..35e498a
--- /dev/null
@@ -0,0 +1,46 @@
+#
+#  Copyright (c) 2012 Rob Clark <rob@ti.com>
+#
+#  Permission is hereby granted, free of charge, to any person obtaining a
+#  copy of this software and associated documentation files (the "Software"),
+#  to deal in the Software without restriction, including without limitation
+#  the rights to use, copy, modify, merge, publish, distribute, sublicense,
+#  and/or sell copies of the Software, and to permit persons to whom the
+#  Software is furnished to do so, subject to the following conditions:
+#
+#  The above copyright notice and this permission notice (including the next
+#  paragraph) shall be included in all copies or substantial portions of the
+#  Software.
+#
+#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+#  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+#  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+#  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+#  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+#  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+#  SOFTWARE.
+#
+
+ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
+
+bin_PROGRAMS = kmscube
+
+kmscube_LDFLAGS = \
+       -no-undefined
+
+kmscube_LDADD = \
+       $(DRM_LIBS) \
+       $(GBM_LIBS) \
+       $(EGL_LIBS) \
+       $(GLES2_LIBS)
+
+kmscube_CFLAGS = \
+       -O0 -g -lm \
+       $(DRM_CFLAGS) \
+       $(GBM_CFLAGS) \
+       $(EGL_CFLAGS) \
+       $(GLES2_CFLAGS)
+
+kmscube_SOURCES = \
+       kmscube.c \
+       esTransform.c
diff --git a/autogen.sh b/autogen.sh
new file mode 100755 (executable)
index 0000000..33a638e
--- /dev/null
@@ -0,0 +1,13 @@
+#! /bin/sh
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+
+mkdir -p m4
+autoreconf -v --install || exit 1
+cd $ORIGDIR || exit $?
+
+$srcdir/configure --enable-maintainer-mode "$@"
diff --git a/configure.ac b/configure.ac
new file mode 100644 (file)
index 0000000..4441d2d
--- /dev/null
@@ -0,0 +1,87 @@
+#
+#  Copyright (c) 2012 Rob Clark <rob@ti.com>
+#
+#  Permission is hereby granted, free of charge, to any person obtaining a
+#  copy of this software and associated documentation files (the "Software"),
+#  to deal in the Software without restriction, including without limitation
+#  the rights to use, copy, modify, merge, publish, distribute, sublicense,
+#  and/or sell copies of the Software, and to permit persons to whom the
+#  Software is furnished to do so, subject to the following conditions:
+#
+#  The above copyright notice and this permission notice (including the next
+#  paragraph) shall be included in all copies or substantial portions of the
+#  Software.
+#
+#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+#  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+#  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+#  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+#  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+#  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+#  SOFTWARE.
+#
+
+# Initialize Autoconf
+AC_PREREQ([2.60])
+AC_INIT([kmscube], [0.0.1], [https://github.com/robclark/kmscube/], [kmscube])
+AC_CONFIG_SRCDIR([Makefile.am])
+AC_CONFIG_HEADERS([config.h])
+AC_CONFIG_AUX_DIR([build-aux])
+AC_CONFIG_MACRO_DIR([m4])
+
+# Initialize Automake
+AM_INIT_AUTOMAKE([foreign dist-bzip2])
+AM_MAINTAINER_MODE
+
+# Enable quiet compiles on automake 1.11.
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
+# Initialize libtool
+AC_PROG_LIBTOOL
+
+# Obtain compiler/linker options for depedencies
+PKG_CHECK_MODULES(DRM, libdrm)
+PKG_CHECK_MODULES(GBM, gbm)
+PKG_CHECK_MODULES(EGL, egl)
+PKG_CHECK_MODULES(GLES2, glesv2)
+
+dnl ===========================================================================
+dnl check compiler flags
+AC_DEFUN([LIBDRM_CC_TRY_FLAG], [
+       AC_MSG_CHECKING([whether $CC supports $1])
+
+       libdrm_save_CFLAGS="$CFLAGS"
+       CFLAGS="$CFLAGS $1"
+
+       AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]],[[]])],
+                                               [libdrm_cc_flag=yes],
+                                               [libdrm_cc_flag=no])
+       CFLAGS="$libdrm_save_CFLAGS"
+       
+       if test "x$libdrm_cc_flag" = "xyes"; then
+               ifelse([$2], , :, [$2])
+       else
+               ifelse([$3], , :, [$3])
+       fi
+       AC_MSG_RESULT([$libdrm_cc_flag])
+])
+
+MAYBE_WARN="-Wall -Wextra -Wno-packed-bitfield-compat \
+-Wsign-compare -Werror-implicit-function-declaration \
+-Wpointer-arith -Wwrite-strings -Wstrict-prototypes \
+-Wnested-externs \
+-Wpacked -Wswitch-enum -Wmissing-format-attribute \
+-Wstrict-aliasing=2 -Winit-self -Wunsafe-loop-optimizations \
+-Wdeclaration-after-statement -Wold-style-definition \
+-Wno-missing-field-initializers -Wno-unused-parameter \
+-Wno-attributes -Wno-long-long -Winline"
+
+AC_MSG_CHECKING([which warning flags were supported])
+for W in $MAYBE_WARN; do
+       LIBDRM_CC_TRY_FLAG([$W], [WARN_CFLAGS="$WARN_CFLAGS $W"])
+done
+
+AC_SUBST(WARN_CFLAGS)
+
+AC_CONFIG_FILES([Makefile])
+AC_OUTPUT
diff --git a/esTransform.c b/esTransform.c
new file mode 100644 (file)
index 0000000..5fff0c9
--- /dev/null
@@ -0,0 +1,235 @@
+//
+// Book:      OpenGL(R) ES 2.0 Programming Guide
+// Authors:   Aaftab Munshi, Dan Ginsburg, Dave Shreiner
+// ISBN-10:   0321502795
+// ISBN-13:   9780321502797
+// Publisher: Addison-Wesley Professional
+// URLs:      http://safari.informit.com/9780321563835
+//            http://www.opengles-book.com
+//
+
+/*
+ * (c) 2009 Aaftab Munshi, Dan Ginsburg, Dave Shreiner
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+// ESUtil.c
+//
+//    A utility library for OpenGL ES.  This library provides a
+//    basic common framework for the example applications in the
+//    OpenGL ES 2.0 Programming Guide.
+//
+
+///
+//  Includes
+//
+#include "esUtil.h"
+#include <math.h>
+#include <string.h>
+
+#define PI 3.1415926535897932384626433832795f
+
+void ESUTIL_API
+esScale(ESMatrix *result, GLfloat sx, GLfloat sy, GLfloat sz)
+{
+    result->m[0][0] *= sx;
+    result->m[0][1] *= sx;
+    result->m[0][2] *= sx;
+    result->m[0][3] *= sx;
+
+    result->m[1][0] *= sy;
+    result->m[1][1] *= sy;
+    result->m[1][2] *= sy;
+    result->m[1][3] *= sy;
+
+    result->m[2][0] *= sz;
+    result->m[2][1] *= sz;
+    result->m[2][2] *= sz;
+    result->m[2][3] *= sz;
+}
+
+void ESUTIL_API
+esTranslate(ESMatrix *result, GLfloat tx, GLfloat ty, GLfloat tz)
+{
+    result->m[3][0] += (result->m[0][0] * tx + result->m[1][0] * ty + result->m[2][0] * tz);
+    result->m[3][1] += (result->m[0][1] * tx + result->m[1][1] * ty + result->m[2][1] * tz);
+    result->m[3][2] += (result->m[0][2] * tx + result->m[1][2] * ty + result->m[2][2] * tz);
+    result->m[3][3] += (result->m[0][3] * tx + result->m[1][3] * ty + result->m[2][3] * tz);
+}
+
+void ESUTIL_API
+esRotate(ESMatrix *result, GLfloat angle, GLfloat x, GLfloat y, GLfloat z)
+{
+   GLfloat sinAngle, cosAngle;
+   GLfloat mag = sqrtf(x * x + y * y + z * z);
+      
+   sinAngle = sinf ( angle * PI / 180.0f );
+   cosAngle = cosf ( angle * PI / 180.0f );
+   if ( mag > 0.0f )
+   {
+      GLfloat xx, yy, zz, xy, yz, zx, xs, ys, zs;
+      GLfloat oneMinusCos;
+      ESMatrix rotMat;
+   
+      x /= mag;
+      y /= mag;
+      z /= mag;
+
+      xx = x * x;
+      yy = y * y;
+      zz = z * z;
+      xy = x * y;
+      yz = y * z;
+      zx = z * x;
+      xs = x * sinAngle;
+      ys = y * sinAngle;
+      zs = z * sinAngle;
+      oneMinusCos = 1.0f - cosAngle;
+
+      rotMat.m[0][0] = (oneMinusCos * xx) + cosAngle;
+      rotMat.m[0][1] = (oneMinusCos * xy) - zs;
+      rotMat.m[0][2] = (oneMinusCos * zx) + ys;
+      rotMat.m[0][3] = 0.0F; 
+
+      rotMat.m[1][0] = (oneMinusCos * xy) + zs;
+      rotMat.m[1][1] = (oneMinusCos * yy) + cosAngle;
+      rotMat.m[1][2] = (oneMinusCos * yz) - xs;
+      rotMat.m[1][3] = 0.0F;
+
+      rotMat.m[2][0] = (oneMinusCos * zx) - ys;
+      rotMat.m[2][1] = (oneMinusCos * yz) + xs;
+      rotMat.m[2][2] = (oneMinusCos * zz) + cosAngle;
+      rotMat.m[2][3] = 0.0F; 
+
+      rotMat.m[3][0] = 0.0F;
+      rotMat.m[3][1] = 0.0F;
+      rotMat.m[3][2] = 0.0F;
+      rotMat.m[3][3] = 1.0F;
+
+      esMatrixMultiply( result, &rotMat, result );
+   }
+}
+
+void ESUTIL_API
+esFrustum(ESMatrix *result, float left, float right, float bottom, float top, float nearZ, float farZ)
+{
+    float       deltaX = right - left;
+    float       deltaY = top - bottom;
+    float       deltaZ = farZ - nearZ;
+    ESMatrix    frust;
+
+    if ( (nearZ <= 0.0f) || (farZ <= 0.0f) ||
+         (deltaX <= 0.0f) || (deltaY <= 0.0f) || (deltaZ <= 0.0f) )
+         return;
+
+    frust.m[0][0] = 2.0f * nearZ / deltaX;
+    frust.m[0][1] = frust.m[0][2] = frust.m[0][3] = 0.0f;
+
+    frust.m[1][1] = 2.0f * nearZ / deltaY;
+    frust.m[1][0] = frust.m[1][2] = frust.m[1][3] = 0.0f;
+
+    frust.m[2][0] = (right + left) / deltaX;
+    frust.m[2][1] = (top + bottom) / deltaY;
+    frust.m[2][2] = -(nearZ + farZ) / deltaZ;
+    frust.m[2][3] = -1.0f;
+
+    frust.m[3][2] = -2.0f * nearZ * farZ / deltaZ;
+    frust.m[3][0] = frust.m[3][1] = frust.m[3][3] = 0.0f;
+
+    esMatrixMultiply(result, &frust, result);
+}
+
+
+void ESUTIL_API 
+esPerspective(ESMatrix *result, float fovy, float aspect, float nearZ, float farZ)
+{
+   GLfloat frustumW, frustumH;
+   
+   frustumH = tanf( fovy / 360.0f * PI ) * nearZ;
+   frustumW = frustumH * aspect;
+
+   esFrustum( result, -frustumW, frustumW, -frustumH, frustumH, nearZ, farZ );
+}
+
+void ESUTIL_API
+esOrtho(ESMatrix *result, float left, float right, float bottom, float top, float nearZ, float farZ)
+{
+    float       deltaX = right - left;
+    float       deltaY = top - bottom;
+    float       deltaZ = farZ - nearZ;
+    ESMatrix    ortho;
+
+    if ( (deltaX == 0.0f) || (deltaY == 0.0f) || (deltaZ == 0.0f) )
+        return;
+
+    esMatrixLoadIdentity(&ortho);
+    ortho.m[0][0] = 2.0f / deltaX;
+    ortho.m[3][0] = -(right + left) / deltaX;
+    ortho.m[1][1] = 2.0f / deltaY;
+    ortho.m[3][1] = -(top + bottom) / deltaY;
+    ortho.m[2][2] = -2.0f / deltaZ;
+    ortho.m[3][2] = -(nearZ + farZ) / deltaZ;
+
+    esMatrixMultiply(result, &ortho, result);
+}
+
+
+void ESUTIL_API
+esMatrixMultiply(ESMatrix *result, ESMatrix *srcA, ESMatrix *srcB)
+{
+    ESMatrix    tmp;
+    int         i;
+
+       for (i=0; i<4; i++)
+       {
+               tmp.m[i][0] =   (srcA->m[i][0] * srcB->m[0][0]) +
+                                               (srcA->m[i][1] * srcB->m[1][0]) +
+                                               (srcA->m[i][2] * srcB->m[2][0]) +
+                                               (srcA->m[i][3] * srcB->m[3][0]) ;
+
+               tmp.m[i][1] =   (srcA->m[i][0] * srcB->m[0][1]) + 
+                                               (srcA->m[i][1] * srcB->m[1][1]) +
+                                               (srcA->m[i][2] * srcB->m[2][1]) +
+                                               (srcA->m[i][3] * srcB->m[3][1]) ;
+
+               tmp.m[i][2] =   (srcA->m[i][0] * srcB->m[0][2]) + 
+                                               (srcA->m[i][1] * srcB->m[1][2]) +
+                                               (srcA->m[i][2] * srcB->m[2][2]) +
+                                               (srcA->m[i][3] * srcB->m[3][2]) ;
+
+               tmp.m[i][3] =   (srcA->m[i][0] * srcB->m[0][3]) + 
+                                               (srcA->m[i][1] * srcB->m[1][3]) +
+                                               (srcA->m[i][2] * srcB->m[2][3]) +
+                                               (srcA->m[i][3] * srcB->m[3][3]) ;
+       }
+    memcpy(result, &tmp, sizeof(ESMatrix));
+}
+
+
+void ESUTIL_API
+esMatrixLoadIdentity(ESMatrix *result)
+{
+    memset(result, 0x0, sizeof(ESMatrix));
+    result->m[0][0] = 1.0f;
+    result->m[1][1] = 1.0f;
+    result->m[2][2] = 1.0f;
+    result->m[3][3] = 1.0f;
+}
+
diff --git a/esUtil.h b/esUtil.h
new file mode 100644 (file)
index 0000000..c2d7c1d
--- /dev/null
+++ b/esUtil.h
@@ -0,0 +1,303 @@
+//
+// Book:      OpenGL(R) ES 2.0 Programming Guide
+// Authors:   Aaftab Munshi, Dan Ginsburg, Dave Shreiner
+// ISBN-10:   0321502795
+// ISBN-13:   9780321502797
+// Publisher: Addison-Wesley Professional
+// URLs:      http://safari.informit.com/9780321563835
+//            http://www.opengles-book.com
+//
+
+/*
+ * (c) 2009 Aaftab Munshi, Dan Ginsburg, Dave Shreiner
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+//
+/// \file ESUtil.h
+/// \brief A utility library for OpenGL ES.  This library provides a
+///        basic common framework for the example applications in the
+///        OpenGL ES 2.0 Programming Guide.
+//
+#ifndef ESUTIL_H
+#define ESUTIL_H
+
+///
+//  Includes
+//
+#include <GLES2/gl2.h>
+#include <EGL/egl.h>
+
+#ifdef __cplusplus
+
+extern "C" {
+#endif
+
+   
+///
+//  Macros
+//
+#define ESUTIL_API
+#define ESCALLBACK
+
+
+/// esCreateWindow flag - RGB color buffer
+#define ES_WINDOW_RGB           0
+/// esCreateWindow flag - ALPHA color buffer
+#define ES_WINDOW_ALPHA         1 
+/// esCreateWindow flag - depth buffer
+#define ES_WINDOW_DEPTH         2 
+/// esCreateWindow flag - stencil buffer
+#define ES_WINDOW_STENCIL       4
+/// esCreateWindow flat - multi-sample buffer
+#define ES_WINDOW_MULTISAMPLE   8
+
+
+///
+// Types
+//
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+typedef struct
+{
+    GLfloat   m[4][4];
+} ESMatrix;
+
+typedef struct _escontext
+{
+   /// Put your user data here...
+   void*       userData;
+
+   /// Window width
+   GLint       width;
+
+   /// Window height
+   GLint       height;
+
+   /// Window handle
+   EGLNativeWindowType  hWnd;
+
+   /// EGL display
+   EGLDisplay  eglDisplay;
+      
+   /// EGL context
+   EGLContext  eglContext;
+
+   /// EGL surface
+   EGLSurface  eglSurface;
+
+   /// Callbacks
+   void (ESCALLBACK *drawFunc) ( struct _escontext * );
+   void (ESCALLBACK *keyFunc) ( struct _escontext *, unsigned char, int, int );
+   void (ESCALLBACK *updateFunc) ( struct _escontext *, float deltaTime );
+} ESContext;
+
+
+///
+//  Public Functions
+//
+
+//
+///
+/// \brief Initialize ES framework context.  This must be called before calling any other functions.
+/// \param esContext Application context
+//
+void ESUTIL_API esInitContext ( ESContext *esContext );
+
+//
+/// \brief Create a window with the specified parameters
+/// \param esContext Application context
+/// \param title Name for title bar of window
+/// \param width Width in pixels of window to create
+/// \param height Height in pixels of window to create
+/// \param flags Bitfield for the window creation flags 
+///         ES_WINDOW_RGB     - specifies that the color buffer should have R,G,B channels
+///         ES_WINDOW_ALPHA   - specifies that the color buffer should have alpha
+///         ES_WINDOW_DEPTH   - specifies that a depth buffer should be created
+///         ES_WINDOW_STENCIL - specifies that a stencil buffer should be created
+///         ES_WINDOW_MULTISAMPLE - specifies that a multi-sample buffer should be created
+/// \return GL_TRUE if window creation is succesful, GL_FALSE otherwise
+GLboolean ESUTIL_API esCreateWindow ( ESContext *esContext, const char *title, GLint width, GLint height, GLuint flags );
+
+//
+/// \brief Start the main loop for the OpenGL ES application
+/// \param esContext Application context
+//
+void ESUTIL_API esMainLoop ( ESContext *esContext );
+
+//
+/// \brief Register a draw callback function to be used to render each frame
+/// \param esContext Application context
+/// \param drawFunc Draw callback function that will be used to render the scene
+//
+void ESUTIL_API esRegisterDrawFunc ( ESContext *esContext, void (ESCALLBACK *drawFunc) ( ESContext* ) );
+
+//
+/// \brief Register an update callback function to be used to update on each time step
+/// \param esContext Application context
+/// \param updateFunc Update callback function that will be used to render the scene
+//
+void ESUTIL_API esRegisterUpdateFunc ( ESContext *esContext, void (ESCALLBACK *updateFunc) ( ESContext*, float ) );
+
+//
+/// \brief Register an keyboard input processing callback function
+/// \param esContext Application context
+/// \param keyFunc Key callback function for application processing of keyboard input
+//
+void ESUTIL_API esRegisterKeyFunc ( ESContext *esContext, 
+                                    void (ESCALLBACK *drawFunc) ( ESContext*, unsigned char, int, int ) );
+//
+/// \brief Log a message to the debug output for the platform
+/// \param formatStr Format string for error log.  
+//
+void ESUTIL_API esLogMessage ( const char *formatStr, ... );
+
+//
+///
+/// \brief Load a shader, check for compile errors, print error messages to output log
+/// \param type Type of shader (GL_VERTEX_SHADER or GL_FRAGMENT_SHADER)
+/// \param shaderSrc Shader source string
+/// \return A new shader object on success, 0 on failure
+//
+GLuint ESUTIL_API esLoadShader ( GLenum type, const char *shaderSrc );
+
+//
+///
+/// \brief Load a vertex and fragment shader, create a program object, link program.
+///        Errors output to log.
+/// \param vertShaderSrc Vertex shader source code
+/// \param fragShaderSrc Fragment shader source code
+/// \return A new program object linked with the vertex/fragment shader pair, 0 on failure
+//
+GLuint ESUTIL_API esLoadProgram ( const char *vertShaderSrc, const char *fragShaderSrc );
+
+
+//
+/// \brief Generates geometry for a sphere.  Allocates memory for the vertex data and stores 
+///        the results in the arrays.  Generate index list for a TRIANGLE_STRIP
+/// \param numSlices The number of slices in the sphere
+/// \param vertices If not NULL, will contain array of float3 positions
+/// \param normals If not NULL, will contain array of float3 normals
+/// \param texCoords If not NULL, will contain array of float2 texCoords
+/// \param indices If not NULL, will contain the array of indices for the triangle strip
+/// \return The number of indices required for rendering the buffers (the number of indices stored in the indices array
+///         if it is not NULL ) as a GL_TRIANGLE_STRIP
+//
+int ESUTIL_API esGenSphere ( int numSlices, float radius, GLfloat **vertices, GLfloat **normals, 
+                             GLfloat **texCoords, GLuint **indices );
+
+//
+/// \brief Generates geometry for a cube.  Allocates memory for the vertex data and stores 
+///        the results in the arrays.  Generate index list for a TRIANGLES
+/// \param scale The size of the cube, use 1.0 for a unit cube.
+/// \param vertices If not NULL, will contain array of float3 positions
+/// \param normals If not NULL, will contain array of float3 normals
+/// \param texCoords If not NULL, will contain array of float2 texCoords
+/// \param indices If not NULL, will contain the array of indices for the triangle strip
+/// \return The number of indices required for rendering the buffers (the number of indices stored in the indices array
+///         if it is not NULL ) as a GL_TRIANGLES
+//
+int ESUTIL_API esGenCube ( float scale, GLfloat **vertices, GLfloat **normals, 
+                           GLfloat **texCoords, GLuint **indices );
+
+//
+/// \brief Loads a 24-bit TGA image from a file
+/// \param fileName Name of the file on disk
+/// \param width Width of loaded image in pixels
+/// \param height Height of loaded image in pixels
+///  \return Pointer to loaded image.  NULL on failure. 
+//
+char* ESUTIL_API esLoadTGA ( char *fileName, int *width, int *height );
+
+
+//
+/// \brief multiply matrix specified by result with a scaling matrix and return new matrix in result
+/// \param result Specifies the input matrix.  Scaled matrix is returned in result.
+/// \param sx, sy, sz Scale factors along the x, y and z axes respectively
+//
+void ESUTIL_API esScale(ESMatrix *result, GLfloat sx, GLfloat sy, GLfloat sz);
+
+//
+/// \brief multiply matrix specified by result with a translation matrix and return new matrix in result
+/// \param result Specifies the input matrix.  Translated matrix is returned in result.
+/// \param tx, ty, tz Scale factors along the x, y and z axes respectively
+//
+void ESUTIL_API esTranslate(ESMatrix *result, GLfloat tx, GLfloat ty, GLfloat tz);
+
+//
+/// \brief multiply matrix specified by result with a rotation matrix and return new matrix in result
+/// \param result Specifies the input matrix.  Rotated matrix is returned in result.
+/// \param angle Specifies the angle of rotation, in degrees.
+/// \param x, y, z Specify the x, y and z coordinates of a vector, respectively
+//
+void ESUTIL_API esRotate(ESMatrix *result, GLfloat angle, GLfloat x, GLfloat y, GLfloat z);
+
+//
+// \brief multiply matrix specified by result with a perspective matrix and return new matrix in result
+/// \param result Specifies the input matrix.  new matrix is returned in result.
+/// \param left, right Coordinates for the left and right vertical clipping planes
+/// \param bottom, top Coordinates for the bottom and top horizontal clipping planes
+/// \param nearZ, farZ Distances to the near and far depth clipping planes.  Both distances must be positive.
+//
+void ESUTIL_API esFrustum(ESMatrix *result, float left, float right, float bottom, float top, float nearZ, float farZ);
+
+//
+/// \brief multiply matrix specified by result with a perspective matrix and return new matrix in result
+/// \param result Specifies the input matrix.  new matrix is returned in result.
+/// \param fovy Field of view y angle in degrees
+/// \param aspect Aspect ratio of screen
+/// \param nearZ Near plane distance
+/// \param farZ Far plane distance
+//
+void ESUTIL_API esPerspective(ESMatrix *result, float fovy, float aspect, float nearZ, float farZ);
+
+//
+/// \brief multiply matrix specified by result with a perspective matrix and return new matrix in result
+/// \param result Specifies the input matrix.  new matrix is returned in result.
+/// \param left, right Coordinates for the left and right vertical clipping planes
+/// \param bottom, top Coordinates for the bottom and top horizontal clipping planes
+/// \param nearZ, farZ Distances to the near and far depth clipping planes.  These values are negative if plane is behind the viewer
+//
+void ESUTIL_API esOrtho(ESMatrix *result, float left, float right, float bottom, float top, float nearZ, float farZ);
+
+//
+/// \brief perform the following operation - result matrix = srcA matrix * srcB matrix
+/// \param result Returns multiplied matrix
+/// \param srcA, srcB Input matrices to be multiplied
+//
+void ESUTIL_API esMatrixMultiply(ESMatrix *result, ESMatrix *srcA, ESMatrix *srcB);
+
+//
+//// \brief return an indentity matrix 
+//// \param result returns identity matrix
+//
+void ESUTIL_API esMatrixLoadIdentity(ESMatrix *result);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // ESUTIL_H
diff --git a/kmscube.c b/kmscube.c
new file mode 100644 (file)
index 0000000..5ae2b2e
--- /dev/null
+++ b/kmscube.c
@@ -0,0 +1,641 @@
+/*
+ * Copyright (c) 2012 Rob Clark <rob@ti.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sub license,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+#include <gbm.h>
+
+#include "esUtil.h"
+
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+
+static struct {
+       EGLDisplay display;
+       EGLConfig config;
+       EGLContext context;
+       EGLSurface surface;
+       GLuint program;
+       GLint modelviewmatrix, modelviewprojectionmatrix, normalmatrix;
+} gl;
+
+static struct {
+       struct gbm_device *dev;
+       struct gbm_surface *surface;
+} gbm;
+
+static struct {
+       int fd;
+       drmModeModeInfo *mode;
+       uint32_t crtc_id;
+       uint32_t connector_id;
+} drm;
+
+struct drm_fb {
+       struct gbm_bo *bo;
+       uint32_t fb_id;
+};
+
+static int init_drm(void)
+{
+       static const char *modules[] = {
+                       "i915", "radeon", "nouveau", "vmwgfx", "omapdrm", "exynos"
+       };
+       drmModeRes *resources;
+       drmModeConnector *connector = NULL;
+       drmModeEncoder *encoder = NULL;
+       int i, area;
+
+       for (i = 0; i < ARRAY_SIZE(modules); i++) {
+               printf("trying to load module %s...", modules[i]);
+               drm.fd = drmOpen(modules[i], NULL);
+               if (drm.fd < 0) {
+                       printf("failed.\n");
+               } else {
+                       printf("success.\n");
+                       break;
+               }
+       }
+
+       if (drm.fd < 0) {
+               printf("could not open drm device\n");
+               return -1;
+       }
+
+       resources = drmModeGetResources(drm.fd);
+       if (!resources) {
+               printf("drmModeGetResources failed: %s\n", strerror(errno));
+               return -1;
+       }
+
+       /* find a connected connector: */
+       for (i = 0; i < resources->count_connectors; i++) {
+               connector = drmModeGetConnector(drm.fd, resources->connectors[i]);
+               if (connector->connection == DRM_MODE_CONNECTED) {
+                       /* it's connected, let's use this! */
+                       break;
+               }
+               drmModeFreeConnector(connector);
+               connector = NULL;
+       }
+
+       if (!connector) {
+               /* we could be fancy and listen for hotplug events and wait for
+                * a connector..
+                */
+               printf("no connected connector!\n");
+               return -1;
+       }
+
+       /* find highest resolution mode: */
+       for (i = 0, area = 0; i < connector->count_modes; i++) {
+               drmModeModeInfo *current_mode = &connector->modes[i];
+               int current_area = current_mode->hdisplay * current_mode->vdisplay;
+               if (current_area > area) {
+                       drm.mode = current_mode;
+                       area = current_area;
+               }
+       }
+
+       if (!drm.mode) {
+               printf("could not find mode!\n");
+               return -1;
+       }
+
+       /* find encoder: */
+       for (i = 0; i < resources->count_encoders; i++) {
+               encoder = drmModeGetEncoder(drm.fd, resources->encoders[i]);
+               if (encoder->encoder_id == connector->encoder_id)
+                       break;
+               drmModeFreeEncoder(encoder);
+               encoder = NULL;
+       }
+
+       if (!encoder) {
+               printf("no encoder!\n");
+               return -1;
+       }
+
+       drm.crtc_id = encoder->crtc_id;
+       drm.connector_id = connector->connector_id;
+
+       return 0;
+}
+
+static int init_gbm(void)
+{
+       gbm.dev = gbm_create_device(drm.fd);
+
+       gbm.surface = gbm_surface_create(gbm.dev,
+                       drm.mode->hdisplay, drm.mode->vdisplay,
+                       GBM_FORMAT_XRGB8888,
+                       GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
+       if (!gbm.surface) {
+               printf("failed to create gbm surface\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+static int init_gl(void)
+{
+       EGLint major, minor, n;
+       GLuint vertex_shader, fragment_shader;
+       GLint ret;
+
+       static const EGLint context_attribs[] = {
+               EGL_CONTEXT_CLIENT_VERSION, 2,
+               EGL_NONE
+       };
+
+       static const EGLint config_attribs[] = {
+               EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+               EGL_RED_SIZE, 1,
+               EGL_GREEN_SIZE, 1,
+               EGL_BLUE_SIZE, 1,
+               EGL_ALPHA_SIZE, 0,
+               EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+               EGL_NONE
+       };
+
+       static const char *vertex_shader_source =
+                       "uniform mat4 modelviewMatrix;      \n"
+                       "uniform mat4 modelviewprojectionMatrix;\n"
+                       "uniform mat3 normalMatrix;         \n"
+                       "                                   \n"
+                       "attribute vec4 in_position;        \n"
+                       "attribute vec3 in_normal;          \n"
+                       "attribute vec4 in_color;           \n"
+                       "\n"
+                       "vec4 lightSource = vec4(2.0, 2.0, 20.0, 0.0);\n"
+                       "                                   \n"
+                       "varying vec4 vVaryingColor;        \n"
+                       "                                   \n"
+                       "void main()                        \n"
+                       "{                                  \n"
+                       "    gl_Position = modelviewprojectionMatrix * in_position;\n"
+                       "    vec3 vEyeNormal = normalMatrix * in_normal;\n"
+                       "    vec4 vPosition4 = modelviewMatrix * in_position;\n"
+                       "    vec3 vPosition3 = vPosition4.xyz / vPosition4.w;\n"
+                       "    vec3 vLightDir = normalize(lightSource.xyz - vPosition3);\n"
+                       "    float diff = max(0.0, dot(vEyeNormal, vLightDir));\n"
+                       "    vVaryingColor = vec4(diff * in_color.rgb, 1.0);\n"
+                       "}                                  \n";
+
+       static const char *fragment_shader_source =
+                       "precision mediump float;           \n"
+                       "                                   \n"
+                       "varying vec4 vVaryingColor;        \n"
+                       "                                   \n"
+                       "void main()                        \n"
+                       "{                                  \n"
+                       "    gl_FragColor = vVaryingColor;  \n"
+                       "}                                  \n";
+
+       gl.display = eglGetDisplay(gbm.dev);
+
+       if (!eglInitialize(gl.display, &major, &minor)) {
+               printf("failed to initialize\n");
+               return -1;
+       }
+
+       printf("Using display %p with EGL version %d.%d\n",
+                       gl.display, major, minor);
+
+       printf("EGL Version \"%s\"\n", eglQueryString(gl.display, EGL_VERSION));
+       printf("EGL Vendor \"%s\"\n", eglQueryString(gl.display, EGL_VENDOR));
+       printf("EGL Extensions \"%s\"\n", eglQueryString(gl.display, EGL_EXTENSIONS));
+
+       if (!eglBindAPI(EGL_OPENGL_ES_API)) {
+               printf("failed to bind api EGL_OPENGL_ES_API\n");
+               return -1;
+       }
+
+       if (!eglChooseConfig(gl.display, config_attribs, &gl.config, 1, &n) || n != 1) {
+               printf("failed to choose config: %d\n", n);
+               return -1;
+       }
+
+       gl.context = eglCreateContext(gl.display, gl.config,
+                       EGL_NO_CONTEXT, context_attribs);
+       if (gl.context == NULL) {
+               printf("failed to create context\n");
+               return -1;
+       }
+
+       gl.surface = eglCreateWindowSurface(gl.display, gl.config, gbm.surface, NULL);
+       if (gl.surface == EGL_NO_SURFACE) {
+               printf("failed to create egl surface\n");
+               return -1;
+       }
+
+       /* connect the context to the surface */
+       eglMakeCurrent(gl.display, gl.surface, gl.surface, gl.context);
+
+
+       vertex_shader = glCreateShader(GL_VERTEX_SHADER);
+
+       glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL);
+       glCompileShader(vertex_shader);
+
+       glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &ret);
+       if (!ret) {
+               char *log;
+
+               printf("vertex shader compilation failed!:\n");
+               glGetShaderiv(vertex_shader, GL_INFO_LOG_LENGTH, &ret);
+               if (ret > 1) {
+                       log = malloc(ret);
+                       glGetShaderInfoLog(vertex_shader, ret, NULL, log);
+                       printf("%s", log);
+               }
+
+               return -1;
+       }
+
+       fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
+
+       glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL);
+       glCompileShader(fragment_shader);
+
+       glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &ret);
+       if (!ret) {
+               char *log;
+
+               printf("fragment shader compilation failed!:\n");
+               glGetShaderiv(fragment_shader, GL_INFO_LOG_LENGTH, &ret);
+
+               if (ret > 1) {
+                       log = malloc(ret);
+                       glGetShaderInfoLog(fragment_shader, ret, NULL, log);
+                       printf("%s", log);
+               }
+
+               return -1;
+       }
+
+       gl.program = glCreateProgram();
+
+       glAttachShader(gl.program, vertex_shader);
+       glAttachShader(gl.program, fragment_shader);
+
+       glBindAttribLocation(gl.program, 0, "in_position");
+       glBindAttribLocation(gl.program, 1, "in_normal");
+       glBindAttribLocation(gl.program, 2, "in_color");
+
+       glLinkProgram(gl.program);
+
+       glGetProgramiv(gl.program, GL_LINK_STATUS, &ret);
+       if (!ret) {
+               char *log;
+
+               printf("program linking failed!:\n");
+               glGetProgramiv(gl.program, GL_INFO_LOG_LENGTH, &ret);
+
+               if (ret > 1) {
+                       log = malloc(ret);
+                       glGetProgramInfoLog(gl.program, ret, NULL, log);
+                       printf("%s", log);
+               }
+
+               return -1;
+       }
+
+       glUseProgram(gl.program);
+
+       gl.modelviewmatrix = glGetUniformLocation(gl.program, "modelviewMatrix");
+       gl.modelviewprojectionmatrix = glGetUniformLocation(gl.program, "modelviewprojectionMatrix");
+       gl.normalmatrix = glGetUniformLocation(gl.program, "normalMatrix");
+
+       glViewport(0, 0, drm.mode->hdisplay, drm.mode->vdisplay);
+
+       return 0;
+}
+
+static void draw(uint32_t i)
+{
+       ESMatrix modelview;
+       static const GLfloat vVertices[] = {
+                       // front
+                       -1.0f, -1.0f, +1.0f, // point blue
+                       +1.0f, -1.0f, +1.0f, // point magenta
+                       -1.0f, +1.0f, +1.0f, // point cyan
+                       +1.0f, +1.0f, +1.0f, // point white
+                       // back
+                       +1.0f, -1.0f, -1.0f, // point red
+                       -1.0f, -1.0f, -1.0f, // point black
+                       +1.0f, +1.0f, -1.0f, // point yellow
+                       -1.0f, +1.0f, -1.0f, // point green
+                       // right
+                       +1.0f, -1.0f, +1.0f, // point magenta
+                       +1.0f, -1.0f, -1.0f, // point red
+                       +1.0f, +1.0f, +1.0f, // point white
+                       +1.0f, +1.0f, -1.0f, // point yellow
+                       // left
+                       -1.0f, -1.0f, -1.0f, // point black
+                       -1.0f, -1.0f, +1.0f, // point blue
+                       -1.0f, +1.0f, -1.0f, // point green
+                       -1.0f, +1.0f, +1.0f, // point cyan
+                       // top
+                       -1.0f, +1.0f, +1.0f, // point cyan
+                       +1.0f, +1.0f, +1.0f, // point white
+                       -1.0f, +1.0f, -1.0f, // point green
+                       +1.0f, +1.0f, -1.0f, // point yellow
+                       // bottom
+                       -1.0f, -1.0f, -1.0f, // point black
+                       +1.0f, -1.0f, -1.0f, // point red
+                       -1.0f, -1.0f, +1.0f, // point blue
+                       +1.0f, -1.0f, +1.0f  // point magenta
+       };
+
+       static const GLfloat vColors[] = {
+                       // front
+                       0.0f,  0.0f,  1.0f, // blue
+                       1.0f,  0.0f,  1.0f, // magenta
+                       0.0f,  1.0f,  1.0f, // cyan
+                       1.0f,  1.0f,  1.0f, // white
+                       // back
+                       1.0f,  0.0f,  0.0f, // red
+                       0.0f,  0.0f,  0.0f, // black
+                       1.0f,  1.0f,  0.0f, // yellow
+                       0.0f,  1.0f,  0.0f, // green
+                       // right
+                       1.0f,  0.0f,  1.0f, // magenta
+                       1.0f,  0.0f,  0.0f, // red
+                       1.0f,  1.0f,  1.0f, // white
+                       1.0f,  1.0f,  0.0f, // yellow
+                       // left
+                       0.0f,  0.0f,  0.0f, // black
+                       0.0f,  0.0f,  1.0f, // blue
+                       0.0f,  1.0f,  0.0f, // green
+                       0.0f,  1.0f,  1.0f, // cyan
+                       // top
+                       0.0f,  1.0f,  1.0f, // cyan
+                       1.0f,  1.0f,  1.0f, // white
+                       0.0f,  1.0f,  0.0f, // green
+                       1.0f,  1.0f,  0.0f, // yellow
+                       // bottom
+                       0.0f,  0.0f,  0.0f, // black
+                       1.0f,  0.0f,  0.0f, // red
+                       0.0f,  0.0f,  1.0f, // blue
+                       1.0f,  0.0f,  1.0f  // magenta
+       };
+
+       static const GLfloat vNormals[] = {
+                       // front
+                       +0.0f, +0.0f, +1.0f, // forward
+                       +0.0f, +0.0f, +1.0f, // forward
+                       +0.0f, +0.0f, +1.0f, // forward
+                       +0.0f, +0.0f, +1.0f, // forward
+                       // back
+                       +0.0f, +0.0f, -1.0f, // backbard
+                       +0.0f, +0.0f, -1.0f, // backbard
+                       +0.0f, +0.0f, -1.0f, // backbard
+                       +0.0f, +0.0f, -1.0f, // backbard
+                       // right
+                       +1.0f, +0.0f, +0.0f, // right
+                       +1.0f, +0.0f, +0.0f, // right
+                       +1.0f, +0.0f, +0.0f, // right
+                       +1.0f, +0.0f, +0.0f, // right
+                       // left
+                       -1.0f, +0.0f, +0.0f, // left
+                       -1.0f, +0.0f, +0.0f, // left
+                       -1.0f, +0.0f, +0.0f, // left
+                       -1.0f, +0.0f, +0.0f, // left
+                       // top
+                       +0.0f, +1.0f, +0.0f, // up
+                       +0.0f, +1.0f, +0.0f, // up
+                       +0.0f, +1.0f, +0.0f, // up
+                       +0.0f, +1.0f, +0.0f, // up
+                       // bottom
+                       +0.0f, -1.0f, +0.0f, // down
+                       +0.0f, -1.0f, +0.0f, // down
+                       +0.0f, -1.0f, +0.0f, // down
+                       +0.0f, -1.0f, +0.0f  // down
+       };
+
+       /* clear the color buffer */
+       glClearColor(0.5, 0.5, 0.5, 1.0);
+       glClear(GL_COLOR_BUFFER_BIT);
+
+       glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vVertices);
+       glEnableVertexAttribArray(0);
+
+       glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, vNormals);
+       glEnableVertexAttribArray(1);
+
+       glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, vColors);
+       glEnableVertexAttribArray(2);
+
+       esMatrixLoadIdentity(&modelview);
+       esTranslate(&modelview, 0.0f, 0.0f, -8.0f);
+       esRotate(&modelview, 45.0f + (0.25f * i), 1.0f, 0.0f, 0.0f);
+       esRotate(&modelview, 45.0f - (0.5f * i), 0.0f, 1.0f, 0.0f);
+       esRotate(&modelview, 10.0f + (0.15f * i), 0.0f, 0.0f, 1.0f);
+
+       GLfloat aspect = (GLfloat)(drm.mode->vdisplay) / (GLfloat)(drm.mode->hdisplay);
+
+       ESMatrix projection;
+       esMatrixLoadIdentity(&projection);
+       esFrustum(&projection, -2.8f, +2.8f, -2.8f * aspect, +2.8f * aspect, 6.0f, 10.0f);
+
+       ESMatrix modelviewprojection;
+       esMatrixLoadIdentity(&modelviewprojection);
+       esMatrixMultiply(&modelviewprojection, &modelview, &projection);
+
+       float normal[9];
+       normal[0] = modelview.m[0][0];
+       normal[1] = modelview.m[0][1];
+       normal[2] = modelview.m[0][2];
+       normal[3] = modelview.m[1][0];
+       normal[4] = modelview.m[1][1];
+       normal[5] = modelview.m[1][2];
+       normal[6] = modelview.m[2][0];
+       normal[7] = modelview.m[2][1];
+       normal[8] = modelview.m[2][2];
+
+       glUniformMatrix4fv(gl.modelviewmatrix, 1, GL_FALSE, &modelview.m[0][0]);
+       glUniformMatrix4fv(gl.modelviewprojectionmatrix, 1, GL_FALSE, &modelviewprojection.m[0][0]);
+       glUniformMatrix3fv(gl.normalmatrix, 1, GL_FALSE, normal);
+
+       glEnable(GL_CULL_FACE);
+
+       glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+       glDrawArrays(GL_TRIANGLE_STRIP, 4, 4);
+       glDrawArrays(GL_TRIANGLE_STRIP, 8, 4);
+       glDrawArrays(GL_TRIANGLE_STRIP, 12, 4);
+       glDrawArrays(GL_TRIANGLE_STRIP, 16, 4);
+       glDrawArrays(GL_TRIANGLE_STRIP, 20, 4);
+}
+
+static void
+drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
+{
+       struct drm_fb *fb = data;
+       struct gbm_device *gbm = gbm_bo_get_device(bo);
+
+       if (fb->fb_id)
+               drmModeRmFB(drm.fd, fb->fb_id);
+
+       free(fb);
+}
+
+static struct drm_fb * drm_fb_get_from_bo(struct gbm_bo *bo)
+{
+       struct drm_fb *fb = gbm_bo_get_user_data(bo);
+       uint32_t width, height, stride, handle;
+       int ret;
+
+       if (fb)
+               return fb;
+
+       fb = calloc(1, sizeof *fb);
+       fb->bo = bo;
+
+       width = gbm_bo_get_width(bo);
+       height = gbm_bo_get_height(bo);
+       stride = gbm_bo_get_stride(bo);
+       handle = gbm_bo_get_handle(bo).u32;
+
+       ret = drmModeAddFB(drm.fd, width, height, 24, 32, stride, handle, &fb->fb_id);
+       if (ret) {
+               printf("failed to create fb: %s\n", strerror(errno));
+               free(fb);
+               return NULL;
+       }
+
+       gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback);
+
+       return fb;
+}
+
+static void page_flip_handler(int fd, unsigned int frame,
+                 unsigned int sec, unsigned int usec, void *data)
+{
+       int *waiting_for_flip = data;
+       *waiting_for_flip = 0;
+}
+
+int main(int argc, char *argv[])
+{
+       fd_set fds;
+       drmEventContext evctx = {
+                       .version = DRM_EVENT_CONTEXT_VERSION,
+                       .page_flip_handler = page_flip_handler,
+       };
+       struct gbm_bo *bo;
+       struct drm_fb *fb;
+       uint32_t i = 0;
+       int ret;
+
+       ret = init_drm();
+       if (ret) {
+               printf("failed to initialize DRM\n");
+               return ret;
+       }
+
+       FD_ZERO(&fds);
+       FD_SET(0, &fds);
+       FD_SET(drm.fd, &fds);
+
+       ret = init_gbm();
+       if (ret) {
+               printf("failed to initialize GBM\n");
+               return ret;
+       }
+
+       ret = init_gl();
+       if (ret) {
+               printf("failed to initialize EGL\n");
+               return ret;
+       }
+
+       /* clear the color buffer */
+       glClearColor(0.5, 0.5, 0.5, 1.0);
+       glClear(GL_COLOR_BUFFER_BIT);
+       eglSwapBuffers(gl.display, gl.surface);
+       bo = gbm_surface_lock_front_buffer(gbm.surface);
+       fb = drm_fb_get_from_bo(bo);
+
+       /* set mode: */
+       ret = drmModeSetCrtc(drm.fd, drm.crtc_id, fb->fb_id, 0, 0,
+                       &drm.connector_id, 1, drm.mode);
+       if (ret) {
+               printf("failed to set mode: %s\n", strerror(errno));
+               return ret;
+       }
+
+       while (1) {
+               struct gbm_bo *next_bo;
+               int waiting_for_flip = 1;
+
+               draw(i++);
+
+               eglSwapBuffers(gl.display, gl.surface);
+               next_bo = gbm_surface_lock_front_buffer(gbm.surface);
+               fb = drm_fb_get_from_bo(next_bo);
+
+               /*
+                * Here you could also update drm plane layers if you want
+                * hw composition
+                */
+
+               ret = drmModePageFlip(drm.fd, drm.crtc_id, fb->fb_id,
+                               DRM_MODE_PAGE_FLIP_EVENT, &waiting_for_flip);
+               if (ret) {
+                       printf("failed to queue page flip: %s\n", strerror(errno));
+                       return -1;
+               }
+
+               while (waiting_for_flip) {
+                       ret = select(drm.fd + 1, &fds, NULL, NULL, NULL);
+                       if (ret < 0) {
+                               printf("select err: %s\n", strerror(errno));
+                               return ret;
+                       } else if (ret == 0) {
+                               printf("select timeout!\n");
+                               return -1;
+                       } else if (FD_ISSET(0, &fds)) {
+                               printf("user interrupted!\n");
+                               break;
+                       }
+                       drmHandleEvent(drm.fd, &evctx);
+               }
+
+               /* release last buffer to render on again: */
+               gbm_surface_release_buffer(gbm.surface, bo);
+               bo = next_bo;
+       }
+
+       return ret;
+}