1/* 2 * Copyright (C) 2011 LunarG Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included 12 * in all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 * DEALINGS IN THE SOFTWARE. 21 * 22 * Authors: 23 * Chia-I Wu <olv@lunarg.com> 24 */ 25 26/* 27 * This is a demonstration of EGL on fbdev: 28 * 29 * The native display is the fd of the device; 30 * There is only one native window, NULL; 31 * There is no native pixmaps. 32 * 33 * It is the app's responsibility to set up the tty, open the fb device, and 34 * initialize EGL. 35 */ 36 37#include <stdio.h> 38#include <stdlib.h> 39#include <string.h> 40#include <stdarg.h> 41 42/* for tty */ 43#include <linux/kd.h> 44#include <linux/vt.h> 45#include <sys/ioctl.h> 46#include <termios.h> 47#include <unistd.h> 48#include <signal.h> 49 50/* for fbdev */ 51#include <linux/fb.h> 52#include <sys/types.h> 53#include <sys/stat.h> 54#include <fcntl.h> 55 56/* for EGL */ 57#include <EGL/egl.h> 58#include <GLES/gl.h> 59 60static int tty_fd = -1; 61static int tty_saved_vtno; 62 63static int tty_open_vt(int vtno) 64{ 65 const char tty[] = "/dev/tty%d"; 66 char name[64]; 67 int size, flags; 68 69 size = snprintf(name, sizeof(name), tty, vtno); 70 if (size >= sizeof(name)) 71 return -1; 72 73 flags = (vtno) ? O_RDWR | O_NDELAY : O_WRONLY; 74 75 return open(name, flags); 76} 77 78static int tty_switch_vt(int fd, int vtno) 79{ 80 int ret; 81 82 ret = ioctl(fd, VT_ACTIVATE, vtno); 83 if (ret >= 0) 84 ret = ioctl(fd, VT_WAITACTIVE, vtno); 85 86 return ret; 87} 88 89 90static int tty_init_vt(void) 91{ 92 struct vt_stat vts; 93 int fd, vtno; 94 95 /* get the next available tty number */ 96 fd = tty_open_vt(0); 97 if (fd < 0) 98 return -1; 99 if (ioctl(fd, VT_OPENQRY, &vtno) < 0) 100 goto fail; 101 close(fd); 102 103 fd = tty_open_vt(vtno); 104 if (fd < 0) 105 return -1; 106 107 /* save the current VT */ 108 if (ioctl(fd, VT_GETSTATE, &vts) < 0) 109 goto fail; 110 tty_saved_vtno = vts.v_active; 111 112 if (tty_switch_vt(fd, vtno)) 113 goto fail; 114 115 return fd; 116 117fail: 118 close(fd); 119 return -1; 120} 121 122static void 123tty_close(void) 124{ 125 /* restore */ 126 ioctl(tty_fd, KDSETMODE, KD_TEXT); 127 tty_switch_vt(tty_fd, tty_saved_vtno); 128 129 close(tty_fd); 130} 131 132static void 133signal_handler(int sig) 134{ 135 if (tty_fd >= 0) 136 tty_close(); 137} 138 139static int tty_open(void) 140{ 141 struct sigaction sa; 142 143 tty_fd = tty_init_vt(); 144 if (tty_fd < 0) 145 return -1; 146 147 /* install the signal handler */ 148 memset(&sa, 0, sizeof(sa)); 149 sigemptyset(&sa.sa_mask); 150 sa.sa_handler = signal_handler; 151 if (sigaction(SIGINT, &sa, NULL)) 152 goto fail; 153 if (sigaction(SIGTERM, &sa, NULL)) 154 goto fail; 155 if (sigaction(SIGABRT, &sa, NULL)) 156 goto fail; 157 158 if (ioctl(tty_fd, KDSETMODE, KD_GRAPHICS) < 0) 159 goto fail; 160 161 tcflush(tty_fd, TCIOFLUSH); 162 163 return 0; 164 165fail: 166 tty_close(); 167 tty_fd = -1; 168 return -1; 169} 170 171static EGLDisplay egl_dpy; 172static EGLContext egl_ctx; 173static EGLSurface egl_surf; 174static EGLBoolean egl_verbose; 175 176static void 177egl_fatal(char *format, ...) 178{ 179 va_list args; 180 181 va_start(args, format); 182 vfprintf(stderr, format, args); 183 va_end(args); 184 putc('\n', stderr); 185 186 abort(); 187} 188 189static void 190egl_init_for_fbdev(int fd, EGLBoolean verbose) 191{ 192 const EGLNativeWindowType native_win = (EGLNativeWindowType) NULL; 193 EGLint major, minor, num_configs; 194 EGLConfig conf; 195 196 egl_verbose = verbose; 197 198 /* make Mesa/EGL happy */ 199 setenv("EGL_PLATFORM", "fbdev", 0); 200 201 egl_dpy = eglGetDisplay((EGLNativeDisplayType) fd); 202 if (egl_dpy == EGL_NO_DISPLAY) 203 egl_fatal("failed to get a display"); 204 if (!eglInitialize(egl_dpy, &major, &minor)) 205 egl_fatal("failed to initialize EGL"); 206 207 if (egl_verbose) { 208 printf("EGL %d.%d\n", major, minor); 209 printf("EGL_VENDOR: %s\n", eglQueryString(egl_dpy, EGL_VENDOR)); 210 printf("EGL_VERSION: %s\n", eglQueryString(egl_dpy, EGL_VERSION)); 211 printf("EGL_EXTENSIONS: %s\n", eglQueryString(egl_dpy, EGL_EXTENSIONS)); 212 printf("EGL_CLIENT_APIS: %s\n", 213 eglQueryString(egl_dpy, EGL_CLIENT_APIS)); 214 } 215 216 if (!eglChooseConfig(egl_dpy, NULL, &conf, 1, &num_configs) || 217 !num_configs) 218 egl_fatal("failed to choose a config"); 219 220 egl_ctx = eglCreateContext(egl_dpy, conf, EGL_NO_CONTEXT, NULL); 221 if (egl_ctx == EGL_NO_CONTEXT) 222 egl_fatal("failed to create a context"); 223 224 egl_surf = eglCreateWindowSurface(egl_dpy, conf, native_win, NULL); 225 if (egl_surf == EGL_NO_SURFACE) 226 egl_fatal("failed to create a surface"); 227 228 if (!eglMakeCurrent(egl_dpy, egl_surf, egl_surf, egl_ctx)) 229 egl_fatal("failed to make context/surface current"); 230} 231 232static void 233egl_present(void) 234{ 235 if (!eglSwapBuffers(egl_dpy, egl_surf)) 236 egl_fatal("failed to swap buffers"); 237} 238 239static void 240egl_destroy(void) 241{ 242 eglMakeCurrent(egl_dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); 243 eglDestroyContext(egl_dpy, egl_surf); 244 eglDestroyContext(egl_dpy, egl_ctx); 245 eglTerminate(egl_dpy); 246 247 egl_surf = EGL_NO_SURFACE; 248 egl_ctx = EGL_NO_CONTEXT; 249 egl_dpy = EGL_NO_DISPLAY; 250} 251 252/* stolen from tri.c */ 253static void 254draw(int frame) 255{ 256 static const GLfloat verts[3][2] = { 257 { -1, -1 }, 258 { 1, -1 }, 259 { 0, 1 } 260 }; 261 static const GLfloat colors[3][4] = { 262 { 1, 0, 0, 1 }, 263 { 0, 1, 0, 1 }, 264 { 0, 0, 1, 1 } 265 }; 266 GLfloat view_rotz = (GLfloat) frame; 267 268 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 269 270 glPushMatrix(); 271 glRotatef(view_rotz, 0, 0, 1); 272 273 glVertexPointer(2, GL_FLOAT, 0, verts); 274 glColorPointer(4, GL_FLOAT, 0, colors); 275 276 glEnableClientState(GL_VERTEX_ARRAY); 277 glEnableClientState(GL_COLOR_ARRAY); 278 279 /* draw triangle */ 280 glDrawArrays(GL_TRIANGLES, 0, 3); 281 282 glDisableClientState(GL_VERTEX_ARRAY); 283 glDisableClientState(GL_COLOR_ARRAY); 284 285 glPopMatrix(); 286} 287 288static void 289init(int width, int height) 290{ 291 GLfloat ar = (GLfloat) width / height; 292 293 glViewport(0, 0, width, height); 294 295 glMatrixMode(GL_PROJECTION); 296 glLoadIdentity(); 297 glFrustumf(-ar, ar, -1, 1, 5.0, 60.0); 298 299 glMatrixMode(GL_MODELVIEW); 300 glLoadIdentity(); 301 glTranslatef(0.0, 0.0, -10.0); 302} 303 304int 305main(int argc, char **argv) 306{ 307 const char fbdev[] = "/dev/fb0"; 308 struct fb_var_screeninfo vinfo; 309 int fd, tty_err, frame; 310 311 fd = open(fbdev, O_RDWR); 312 if (fd < 0) 313 egl_fatal("failed to open %s", fbdev); 314 315 memset(&vinfo, 0, sizeof(vinfo)); 316 if (ioctl(fd, FBIOGET_VSCREENINFO, &vinfo)) 317 egl_fatal("failed to get fb info"); 318 319 /* initialize EGL */ 320 egl_init_for_fbdev(fd, EGL_TRUE); 321 322 /* try to open a new tty */ 323 tty_err = tty_open(); 324 325 init(vinfo.xres, vinfo.yres); 326 for (frame = 0; frame <= 180; frame++) { 327 draw(frame); 328 egl_present(); 329 } 330 331 if (!tty_err) 332 tty_close(); 333 334 egl_destroy(); 335 close(fd); 336 337 return 0; 338} 339