bc2e89d19a772f623431b0fd832416db7f90e69e
[glsdk/kmscube.git] / kmscube.c
1 /*
2  * Copyright (c) 2012 Arvin Schnell <arvin.schnell@gmail.com>
3  * Copyright (c) 2012 Rob Clark <rob@ti.com>
4  * Copyright (c) 2013 Anand Balagopalakrishnan <anandb@ti.com>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sub license,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice (including the
14  * next paragraph) shall be included in all copies or substantial portions
15  * of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
20  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23  * DEALINGS IN THE SOFTWARE.
24  */
26 /* Based on a egl cube test app originally written by Arvin Schnell */
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 #include <string.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <errno.h>
36 #include <signal.h>
38 #include <xf86drm.h>
39 #include <xf86drmMode.h>
40 #include <gbm.h>
42 #include "esUtil.h"
44 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
46 #define MAX_DISPLAYS    (4)
47 uint8_t DISP_ID = 0;
48 uint8_t all_display = 0;
49 int8_t connector_id = -1;
51 static struct {
52         EGLDisplay display;
53         EGLConfig config;
54         EGLContext context;
55         EGLSurface surface;
56         GLuint program;
57         GLint modelviewmatrix, modelviewprojectionmatrix, normalmatrix;
58         GLuint vbo;
59         GLuint positionsoffset, colorsoffset, normalsoffset;
60         GLuint vertex_shader, fragment_shader;
61 } gl;
63 static struct {
64         struct gbm_device *dev;
65         struct gbm_surface *surface;
66 } gbm;
68 static struct {
69         int fd;
70         uint32_t ndisp;
71         uint32_t crtc_id[MAX_DISPLAYS];
72         uint32_t connector_id[MAX_DISPLAYS];
73         uint32_t resource_id;
74         uint32_t encoder[MAX_DISPLAYS];
75         drmModeModeInfo *mode[MAX_DISPLAYS];
76         drmModeConnector *connectors[MAX_DISPLAYS];
77 } drm;
79 struct drm_fb {
80         struct gbm_bo *bo;
81         uint32_t fb_id;
82 };
84 static int init_drm(void)
85 {
86         static const char *modules[] = {
87                         "omapdrm", "i915", "radeon", "nouveau", "vmwgfx", "exynos"
88         };
89         drmModeRes *resources;
90         drmModeConnector *connector = NULL;
91         drmModeEncoder *encoder = NULL;
92         int i, j;
93         uint32_t maxRes, curRes;
95         for (i = 0; i < ARRAY_SIZE(modules); i++) {
96                 printf("trying to load module %s...", modules[i]);
97                 drm.fd = drmOpen(modules[i], NULL);
98                 if (drm.fd < 0) {
99                         printf("failed.\n");
100                 } else {
101                         printf("success.\n");
102                         break;
103                 }
104         }
106         if (drm.fd < 0) {
107                 printf("could not open drm device\n");
108                 return -1;
109         }
111         resources = drmModeGetResources(drm.fd);
112         if (!resources) {
113                 printf("drmModeGetResources failed: %s\n", strerror(errno));
114                 return -1;
115         }
116         drm.resource_id = (uint32_t) resources;
118         /* find a connected connector: */
119         for (i = 0; i < resources->count_connectors; i++) {
120                 connector = drmModeGetConnector(drm.fd, resources->connectors[i]);
121                 if (connector->connection == DRM_MODE_CONNECTED) {
122                         /* choose the first supported mode */
123                         drm.mode[drm.ndisp] = &connector->modes[0];
124                         drm.connector_id[drm.ndisp] = connector->connector_id;
126                         for (j=0; j<resources->count_encoders; j++) {
127                                 encoder = drmModeGetEncoder(drm.fd, resources->encoders[j]);
128                                 if (encoder->encoder_id == connector->encoder_id)
129                                         break;
131                                 drmModeFreeEncoder(encoder);
132                                 encoder = NULL;
133                         }
135                         if (!encoder) {
136                                 printf("no encoder!\n");
137                                 return -1;
138                         }
140                         drm.encoder[drm.ndisp]  = (uint32_t) encoder;
141                         drm.crtc_id[drm.ndisp] = encoder->crtc_id;
142                         drm.connectors[drm.ndisp] = connector;
144                         printf("### Display [%d]: CRTC = %d, Connector = %d\n", drm.ndisp, drm.crtc_id[drm.ndisp], drm.connector_id[drm.ndisp]);
145                         printf("\tMode chosen [%s] : Clock => %d, Vertical refresh => %d, Type => %d\n", drm.mode[drm.ndisp]->name, drm.mode[drm.ndisp]->clock, drm.mode[drm.ndisp]->vrefresh, drm.mode[drm.ndisp]->type);
146                         printf("\tHorizontal => %d, %d, %d, %d, %d\n", drm.mode[drm.ndisp]->hdisplay, drm.mode[drm.ndisp]->hsync_start, drm.mode[drm.ndisp]->hsync_end, drm.mode[drm.ndisp]->htotal, drm.mode[drm.ndisp]->hskew);
147                         printf("\tVertical => %d, %d, %d, %d, %d\n", drm.mode[drm.ndisp]->vdisplay, drm.mode[drm.ndisp]->vsync_start, drm.mode[drm.ndisp]->vsync_end, drm.mode[drm.ndisp]->vtotal, drm.mode[drm.ndisp]->vscan);
149                         /* If a connector_id is specified, use the corresponding display */
150                         if ((connector_id != -1) && (connector_id == drm.connector_id[drm.ndisp]))
151                                 DISP_ID = drm.ndisp;
153                         /* If all displays are enabled, choose the connector with maximum
154                         * resolution as the primary display */
155                         if (all_display) {
156                                 maxRes = drm.mode[DISP_ID]->vdisplay * drm.mode[DISP_ID]->hdisplay;
157                                 curRes = drm.mode[drm.ndisp]->vdisplay * drm.mode[drm.ndisp]->hdisplay;
159                                 if (curRes > maxRes)
160                                         DISP_ID = drm.ndisp;
161                         }
163                         drm.ndisp++;
164                 } else {
165                         drmModeFreeConnector(connector);
166                 }
167         }
169         if (drm.ndisp == 0) {
170                 /* we could be fancy and listen for hotplug events and wait for
171                  * a connector..
172                  */
173                 printf("no connected connector!\n");
174                 return -1;
175         }
177         return 0;
180 static int init_gbm(void)
182         gbm.dev = gbm_create_device(drm.fd);
184         gbm.surface = gbm_surface_create(gbm.dev,
185                         drm.mode[DISP_ID]->hdisplay, drm.mode[DISP_ID]->vdisplay,
186                         GBM_FORMAT_XRGB8888,
187                         GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
188         if (!gbm.surface) {
189                 printf("failed to create gbm surface\n");
190                 return -1;
191         }
193         return 0;
196 static int init_gl(void)
198         EGLint major, minor, n;
199         GLint ret;
201         static const GLfloat vVertices[] = {
202                         // front
203                         -1.0f, -1.0f, +1.0f, // point blue
204                         +1.0f, -1.0f, +1.0f, // point magenta
205                         -1.0f, +1.0f, +1.0f, // point cyan
206                         +1.0f, +1.0f, +1.0f, // point white
207                         // back
208                         +1.0f, -1.0f, -1.0f, // point red
209                         -1.0f, -1.0f, -1.0f, // point black
210                         +1.0f, +1.0f, -1.0f, // point yellow
211                         -1.0f, +1.0f, -1.0f, // point green
212                         // right
213                         +1.0f, -1.0f, +1.0f, // point magenta
214                         +1.0f, -1.0f, -1.0f, // point red
215                         +1.0f, +1.0f, +1.0f, // point white
216                         +1.0f, +1.0f, -1.0f, // point yellow
217                         // left
218                         -1.0f, -1.0f, -1.0f, // point black
219                         -1.0f, -1.0f, +1.0f, // point blue
220                         -1.0f, +1.0f, -1.0f, // point green
221                         -1.0f, +1.0f, +1.0f, // point cyan
222                         // top
223                         -1.0f, +1.0f, +1.0f, // point cyan
224                         +1.0f, +1.0f, +1.0f, // point white
225                         -1.0f, +1.0f, -1.0f, // point green
226                         +1.0f, +1.0f, -1.0f, // point yellow
227                         // bottom
228                         -1.0f, -1.0f, -1.0f, // point black
229                         +1.0f, -1.0f, -1.0f, // point red
230                         -1.0f, -1.0f, +1.0f, // point blue
231                         +1.0f, -1.0f, +1.0f  // point magenta
232         };
234         static const GLfloat vColors[] = {
235                         // front
236                         0.0f,  0.0f,  1.0f, // blue
237                         1.0f,  0.0f,  1.0f, // magenta
238                         0.0f,  1.0f,  1.0f, // cyan
239                         1.0f,  1.0f,  1.0f, // white
240                         // back
241                         1.0f,  0.0f,  0.0f, // red
242                         0.0f,  0.0f,  0.0f, // black
243                         1.0f,  1.0f,  0.0f, // yellow
244                         0.0f,  1.0f,  0.0f, // green
245                         // right
246                         1.0f,  0.0f,  1.0f, // magenta
247                         1.0f,  0.0f,  0.0f, // red
248                         1.0f,  1.0f,  1.0f, // white
249                         1.0f,  1.0f,  0.0f, // yellow
250                         // left
251                         0.0f,  0.0f,  0.0f, // black
252                         0.0f,  0.0f,  1.0f, // blue
253                         0.0f,  1.0f,  0.0f, // green
254                         0.0f,  1.0f,  1.0f, // cyan
255                         // top
256                         0.0f,  1.0f,  1.0f, // cyan
257                         1.0f,  1.0f,  1.0f, // white
258                         0.0f,  1.0f,  0.0f, // green
259                         1.0f,  1.0f,  0.0f, // yellow
260                         // bottom
261                         0.0f,  0.0f,  0.0f, // black
262                         1.0f,  0.0f,  0.0f, // red
263                         0.0f,  0.0f,  1.0f, // blue
264                         1.0f,  0.0f,  1.0f  // magenta
265         };
267         static const GLfloat vNormals[] = {
268                         // front
269                         +0.0f, +0.0f, +1.0f, // forward
270                         +0.0f, +0.0f, +1.0f, // forward
271                         +0.0f, +0.0f, +1.0f, // forward
272                         +0.0f, +0.0f, +1.0f, // forward
273                         // back
274                         +0.0f, +0.0f, -1.0f, // backbard
275                         +0.0f, +0.0f, -1.0f, // backbard
276                         +0.0f, +0.0f, -1.0f, // backbard
277                         +0.0f, +0.0f, -1.0f, // backbard
278                         // right
279                         +1.0f, +0.0f, +0.0f, // right
280                         +1.0f, +0.0f, +0.0f, // right
281                         +1.0f, +0.0f, +0.0f, // right
282                         +1.0f, +0.0f, +0.0f, // right
283                         // left
284                         -1.0f, +0.0f, +0.0f, // left
285                         -1.0f, +0.0f, +0.0f, // left
286                         -1.0f, +0.0f, +0.0f, // left
287                         -1.0f, +0.0f, +0.0f, // left
288                         // top
289                         +0.0f, +1.0f, +0.0f, // up
290                         +0.0f, +1.0f, +0.0f, // up
291                         +0.0f, +1.0f, +0.0f, // up
292                         +0.0f, +1.0f, +0.0f, // up
293                         // bottom
294                         +0.0f, -1.0f, +0.0f, // down
295                         +0.0f, -1.0f, +0.0f, // down
296                         +0.0f, -1.0f, +0.0f, // down
297                         +0.0f, -1.0f, +0.0f  // down
298         };
300         static const EGLint context_attribs[] = {
301                 EGL_CONTEXT_CLIENT_VERSION, 2,
302                 EGL_NONE
303         };
305         static const EGLint config_attribs[] = {
306                 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
307                 EGL_RED_SIZE, 1,
308                 EGL_GREEN_SIZE, 1,
309                 EGL_BLUE_SIZE, 1,
310                 EGL_ALPHA_SIZE, 0,
311                 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
312                 EGL_NONE
313         };
315         static const char *vertex_shader_source =
316                         "uniform mat4 modelviewMatrix;      \n"
317                         "uniform mat4 modelviewprojectionMatrix;\n"
318                         "uniform mat3 normalMatrix;         \n"
319                         "                                   \n"
320                         "attribute vec4 in_position;        \n"
321                         "attribute vec3 in_normal;          \n"
322                         "attribute vec4 in_color;           \n"
323                         "\n"
324                         "vec4 lightSource = vec4(2.0, 2.0, 20.0, 0.0);\n"
325                         "                                   \n"
326                         "varying vec4 vVaryingColor;        \n"
327                         "                                   \n"
328                         "void main()                        \n"
329                         "{                                  \n"
330                         "    gl_Position = modelviewprojectionMatrix * in_position;\n"
331                         "    vec3 vEyeNormal = normalMatrix * in_normal;\n"
332                         "    vec4 vPosition4 = modelviewMatrix * in_position;\n"
333                         "    vec3 vPosition3 = vPosition4.xyz / vPosition4.w;\n"
334                         "    vec3 vLightDir = normalize(lightSource.xyz - vPosition3);\n"
335                         "    float diff = max(0.0, dot(vEyeNormal, vLightDir));\n"
336                         "    vVaryingColor = vec4(diff * in_color.rgb, 1.0);\n"
337                         "}                                  \n";
339         static const char *fragment_shader_source =
340                         "precision mediump float;           \n"
341                         "                                   \n"
342                         "varying vec4 vVaryingColor;        \n"
343                         "                                   \n"
344                         "void main()                        \n"
345                         "{                                  \n"
346                         "    gl_FragColor = vVaryingColor;  \n"
347                         "}                                  \n";
349         gl.display = eglGetDisplay(gbm.dev);
351         if (!eglInitialize(gl.display, &major, &minor)) {
352                 printf("failed to initialize\n");
353                 return -1;
354         }
356         printf("Using display %p with EGL version %d.%d\n",
357                         gl.display, major, minor);
359         printf("EGL Version \"%s\"\n", eglQueryString(gl.display, EGL_VERSION));
360         printf("EGL Vendor \"%s\"\n", eglQueryString(gl.display, EGL_VENDOR));
361         printf("EGL Extensions \"%s\"\n", eglQueryString(gl.display, EGL_EXTENSIONS));
363         if (!eglBindAPI(EGL_OPENGL_ES_API)) {
364                 printf("failed to bind api EGL_OPENGL_ES_API\n");
365                 return -1;
366         }
368         if (!eglChooseConfig(gl.display, config_attribs, &gl.config, 1, &n) || n != 1) {
369                 printf("failed to choose config: %d\n", n);
370                 return -1;
371         }
373         gl.context = eglCreateContext(gl.display, gl.config,
374                         EGL_NO_CONTEXT, context_attribs);
375         if (gl.context == NULL) {
376                 printf("failed to create context\n");
377                 return -1;
378         }
380         gl.surface = eglCreateWindowSurface(gl.display, gl.config, gbm.surface, NULL);
381         if (gl.surface == EGL_NO_SURFACE) {
382                 printf("failed to create egl surface\n");
383                 return -1;
384         }
386         /* connect the context to the surface */
387         eglMakeCurrent(gl.display, gl.surface, gl.surface, gl.context);
390         gl.vertex_shader = glCreateShader(GL_VERTEX_SHADER);
392         glShaderSource(gl.vertex_shader, 1, &vertex_shader_source, NULL);
393         glCompileShader(gl.vertex_shader);
395         glGetShaderiv(gl.vertex_shader, GL_COMPILE_STATUS, &ret);
396         if (!ret) {
397                 char *log;
399                 printf("vertex shader compilation failed!:\n");
400                 glGetShaderiv(gl.vertex_shader, GL_INFO_LOG_LENGTH, &ret);
401                 if (ret > 1) {
402                         log = malloc(ret);
403                         glGetShaderInfoLog(gl.vertex_shader, ret, NULL, log);
404                         printf("%s", log);
405                 }
407                 return -1;
408         }
410         gl.fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
412         glShaderSource(gl.fragment_shader, 1, &fragment_shader_source, NULL);
413         glCompileShader(gl.fragment_shader);
415         glGetShaderiv(gl.fragment_shader, GL_COMPILE_STATUS, &ret);
416         if (!ret) {
417                 char *log;
419                 printf("fragment shader compilation failed!:\n");
420                 glGetShaderiv(gl.fragment_shader, GL_INFO_LOG_LENGTH, &ret);
422                 if (ret > 1) {
423                         log = malloc(ret);
424                         glGetShaderInfoLog(gl.fragment_shader, ret, NULL, log);
425                         printf("%s", log);
426                 }
428                 return -1;
429         }
431         gl.program = glCreateProgram();
433         glAttachShader(gl.program, gl.vertex_shader);
434         glAttachShader(gl.program, gl.fragment_shader);
436         glBindAttribLocation(gl.program, 0, "in_position");
437         glBindAttribLocation(gl.program, 1, "in_normal");
438         glBindAttribLocation(gl.program, 2, "in_color");
440         glLinkProgram(gl.program);
442         glGetProgramiv(gl.program, GL_LINK_STATUS, &ret);
443         if (!ret) {
444                 char *log;
446                 printf("program linking failed!:\n");
447                 glGetProgramiv(gl.program, GL_INFO_LOG_LENGTH, &ret);
449                 if (ret > 1) {
450                         log = malloc(ret);
451                         glGetProgramInfoLog(gl.program, ret, NULL, log);
452                         printf("%s", log);
453                 }
455                 return -1;
456         }
458         glUseProgram(gl.program);
460         gl.modelviewmatrix = glGetUniformLocation(gl.program, "modelviewMatrix");
461         gl.modelviewprojectionmatrix = glGetUniformLocation(gl.program, "modelviewprojectionMatrix");
462         gl.normalmatrix = glGetUniformLocation(gl.program, "normalMatrix");
464         glViewport(0, 0, drm.mode[DISP_ID]->hdisplay, drm.mode[DISP_ID]->vdisplay);
465         glEnable(GL_CULL_FACE);
467         gl.positionsoffset = 0;
468         gl.colorsoffset = sizeof(vVertices);
469         gl.normalsoffset = sizeof(vVertices) + sizeof(vColors);
470         glGenBuffers(1, &gl.vbo);
471         glBindBuffer(GL_ARRAY_BUFFER, gl.vbo);
472         glBufferData(GL_ARRAY_BUFFER, sizeof(vVertices) + sizeof(vColors) + sizeof(vNormals), 0, GL_STATIC_DRAW);
473         glBufferSubData(GL_ARRAY_BUFFER, gl.positionsoffset, sizeof(vVertices), &vVertices[0]);
474         glBufferSubData(GL_ARRAY_BUFFER, gl.colorsoffset, sizeof(vColors), &vColors[0]);
475         glBufferSubData(GL_ARRAY_BUFFER, gl.normalsoffset, sizeof(vNormals), &vNormals[0]);
476         glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)gl.positionsoffset);
477         glEnableVertexAttribArray(0);
478         glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)gl.normalsoffset);
479         glEnableVertexAttribArray(1);
480         glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)gl.colorsoffset);
481         glEnableVertexAttribArray(2);
483         return 0;
486 static void exit_gbm(void)
488         gbm_surface_destroy(gbm.surface);
489         gbm_device_destroy(gbm.dev);
490         return;
493 static void exit_gl(void)
495         glDeleteProgram(gl.program);
496         glDeleteBuffers(1, &gl.vbo);
497         glDeleteShader(gl.fragment_shader);
498         glDeleteShader(gl.vertex_shader);
499         eglDestroySurface(gl.display, gl.surface);
500         eglDestroyContext(gl.display, gl.context);
501         eglTerminate(gl.display);
502         return;
505 static void exit_drm(void)
508         drmModeRes *resources;
509         int i;
511         resources = (drmModeRes *)drm.resource_id;
512         for (i = 0; i < resources->count_connectors; i++) {
513                 drmModeFreeEncoder(drm.encoder[i]);
514                 drmModeFreeConnector(drm.connectors[i]);
515         }
516         drmModeFreeResources(drm.resource_id);
517         drmClose(drm.fd);
518         return;
521 void cleanup_kmscube(void)
523         exit_gl();
524         exit_gbm();
525         exit_drm();
526         printf("Cleanup of GL, GBM and DRM completed\n");
527         return;
530 static void draw(uint32_t i)
532         ESMatrix modelview;
534         /* clear the color buffer */
535         glClearColor(0.5, 0.5, 0.5, 1.0);
536         glClear(GL_COLOR_BUFFER_BIT);
538         esMatrixLoadIdentity(&modelview);
539         esTranslate(&modelview, 0.0f, 0.0f, -8.0f);
540         esRotate(&modelview, 45.0f + (0.25f * i), 1.0f, 0.0f, 0.0f);
541         esRotate(&modelview, 45.0f - (0.5f * i), 0.0f, 1.0f, 0.0f);
542         esRotate(&modelview, 10.0f + (0.15f * i), 0.0f, 0.0f, 1.0f);
544         GLfloat aspect = (GLfloat)(drm.mode[DISP_ID]->vdisplay) / (GLfloat)(drm.mode[DISP_ID]->hdisplay);
546         ESMatrix projection;
547         esMatrixLoadIdentity(&projection);
548         esFrustum(&projection, -2.8f, +2.8f, -2.8f * aspect, +2.8f * aspect, 6.0f, 10.0f);
550         ESMatrix modelviewprojection;
551         esMatrixLoadIdentity(&modelviewprojection);
552         esMatrixMultiply(&modelviewprojection, &modelview, &projection);
554         float normal[9];
555         normal[0] = modelview.m[0][0];
556         normal[1] = modelview.m[0][1];
557         normal[2] = modelview.m[0][2];
558         normal[3] = modelview.m[1][0];
559         normal[4] = modelview.m[1][1];
560         normal[5] = modelview.m[1][2];
561         normal[6] = modelview.m[2][0];
562         normal[7] = modelview.m[2][1];
563         normal[8] = modelview.m[2][2];
565         glUniformMatrix4fv(gl.modelviewmatrix, 1, GL_FALSE, &modelview.m[0][0]);
566         glUniformMatrix4fv(gl.modelviewprojectionmatrix, 1, GL_FALSE, &modelviewprojection.m[0][0]);
567         glUniformMatrix3fv(gl.normalmatrix, 1, GL_FALSE, normal);
569         glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
570         glDrawArrays(GL_TRIANGLE_STRIP, 4, 4);
571         glDrawArrays(GL_TRIANGLE_STRIP, 8, 4);
572         glDrawArrays(GL_TRIANGLE_STRIP, 12, 4);
573         glDrawArrays(GL_TRIANGLE_STRIP, 16, 4);
574         glDrawArrays(GL_TRIANGLE_STRIP, 20, 4);
577 static void
578 drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
580         struct drm_fb *fb = data;
581         struct gbm_device *gbm = gbm_bo_get_device(bo);
583         if (fb->fb_id)
584                 drmModeRmFB(drm.fd, fb->fb_id);
586         free(fb);
589 static struct drm_fb * drm_fb_get_from_bo(struct gbm_bo *bo)
591         struct drm_fb *fb = gbm_bo_get_user_data(bo);
592         uint32_t width, height, stride, handle;
593         int ret;
595         if (fb)
596                 return fb;
598         fb = calloc(1, sizeof *fb);
599         fb->bo = bo;
601         width = gbm_bo_get_width(bo);
602         height = gbm_bo_get_height(bo);
603         stride = gbm_bo_get_stride(bo);
604         handle = gbm_bo_get_handle(bo).u32;
606         ret = drmModeAddFB(drm.fd, width, height, 24, 32, stride, handle, &fb->fb_id);
607         if (ret) {
608                 printf("failed to create fb: %s\n", strerror(errno));
609                 free(fb);
610                 return NULL;
611         }
613         gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback);
615         return fb;
618 static void page_flip_handler(int fd, unsigned int frame,
619                   unsigned int sec, unsigned int usec, void *data)
621         int *waiting_for_flip = data;
622         *waiting_for_flip = 0;
625 void print_usage()
627         printf("Usage : kmscube <options>\n");
628         printf("\t-h : Help\n");
629         printf("\t-a : Enable all displays\n");
630         printf("\t-c <id> : Display using connector_id [if not specified, use the first connected connector]\n");
633 int kms_signalhandler(int signum)
635         switch(signum) {
636         case SIGINT:
637         case SIGTERM:
638                 printf("Handling signal number = %d\n", signum);
639                 cleanup_kmscube();
640                 break;
641         default:
642                 printf("Unknown signal\n");
643                 break;
644         }
645         exit(1);
648 int main(int argc, char *argv[])
650         fd_set fds;
651         drmEventContext evctx = {
652                         .version = DRM_EVENT_CONTEXT_VERSION,
653                         .page_flip_handler = page_flip_handler,
654         };
655         struct gbm_bo *bo;
656         struct drm_fb *fb;
657         uint32_t i = 0;
658         int ret;
659         int opt;
661         signal(SIGINT, kms_signalhandler);
662         signal(SIGTERM, kms_signalhandler);
664         while ((opt = getopt(argc, argv, "ahc:")) != -1) {
665                 switch(opt) {
666                 case 'a':
667                         all_display = 1;
668                         break;
670                 case 'h':
671                         print_usage();
672                         return 0;
674                 case 'c':
675                         connector_id = atoi(optarg);
676                         break;
678                 default:
679                         printf("Undefined option %s\n", argv[optind]);
680                         print_usage();
681                         return -1;
682                 }
683         }
685         if (all_display) {
686                 printf("### Enabling all displays\n");
687                 connector_id = -1;
688         }
690         ret = init_drm();
691         if (ret) {
692                 printf("failed to initialize DRM\n");
693                 return ret;
694         }
695         printf("### Primary display => ConnectorId = %d, Resolution = %dx%d\n",
696                         drm.connector_id[DISP_ID], drm.mode[DISP_ID]->hdisplay,
697                         drm.mode[DISP_ID]->vdisplay);
699         FD_ZERO(&fds);
700         FD_SET(drm.fd, &fds);
702         ret = init_gbm();
703         if (ret) {
704                 printf("failed to initialize GBM\n");
705                 return ret;
706         }
708         ret = init_gl();
709         if (ret) {
710                 printf("failed to initialize EGL\n");
711                 return ret;
712         }
714         /* clear the color buffer */
715         glClearColor(0.5, 0.5, 0.5, 1.0);
716         glClear(GL_COLOR_BUFFER_BIT);
717         eglSwapBuffers(gl.display, gl.surface);
718         bo = gbm_surface_lock_front_buffer(gbm.surface);
719         fb = drm_fb_get_from_bo(bo);
721         /* set mode: */
722         if (all_display) {
723                 for (i=0; i<drm.ndisp; i++) {
724                         ret = drmModeSetCrtc(drm.fd, drm.crtc_id[i], fb->fb_id, 0, 0,
725                                         &drm.connector_id[i], 1, drm.mode[i]);
726                         if (ret) {
727                                 printf("display %d failed to set mode: %s\n", i, strerror(errno));
728                                 return ret;
729                         }
730                 }
731         } else {
732                 ret = drmModeSetCrtc(drm.fd, drm.crtc_id[DISP_ID], fb->fb_id,
733                                 0, 0, &drm.connector_id[DISP_ID], 1, drm.mode[DISP_ID]);
734                 if (ret) {
735                         printf("display %d failed to set mode: %s\n", DISP_ID, strerror(errno));
736                         return ret;
737                 }
738         }
740         while (1) {
741                 struct gbm_bo *next_bo;
742                 int waiting_for_flip = 1;
744                 draw(i++);
746                 eglSwapBuffers(gl.display, gl.surface);
747                 next_bo = gbm_surface_lock_front_buffer(gbm.surface);
748                 fb = drm_fb_get_from_bo(next_bo);
750                 /*
751                  * Here you could also update drm plane layers if you want
752                  * hw composition
753                  */
755                 ret = drmModePageFlip(drm.fd, drm.crtc_id[DISP_ID], fb->fb_id,
756                                 DRM_MODE_PAGE_FLIP_EVENT, &waiting_for_flip);
757                 if (ret) {
758                         printf("failed to queue page flip: %s\n", strerror(errno));
759                         return -1;
760                 }
762                 while (waiting_for_flip) {
763                         ret = select(drm.fd + 1, &fds, NULL, NULL, NULL);
764                         if (ret < 0) {
765                                 printf("select err: %s\n", strerror(errno));
766                                 return ret;
767                         } else if (ret == 0) {
768                                 printf("select timeout!\n");
769                                 return -1;
770                         } else if (FD_ISSET(0, &fds)) {
771                                 continue;
772                         }
773                         drmHandleEvent(drm.fd, &evctx);
774                 }
776                 /* release last buffer to render on again: */
777                 gbm_surface_release_buffer(gbm.surface, bo);
778                 bo = next_bo;
779         }
781         cleanup_kmscube();
782         printf("\n Exiting kmscube \n");
784         return ret;