1/*
2 * Copyright (C) 2008  Brian Paul   All Rights Reserved.
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
15 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 */
21
22/*
23 * Test drawing to two windows.
24 * Brian Paul
25 * August 2008
26 */
27
28
29#include <assert.h>
30#include <math.h>
31#include <stdlib.h>
32#include <stdio.h>
33#include <string.h>
34#include <X11/Xlib.h>
35#include <X11/Xutil.h>
36#include <X11/keysym.h>
37#include <GLES/gl.h>
38#include <GLES/glext.h>
39#include <EGL/egl.h>
40
41
42static int WinWidth[2] = {150, 300}, WinHeight[2] = {150, 300};
43
44
45static GLfloat view_rotx = 0.0, view_roty = 0.0, view_rotz = 0.0;
46
47
48/* new window size or exposure */
49static void
50reshape(int width, int height)
51{
52   GLfloat ar = (GLfloat) width / (GLfloat) height;
53
54   glViewport(0, 0, (GLint) width, (GLint) height);
55
56   glMatrixMode(GL_PROJECTION);
57   glLoadIdentity();
58#ifdef GL_VERSION_ES_CM_1_0
59   glFrustumf(-ar, ar, -1, 1, 5.0, 60.0);
60#else
61   glFrustum(-ar, ar, -1, 1, 5.0, 60.0);
62#endif
63
64   glMatrixMode(GL_MODELVIEW);
65   glLoadIdentity();
66   glTranslatef(0.0, 0.0, -10.0);
67}
68
69
70static void
71draw(int win)
72{
73   static const GLfloat verts[3][2] = {
74      { -1, -1 },
75      {  1, -1 },
76      {  0,  1 }
77   };
78   static const GLfloat colors[3][4] = {
79      { 1, 0, 0, 1 },
80      { 0, 1, 0, 1 },
81      { 0, 0, 1, 1 }
82   };
83
84   assert(win == 0 || win == 1);
85
86   reshape(WinWidth[win], WinHeight[win]);
87
88   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
89
90   glPushMatrix();
91   glRotatef(view_rotx, 1, 0, 0);
92   glRotatef(view_roty, 0, 1, 0);
93   glRotatef(view_rotz, 0, 0, 1);
94
95   /* draw triangle */
96   {
97      glVertexPointer(2, GL_FLOAT, 0, verts);
98      glColorPointer(4, GL_FLOAT, 0, colors);
99
100      glEnableClientState(GL_VERTEX_ARRAY);
101      glEnableClientState(GL_COLOR_ARRAY);
102
103      glDrawArrays(GL_TRIANGLES, 0, 3);
104
105      glDisableClientState(GL_VERTEX_ARRAY);
106      glDisableClientState(GL_COLOR_ARRAY);
107   }
108
109   glPopMatrix();
110}
111
112
113static void
114init(void)
115{
116   glClearColor(0.4, 0.4, 0.4, 0.0);
117}
118
119
120/*
121 * Create an RGB, double-buffered X window.
122 * Return the window and context handles.
123 */
124static void
125make_x_window(Display *x_dpy, EGLDisplay egl_dpy,
126              const char *name,
127              int x, int y, int width, int height,
128              Window *winRet,
129              EGLContext *ctxRet,
130              EGLSurface *surfRet)
131{
132   static const EGLint attribs[] = {
133      EGL_RED_SIZE, 1,
134      EGL_GREEN_SIZE, 1,
135      EGL_BLUE_SIZE, 1,
136      EGL_DEPTH_SIZE, 1,
137      EGL_NONE
138   };
139
140   int scrnum;
141   XSetWindowAttributes attr;
142   unsigned long mask;
143   Window root;
144   Window win;
145   XVisualInfo *visInfo, visTemplate;
146   int num_visuals;
147   EGLContext ctx;
148   EGLConfig config;
149   EGLint num_configs;
150   EGLint vid;
151
152   scrnum = DefaultScreen( x_dpy );
153   root = RootWindow( x_dpy, scrnum );
154
155   if (!eglChooseConfig( egl_dpy, attribs, &config, 1, &num_configs)) {
156      printf("Error: couldn't get an EGL visual config\n");
157      exit(1);
158   }
159
160   assert(config);
161   assert(num_configs > 0);
162
163   if (!eglGetConfigAttrib(egl_dpy, config, EGL_NATIVE_VISUAL_ID, &vid)) {
164      printf("Error: eglGetConfigAttrib() failed\n");
165      exit(1);
166   }
167
168   /* The X window visual must match the EGL config */
169   visTemplate.visualid = vid;
170   visInfo = XGetVisualInfo(x_dpy, VisualIDMask, &visTemplate, &num_visuals);
171   if (!visInfo) {
172      printf("Error: couldn't get X visual\n");
173      exit(1);
174   }
175
176   /* window attributes */
177   attr.background_pixel = 0;
178   attr.border_pixel = 0;
179   attr.colormap = XCreateColormap( x_dpy, root, visInfo->visual, AllocNone);
180   attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask;
181   mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
182
183   win = XCreateWindow( x_dpy, root, x, y, width, height,
184		        0, visInfo->depth, InputOutput,
185		        visInfo->visual, mask, &attr );
186
187   /* set hints and properties */
188   {
189      XSizeHints sizehints;
190      sizehints.x = x;
191      sizehints.y = y;
192      sizehints.width  = width;
193      sizehints.height = height;
194      sizehints.flags = USSize | USPosition;
195      XSetNormalHints(x_dpy, win, &sizehints);
196      XSetStandardProperties(x_dpy, win, name, name,
197                              None, (char **)NULL, 0, &sizehints);
198   }
199
200#if USE_FULL_GL
201   eglBindAPI(EGL_OPENGL_API);
202#else
203   eglBindAPI(EGL_OPENGL_ES_API);
204#endif
205
206   if (ctxRet) {
207      ctx = eglCreateContext(egl_dpy, config, EGL_NO_CONTEXT, NULL );
208      if (!ctx) {
209         printf("Error: eglCreateContext failed\n");
210         exit(1);
211      }
212      *ctxRet = ctx;
213   }
214
215   *surfRet = eglCreateWindowSurface(egl_dpy, config, win, NULL);
216
217   if (!*surfRet) {
218      printf("Error: eglCreateWindowSurface failed\n");
219      exit(1);
220   }
221
222   XFree(visInfo);
223
224   *winRet = win;
225}
226
227
228static void
229event_loop(Display *dpy, Window win1, Window win2,
230           EGLDisplay egl_dpy, EGLSurface egl_surf1, EGLSurface egl_surf2,
231           EGLContext egl_ctx)
232{
233   while (1) {
234      int redraw = 0;
235      int win;
236      XEvent event;
237
238      XNextEvent(dpy, &event);
239
240      switch (event.type) {
241      case Expose:
242         redraw = 1;
243         break;
244      case ConfigureNotify:
245         if (event.xconfigure.window == win1)
246            win = 0;
247         else
248            win = 1;
249         WinWidth[win] = event.xconfigure.width;
250         WinHeight[win] = event.xconfigure.height;
251         break;
252      case KeyPress:
253         {
254            char buffer[10];
255            int r, code;
256            code = XLookupKeysym(&event.xkey, 0);
257            if (code == XK_Left) {
258               view_roty += 5.0;
259            }
260            else if (code == XK_Right) {
261               view_roty -= 5.0;
262            }
263            else if (code == XK_Up) {
264               view_rotx += 5.0;
265            }
266            else if (code == XK_Down) {
267               view_rotx -= 5.0;
268            }
269            else {
270               r = XLookupString(&event.xkey, buffer, sizeof(buffer),
271                                 NULL, NULL);
272               if (buffer[0] == 27) {
273                  /* escape */
274                  return;
275               }
276            }
277         }
278         redraw = 1;
279         break;
280      default:
281         ; /*no-op*/
282      }
283
284      if (redraw) {
285         /* win 1 */
286         if (!eglMakeCurrent(egl_dpy, egl_surf1, egl_surf1, egl_ctx)) {
287            printf("Error: eglMakeCurrent(1) failed\n");
288            return;
289         }
290         draw(0);
291         eglSwapBuffers(egl_dpy, egl_surf1);
292
293         /* win 2 */
294         if (!eglMakeCurrent(egl_dpy, egl_surf2, egl_surf2, egl_ctx)) {
295            printf("Error: eglMakeCurrent(2) failed\n");
296            return;
297         }
298         draw(1);
299         eglSwapBuffers(egl_dpy, egl_surf2);
300      }
301   }
302}
303
304
305static void
306usage(void)
307{
308   printf("Usage:\n");
309   printf("  -display <displayname>  set the display to run on\n");
310   printf("  -info                   display OpenGL renderer info\n");
311}
312
313
314int
315main(int argc, char *argv[])
316{
317   Display *x_dpy;
318   Window win1, win2;
319   EGLSurface egl_surf1, egl_surf2;
320   EGLContext egl_ctx;
321   EGLDisplay egl_dpy;
322   char *dpyName = NULL;
323   GLboolean printInfo = GL_FALSE;
324   EGLint egl_major, egl_minor;
325   int i;
326   const char *s;
327
328   static struct {
329      char *name;
330      GLenum value;
331      enum {GetString, GetInteger} type;
332   } info_items[] = {
333      {"GL_RENDERER", GL_RENDERER, GetString},
334      {"GL_VERSION", GL_VERSION, GetString},
335      {"GL_VENDOR", GL_VENDOR, GetString},
336      {"GL_EXTENSIONS", GL_EXTENSIONS, GetString},
337      {"GL_MAX_PALETTE_MATRICES_OES", GL_MAX_PALETTE_MATRICES_OES, GetInteger},
338      {"GL_MAX_VERTEX_UNITS_OES", GL_MAX_VERTEX_UNITS_OES, GetInteger},
339   };
340
341   for (i = 1; i < argc; i++) {
342      if (strcmp(argv[i], "-display") == 0) {
343         dpyName = argv[i+1];
344         i++;
345      }
346      else if (strcmp(argv[i], "-info") == 0) {
347         printInfo = GL_TRUE;
348      }
349      else {
350         usage();
351         return -1;
352      }
353   }
354
355   x_dpy = XOpenDisplay(dpyName);
356   if (!x_dpy) {
357      printf("Error: couldn't open display %s\n",
358	     dpyName ? dpyName : getenv("DISPLAY"));
359      return -1;
360   }
361
362   egl_dpy = eglGetDisplay(x_dpy);
363   if (!egl_dpy) {
364      printf("Error: eglGetDisplay() failed\n");
365      return -1;
366   }
367
368   if (!eglInitialize(egl_dpy, &egl_major, &egl_minor)) {
369      printf("Error: eglInitialize() failed\n");
370      return -1;
371   }
372
373   s = eglQueryString(egl_dpy, EGL_VERSION);
374   printf("EGL_VERSION = %s\n", s);
375
376   s = eglQueryString(egl_dpy, EGL_VENDOR);
377   printf("EGL_VENDOR = %s\n", s);
378
379   s = eglQueryString(egl_dpy, EGL_EXTENSIONS);
380   printf("EGL_EXTENSIONS = %s\n", s);
381
382   s = eglQueryString(egl_dpy, EGL_CLIENT_APIS);
383   printf("EGL_CLIENT_APIS = %s\n", s);
384
385   make_x_window(x_dpy, egl_dpy,
386                 "xegl_two_win #1", 0, 0, WinWidth[0], WinHeight[0],
387                 &win1, &egl_ctx, &egl_surf1);
388
389   make_x_window(x_dpy, egl_dpy,
390                 "xegl_two_win #2", WinWidth[0] + 50, 0,
391                 WinWidth[1], WinHeight[1],
392                 &win2, NULL, &egl_surf2);
393
394   XMapWindow(x_dpy, win1);
395
396   XMapWindow(x_dpy, win2);
397
398   if (!eglMakeCurrent(egl_dpy, egl_surf1, egl_surf1, egl_ctx)) {
399      printf("Error: eglMakeCurrent() failed\n");
400      return -1;
401   }
402
403   if (printInfo) {
404      for (i = 0; i < sizeof(info_items)/sizeof(info_items[0]); i++) {
405         switch (info_items[i].type) {
406            case GetString:
407               printf("%s = %s\n", info_items[i].name, (char *)glGetString(info_items[i].value));
408               break;
409            case GetInteger: {
410               GLint rv = -1;
411               glGetIntegerv(info_items[i].value, &rv);
412               printf("%s = %d\n", info_items[i].name, rv);
413               break;
414            }
415         }
416      }
417   };
418
419   init();
420
421   event_loop(x_dpy, win1, win2, egl_dpy, egl_surf1, egl_surf2, egl_ctx);
422
423   eglDestroyContext(egl_dpy, egl_ctx);
424   eglDestroySurface(egl_dpy, egl_surf1);
425   eglDestroySurface(egl_dpy, egl_surf2);
426   eglTerminate(egl_dpy);
427
428   XDestroyWindow(x_dpy, win1);
429   XDestroyWindow(x_dpy, win2);
430   XCloseDisplay(x_dpy);
431
432   return 0;
433}
434