choose the maximum resolution supported by a connector
[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>
37 #include <xf86drm.h>
38 #include <xf86drmMode.h>
39 #include <gbm.h>
41 #include "esUtil.h"
43 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
45 #define MAX_DISPLAYS    (4)
46 uint8_t DISP_ID = 0;
47 uint8_t all_display = 0;
48 int8_t connector_id = -1;
50 static struct {
51         EGLDisplay display;
52         EGLConfig config;
53         EGLContext context;
54         EGLSurface surface;
55         GLuint program;
56         GLint modelviewmatrix, modelviewprojectionmatrix, normalmatrix;
57         GLuint vbo;
58         GLuint positionsoffset, colorsoffset, normalsoffset;
59 } gl;
61 static struct {
62         struct gbm_device *dev;
63         struct gbm_surface *surface;
64 } gbm;
66 static struct {
67         int fd;
68         uint32_t ndisp;
69         uint32_t crtc_id[MAX_DISPLAYS];
70         uint32_t connector_id[MAX_DISPLAYS];
71         drmModeModeInfo *mode[MAX_DISPLAYS];
72         drmModeConnector *connectors[MAX_DISPLAYS];
73 } drm;
75 struct drm_fb {
76         struct gbm_bo *bo;
77         uint32_t fb_id;
78 };
80 static int init_drm(void)
81 {
82         static const char *modules[] = {
83                         "omapdrm", "i915", "radeon", "nouveau", "vmwgfx", "exynos"
84         };
85         drmModeRes *resources;
86         drmModeConnector *connector = NULL;
87         drmModeEncoder *encoder = NULL;
88         int i, j;
89         uint32_t maxRes, curRes;
91         for (i = 0; i < ARRAY_SIZE(modules); i++) {
92                 printf("trying to load module %s...", modules[i]);
93                 drm.fd = drmOpen(modules[i], NULL);
94                 if (drm.fd < 0) {
95                         printf("failed.\n");
96                 } else {
97                         printf("success.\n");
98                         break;
99                 }
100         }
102         if (drm.fd < 0) {
103                 printf("could not open drm device\n");
104                 return -1;
105         }
107         resources = drmModeGetResources(drm.fd);
108         if (!resources) {
109                 printf("drmModeGetResources failed: %s\n", strerror(errno));
110                 return -1;
111         }
113         /* find a connected connector: */
114         for (i = 0; i < resources->count_connectors; i++) {
115                 connector = drmModeGetConnector(drm.fd, resources->connectors[i]);
116                 if (connector->connection == DRM_MODE_CONNECTED) {
117                         /* choose the first supported mode */
118                         drm.mode[drm.ndisp] = &connector->modes[0];
119                         drm.connector_id[drm.ndisp] = connector->connector_id;
121                         for (j=0; j<resources->count_encoders; j++) {
122                                 encoder = drmModeGetEncoder(drm.fd, resources->encoders[j]);
123                                 if (encoder->encoder_id == connector->encoder_id)
124                                         break;
126                                 drmModeFreeEncoder(encoder);
127                                 encoder = NULL;
128                         }
130                         if (!encoder) {
131                                 printf("no encoder!\n");
132                                 return -1;
133                         }
135                         drm.crtc_id[drm.ndisp] = encoder->crtc_id;
136                         drm.connectors[drm.ndisp] = connector;
138                         printf("### Display [%d]: CRTC = %d, Connector = %d\n", drm.ndisp, drm.crtc_id[drm.ndisp], drm.connector_id[drm.ndisp]);
139                         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);
140                         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);
141                         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);
143                         /* If a connector_id is specified, use the corresponding display */
144                         if ((connector_id != -1) && (connector_id == drm.connector_id[drm.ndisp]))
145                                 DISP_ID = drm.ndisp;
147                         /* If all displays are enabled, choose the connector with maximum
148                         * resolution as the primary display */
149                         if (all_display) {
150                                 maxRes = drm.mode[DISP_ID]->vdisplay * drm.mode[DISP_ID]->hdisplay;
151                                 curRes = drm.mode[drm.ndisp]->vdisplay * drm.mode[drm.ndisp]->hdisplay;
153                                 if (curRes > maxRes)
154                                         DISP_ID = drm.ndisp;
155                         }
157                         drm.ndisp++;
158                 } else {
159                         drmModeFreeConnector(connector);
160                 }
161         }
163         if (drm.ndisp == 0) {
164                 /* we could be fancy and listen for hotplug events and wait for
165                  * a connector..
166                  */
167                 printf("no connected connector!\n");
168                 return -1;
169         }
171         return 0;
174 static int init_gbm(void)
176         gbm.dev = gbm_create_device(drm.fd);
178         gbm.surface = gbm_surface_create(gbm.dev,
179                         drm.mode[DISP_ID]->hdisplay, drm.mode[DISP_ID]->vdisplay,
180                         GBM_FORMAT_XRGB8888,
181                         GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
182         if (!gbm.surface) {
183                 printf("failed to create gbm surface\n");
184                 return -1;
185         }
187         return 0;
190 static int init_gl(void)
192         EGLint major, minor, n;
193         GLuint vertex_shader, fragment_shader;
194         GLint ret;
196         static const GLfloat vVertices[] = {
197                         // front
198                         -1.0f, -1.0f, +1.0f, // point blue
199                         +1.0f, -1.0f, +1.0f, // point magenta
200                         -1.0f, +1.0f, +1.0f, // point cyan
201                         +1.0f, +1.0f, +1.0f, // point white
202                         // back
203                         +1.0f, -1.0f, -1.0f, // point red
204                         -1.0f, -1.0f, -1.0f, // point black
205                         +1.0f, +1.0f, -1.0f, // point yellow
206                         -1.0f, +1.0f, -1.0f, // point green
207                         // right
208                         +1.0f, -1.0f, +1.0f, // point magenta
209                         +1.0f, -1.0f, -1.0f, // point red
210                         +1.0f, +1.0f, +1.0f, // point white
211                         +1.0f, +1.0f, -1.0f, // point yellow
212                         // left
213                         -1.0f, -1.0f, -1.0f, // point black
214                         -1.0f, -1.0f, +1.0f, // point blue
215                         -1.0f, +1.0f, -1.0f, // point green
216                         -1.0f, +1.0f, +1.0f, // point cyan
217                         // top
218                         -1.0f, +1.0f, +1.0f, // point cyan
219                         +1.0f, +1.0f, +1.0f, // point white
220                         -1.0f, +1.0f, -1.0f, // point green
221                         +1.0f, +1.0f, -1.0f, // point yellow
222                         // bottom
223                         -1.0f, -1.0f, -1.0f, // point black
224                         +1.0f, -1.0f, -1.0f, // point red
225                         -1.0f, -1.0f, +1.0f, // point blue
226                         +1.0f, -1.0f, +1.0f  // point magenta
227         };
229         static const GLfloat vColors[] = {
230                         // front
231                         0.0f,  0.0f,  1.0f, // blue
232                         1.0f,  0.0f,  1.0f, // magenta
233                         0.0f,  1.0f,  1.0f, // cyan
234                         1.0f,  1.0f,  1.0f, // white
235                         // back
236                         1.0f,  0.0f,  0.0f, // red
237                         0.0f,  0.0f,  0.0f, // black
238                         1.0f,  1.0f,  0.0f, // yellow
239                         0.0f,  1.0f,  0.0f, // green
240                         // right
241                         1.0f,  0.0f,  1.0f, // magenta
242                         1.0f,  0.0f,  0.0f, // red
243                         1.0f,  1.0f,  1.0f, // white
244                         1.0f,  1.0f,  0.0f, // yellow
245                         // left
246                         0.0f,  0.0f,  0.0f, // black
247                         0.0f,  0.0f,  1.0f, // blue
248                         0.0f,  1.0f,  0.0f, // green
249                         0.0f,  1.0f,  1.0f, // cyan
250                         // top
251                         0.0f,  1.0f,  1.0f, // cyan
252                         1.0f,  1.0f,  1.0f, // white
253                         0.0f,  1.0f,  0.0f, // green
254                         1.0f,  1.0f,  0.0f, // yellow
255                         // bottom
256                         0.0f,  0.0f,  0.0f, // black
257                         1.0f,  0.0f,  0.0f, // red
258                         0.0f,  0.0f,  1.0f, // blue
259                         1.0f,  0.0f,  1.0f  // magenta
260         };
262         static const GLfloat vNormals[] = {
263                         // front
264                         +0.0f, +0.0f, +1.0f, // forward
265                         +0.0f, +0.0f, +1.0f, // forward
266                         +0.0f, +0.0f, +1.0f, // forward
267                         +0.0f, +0.0f, +1.0f, // forward
268                         // back
269                         +0.0f, +0.0f, -1.0f, // backbard
270                         +0.0f, +0.0f, -1.0f, // backbard
271                         +0.0f, +0.0f, -1.0f, // backbard
272                         +0.0f, +0.0f, -1.0f, // backbard
273                         // right
274                         +1.0f, +0.0f, +0.0f, // right
275                         +1.0f, +0.0f, +0.0f, // right
276                         +1.0f, +0.0f, +0.0f, // right
277                         +1.0f, +0.0f, +0.0f, // right
278                         // left
279                         -1.0f, +0.0f, +0.0f, // left
280                         -1.0f, +0.0f, +0.0f, // left
281                         -1.0f, +0.0f, +0.0f, // left
282                         -1.0f, +0.0f, +0.0f, // left
283                         // top
284                         +0.0f, +1.0f, +0.0f, // up
285                         +0.0f, +1.0f, +0.0f, // up
286                         +0.0f, +1.0f, +0.0f, // up
287                         +0.0f, +1.0f, +0.0f, // up
288                         // bottom
289                         +0.0f, -1.0f, +0.0f, // down
290                         +0.0f, -1.0f, +0.0f, // down
291                         +0.0f, -1.0f, +0.0f, // down
292                         +0.0f, -1.0f, +0.0f  // down
293         };
295         static const EGLint context_attribs[] = {
296                 EGL_CONTEXT_CLIENT_VERSION, 2,
297                 EGL_NONE
298         };
300         static const EGLint config_attribs[] = {
301                 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
302                 EGL_RED_SIZE, 1,
303                 EGL_GREEN_SIZE, 1,
304                 EGL_BLUE_SIZE, 1,
305                 EGL_ALPHA_SIZE, 0,
306                 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
307                 EGL_NONE
308         };
310         static const char *vertex_shader_source =
311                         "uniform mat4 modelviewMatrix;      \n"
312                         "uniform mat4 modelviewprojectionMatrix;\n"
313                         "uniform mat3 normalMatrix;         \n"
314                         "                                   \n"
315                         "attribute vec4 in_position;        \n"
316                         "attribute vec3 in_normal;          \n"
317                         "attribute vec4 in_color;           \n"
318                         "\n"
319                         "vec4 lightSource = vec4(2.0, 2.0, 20.0, 0.0);\n"
320                         "                                   \n"
321                         "varying vec4 vVaryingColor;        \n"
322                         "                                   \n"
323                         "void main()                        \n"
324                         "{                                  \n"
325                         "    gl_Position = modelviewprojectionMatrix * in_position;\n"
326                         "    vec3 vEyeNormal = normalMatrix * in_normal;\n"
327                         "    vec4 vPosition4 = modelviewMatrix * in_position;\n"
328                         "    vec3 vPosition3 = vPosition4.xyz / vPosition4.w;\n"
329                         "    vec3 vLightDir = normalize(lightSource.xyz - vPosition3);\n"
330                         "    float diff = max(0.0, dot(vEyeNormal, vLightDir));\n"
331                         "    vVaryingColor = vec4(diff * in_color.rgb, 1.0);\n"
332                         "}                                  \n";
334         static const char *fragment_shader_source =
335                         "precision mediump float;           \n"
336                         "                                   \n"
337                         "varying vec4 vVaryingColor;        \n"
338                         "                                   \n"
339                         "void main()                        \n"
340                         "{                                  \n"
341                         "    gl_FragColor = vVaryingColor;  \n"
342                         "}                                  \n";
344         gl.display = eglGetDisplay(gbm.dev);
346         if (!eglInitialize(gl.display, &major, &minor)) {
347                 printf("failed to initialize\n");
348                 return -1;
349         }
351         printf("Using display %p with EGL version %d.%d\n",
352                         gl.display, major, minor);
354         printf("EGL Version \"%s\"\n", eglQueryString(gl.display, EGL_VERSION));
355         printf("EGL Vendor \"%s\"\n", eglQueryString(gl.display, EGL_VENDOR));
356         printf("EGL Extensions \"%s\"\n", eglQueryString(gl.display, EGL_EXTENSIONS));
358         if (!eglBindAPI(EGL_OPENGL_ES_API)) {
359                 printf("failed to bind api EGL_OPENGL_ES_API\n");
360                 return -1;
361         }
363         if (!eglChooseConfig(gl.display, config_attribs, &gl.config, 1, &n) || n != 1) {
364                 printf("failed to choose config: %d\n", n);
365                 return -1;
366         }
368         gl.context = eglCreateContext(gl.display, gl.config,
369                         EGL_NO_CONTEXT, context_attribs);
370         if (gl.context == NULL) {
371                 printf("failed to create context\n");
372                 return -1;
373         }
375         gl.surface = eglCreateWindowSurface(gl.display, gl.config, gbm.surface, NULL);
376         if (gl.surface == EGL_NO_SURFACE) {
377                 printf("failed to create egl surface\n");
378                 return -1;
379         }
381         /* connect the context to the surface */
382         eglMakeCurrent(gl.display, gl.surface, gl.surface, gl.context);
385         vertex_shader = glCreateShader(GL_VERTEX_SHADER);
387         glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL);
388         glCompileShader(vertex_shader);
390         glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &ret);
391         if (!ret) {
392                 char *log;
394                 printf("vertex shader compilation failed!:\n");
395                 glGetShaderiv(vertex_shader, GL_INFO_LOG_LENGTH, &ret);
396                 if (ret > 1) {
397                         log = malloc(ret);
398                         glGetShaderInfoLog(vertex_shader, ret, NULL, log);
399                         printf("%s", log);
400                 }
402                 return -1;
403         }
405         fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
407         glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL);
408         glCompileShader(fragment_shader);
410         glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &ret);
411         if (!ret) {
412                 char *log;
414                 printf("fragment shader compilation failed!:\n");
415                 glGetShaderiv(fragment_shader, GL_INFO_LOG_LENGTH, &ret);
417                 if (ret > 1) {
418                         log = malloc(ret);
419                         glGetShaderInfoLog(fragment_shader, ret, NULL, log);
420                         printf("%s", log);
421                 }
423                 return -1;
424         }
426         gl.program = glCreateProgram();
428         glAttachShader(gl.program, vertex_shader);
429         glAttachShader(gl.program, fragment_shader);
431         glBindAttribLocation(gl.program, 0, "in_position");
432         glBindAttribLocation(gl.program, 1, "in_normal");
433         glBindAttribLocation(gl.program, 2, "in_color");
435         glLinkProgram(gl.program);
437         glGetProgramiv(gl.program, GL_LINK_STATUS, &ret);
438         if (!ret) {
439                 char *log;
441                 printf("program linking failed!:\n");
442                 glGetProgramiv(gl.program, GL_INFO_LOG_LENGTH, &ret);
444                 if (ret > 1) {
445                         log = malloc(ret);
446                         glGetProgramInfoLog(gl.program, ret, NULL, log);
447                         printf("%s", log);
448                 }
450                 return -1;
451         }
453         glUseProgram(gl.program);
455         gl.modelviewmatrix = glGetUniformLocation(gl.program, "modelviewMatrix");
456         gl.modelviewprojectionmatrix = glGetUniformLocation(gl.program, "modelviewprojectionMatrix");
457         gl.normalmatrix = glGetUniformLocation(gl.program, "normalMatrix");
459         glViewport(0, 0, drm.mode[DISP_ID]->hdisplay, drm.mode[DISP_ID]->vdisplay);
460         glEnable(GL_CULL_FACE);
462         gl.positionsoffset = 0;
463         gl.colorsoffset = sizeof(vVertices);
464         gl.normalsoffset = sizeof(vVertices) + sizeof(vColors);
465         glGenBuffers(1, &gl.vbo);
466         glBindBuffer(GL_ARRAY_BUFFER, gl.vbo);
467         glBufferData(GL_ARRAY_BUFFER, sizeof(vVertices) + sizeof(vColors) + sizeof(vNormals), 0, GL_STATIC_DRAW);
468         glBufferSubData(GL_ARRAY_BUFFER, gl.positionsoffset, sizeof(vVertices), &vVertices[0]);
469         glBufferSubData(GL_ARRAY_BUFFER, gl.colorsoffset, sizeof(vColors), &vColors[0]);
470         glBufferSubData(GL_ARRAY_BUFFER, gl.normalsoffset, sizeof(vNormals), &vNormals[0]);
471         glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)gl.positionsoffset);
472         glEnableVertexAttribArray(0);
473         glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)gl.normalsoffset);
474         glEnableVertexAttribArray(1);
475         glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)gl.colorsoffset);
476         glEnableVertexAttribArray(2);
478         return 0;
481 static void draw(uint32_t i)
483         ESMatrix modelview;
485         /* clear the color buffer */
486         glClearColor(0.5, 0.5, 0.5, 1.0);
487         glClear(GL_COLOR_BUFFER_BIT);
489         esMatrixLoadIdentity(&modelview);
490         esTranslate(&modelview, 0.0f, 0.0f, -8.0f);
491         esRotate(&modelview, 45.0f + (0.25f * i), 1.0f, 0.0f, 0.0f);
492         esRotate(&modelview, 45.0f - (0.5f * i), 0.0f, 1.0f, 0.0f);
493         esRotate(&modelview, 10.0f + (0.15f * i), 0.0f, 0.0f, 1.0f);
495         GLfloat aspect = (GLfloat)(drm.mode[DISP_ID]->vdisplay) / (GLfloat)(drm.mode[DISP_ID]->hdisplay);
497         ESMatrix projection;
498         esMatrixLoadIdentity(&projection);
499         esFrustum(&projection, -2.8f, +2.8f, -2.8f * aspect, +2.8f * aspect, 6.0f, 10.0f);
501         ESMatrix modelviewprojection;
502         esMatrixLoadIdentity(&modelviewprojection);
503         esMatrixMultiply(&modelviewprojection, &modelview, &projection);
505         float normal[9];
506         normal[0] = modelview.m[0][0];
507         normal[1] = modelview.m[0][1];
508         normal[2] = modelview.m[0][2];
509         normal[3] = modelview.m[1][0];
510         normal[4] = modelview.m[1][1];
511         normal[5] = modelview.m[1][2];
512         normal[6] = modelview.m[2][0];
513         normal[7] = modelview.m[2][1];
514         normal[8] = modelview.m[2][2];
516         glUniformMatrix4fv(gl.modelviewmatrix, 1, GL_FALSE, &modelview.m[0][0]);
517         glUniformMatrix4fv(gl.modelviewprojectionmatrix, 1, GL_FALSE, &modelviewprojection.m[0][0]);
518         glUniformMatrix3fv(gl.normalmatrix, 1, GL_FALSE, normal);
520         glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
521         glDrawArrays(GL_TRIANGLE_STRIP, 4, 4);
522         glDrawArrays(GL_TRIANGLE_STRIP, 8, 4);
523         glDrawArrays(GL_TRIANGLE_STRIP, 12, 4);
524         glDrawArrays(GL_TRIANGLE_STRIP, 16, 4);
525         glDrawArrays(GL_TRIANGLE_STRIP, 20, 4);
528 static void
529 drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
531         struct drm_fb *fb = data;
532         struct gbm_device *gbm = gbm_bo_get_device(bo);
534         if (fb->fb_id)
535                 drmModeRmFB(drm.fd, fb->fb_id);
537         free(fb);
540 static struct drm_fb * drm_fb_get_from_bo(struct gbm_bo *bo)
542         struct drm_fb *fb = gbm_bo_get_user_data(bo);
543         uint32_t width, height, stride, handle;
544         int ret;
546         if (fb)
547                 return fb;
549         fb = calloc(1, sizeof *fb);
550         fb->bo = bo;
552         width = gbm_bo_get_width(bo);
553         height = gbm_bo_get_height(bo);
554         stride = gbm_bo_get_stride(bo);
555         handle = gbm_bo_get_handle(bo).u32;
557         ret = drmModeAddFB(drm.fd, width, height, 24, 32, stride, handle, &fb->fb_id);
558         if (ret) {
559                 printf("failed to create fb: %s\n", strerror(errno));
560                 free(fb);
561                 return NULL;
562         }
564         gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback);
566         return fb;
569 static void page_flip_handler(int fd, unsigned int frame,
570                   unsigned int sec, unsigned int usec, void *data)
572         int *waiting_for_flip = data;
573         *waiting_for_flip = 0;
576 void print_usage()
578         printf("Usage : kmscube <options>\n");
579         printf("\t-h : Help\n");
580         printf("\t-a : Enable all displays\n");
581         printf("\t-c <id> : Display using connector_id [if not specified, use the first connected connector]\n");
584 int main(int argc, char *argv[])
586         fd_set fds;
587         drmEventContext evctx = {
588                         .version = DRM_EVENT_CONTEXT_VERSION,
589                         .page_flip_handler = page_flip_handler,
590         };
591         struct gbm_bo *bo;
592         struct drm_fb *fb;
593         uint32_t i = 0;
594         int ret;
595         int opt;
597         while ((opt = getopt(argc, argv, "ahc:")) != -1) {
598                 switch(opt) {
599                 case 'a':
600                         all_display = 1;
601                         break;
603                 case 'h':
604                         print_usage();
605                         return 0;
607                 case 'c':
608                         connector_id = atoi(optarg);
609                         break;
611                 default:
612                         printf("Undefined option %s\n", argv[optind]);
613                         print_usage();
614                         return -1;
615                 }
616         }
618         if (all_display) {
619                 printf("### Enabling all displays\n");
620                 connector_id = -1;
621         }
623         ret = init_drm();
624         if (ret) {
625                 printf("failed to initialize DRM\n");
626                 return ret;
627         }
628         printf("### Primary display => ConnectorId = %d, Resolution = %dx%d\n",
629                         drm.connector_id[DISP_ID], drm.mode[DISP_ID]->hdisplay,
630                         drm.mode[DISP_ID]->vdisplay);
632         FD_ZERO(&fds);
633         FD_SET(0, &fds);
634         FD_SET(drm.fd, &fds);
636         ret = init_gbm();
637         if (ret) {
638                 printf("failed to initialize GBM\n");
639                 return ret;
640         }
642         ret = init_gl();
643         if (ret) {
644                 printf("failed to initialize EGL\n");
645                 return ret;
646         }
648         /* clear the color buffer */
649         glClearColor(0.5, 0.5, 0.5, 1.0);
650         glClear(GL_COLOR_BUFFER_BIT);
651         eglSwapBuffers(gl.display, gl.surface);
652         bo = gbm_surface_lock_front_buffer(gbm.surface);
653         fb = drm_fb_get_from_bo(bo);
655         /* set mode: */
656         if (all_display) {
657                 for (i=0; i<drm.ndisp; i++) {
658                         ret = drmModeSetCrtc(drm.fd, drm.crtc_id[i], fb->fb_id, 0, 0,
659                                         &drm.connector_id[i], 1, drm.mode[i]);
660                         if (ret) {
661                                 printf("display %d failed to set mode: %s\n", i, strerror(errno));
662                                 return ret;
663                         }
664                 }
665         } else {
666                 ret = drmModeSetCrtc(drm.fd, drm.crtc_id[DISP_ID], fb->fb_id,
667                                 0, 0, &drm.connector_id[DISP_ID], 1, drm.mode[DISP_ID]);
668                 if (ret) {
669                         printf("display %d failed to set mode: %s\n", DISP_ID, strerror(errno));
670                         return ret;
671                 }
672         }
674         while (1) {
675                 struct gbm_bo *next_bo;
676                 int waiting_for_flip = 1;
678                 draw(i++);
680                 eglSwapBuffers(gl.display, gl.surface);
681                 next_bo = gbm_surface_lock_front_buffer(gbm.surface);
682                 fb = drm_fb_get_from_bo(next_bo);
684                 /*
685                  * Here you could also update drm plane layers if you want
686                  * hw composition
687                  */
689                 ret = drmModePageFlip(drm.fd, drm.crtc_id[DISP_ID], fb->fb_id,
690                                 DRM_MODE_PAGE_FLIP_EVENT, &waiting_for_flip);
691                 if (ret) {
692                         printf("failed to queue page flip: %s\n", strerror(errno));
693                         return -1;
694                 }
696                 while (waiting_for_flip) {
697                         ret = select(drm.fd + 1, &fds, NULL, NULL, NULL);
698                         if (ret < 0) {
699                                 printf("select err: %s\n", strerror(errno));
700                                 return ret;
701                         } else if (ret == 0) {
702                                 printf("select timeout!\n");
703                                 return -1;
704                         } else if (FD_ISSET(0, &fds)) {
705                                 printf("user interrupted!\n");
706                                 break;
707                         }
708                         drmHandleEvent(drm.fd, &evctx);
709                 }
711                 /* release last buffer to render on again: */
712                 gbm_surface_release_buffer(gbm.surface, bo);
713                 bo = next_bo;
714         }
716         return ret;