1/*
2 * Copyright (C) 2010 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#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include <stdarg.h>
30#include <sys/time.h>
31
32#include "EGL/egl.h"
33#include "EGL/eglext.h"
34
35#include "eglutint.h"
36
37static struct eglut_state _eglut_state = {
38   .api_mask = EGLUT_OPENGL_ES1_BIT,
39   .window_width = 300,
40   .window_height = 300,
41   .verbose = 0,
42   .num_windows = 0,
43};
44
45struct eglut_state *_eglut = &_eglut_state;
46
47void
48_eglutFatal(char *format, ...)
49{
50  va_list args;
51
52  va_start(args, format);
53
54  fprintf(stderr, "EGLUT: ");
55  vfprintf(stderr, format, args);
56  va_end(args);
57  putc('\n', stderr);
58
59  exit(1);
60}
61
62/* return current time (in milliseconds) */
63int
64_eglutNow(void)
65{
66   struct timeval tv;
67#ifdef __VMS
68   (void) gettimeofday(&tv, NULL );
69#else
70   struct timezone tz;
71   (void) gettimeofday(&tv, &tz);
72#endif
73   return tv.tv_sec * 1000 + tv.tv_usec / 1000;
74}
75
76static void
77_eglutDestroyWindow(struct eglut_window *win)
78{
79   if (_eglut->surface_type != EGL_PBUFFER_BIT)
80      eglDestroySurface(_eglut->dpy, win->surface);
81
82   _eglutNativeFiniWindow(win);
83
84   eglDestroyContext(_eglut->dpy, win->context);
85}
86
87static EGLConfig
88_eglutChooseConfig(void)
89{
90   EGLConfig config;
91   EGLint config_attribs[32];
92   EGLint renderable_type, num_configs, i;
93
94   i = 0;
95   config_attribs[i++] = EGL_RED_SIZE;
96   config_attribs[i++] = 1;
97   config_attribs[i++] = EGL_GREEN_SIZE;
98   config_attribs[i++] = 1;
99   config_attribs[i++] = EGL_BLUE_SIZE;
100   config_attribs[i++] = 1;
101   config_attribs[i++] = EGL_DEPTH_SIZE;
102   config_attribs[i++] = 1;
103
104   config_attribs[i++] = EGL_SURFACE_TYPE;
105   config_attribs[i++] = _eglut->surface_type;
106
107   config_attribs[i++] = EGL_RENDERABLE_TYPE;
108   renderable_type = 0x0;
109   if (_eglut->api_mask & EGLUT_OPENGL_BIT)
110      renderable_type |= EGL_OPENGL_BIT;
111   if (_eglut->api_mask & EGLUT_OPENGL_ES1_BIT)
112      renderable_type |= EGL_OPENGL_ES_BIT;
113   if (_eglut->api_mask & EGLUT_OPENGL_ES2_BIT)
114      renderable_type |= EGL_OPENGL_ES2_BIT;
115   if (_eglut->api_mask & EGLUT_OPENVG_BIT)
116      renderable_type |= EGL_OPENVG_BIT;
117   config_attribs[i++] = renderable_type;
118
119   config_attribs[i] = EGL_NONE;
120
121   if (!eglChooseConfig(_eglut->dpy,
122            config_attribs, &config, 1, &num_configs) || !num_configs)
123      _eglutFatal("failed to choose a config");
124
125   return config;
126}
127
128static struct eglut_window *
129_eglutCreateWindow(const char *title, int x, int y, int w, int h)
130{
131   struct eglut_window *win;
132   EGLint context_attribs[4];
133   EGLint api, i;
134
135   win = calloc(1, sizeof(*win));
136   if (!win)
137      _eglutFatal("failed to allocate window");
138
139   win->config = _eglutChooseConfig();
140
141   i = 0;
142   context_attribs[i] = EGL_NONE;
143
144   /* multiple APIs? */
145
146   api = EGL_OPENGL_ES_API;
147   if (_eglut->api_mask & EGLUT_OPENGL_BIT) {
148      api = EGL_OPENGL_API;
149   }
150   else if (_eglut->api_mask & EGLUT_OPENVG_BIT) {
151      api = EGL_OPENVG_API;
152   }
153   else if (_eglut->api_mask & EGLUT_OPENGL_ES2_BIT) {
154      context_attribs[i++] = EGL_CONTEXT_CLIENT_VERSION;
155      context_attribs[i++] = 2;
156   }
157
158   context_attribs[i] = EGL_NONE;
159
160   eglBindAPI(api);
161   win->context = eglCreateContext(_eglut->dpy,
162         win->config, EGL_NO_CONTEXT, context_attribs);
163   if (!win->context)
164      _eglutFatal("failed to create context");
165
166   _eglutNativeInitWindow(win, title, x, y, w, h);
167   switch (_eglut->surface_type) {
168   case EGL_WINDOW_BIT:
169      win->surface = eglCreateWindowSurface(_eglut->dpy,
170            win->config, win->native.u.window, NULL);
171      break;
172   case EGL_PIXMAP_BIT:
173      win->surface = eglCreatePixmapSurface(_eglut->dpy,
174            win->config, win->native.u.pixmap, NULL);
175      break;
176   case EGL_PBUFFER_BIT:
177      win->surface = win->native.u.surface;
178      break;
179   default:
180      break;
181   }
182   if (win->surface == EGL_NO_SURFACE)
183      _eglutFatal("failed to create surface");
184
185   return win;
186}
187
188void
189eglutInitAPIMask(int mask)
190{
191   _eglut->api_mask = mask;
192}
193
194void
195eglutInitWindowSize(int width, int height)
196{
197   _eglut->window_width = width;
198   _eglut->window_height = height;
199}
200
201void
202eglutInit(int argc, char **argv)
203{
204   int i;
205
206   for (i = 1; i < argc; i++) {
207      if (strcmp(argv[i], "-display") == 0)
208         _eglut->display_name = argv[++i];
209      else if (strcmp(argv[i], "-info") == 0) {
210         _eglut->verbose = 1;
211      }
212   }
213
214   _eglutNativeInitDisplay();
215   _eglut->dpy = eglGetDisplay(_eglut->native_dpy);
216
217   if (!eglInitialize(_eglut->dpy, &_eglut->major, &_eglut->minor))
218      _eglutFatal("failed to initialize EGL display");
219
220   _eglut->init_time = _eglutNow();
221
222   printf("EGL_VERSION = %s\n", eglQueryString(_eglut->dpy, EGL_VERSION));
223   if (_eglut->verbose) {
224      printf("EGL_VENDOR = %s\n", eglQueryString(_eglut->dpy, EGL_VENDOR));
225      printf("EGL_EXTENSIONS = %s\n",
226            eglQueryString(_eglut->dpy, EGL_EXTENSIONS));
227      printf("EGL_CLIENT_APIS = %s\n",
228            eglQueryString(_eglut->dpy, EGL_CLIENT_APIS));
229   }
230}
231
232int
233eglutGet(int state)
234{
235   int val;
236
237   switch (state) {
238   case EGLUT_ELAPSED_TIME:
239      val = _eglutNow() - _eglut->init_time;
240      break;
241   default:
242      val = -1;
243      break;
244   }
245
246   return val;
247}
248
249void
250eglutIdleFunc(EGLUTidleCB func)
251{
252   _eglut->idle_cb = func;
253}
254
255void
256eglutPostRedisplay(void)
257{
258   _eglut->redisplay = 1;
259}
260
261void
262eglutMainLoop(void)
263{
264   struct eglut_window *win = _eglut->current;
265
266   if (!win)
267      _eglutFatal("no window is created\n");
268
269   if (win->reshape_cb)
270      win->reshape_cb(win->native.width, win->native.height);
271
272   _eglutNativeEventLoop();
273}
274
275static void
276_eglutFini(void)
277{
278   eglTerminate(_eglut->dpy);
279   _eglutNativeFiniDisplay();
280}
281
282void
283eglutDestroyWindow(int win)
284{
285   struct eglut_window *window = _eglut->current;
286
287   if (window->index != win)
288      return;
289
290   eglMakeCurrent(_eglut->dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
291
292   _eglutDestroyWindow(_eglut->current);
293}
294
295static void
296_eglutDefaultKeyboard(unsigned char key)
297{
298   if (key == 27) {
299      if (_eglut->current)
300         eglutDestroyWindow(_eglut->current->index);
301      _eglutFini();
302
303      exit(0);
304   }
305}
306
307int
308eglutCreateWindow(const char *title)
309{
310   struct eglut_window *win;
311
312   win = _eglutCreateWindow(title, 0, 0,
313         _eglut->window_width, _eglut->window_height);
314
315   win->index = _eglut->num_windows++;
316   win->reshape_cb = NULL;
317   win->display_cb = NULL;
318   win->keyboard_cb = _eglutDefaultKeyboard;
319   win->special_cb = NULL;
320
321   if (!eglMakeCurrent(_eglut->dpy, win->surface, win->surface, win->context))
322      _eglutFatal("failed to make window current");
323   _eglut->current = win;
324
325   return win->index;
326}
327
328int
329eglutGetWindowWidth(void)
330{
331   struct eglut_window *win = _eglut->current;
332   return win->native.width;
333}
334
335int
336eglutGetWindowHeight(void)
337{
338   struct eglut_window *win = _eglut->current;
339   return win->native.height;
340}
341
342void
343eglutDisplayFunc(EGLUTdisplayCB func)
344{
345   struct eglut_window *win = _eglut->current;
346   win->display_cb = func;
347
348}
349
350void
351eglutReshapeFunc(EGLUTreshapeCB func)
352{
353   struct eglut_window *win = _eglut->current;
354   win->reshape_cb = func;
355}
356
357void
358eglutKeyboardFunc(EGLUTkeyboardCB func)
359{
360   struct eglut_window *win = _eglut->current;
361   win->keyboard_cb = func;
362}
363
364void
365eglutSpecialFunc(EGLUTspecialCB func)
366{
367   struct eglut_window *win = _eglut->current;
368   win->special_cb = func;
369}
370