summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: b2add08)
raw | patch | inline | side by side (parent: b2add08)
author | Vincent Stehlé <v-stehle@ti.com> | |
Mon, 24 Sep 2012 08:09:22 +0000 (10:09 +0200) | ||
committer | Nikhil Devshatwar <a0132237@ti.com> | |
Wed, 22 May 2013 09:45:30 +0000 (15:15 +0530) |
Add a new display 'kmscube', based on the kmscube application by Arvin Schnell
and Rob Clark.
This display 'kmscube' draws a rotating 3d cube with EGL on kms display, with
video mapped as a texture on the cube faces.
Signed-off-by: Vincent Stehlé <v-stehle@ti.com>
and Rob Clark.
This display 'kmscube' draws a rotating 3d cube with EGL on kms display, with
video mapped as a texture on the cube faces.
Signed-off-by: Vincent Stehlé <v-stehle@ti.com>
Makefile.am | patch | blob | history | |
README | patch | blob | history | |
configure.ac | patch | blob | history | |
util/Makefile.am | patch | blob | history | |
util/display-kmscube.c | [new file with mode: 0644] | patch | blob |
util/esTransform.c | [new file with mode: 0644] | patch | blob |
util/esUtil.h | [new file with mode: 0644] | patch | blob |
util/util.c | patch | blob | history |
diff --git a/Makefile.am b/Makefile.am
index a51373b89eb41846ff08afd883d7f6a94c4459d3..5bf2c6c7f2f4b777c2a88662280ee8738a86caed 100644 (file)
--- a/Makefile.am
+++ b/Makefile.am
bin_PROGRAMS += viddec3test
endif
-LDADD_COMMON = util/libutil.la @DRM_LIBS@ @X11_LIBS@ @DCE_LIBS@
-AM_CFLAGS = @LIN_CFLAGS@ @DRM_CFLAGS@ @X11_CFLAGS@ @DCE_CFLAGS@ @WARN_CFLAGS@ -I$(top_srcdir)/util
+LDADD_COMMON = util/libutil.la @DRM_LIBS@ @X11_LIBS@ @DCE_LIBS@ @GBM_LIBS@ @EGL_LIBS@ @GLES2_LIBS@
+AM_CFLAGS = @LIN_CFLAGS@ @DRM_CFLAGS@ @X11_CFLAGS@ @DCE_CFLAGS@ @GBM_CFLAGS@ @EGL_CFLAGS@ @GLES2_CFLAGS@ @WARN_CFLAGS@ -I$(top_srcdir)/util
fliptest_SOURCES = fliptest.c
fliptest_LDADD = $(LDADD_COMMON)
index e0668abb26566208b8577596a46fc52e198b2d9a..975942aa13090a9aadc46e1ff3335669f69b6e41 100644 (file)
--- a/README
+++ b/README
Example to run with x11/dri2video for windowed display:
export DISPLAY=:0.0
./viddec3test ~/Videos/h264/quantum_of_solace-tlr1_h1080p.mov
+
+Example to run with video on 3D cube in kms mode:
+sudo ./viddec3test --kmscube artbeats.mov --loop --fps --fov 20
diff --git a/configure.ac b/configure.ac
index a87480907e535fddc67ae66f273899516a5ef7d3..332030478be8fa71afb975e1744a312922a20a37 100644 (file)
--- a/configure.ac
+++ b/configure.ac
fi
AM_CONDITIONAL(ENABLE_X11, [test "x$HAVE_X11" = xyes])
+# Check optional KMSCUBE:
+AC_ARG_ENABLE([kmscube], AS_HELP_STRING([--disable-kmscube], [disable kmscube display support]))
+AS_IF([test "x$enable_kmscube" != "xno"], [PKG_CHECK_EXISTS(gbm egl glesv2, [HAVE_KMSCUBE=yes], [HAVE_KMSCUBE=no])])
+if test "x$HAVE_KMSCUBE" = "xyes"; then
+ AC_DEFINE(HAVE_KMSCUBE, 1, [Have KMSCUBE support])
+ PKG_CHECK_MODULES(GBM, gbm)
+ PKG_CHECK_MODULES(EGL, egl)
+ PKG_CHECK_MODULES(GLES2, glesv2)
+else
+ AC_MSG_WARN([No KMSCUBE support detected, disabling KMSCUBE support])
+fi
+AM_CONDITIONAL(ENABLE_KMSCUBE, [test "x$HAVE_KMSCUBE" = xyes])
+
# Check for libdce and libav..
PKG_CHECK_MODULES(DCE, libdce libavformat libavutil, [HAVE_DCE=yes], [HAVE_DCE=no])
if test "x$HAVE_DCE" = "xyes"; then
diff --git a/util/Makefile.am b/util/Makefile.am
index eb1341180d95fd5d24d6774178dc42a3ee294e82..e88215d5c81efe71d2531252ded1341aaeb0935a 100644 (file)
--- a/util/Makefile.am
+++ b/util/Makefile.am
libutil_la_SOURCES += demux.c
endif
-libutil_la_LIBADD = @DRM_LIBS@ @X11_LIBS@ @DCE_LIBS@
-libutil_la_CFLAGS = @LIN_CFLAGS@ @DRM_CFLAGS@ @X11_CFLAGS@ @DCE_CFLAGS@ @WARN_CFLAGS@
+if ENABLE_KMSCUBE
+libutil_la_SOURCES += display-kmscube.c esTransform.c
+endif
+
+libutil_la_LIBADD = @DRM_LIBS@ @X11_LIBS@ @DCE_LIBS@ @GBM_LIBS@ @EGL_LIBS@ @GLES2_LIBS@
+libutil_la_CFLAGS = @LIN_CFLAGS@ @DRM_CFLAGS@ @X11_CFLAGS@ @DCE_CFLAGS@ @WARN_CFLAGS@ @GBM_CFLAGS@ @EGL_CFLAGS@ @GLES2_CFLAGS@
diff --git a/util/display-kmscube.c b/util/display-kmscube.c
--- /dev/null
+++ b/util/display-kmscube.c
@@ -0,0 +1,1142 @@
+/*
+ * Copyright (c) 2012 Arvin Schnell <arvin.schnell@gmail.com>
+ * Copyright (c) 2012 Rob Clark <rob@ti.com>
+ * Copyright (c) 2012 Vincent Stehlé <v-stehle@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.
+ *
+ * Based on kmscube application by Rob Clark, which was based on a egl cube
+ * test app originally written by Arvin Schnell. And based on display-kms by
+ * Rob Clark. And also based on xbmc eglImageExternal and
+ * TIRawVideoEGLImageHandle additions by Rob Clark.
+ *
+ * This display 'kmscube' draws a rotating 3d cube with EGL on kms display,
+ * with video mapped as a texture on the cube faces. This display has the
+ * particularity to have no framebuffer, per se, as we expect to render with
+ * EGL/GL ES, rather.
+ *
+ * TODO:
+ * - Remove static structs
+ * - Factorize kms code with display-kms
+ * - Enable from command line only; no auto detect at open like x11/kms
+ * - Cleanup display_kmscube options
+ * - Implement the "post" functions so that cube faces are updated
+ * - Do the necessary cleanup in the close function
+ * - Remove the extra level of structure inside display_kmscube
+ * - Cleanup commented out code
+ * - Revisit disp pointer in struct drm_fb
+ * - Better handling of rounding and alignment when allocating single planar
+ * YUV buffers.
+ * - Remove unused vertex colors.
+ * - Fix warnings
+ * - Allow selecting the mode from CLI, like display-kms
+ * - Better handling of cropping
+ * - Interactive keyboard keys for FOV, distance... with OSD?
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "util.h"
+
+#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 <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+
+#include "esUtil.h"
+
+#ifndef EGLIMAGE_FLAGS_YUV_CONFORMANT_RANGE
+// XXX these should come from some egl header??
+#define EGLIMAGE_FLAGS_YUV_CONFORMANT_RANGE (0 << 0)
+#define EGLIMAGE_FLAGS_YUV_FULL_RANGE (1 << 0)
+#define EGLIMAGE_FLAGS_YUV_BT601 (0 << 1)
+#define EGLIMAGE_FLAGS_YUV_BT709 (1 << 1)
+#endif
+#ifndef EGL_TI_raw_video
+# define EGL_TI_raw_video 1
+# define EGL_RAW_VIDEO_TI 0x333A /* eglCreateImageKHR target */
+# define EGL_RAW_VIDEO_TI2 0x333B /* eglCreateImageKHR target */
+# define EGL_GL_VIDEO_FOURCC_TI 0x3331 /* eglCreateImageKHR attribute */
+# define EGL_GL_VIDEO_WIDTH_TI 0x3332 /* eglCreateImageKHR attribute */
+# define EGL_GL_VIDEO_HEIGHT_TI 0x3333 /* eglCreateImageKHR attribute */
+# define EGL_GL_VIDEO_BYTE_STRIDE_TI 0x3334 /* eglCreateImageKHR attribute */
+# define EGL_GL_VIDEO_BYTE_SIZE_TI 0x3335 /* eglCreateImageKHR attribute */
+# define EGL_GL_VIDEO_YUV_FLAGS_TI 0x3336 /* eglCreateImageKHR attribute */
+#endif
+
+typedef EGLImageKHR (eglCreateImageKHR_t)(EGLDisplay dpy, EGLContext ctx,
+ EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list);
+
+typedef EGLBoolean (eglDestroyImageKHR_t)(EGLDisplay dpy, EGLImageKHR image);
+
+typedef void (glEGLImageTargetTexture2DOES_t)(GLenum target, GLeglImageOES image);
+
+
+#define to_display_kmscube(x) container_of(x, struct display_kmscube, base)
+struct display_kmscube {
+ struct display base;
+ uint32_t bo_flags,
+ i; // This is used to animate the cube.
+
+ // GL.
+ struct {
+ EGLDisplay display;
+ EGLConfig config;
+ EGLContext context;
+ EGLSurface surface;
+ GLuint program;
+ GLint modelviewmatrix, modelviewprojectionmatrix, normalmatrix, uniform_texture;
+ GLuint texture_name;
+ eglCreateImageKHR_t *eglCreateImageKHR;
+ eglDestroyImageKHR_t *eglDestroyImageKHR;
+ glEGLImageTargetTexture2DOES_t *glEGLImageTargetTexture2DOES;
+ float distance, fov;
+ } gl;
+
+ // GBM.
+ struct {
+ struct gbm_device *dev;
+ struct gbm_surface *surface;
+ } gbm;
+
+ // DRM.
+ struct {
+ // Note: fd is in base display
+ drmModeModeInfo *mode;
+ uint32_t crtc_id;
+ uint32_t connector_id;
+ drmModePlaneRes *plane_resources;
+ } drm;
+};
+
+/* All our buffers are only vid buffers, and they all have an EGLImage. */
+#define to_buffer_kmscube(x) container_of(x, struct buffer_kmscube, base)
+struct buffer_kmscube {
+ struct buffer base;
+ uint32_t fb_id;
+ EGLImageKHR egl_img;
+};
+
+struct drm_fb {
+ struct gbm_bo *bo;
+ struct display_kmscube *disp_kmsc;
+ uint32_t fb_id;
+};
+
+static int init_drm(struct display_kmscube *disp_kmsc)
+{
+ drmModeRes *resources;
+ drmModeConnector *connector = NULL;
+ drmModeEncoder *encoder = NULL;
+ int i, area;
+
+ resources = drmModeGetResources(disp_kmsc->base.fd);
+ if (!resources) {
+ ERROR("drmModeGetResources failed: %s", strerror(errno));
+ return -1;
+ }
+
+ /* find a connected connector: */
+ for (i = 0; i < resources->count_connectors; i++) {
+ connector = drmModeGetConnector(disp_kmsc->base.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..
+ */
+ ERROR("no connected connector!");
+ 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) {
+ disp_kmsc->drm.mode = current_mode;
+ area = current_area;
+ }
+ }
+
+ if (!disp_kmsc->drm.mode) {
+ ERROR("could not find mode!");
+ return -1;
+ }
+
+ /* find encoder: */
+ for (i = 0; i < resources->count_encoders; i++) {
+ encoder = drmModeGetEncoder(disp_kmsc->base.fd, resources->encoders[i]);
+ if (encoder->encoder_id == connector->encoder_id)
+ break;
+ drmModeFreeEncoder(encoder);
+ encoder = NULL;
+ }
+
+ if (!encoder) {
+ ERROR("no encoder!");
+ return -1;
+ }
+
+ disp_kmsc->drm.crtc_id = encoder->crtc_id;
+ disp_kmsc->drm.connector_id = connector->connector_id;
+
+ return 0;
+}
+
+static int init_gbm(struct display_kmscube *disp_kmsc)
+{
+ disp_kmsc->gbm.dev = gbm_create_device(disp_kmsc->base.fd);
+
+ disp_kmsc->gbm.surface = gbm_surface_create(disp_kmsc->gbm.dev,
+ disp_kmsc->drm.mode->hdisplay, disp_kmsc->drm.mode->vdisplay,
+ GBM_FORMAT_XRGB8888,
+ GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
+ if (!disp_kmsc->gbm.surface) {
+ ERROR("failed to create gbm surface");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int init_gl(struct display_kmscube *disp_kmsc)
+{
+ 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"
+ "attribute vec2 in_texuv; \n"
+ "\n"
+ "vec4 lightSource = vec4(2.0, 2.0, 20.0, 0.0);\n"
+ " \n"
+ "varying float VaryingLight; \n"
+ "varying vec2 vVaryingTexUV; \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"
+ " VaryingLight = max(0.0, dot(vEyeNormal, vLightDir));\n"
+ " vVaryingTexUV = in_texuv; \n"
+ "} \n";
+
+ static const char *fragment_shader_source =
+ "#extension GL_OES_EGL_image_external : require\n"
+ " \n"
+ "precision mediump float; \n"
+ " \n"
+ "uniform samplerExternalOES texture;\n"
+ " \n"
+ "varying float VaryingLight; \n"
+ "varying vec2 vVaryingTexUV; \n"
+ " \n"
+ "void main() \n"
+ "{ \n"
+ " vec4 t = texture2D(texture, vVaryingTexUV);\n"
+ " gl_FragColor = vec4(VaryingLight * t.rgb, 1.0);\n"
+ "} \n";
+
+ disp_kmsc->gl.display = eglGetDisplay(disp_kmsc->gbm.dev);
+
+ if (!eglInitialize(disp_kmsc->gl.display, &major, &minor)) {
+ ERROR("failed to initialize");
+ return -1;
+ }
+
+ printf("Using display %p with EGL version %d.%d\n",
+ disp_kmsc->gl.display, major, minor);
+
+ printf("EGL Version \"%s\"\n", eglQueryString(disp_kmsc->gl.display, EGL_VERSION));
+ printf("EGL Vendor \"%s\"\n", eglQueryString(disp_kmsc->gl.display, EGL_VENDOR));
+ printf("EGL Extensions \"%s\"\n", eglQueryString(disp_kmsc->gl.display, EGL_EXTENSIONS));
+
+ if (!eglBindAPI(EGL_OPENGL_ES_API)) {
+ ERROR("failed to bind api EGL_OPENGL_ES_API");
+ return -1;
+ }
+
+ if (!eglChooseConfig(disp_kmsc->gl.display, config_attribs, &disp_kmsc->gl.config, 1, &n) || n != 1) {
+ ERROR("failed to choose config: %d", n);
+ return -1;
+ }
+
+ disp_kmsc->gl.context = eglCreateContext(disp_kmsc->gl.display, disp_kmsc->gl.config,
+ EGL_NO_CONTEXT, context_attribs);
+ if (disp_kmsc->gl.context == NULL) {
+ ERROR("failed to create context");
+ return -1;
+ }
+
+ disp_kmsc->gl.surface = eglCreateWindowSurface(disp_kmsc->gl.display,
+ disp_kmsc->gl.config, disp_kmsc->gbm.surface, NULL);
+ if (disp_kmsc->gl.surface == EGL_NO_SURFACE) {
+ ERROR("failed to create egl surface");
+ return -1;
+ }
+
+ /* connect the context to the surface */
+ eglMakeCurrent(disp_kmsc->gl.display, disp_kmsc->gl.surface,
+ disp_kmsc->gl.surface, disp_kmsc->gl.context);
+
+ // EGL Image.
+ if (!(disp_kmsc->gl.eglCreateImageKHR = eglGetProcAddress("eglCreateImageKHR"))) {
+ ERROR("No eglCreateImageKHR?!");
+ return -1;
+ }
+
+ if (!(disp_kmsc->gl.eglDestroyImageKHR = eglGetProcAddress("eglDestroyImageKHR"))) {
+ ERROR("No eglDestroyImageKHR?!");
+ return -1;
+ }
+
+ if (!(disp_kmsc->gl.glEGLImageTargetTexture2DOES = eglGetProcAddress("glEGLImageTargetTexture2DOES"))) {
+ ERROR("No glEGLImageTargetTexture2DOES?!");
+ return -1;
+ }
+
+ const char *exts = glGetString(GL_EXTENSIONS);
+ printf("GL Extensions \"%s\"\n", exts);
+
+ if (!strstr(exts, "GL_TI_image_external_raw_video")) {
+ ERROR("No GL_TI_image_external_raw_video extension?!");
+ return -1;
+ }
+
+ 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;
+
+ ERROR("vertex shader compilation failed!:");
+ glGetShaderiv(vertex_shader, GL_INFO_LOG_LENGTH, &ret);
+ if (ret > 1) {
+ log = malloc(ret);
+ glGetShaderInfoLog(vertex_shader, ret, NULL, log);
+ ERROR("%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;
+
+ ERROR("fragment shader compilation failed!:");
+ glGetShaderiv(fragment_shader, GL_INFO_LOG_LENGTH, &ret);
+
+ if (ret > 1) {
+ log = malloc(ret);
+ glGetShaderInfoLog(fragment_shader, ret, NULL, log);
+ ERROR("%s", log);
+ }
+
+ return -1;
+ }
+
+ disp_kmsc->gl.program = glCreateProgram();
+
+ glAttachShader(disp_kmsc->gl.program, vertex_shader);
+ glAttachShader(disp_kmsc->gl.program, fragment_shader);
+
+ glBindAttribLocation(disp_kmsc->gl.program, 0, "in_position");
+ glBindAttribLocation(disp_kmsc->gl.program, 1, "in_normal");
+ glBindAttribLocation(disp_kmsc->gl.program, 2, "in_color");
+ glBindAttribLocation(disp_kmsc->gl.program, 3, "in_texuv");
+
+ glLinkProgram(disp_kmsc->gl.program);
+
+ glGetProgramiv(disp_kmsc->gl.program, GL_LINK_STATUS, &ret);
+ if (!ret) {
+ char *log;
+
+ ERROR("program linking failed!:");
+ glGetProgramiv(disp_kmsc->gl.program, GL_INFO_LOG_LENGTH, &ret);
+
+ if (ret > 1) {
+ log = malloc(ret);
+ glGetProgramInfoLog(disp_kmsc->gl.program, ret, NULL, log);
+ ERROR("%s", log);
+ }
+
+ return -1;
+ }
+
+ glUseProgram(disp_kmsc->gl.program);
+
+ disp_kmsc->gl.modelviewmatrix = glGetUniformLocation(disp_kmsc->gl.program, "modelviewMatrix");
+ disp_kmsc->gl.modelviewprojectionmatrix =
+ glGetUniformLocation(disp_kmsc->gl.program, "modelviewprojectionMatrix");
+ disp_kmsc->gl.normalmatrix = glGetUniformLocation(disp_kmsc->gl.program, "normalMatrix");
+
+ glViewport(0, 0, disp_kmsc->drm.mode->hdisplay, disp_kmsc->drm.mode->vdisplay);
+
+ // Texture.
+ glGenTextures(1, &disp_kmsc->gl.texture_name);
+ glBindTexture(GL_TEXTURE_EXTERNAL_OES, disp_kmsc->gl.texture_name);
+
+ if (glGetError() != GL_NO_ERROR) {
+ ERROR("glBindTexture!");
+ return -1;
+ }
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ disp_kmsc->gl.uniform_texture = glGetUniformLocation(disp_kmsc->gl.program, "uniform_texture");
+ glUniform1i(disp_kmsc->gl.uniform_texture, 0);
+
+ return 0;
+}
+
+static void draw(struct display_kmscube *disp_kmsc)
+{
+ 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
+ };
+
+ static const GLfloat vTexUVs[] = {
+ // front
+ 0.0f, 1.0f,
+ 1.0f, 1.0f,
+ 0.0f, 0.0f,
+ 1.0f, 0.0f,
+ // back
+ 0.0f, 1.0f,
+ 1.0f, 1.0f,
+ 0.0f, 0.0f,
+ 1.0f, 0.0f,
+ // right
+ 0.0f, 1.0f,
+ 1.0f, 1.0f,
+ 0.0f, 0.0f,
+ 1.0f, 0.0f,
+ // left
+ 0.0f, 1.0f,
+ 1.0f, 1.0f,
+ 0.0f, 0.0f,
+ 1.0f, 0.0f,
+ // top
+ 0.0f, 1.0f,
+ 1.0f, 1.0f,
+ 0.0f, 0.0f,
+ 1.0f, 0.0f,
+ // bottom
+ 0.0f, 1.0f,
+ 1.0f, 1.0f,
+ 0.0f, 0.0f,
+ 1.0f, 0.0f,
+ };
+
+ /* 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);
+
+ glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, 0, vTexUVs);
+ glEnableVertexAttribArray(3);
+
+ esMatrixLoadIdentity(&modelview);
+ esTranslate(&modelview, 0.0f, 0.0f, -disp_kmsc->gl.distance);
+ esRotate(&modelview, 45.0f + (0.25f * disp_kmsc->i), 1.0f, 0.0f, 0.0f);
+ esRotate(&modelview, 45.0f - (0.5f * disp_kmsc->i), 0.0f, 1.0f, 0.0f);
+ esRotate(&modelview, 10.0f + (0.15f * disp_kmsc->i), 0.0f, 0.0f, 1.0f);
+
+ GLfloat aspect = (GLfloat)(disp_kmsc->drm.mode->hdisplay) / (GLfloat)(disp_kmsc->drm.mode->vdisplay);
+
+ ESMatrix projection;
+ esMatrixLoadIdentity(&projection);
+ esPerspective(&projection, disp_kmsc->gl.fov, aspect, 1.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(disp_kmsc->gl.modelviewmatrix, 1, GL_FALSE, &modelview.m[0][0]);
+ glUniformMatrix4fv(disp_kmsc->gl.modelviewprojectionmatrix, 1, GL_FALSE, &modelviewprojection.m[0][0]);
+ glUniformMatrix3fv(disp_kmsc->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);
+ struct display_kmscube *disp_kmsc = fb->disp_kmsc;
+
+ if (fb->fb_id)
+ drmModeRmFB(disp_kmsc->base.fd, fb->fb_id);
+
+ free(fb);
+}
+
+static struct drm_fb * drm_fb_get_from_bo(struct display_kmscube *disp_kmsc, 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;
+ fb->disp_kmsc = disp_kmsc;
+
+ 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(disp_kmsc->base.fd, width, height, 24, 32, stride, handle, &fb->fb_id);
+ if (ret) {
+ ERROR("failed to create fb: %s", 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;
+}
+
+static struct omap_bo *
+alloc_bo(struct display *disp, uint32_t bpp, uint32_t width, uint32_t height,
+ uint32_t *bo_handle, uint32_t *pitch)
+{
+ struct display_kmscube *disp_kmsc = to_display_kmscube(disp);
+ struct omap_bo *bo;
+ uint32_t bo_flags = disp_kmsc->bo_flags;
+
+ if ((bo_flags & OMAP_BO_TILED) == OMAP_BO_TILED) {
+ bo_flags &= ~OMAP_BO_TILED;
+ if (bpp == 8) {
+ bo_flags |= OMAP_BO_TILED_8;
+ } else if (bpp == 16) {
+ bo_flags |= OMAP_BO_TILED_16;
+ } else if (bpp == 32) {
+ bo_flags |= OMAP_BO_TILED_32;
+ }
+ }
+
+ bo_flags |= OMAP_BO_WC;
+
+ if (bo_flags & OMAP_BO_TILED) {
+ bo = omap_bo_new_tiled(disp->dev, width, height, bo_flags);
+ } else {
+ bo = omap_bo_new(disp->dev, width * height * bpp / 8, bo_flags);
+ }
+
+ if (bo) {
+ *bo_handle = omap_bo_handle(bo);
+ *pitch = width * bpp / 8;
+ if (bo_flags & OMAP_BO_TILED)
+ *pitch = ALIGN2(*pitch, PAGE_SHIFT);
+ }
+
+ return bo;
+}
+
+/* We allocate single planar buffers, always. This, for EGLImage. Also, we
+ * create on EGLImageKHR per buffer. */
+static struct buffer *
+alloc_buffer(struct display *disp, uint32_t fourcc, uint32_t w, uint32_t h)
+{
+ struct display_kmscube *disp_kmsc = to_display_kmscube(disp);
+ struct buffer_kmscube *buf_kmsc;
+ struct buffer *buf;
+ uint32_t bo_handles[4] = {0}, offsets[4] = {0};
+ int ret;
+
+ buf_kmsc = calloc(1, sizeof(*buf_kmsc));
+ if (!buf_kmsc) {
+ ERROR("allocation failed");
+ return NULL;
+ }
+ buf = &buf_kmsc->base;
+
+ buf->fourcc = fourcc;
+ buf->width = w;
+ buf->height = h;
+ buf->multiplanar = false;
+
+ buf->nbo = 1;
+
+ if (!fourcc)
+ fourcc = FOURCC('A','R','2','4');
+
+ switch(fourcc) {
+ case FOURCC('A','R','2','4'):
+ buf->nbo = 1;
+ buf->bo[0] = alloc_bo(disp, 32, buf->width, buf->height,
+ &bo_handles[0], &buf->pitches[0]);
+ break;
+ case FOURCC('U','Y','V','Y'):
+ case FOURCC('Y','U','Y','V'):
+ buf->nbo = 1;
+ buf->bo[0] = alloc_bo(disp, 16, buf->width, buf->height,
+ &bo_handles[0], &buf->pitches[0]);
+ break;
+ case FOURCC('N','V','1','2'):
+ buf->nbo = 1;
+ buf->bo[0] = alloc_bo(disp, 8, buf->width, (buf->height + buf->height/2),
+ &bo_handles[0], &buf->pitches[0]);
+ break;
+ case FOURCC('I','4','2','0'):
+ buf->nbo = 1;
+ buf->bo[0] = alloc_bo(disp, 8, buf->width, (buf->height + buf->height/2),
+ &bo_handles[0], &buf->pitches[0]);
+ break;
+ default:
+ ERROR("invalid format: 0x%08x", fourcc);
+ goto fail;
+ }
+
+ // Create EGLImage and return.
+ // TODO: cropping attributes when this will be supported.
+ EGLint attr[] = {
+ EGL_GL_VIDEO_FOURCC_TI, buf->fourcc,
+ EGL_GL_VIDEO_WIDTH_TI, buf->width,
+ EGL_GL_VIDEO_HEIGHT_TI, buf->height,
+ EGL_GL_VIDEO_BYTE_SIZE_TI, omap_bo_size(buf->bo[0]),
+ // TODO: pick proper YUV flags..
+ EGL_GL_VIDEO_YUV_FLAGS_TI, EGLIMAGE_FLAGS_YUV_CONFORMANT_RANGE | EGLIMAGE_FLAGS_YUV_BT601,
+ EGL_NONE
+ };
+
+ int fd = omap_bo_dmabuf(buf->bo[0]);
+
+ buf_kmsc->egl_img =
+ disp_kmsc->gl.eglCreateImageKHR(disp_kmsc->gl.display, EGL_NO_CONTEXT,
+ EGL_RAW_VIDEO_TI2, (EGLClientBuffer)fd, attr);
+
+ if (buf_kmsc->egl_img == EGL_NO_IMAGE_KHR) {
+ ERROR("eglCreateImageKHR failed!\n");
+ return NULL;
+ }
+
+ return buf;
+
+fail:
+ // XXX cleanup
+ return NULL;
+}
+
+static struct buffer **
+alloc_buffers(struct display *disp, uint32_t n,
+ uint32_t fourcc, uint32_t w, uint32_t h)
+{
+ struct buffer **bufs;
+ uint32_t i = 0;
+
+ bufs = calloc(n, sizeof(*bufs));
+ if (!bufs) {
+ ERROR("allocation failed");
+ goto fail;
+ }
+
+ for (i = 0; i < n; i++) {
+ bufs[i] = alloc_buffer(disp, fourcc, w, h);
+ if (!bufs[i]) {
+ ERROR("allocation failed");
+ goto fail;
+ }
+ }
+
+ return bufs;
+
+fail:
+ // XXX cleanup
+ return NULL;
+}
+
+static struct buffer **
+get_buffers(struct display *disp, uint32_t n)
+{
+ MSG("get_buffers not supported!");
+ return NULL;
+}
+
+static struct buffer **
+get_vid_buffers(struct display *disp, uint32_t n,
+ uint32_t fourcc, uint32_t w, uint32_t h)
+{
+ return alloc_buffers(disp, n, fourcc, w, h);
+}
+
+static int
+post_buffer(struct display *disp, struct buffer *buf)
+{
+ ERROR("post_buffer not supported!");
+ return -1;
+}
+
+static int
+post_vid_buffer(struct display *disp, struct buffer *buf,
+ uint32_t x, uint32_t y, uint32_t w, uint32_t h)
+{
+ struct display_kmscube *disp_kmsc = to_display_kmscube(disp);
+ struct buffer_kmscube *buf_kmsc = to_buffer_kmscube(buf);
+
+// struct buffer_kmscube *buf_kmsc = to_buffer_kmscube(buf);
+// int ret = 0;
+// uint32_t i, j;
+//
+// /* ensure we have the overlay setup: */
+// for (i = 0; i < disp_kmsc->connectors_count; i++) {
+// struct connector *connector = &disp_kmsc->connector[i];
+// uint32_t used_planes = 0;
+// drmModeModeInfo *mode = connector->mode;
+//
+// if (! mode) {
+// continue;
+// }
+//
+// if (! disp_kmsc->ovr[i]) {
+//
+// for (j = 0; j < disp_kmsc->plane_resources->count_planes; j++) {
+// drmModePlane *ovr = drmModeGetPlane(disp->fd,
+// disp_kmsc->plane_resources->planes[j]);
+// if ((ovr->possible_crtcs & (1 << connector->pipe)) &&
+// !(used_planes & (1 << j))) {
+// disp_kmsc->ovr[i] = ovr;
+// used_planes |= (1 << j);
+// break;
+// }
+// }
+// }
+//
+// if (! disp_kmsc->ovr[i]) {
+// MSG("Could not find plane for crtc %d", connector->crtc);
+// ret = -1;
+// /* carry on and see if we can find at least one usable plane */
+// continue;
+// }
+//
+// ret = drmModeSetPlane(disp->fd, disp_kmsc->ovr[i]->plane_id,
+// connector->crtc, buf_kmsc->fb_id, 0,
+// /* make video fullscreen: */
+// 0, 0, mode->hdisplay, mode->vdisplay,
+// /* source/cropping coordinates are given in Q16 */
+// x << 16, y << 16, w << 16, h << 16);
+// if (ret) {
+// ERROR("failed to enable plane %d: %s",
+// disp_kmsc->ovr[i]->plane_id, strerror(errno));
+// }
+// }
+//
+// return ret;
+
+ // TODO: For now, draw cube...
+
+ fd_set fds;
+ drmEventContext evctx = {
+ .version = DRM_EVENT_CONTEXT_VERSION,
+ .page_flip_handler = page_flip_handler,
+ };
+ struct gbm_bo *bo;
+ struct drm_fb *fb;
+ int ret;
+ struct gbm_bo *next_bo;
+ int waiting_for_flip = 1;
+
+ FD_ZERO(&fds);
+ FD_SET(0, &fds);
+ FD_SET(disp_kmsc->base.fd, &fds);
+
+ // Update video texture / EGL Image.
+ disp_kmsc->gl.glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, buf_kmsc->egl_img);
+
+ if (glGetError() != GL_NO_ERROR) {
+ ERROR("glEGLImageTargetTexture2DOES!\n");
+ return -1;
+ }
+
+ // Draw cube.
+ draw(disp_kmsc);
+ (disp_kmsc->i)++;
+
+ eglSwapBuffers(disp_kmsc->gl.display, disp_kmsc->gl.surface);
+ next_bo = gbm_surface_lock_front_buffer(disp_kmsc->gbm.surface);
+ fb = drm_fb_get_from_bo(disp_kmsc, next_bo);
+
+ /*
+ * Here you could also update drm plane layers if you want
+ * hw composition
+ */
+
+ ret = drmModePageFlip(disp_kmsc->base.fd, disp_kmsc->drm.crtc_id, fb->fb_id,
+ DRM_MODE_PAGE_FLIP_EVENT, &waiting_for_flip);
+ if (ret) {
+ ERROR("failed to queue page flip: %s\n", strerror(errno));
+ return -1;
+ }
+
+ while (waiting_for_flip) {
+ ret = select(disp_kmsc->base.fd + 1, &fds, NULL, NULL, NULL);
+ if (ret < 0) {
+ ERROR("select err: %s\n", strerror(errno));
+ return ret;
+ } else if (ret == 0) {
+ ERROR("select timeout!\n");
+ return -1;
+ } else if (FD_ISSET(0, &fds)) {
+ ERROR("user interrupted!\n");
+ break;
+ }
+ drmHandleEvent(disp_kmsc->base.fd, &evctx);
+ }
+
+ /* release last buffer to render on again: */
+ gbm_surface_release_buffer(disp_kmsc->gbm.surface, bo);
+ bo = next_bo;
+
+ return 0;
+}
+
+static void
+close_kmscube(struct display *disp)
+{
+}
+
+void
+disp_kmscube_usage(void)
+{
+ MSG("KMSCUBE Display Options:");
+ MSG("\t--distance <float>\tset cube distance (default 8.0)");
+ MSG("\t--fov <float>\tset field of vision (default 45.0)");
+ MSG("\t--kmscube\tEnable display kmscube (default: disabled)");
+}
+
+struct display *
+disp_kmscube_open(int argc, char **argv)
+{
+ struct display_kmscube *disp_kmsc = NULL;
+ struct display *disp;
+ struct gbm_bo *bo;
+ struct drm_fb *fb;
+ int ret, i, enabled = 0;
+ float fov = 45, distance = 8;
+
+ /* note: set args to NULL after we've parsed them so other modules know
+ * that it is already parsed (since the arg parsing is decentralized)
+ */
+ for (i = 1; i < argc; i++) {
+ if (!argv[i]) {
+ continue;
+ }
+ if (!strcmp("--distance", argv[i])) {
+ argv[i++] = NULL;
+ if (sscanf(argv[i], "%f", &distance) != 1) {
+ ERROR("invalid arg: %s", argv[i]);
+ goto fail;
+ }
+ } else if (!strcmp("--fov", argv[i])) {
+ argv[i++] = NULL;
+ if (sscanf(argv[i], "%f", &fov) != 1) {
+ ERROR("invalid arg: %s", argv[i]);
+ goto fail;
+ }
+ } else if (!strcmp("--kmscube", argv[i])) {
+ enabled = 1;
+ } else {
+ /* ignore */
+ continue;
+ }
+ argv[i] = NULL;
+ }
+
+ // If not explicitely enabled from command line, fail (and fallback to disp-kms).
+ if (!enabled)
+ goto fail;
+
+ disp_kmsc = calloc(1, sizeof(*disp_kmsc));
+ if (!disp_kmsc) {
+ ERROR("allocation failed");
+ goto fail;
+ }
+ disp_kmsc->gl.distance = distance;
+ disp_kmsc->gl.fov = fov;
+ disp = &disp_kmsc->base;
+
+ disp->fd = drmOpen("omapdrm", NULL);
+ if (disp->fd < 0) {
+ ERROR("could not open drm device: %s (%d)", strerror(errno), errno);
+ goto fail;
+ }
+
+ disp->dev = omap_device_new(disp->fd);
+ if (!disp->dev) {
+ ERROR("couldn't create device");
+ goto fail;
+ }
+
+ disp->get_buffers = get_buffers;
+ disp->get_vid_buffers = get_vid_buffers;
+ disp->post_buffer = post_buffer;
+ disp->post_vid_buffer = post_vid_buffer;
+ disp->close = close_kmscube;
+
+ if (init_drm(disp_kmsc)) {
+ ERROR("couldn't init drm");
+ goto fail;
+ }
+
+ disp_kmsc->drm.plane_resources = drmModeGetPlaneResources(disp->fd);
+ if (!disp_kmsc->drm.plane_resources) {
+ ERROR("drmModeGetPlaneResources failed: %s", strerror(errno));
+ goto fail;
+ }
+
+ if (init_gbm(disp_kmsc)) {
+ ERROR("couldn't init gbm");
+ goto fail;
+ }
+
+ if (init_gl(disp_kmsc)) {
+ ERROR("couldn't init gl(es)");
+ goto fail;
+ }
+
+ /* clear the color buffer */
+ glClearColor(0.5, 0.5, 0.5, 1.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+ eglSwapBuffers(disp_kmsc->gl.display, disp_kmsc->gl.surface);
+ bo = gbm_surface_lock_front_buffer(disp_kmsc->gbm.surface);
+ fb = drm_fb_get_from_bo(disp_kmsc, bo);
+
+ /* set mode: */
+ ret = drmModeSetCrtc(disp_kmsc->base.fd, disp_kmsc->drm.crtc_id, fb->fb_id, 0, 0,
+ &disp_kmsc->drm.connector_id, 1, disp_kmsc->drm.mode);
+ if (ret) {
+ ERROR("failed to set mode: %s\n", strerror(errno));
+ return ret;
+ }
+
+ disp->width = 0;
+ disp->height = 0;
+ disp->multiplanar = false;
+// for (i = 0; i < (int)disp_kmsc->connectors_count; i++) {
+// struct connector *c = &disp_kmsc->connector[i];
+// connector_find_mode(disp, c);
+// if (c->mode == NULL)
+// continue;
+// /* setup side-by-side virtual display */
+// disp->width += c->mode->hdisplay;
+// if (disp->height < c->mode->vdisplay) {
+// disp->height = c->mode->vdisplay;
+// }
+// }
+//
+// MSG("using %d connectors, %dx%d display, multiplanar: %d",
+// disp_kmsc->connectors_count, disp->width, disp->height, disp->multiplanar);
+
+ return disp;
+
+fail:
+ // XXX cleanup
+ return NULL;
+}
diff --git a/util/esTransform.c b/util/esTransform.c
--- /dev/null
+++ b/util/esTransform.c
@@ -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/util/esUtil.h b/util/esUtil.h
--- /dev/null
+++ b/util/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/util/util.c b/util/util.c
index 0b9e0eb984ff69fc19568d4cd19ff70d6375f41d..0c260f2f707c82a363b649a2324b15fb942e30fa 100644 (file)
--- a/util/util.c
+++ b/util/util.c
void disp_x11_close(struct display *disp);
#endif
+#ifdef HAVE_KMSCUBE
+void disp_kmscube_usage(void);
+struct display * disp_kmscube_open(int argc, char **argv);
+#endif
+
void
disp_usage(void)
{
#ifdef HAVE_X11
disp_x11_usage();
+#endif
+#ifdef HAVE_KMSCUBE
+ disp_kmscube_usage();
#endif
disp_kms_usage();
}
if (disp)
goto out;
#endif
+#ifdef HAVE_KMSCUBE
+ disp = disp_kmscube_open(argc, argv);
+ if (disp)
+ goto out;
+#endif
disp = disp_kms_open(argc, argv);