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;
178 }
180 static int init_gbm(void)
181 {
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;
194 }
196 static int init_gl(void)
197 {
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;
484 }
486 static void exit_gbm(void)
487 {
488 gbm_surface_destroy(gbm.surface);
489 gbm_device_destroy(gbm.dev);
490 return;
491 }
493 static void exit_gl(void)
494 {
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;
503 }
505 static void exit_drm(void)
506 {
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;
519 }
521 void cleanup_kmscube(void)
522 {
523 exit_gl();
524 exit_gbm();
525 exit_drm();
526 printf("Cleanup of GL, GBM and DRM completed\n");
527 return;
528 }
530 static void draw(uint32_t i)
531 {
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);
575 }
577 static void
578 drm_fb_destroy_callback(struct gbm_bo *bo, void *data)
579 {
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);
587 }
589 static struct drm_fb * drm_fb_get_from_bo(struct gbm_bo *bo)
590 {
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;
616 }
618 static void page_flip_handler(int fd, unsigned int frame,
619 unsigned int sec, unsigned int usec, void *data)
620 {
621 int *waiting_for_flip = data;
622 *waiting_for_flip = 0;
623 }
625 void print_usage()
626 {
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");
631 }
633 int kms_signalhandler(int signum)
634 {
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);
646 }
648 int main(int argc, char *argv[])
649 {
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;
785 }