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