1/*
2 * Copyright (C) 1999-2001  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 * \file glxgears_fbconfig.c
24 * Yet-another-version of gears.  Originally ported to GLX by Brian Paul on
25 * 23 March 2001.  Modified to use fbconfigs by Ian Romanick on 10 Feb 2004.
26 *
27 * Command line options:
28 *    -info      print GL implementation information
29 *
30 * \author Brian Paul
31 * \author Ian Romanick <idr@us.ibm.com>
32 */
33
34
35#define GLX_GLXEXT_PROTOTYPES
36
37#include <math.h>
38#include <stdlib.h>
39#include <stdio.h>
40#include <string.h>
41#include <X11/Xlib.h>
42#include <X11/keysym.h>
43#include <GL/gl.h>
44#include <GL/glx.h>
45#include <GL/glxext.h>
46#include <assert.h>
47#include "pbutil.h"
48
49static PFNGLXCHOOSEFBCONFIGPROC choose_fbconfig = NULL;
50static PFNGLXGETVISUALFROMFBCONFIGPROC get_visual_from_fbconfig = NULL;
51static PFNGLXCREATENEWCONTEXTPROC create_new_context = NULL;
52static PFNGLXCREATEWINDOWPROC create_window = NULL;
53static PFNGLXDESTROYWINDOWPROC destroy_window = NULL;
54
55#define BENCHMARK
56
57#ifdef BENCHMARK
58
59/* XXX this probably isn't very portable */
60
61#include <sys/time.h>
62#include <unistd.h>
63
64/* return current time (in seconds) */
65static int
66current_time(void)
67{
68   struct timeval tv;
69#ifdef __VMS
70   (void) gettimeofday(&tv, NULL );
71#else
72   struct timezone tz;
73   (void) gettimeofday(&tv, &tz);
74#endif
75   return (int) tv.tv_sec;
76}
77
78#else /*BENCHMARK*/
79
80/* dummy */
81static int
82current_time(void)
83{
84   return 0;
85}
86
87#endif /*BENCHMARK*/
88
89
90
91#ifndef M_PI
92#define M_PI 3.14159265
93#endif
94
95
96static GLfloat view_rotx = 20.0, view_roty = 30.0, view_rotz = 0.0;
97static GLint gear1, gear2, gear3;
98static GLfloat angle = 0.0;
99
100
101/*
102 *
103 *  Draw a gear wheel.  You'll probably want to call this function when
104 *  building a display list since we do a lot of trig here.
105 *
106 *  Input:  inner_radius - radius of hole at center
107 *          outer_radius - radius at center of teeth
108 *          width - width of gear
109 *          teeth - number of teeth
110 *          tooth_depth - depth of tooth
111 */
112static void
113gear(GLfloat inner_radius, GLfloat outer_radius, GLfloat width,
114     GLint teeth, GLfloat tooth_depth)
115{
116   GLint i;
117   GLfloat r0, r1, r2;
118   GLfloat angle, da;
119   GLfloat u, v, len;
120
121   r0 = inner_radius;
122   r1 = outer_radius - tooth_depth / 2.0;
123   r2 = outer_radius + tooth_depth / 2.0;
124
125   da = 2.0 * M_PI / teeth / 4.0;
126
127   glShadeModel(GL_FLAT);
128
129   glNormal3f(0.0, 0.0, 1.0);
130
131   /* draw front face */
132   glBegin(GL_QUAD_STRIP);
133   for (i = 0; i <= teeth; i++) {
134      angle = i * 2.0 * M_PI / teeth;
135      glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5);
136      glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5);
137      if (i < teeth) {
138	 glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5);
139	 glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da),
140		    width * 0.5);
141      }
142   }
143   glEnd();
144
145   /* draw front sides of teeth */
146   glBegin(GL_QUADS);
147   da = 2.0 * M_PI / teeth / 4.0;
148   for (i = 0; i < teeth; i++) {
149      angle = i * 2.0 * M_PI / teeth;
150
151      glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5);
152      glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width * 0.5);
153      glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da),
154		 width * 0.5);
155      glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da),
156		 width * 0.5);
157   }
158   glEnd();
159
160   glNormal3f(0.0, 0.0, -1.0);
161
162   /* draw back face */
163   glBegin(GL_QUAD_STRIP);
164   for (i = 0; i <= teeth; i++) {
165      angle = i * 2.0 * M_PI / teeth;
166      glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5);
167      glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5);
168      if (i < teeth) {
169	 glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da),
170		    -width * 0.5);
171	 glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5);
172      }
173   }
174   glEnd();
175
176   /* draw back sides of teeth */
177   glBegin(GL_QUADS);
178   da = 2.0 * M_PI / teeth / 4.0;
179   for (i = 0; i < teeth; i++) {
180      angle = i * 2.0 * M_PI / teeth;
181
182      glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da),
183		 -width * 0.5);
184      glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da),
185		 -width * 0.5);
186      glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width * 0.5);
187      glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5);
188   }
189   glEnd();
190
191   /* draw outward faces of teeth */
192   glBegin(GL_QUAD_STRIP);
193   for (i = 0; i < teeth; i++) {
194      angle = i * 2.0 * M_PI / teeth;
195
196      glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5);
197      glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5);
198      u = r2 * cos(angle + da) - r1 * cos(angle);
199      v = r2 * sin(angle + da) - r1 * sin(angle);
200      len = sqrt(u * u + v * v);
201      u /= len;
202      v /= len;
203      glNormal3f(v, -u, 0.0);
204      glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width * 0.5);
205      glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width * 0.5);
206      glNormal3f(cos(angle), sin(angle), 0.0);
207      glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da),
208		 width * 0.5);
209      glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da),
210		 -width * 0.5);
211      u = r1 * cos(angle + 3 * da) - r2 * cos(angle + 2 * da);
212      v = r1 * sin(angle + 3 * da) - r2 * sin(angle + 2 * da);
213      glNormal3f(v, -u, 0.0);
214      glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da),
215		 width * 0.5);
216      glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da),
217		 -width * 0.5);
218      glNormal3f(cos(angle), sin(angle), 0.0);
219   }
220
221   glVertex3f(r1 * cos(0), r1 * sin(0), width * 0.5);
222   glVertex3f(r1 * cos(0), r1 * sin(0), -width * 0.5);
223
224   glEnd();
225
226   glShadeModel(GL_SMOOTH);
227
228   /* draw inside radius cylinder */
229   glBegin(GL_QUAD_STRIP);
230   for (i = 0; i <= teeth; i++) {
231      angle = i * 2.0 * M_PI / teeth;
232      glNormal3f(-cos(angle), -sin(angle), 0.0);
233      glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5);
234      glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5);
235   }
236   glEnd();
237}
238
239
240static void
241draw(void)
242{
243   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
244
245   glPushMatrix();
246   glRotatef(view_rotx, 1.0, 0.0, 0.0);
247   glRotatef(view_roty, 0.0, 1.0, 0.0);
248   glRotatef(view_rotz, 0.0, 0.0, 1.0);
249
250   glPushMatrix();
251   glTranslatef(-3.0, -2.0, 0.0);
252   glRotatef(angle, 0.0, 0.0, 1.0);
253   glCallList(gear1);
254   glPopMatrix();
255
256   glPushMatrix();
257   glTranslatef(3.1, -2.0, 0.0);
258   glRotatef(-2.0 * angle - 9.0, 0.0, 0.0, 1.0);
259   glCallList(gear2);
260   glPopMatrix();
261
262   glPushMatrix();
263   glTranslatef(-3.1, 4.2, 0.0);
264   glRotatef(-2.0 * angle - 25.0, 0.0, 0.0, 1.0);
265   glCallList(gear3);
266   glPopMatrix();
267
268   glPopMatrix();
269}
270
271
272/* new window size or exposure */
273static void
274reshape(int width, int height)
275{
276   GLfloat h = (GLfloat) height / (GLfloat) width;
277
278   glViewport(0, 0, (GLint) width, (GLint) height);
279   glMatrixMode(GL_PROJECTION);
280   glLoadIdentity();
281   glFrustum(-1.0, 1.0, -h, h, 5.0, 60.0);
282   glMatrixMode(GL_MODELVIEW);
283   glLoadIdentity();
284   glTranslatef(0.0, 0.0, -40.0);
285}
286
287
288static void
289init(void)
290{
291   static GLfloat pos[4] = { 5.0, 5.0, 10.0, 0.0 };
292   static GLfloat red[4] = { 0.8, 0.1, 0.0, 1.0 };
293   static GLfloat green[4] = { 0.0, 0.8, 0.2, 1.0 };
294   static GLfloat blue[4] = { 0.2, 0.2, 1.0, 1.0 };
295
296   glLightfv(GL_LIGHT0, GL_POSITION, pos);
297   glEnable(GL_CULL_FACE);
298   glEnable(GL_LIGHTING);
299   glEnable(GL_LIGHT0);
300   glEnable(GL_DEPTH_TEST);
301
302   /* make the gears */
303   gear1 = glGenLists(1);
304   glNewList(gear1, GL_COMPILE);
305   glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, red);
306   gear(1.0, 4.0, 1.0, 20, 0.7);
307   glEndList();
308
309   gear2 = glGenLists(1);
310   glNewList(gear2, GL_COMPILE);
311   glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, green);
312   gear(0.5, 2.0, 2.0, 10, 0.7);
313   glEndList();
314
315   gear3 = glGenLists(1);
316   glNewList(gear3, GL_COMPILE);
317   glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, blue);
318   gear(1.3, 2.0, 0.5, 10, 0.7);
319   glEndList();
320
321   glEnable(GL_NORMALIZE);
322}
323
324
325static GLXWindow
326dummy_create_window(Display *dpy, GLXFBConfig config, Window win,
327                   const int *attrib_list)
328{
329   (void) dpy;
330   (void) config;
331   (void) attrib_list;
332
333   return (GLXWindow) win;
334}
335
336
337static void
338dummy_destroy_window(Display *dpy, GLXWindow win)
339{
340   (void) dpy;
341   (void) win;
342}
343
344
345/**
346 * Initialize fbconfig related function pointers.
347 */
348static void
349init_fbconfig_functions(Display *dpy, int scrnum)
350{
351   const char * glx_extensions;
352   const char * match;
353   static const char ext_name[] = "GLX_SGIX_fbconfig";
354   const size_t len = strlen( ext_name );
355   int major;
356   int minor;
357   GLboolean ext_version_supported;
358   GLboolean glx_1_3_supported;
359
360
361   /* Determine if GLX 1.3 or greater is supported.
362    */
363   glXQueryVersion(dpy, & major, & minor);
364   glx_1_3_supported = (major == 1) && (minor >= 3);
365
366   /* Determine if GLX_SGIX_fbconfig is supported.
367    */
368   glx_extensions = glXQueryExtensionsString(dpy, scrnum);
369   match = strstr( glx_extensions, ext_name );
370
371   ext_version_supported = (match != NULL)
372       && ((match[len] == '\0') || (match[len] == ' '));
373
374   printf( "GLX 1.3 is %ssupported.\n",
375	   (glx_1_3_supported) ? "" : "not " );
376   printf( "%s is %ssupported.\n",
377	   ext_name, (ext_version_supported) ? "" : "not " );
378
379   if ( glx_1_3_supported ) {
380      choose_fbconfig = (PFNGLXCHOOSEFBCONFIGPROC)
381	 glXGetProcAddressARB((GLubyte *) "glXChooseFBConfig");
382      get_visual_from_fbconfig = (PFNGLXGETVISUALFROMFBCONFIGPROC)
383	 glXGetProcAddressARB((GLubyte *) "glXGetVisualFromFBConfig");
384      create_new_context = (PFNGLXCREATENEWCONTEXTPROC)
385	 glXGetProcAddressARB((GLubyte *) "glXCreateNewContext");
386      create_window = (PFNGLXCREATEWINDOWPROC)
387	 glXGetProcAddressARB((GLubyte *) "glXCreateWindow");
388      destroy_window = (PFNGLXDESTROYWINDOWPROC)
389	 glXGetProcAddressARB((GLubyte *) "glXDestroyWindow");
390   }
391   else if ( ext_version_supported ) {
392      choose_fbconfig = (PFNGLXCHOOSEFBCONFIGPROC)
393	 glXGetProcAddressARB((GLubyte *) "glXChooseFBConfigSGIX");
394      get_visual_from_fbconfig = (PFNGLXGETVISUALFROMFBCONFIGPROC)
395	 glXGetProcAddressARB((GLubyte *) "glXGetVisualFromFBConfigSGIX");
396      create_new_context = (PFNGLXCREATENEWCONTEXTPROC)
397	 glXGetProcAddressARB((GLubyte *) "glXCreateContextWithConfigSGIX");
398      create_window = dummy_create_window;
399      destroy_window = dummy_destroy_window;
400   }
401   else {
402      printf( "This demo requires either GLX 1.3 or %s be supported.\n",
403	      ext_name );
404      exit(1);
405   }
406
407   if ( choose_fbconfig == NULL ) {
408      printf( "glXChooseFBConfig not found!\n" );
409      exit(1);
410   }
411
412   if ( get_visual_from_fbconfig == NULL ) {
413      printf( "glXGetVisualFromFBConfig not found!\n" );
414      exit(1);
415   }
416
417   if ( create_new_context == NULL ) {
418      printf( "glXCreateNewContext not found!\n" );
419      exit(1);
420   }
421}
422
423
424/*
425 * Create an RGB, double-buffered window.
426 * Return the window and context handles.
427 */
428static void
429make_window( Display *dpy, const char *name,
430             int x, int y, int width, int height,
431             Window *winRet, GLXWindow *glxWinRet, GLXContext *ctxRet)
432{
433   int attrib[] = { GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
434		    GLX_RENDER_TYPE,   GLX_RGBA_BIT,
435		    GLX_RED_SIZE,      1,
436		    GLX_GREEN_SIZE,    1,
437		    GLX_BLUE_SIZE,     1,
438		    GLX_DOUBLEBUFFER,  GL_TRUE,
439		    GLX_DEPTH_SIZE,    1,
440		    None };
441   GLXFBConfig * fbconfig;
442   int num_configs;
443   int scrnum;
444   int i;
445   XSetWindowAttributes attr;
446   unsigned long mask;
447   Window root;
448   Window win;
449   GLXWindow glxWin;
450   GLXContext ctx;
451   XVisualInfo *visinfo;
452
453   scrnum = DefaultScreen( dpy );
454   root = RootWindow( dpy, scrnum );
455
456   init_fbconfig_functions(dpy, scrnum);
457   fbconfig = (*choose_fbconfig)(dpy, scrnum, attrib, & num_configs);
458   if (fbconfig == NULL) {
459      printf("Error: couldn't get an RGB, Double-buffered visual\n");
460      exit(1);
461   }
462
463   printf("\nThe following fbconfigs meet the requirements.  The first one "
464	  "will be used.\n\n");
465   for ( i = 0 ; i < num_configs ; i++ ) {
466      PrintFBConfigInfo(dpy, scrnum, fbconfig[i], GL_TRUE);
467   }
468
469   /* window attributes */
470   visinfo = (*get_visual_from_fbconfig)(dpy, fbconfig[0]);
471   assert(visinfo != NULL);
472   attr.background_pixel = 0;
473   attr.border_pixel = 0;
474   attr.colormap = XCreateColormap( dpy, root, visinfo->visual, AllocNone);
475   attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask;
476   mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
477
478   win = XCreateWindow( dpy, root, 0, 0, width, height,
479		        0, visinfo->depth, InputOutput,
480		        visinfo->visual, mask, &attr );
481
482   /* set hints and properties */
483   {
484      XSizeHints sizehints;
485      sizehints.x = x;
486      sizehints.y = y;
487      sizehints.width  = width;
488      sizehints.height = height;
489      sizehints.flags = USSize | USPosition;
490      XSetNormalHints(dpy, win, &sizehints);
491      XSetStandardProperties(dpy, win, name, name,
492                              None, (char **)NULL, 0, &sizehints);
493   }
494
495   glxWin = (*create_window)(dpy, fbconfig[0], win, NULL);
496
497   ctx = (*create_new_context)(dpy, fbconfig[0], GLX_RGBA_TYPE, NULL, GL_TRUE);
498   if (!ctx) {
499      printf("Error: glXCreateNewContext failed\n");
500      exit(1);
501   }
502
503   XFree(fbconfig);
504
505   *glxWinRet = glxWin;
506   *winRet = win;
507   *ctxRet = ctx;
508}
509
510
511static void
512event_loop(Display *dpy, GLXWindow win)
513{
514   while (1) {
515      while (XPending(dpy) > 0) {
516         XEvent event;
517         XNextEvent(dpy, &event);
518         switch (event.type) {
519	 case Expose:
520            /* we'll redraw below */
521	    break;
522	 case ConfigureNotify:
523	    reshape(event.xconfigure.width, event.xconfigure.height);
524	    break;
525         case KeyPress:
526            {
527               char buffer[10];
528               int code;
529               code = XLookupKeysym(&event.xkey, 0);
530               if (code == XK_Left) {
531                  view_roty += 5.0;
532               }
533               else if (code == XK_Right) {
534                  view_roty -= 5.0;
535               }
536               else if (code == XK_Up) {
537                  view_rotx += 5.0;
538               }
539               else if (code == XK_Down) {
540                  view_rotx -= 5.0;
541               }
542               else {
543                  XLookupString(&event.xkey, buffer, sizeof(buffer),
544                                NULL, NULL);
545                  if (buffer[0] == 27) {
546                     /* escape */
547                     return;
548                  }
549               }
550            }
551         }
552      }
553
554      /* next frame */
555      angle += 2.0;
556
557      draw();
558      glXSwapBuffers(dpy, win);
559
560      /* calc framerate */
561      {
562         static int t0 = -1;
563         static int frames = 0;
564         int t = current_time();
565
566         if (t0 < 0)
567            t0 = t;
568
569         frames++;
570
571         if (t - t0 >= 5.0) {
572            GLfloat seconds = t - t0;
573            GLfloat fps = frames / seconds;
574            printf("%d frames in %3.1f seconds = %6.3f FPS\n", frames, seconds,
575                   fps);
576            fflush(stdout);
577            t0 = t;
578            frames = 0;
579         }
580      }
581   }
582}
583
584
585int
586main(int argc, char *argv[])
587{
588   Display *dpy;
589   Window win;
590   GLXWindow glxWin;
591   GLXContext ctx;
592   const char *dpyName = NULL;
593   GLboolean printInfo = GL_FALSE;
594   int i;
595
596   for (i = 1; i < argc; i++) {
597      if (strcmp(argv[i], "-display") == 0) {
598         dpyName = argv[i+1];
599         i++;
600      }
601      else if (strcmp(argv[i], "-info") == 0) {
602         printInfo = GL_TRUE;
603      }
604   }
605
606   dpy = XOpenDisplay(dpyName);
607   if (!dpy) {
608      printf("Error: couldn't open display %s\n", XDisplayName(dpyName));
609      return -1;
610   }
611
612   make_window(dpy, "glxgears", 0, 0, 300, 300, &win, &glxWin, &ctx);
613   XMapWindow(dpy, win);
614   glXMakeCurrent(dpy, glxWin, ctx);
615
616   if (printInfo) {
617      printf("GL_RENDERER   = %s\n", (char *) glGetString(GL_RENDERER));
618      printf("GL_VERSION    = %s\n", (char *) glGetString(GL_VERSION));
619      printf("GL_VENDOR     = %s\n", (char *) glGetString(GL_VENDOR));
620      printf("GL_EXTENSIONS = %s\n", (char *) glGetString(GL_EXTENSIONS));
621   }
622
623   init();
624
625   event_loop(dpy, glxWin);
626
627   glXDestroyContext(dpy, ctx);
628   destroy_window(dpy, glxWin);
629   XDestroyWindow(dpy, win);
630   XCloseDisplay(dpy);
631
632   return 0;
633}
634