blob: f6acd99f6bfb29615417a88219c311fde60cf042 [file] [log] [blame]
Brian Paul355da232001-03-23 22:46:26 +00001/*
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 * This is a port of the infamous "gears" demo to straight GLX (i.e. no GLUT)
24 * Port by Brian Paul 23 March 2001
25 *
Brian Paulad802332003-04-09 21:47:19 +000026 * Modified by Ian Romanick <idr@us.ibm.com> 09 April 2003 to support
27 * GLX_{MESA,SGI}_swap_control and GLX_OML_sync_control.
Brian Paul355da232001-03-23 22:46:26 +000028 *
Brian Paulad802332003-04-09 21:47:19 +000029 * Command line options:
30 * -display Name of the display to use.
31 * -info print GL implementation information
32 * -swap N Attempt to set the swap interval to 1/N second
33 * -forcegetrate Get the display refresh rate even if the required GLX
34 * extension is not supported.
Brian Paul355da232001-03-23 22:46:26 +000035 */
36
37
38#include <math.h>
39#include <stdlib.h>
40#include <stdio.h>
41#include <string.h>
42#include <X11/Xlib.h>
43#include <X11/keysym.h>
Jouk Jansen370d7ad2003-04-29 07:15:48 +000044#ifndef __VMS
45# include <stdint.h>
46#endif
47# define GLX_GLXEXT_PROTOTYPES
Brian Paul355da232001-03-23 22:46:26 +000048#include <GL/gl.h>
49#include <GL/glx.h>
50
Brian Paulad802332003-04-09 21:47:19 +000051#ifndef GLX_MESA_swap_control
52typedef GLint ( * PFNGLXSWAPINTERVALMESAPROC) (unsigned interval);
53typedef GLint ( * PFNGLXGETSWAPINTERVALMESAPROC) ( void );
54#endif
55
56#if !defined( GLX_OML_sync_control ) && defined( _STDINT_H )
57#define GLX_OML_sync_control 1
58typedef Bool ( * PFNGLXGETMSCRATEOMLPROC) (Display *dpy, GLXDrawable drawable, int32_t *numerator, int32_t *denominator);
59#endif
Brian Paul355da232001-03-23 22:46:26 +000060
Brian Paul785774d2003-05-30 15:30:16 +000061#ifndef GLX_MESA_swap_frame_usage
62#define GLX_MESA_swap_frame_usage 1
63typedef int ( * PFNGLXGETFRAMEUSAGEMESAPROC) (Display *dpy, GLXDrawable drawable, float * usage );
64#endif
65
Brian Paul355da232001-03-23 22:46:26 +000066#define BENCHMARK
67
Brian Paul785774d2003-05-30 15:30:16 +000068PFNGLXGETFRAMEUSAGEMESAPROC get_frame_usage = NULL;
69
Brian Paul355da232001-03-23 22:46:26 +000070#ifdef BENCHMARK
71
72/* XXX this probably isn't very portable */
73
74#include <sys/time.h>
75#include <unistd.h>
76
Brian Paulad802332003-04-09 21:47:19 +000077#define NUL '\0'
78
Brian Paul355da232001-03-23 22:46:26 +000079/* return current time (in seconds) */
80static int
81current_time(void)
82{
83 struct timeval tv;
Jouk Jansenf72a3da2002-10-08 08:38:26 +000084#ifdef __VMS
85 (void) gettimeofday(&tv, NULL );
86#else
Brian Paul355da232001-03-23 22:46:26 +000087 struct timezone tz;
88 (void) gettimeofday(&tv, &tz);
Jouk Jansenf72a3da2002-10-08 08:38:26 +000089#endif
Brian Paul355da232001-03-23 22:46:26 +000090 return (int) tv.tv_sec;
91}
92
93#else /*BENCHMARK*/
94
95/* dummy */
96static int
97current_time(void)
98{
99 return 0;
100}
101
102#endif /*BENCHMARK*/
103
104
105
106#ifndef M_PI
107#define M_PI 3.14159265
108#endif
109
110
111static GLfloat view_rotx = 20.0, view_roty = 30.0, view_rotz = 0.0;
112static GLint gear1, gear2, gear3;
113static GLfloat angle = 0.0;
114
Brian Paulad802332003-04-09 21:47:19 +0000115static GLboolean has_OML_sync_control = GL_FALSE;
116static GLboolean has_SGI_swap_control = GL_FALSE;
117static GLboolean has_MESA_swap_control = GL_FALSE;
Brian Paul785774d2003-05-30 15:30:16 +0000118static GLboolean has_MESA_swap_frame_usage = GL_FALSE;
Brian Paulad802332003-04-09 21:47:19 +0000119
120static char ** extension_table = NULL;
121static unsigned num_extensions;
Brian Paul355da232001-03-23 22:46:26 +0000122
Brian Paul785774d2003-05-30 15:30:16 +0000123static GLboolean use_ztrick = GL_FALSE;
124static GLfloat aspect;
125
Brian Paul355da232001-03-23 22:46:26 +0000126/*
127 *
128 * Draw a gear wheel. You'll probably want to call this function when
129 * building a display list since we do a lot of trig here.
130 *
131 * Input: inner_radius - radius of hole at center
132 * outer_radius - radius at center of teeth
133 * width - width of gear
134 * teeth - number of teeth
135 * tooth_depth - depth of tooth
136 */
137static void
138gear(GLfloat inner_radius, GLfloat outer_radius, GLfloat width,
139 GLint teeth, GLfloat tooth_depth)
140{
141 GLint i;
142 GLfloat r0, r1, r2;
143 GLfloat angle, da;
144 GLfloat u, v, len;
145
146 r0 = inner_radius;
147 r1 = outer_radius - tooth_depth / 2.0;
148 r2 = outer_radius + tooth_depth / 2.0;
149
150 da = 2.0 * M_PI / teeth / 4.0;
151
152 glShadeModel(GL_FLAT);
Brian Paul785774d2003-05-30 15:30:16 +0000153 glPolygonMode( GL_FRONT, GL_LINE );
Brian Paul355da232001-03-23 22:46:26 +0000154
155 glNormal3f(0.0, 0.0, 1.0);
156
157 /* draw front face */
158 glBegin(GL_QUAD_STRIP);
159 for (i = 0; i <= teeth; i++) {
160 angle = i * 2.0 * M_PI / teeth;
161 glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5);
162 glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5);
163 if (i < teeth) {
164 glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5);
165 glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da),
166 width * 0.5);
167 }
168 }
169 glEnd();
170
171 /* draw front sides of teeth */
172 glBegin(GL_QUADS);
173 da = 2.0 * M_PI / teeth / 4.0;
174 for (i = 0; i < teeth; i++) {
175 angle = i * 2.0 * M_PI / teeth;
176
177 glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5);
178 glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width * 0.5);
179 glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da),
180 width * 0.5);
181 glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da),
182 width * 0.5);
183 }
184 glEnd();
185
186 glNormal3f(0.0, 0.0, -1.0);
187
188 /* draw back face */
189 glBegin(GL_QUAD_STRIP);
190 for (i = 0; i <= teeth; i++) {
191 angle = i * 2.0 * M_PI / teeth;
192 glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5);
193 glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5);
194 if (i < teeth) {
195 glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da),
196 -width * 0.5);
197 glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5);
198 }
199 }
200 glEnd();
201
202 /* draw back sides of teeth */
203 glBegin(GL_QUADS);
204 da = 2.0 * M_PI / teeth / 4.0;
205 for (i = 0; i < teeth; i++) {
206 angle = i * 2.0 * M_PI / teeth;
207
208 glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da),
209 -width * 0.5);
210 glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da),
211 -width * 0.5);
212 glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width * 0.5);
213 glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5);
214 }
215 glEnd();
216
217 /* draw outward faces of teeth */
218 glBegin(GL_QUAD_STRIP);
219 for (i = 0; i < teeth; i++) {
220 angle = i * 2.0 * M_PI / teeth;
221
222 glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5);
223 glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5);
224 u = r2 * cos(angle + da) - r1 * cos(angle);
225 v = r2 * sin(angle + da) - r1 * sin(angle);
226 len = sqrt(u * u + v * v);
227 u /= len;
228 v /= len;
229 glNormal3f(v, -u, 0.0);
230 glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width * 0.5);
231 glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width * 0.5);
232 glNormal3f(cos(angle), sin(angle), 0.0);
233 glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da),
234 width * 0.5);
235 glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da),
236 -width * 0.5);
237 u = r1 * cos(angle + 3 * da) - r2 * cos(angle + 2 * da);
238 v = r1 * sin(angle + 3 * da) - r2 * sin(angle + 2 * da);
239 glNormal3f(v, -u, 0.0);
240 glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da),
241 width * 0.5);
242 glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da),
243 -width * 0.5);
244 glNormal3f(cos(angle), sin(angle), 0.0);
245 }
246
247 glVertex3f(r1 * cos(0), r1 * sin(0), width * 0.5);
248 glVertex3f(r1 * cos(0), r1 * sin(0), -width * 0.5);
249
250 glEnd();
251
252 glShadeModel(GL_SMOOTH);
253
254 /* draw inside radius cylinder */
255 glBegin(GL_QUAD_STRIP);
256 for (i = 0; i <= teeth; i++) {
257 angle = i * 2.0 * M_PI / teeth;
258 glNormal3f(-cos(angle), -sin(angle), 0.0);
259 glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5);
260 glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5);
261 }
262 glEnd();
263}
264
265
266static void
267draw(void)
268{
Brian Paul785774d2003-05-30 15:30:16 +0000269 if ( use_ztrick ) {
270 static GLboolean flip = GL_FALSE;
271 static const GLfloat vert[4][3] = {
272 { -1, -1, -0.999 },
273 { 1, -1, -0.999 },
274 { 1, 1, -0.999 },
275 { -1, 1, -0.999 }
276 };
277 static const GLfloat col[4][3] = {
278 { 1.0, 0.6, 0.0 },
279 { 1.0, 0.6, 0.0 },
280 { 0.0, 0.0, 0.0 },
281 { 0.0, 0.0, 0.0 },
282 };
283
284 if ( flip ) {
285 glDepthRange(0, 0.5);
286 glDepthFunc(GL_LEQUAL);
287 }
288 else {
289 glDepthRange(1.0, 0.4999);
290 glDepthFunc(GL_GEQUAL);
291 }
292
293 flip = !flip;
294
295 /* The famous Quake "Z trick" only works when the whole screen is
296 * re-drawn each frame.
297 */
298
299 glMatrixMode(GL_MODELVIEW);
300 glLoadIdentity();
301 glMatrixMode(GL_PROJECTION);
302 glLoadIdentity();
303 glOrtho(-1, 1, -1, 1, -1, 1);
304 glDisable(GL_LIGHTING);
305 glShadeModel(GL_SMOOTH);
306
307 glEnable( GL_VERTEX_ARRAY );
308 glEnable( GL_COLOR_ARRAY );
309 glVertexPointer( 3, GL_FLOAT, 0, vert );
310 glColorPointer( 3, GL_FLOAT, 0, col );
311 glDrawArrays( GL_POLYGON, 0, 4 );
312 glDisable( GL_COLOR_ARRAY );
313 glDisable( GL_VERTEX_ARRAY );
314
315 glMatrixMode(GL_PROJECTION);
316 glLoadIdentity();
317 glFrustum(-1.0, 1.0, -aspect, aspect, 5.0, 60.0);
318
319 glEnable(GL_LIGHTING);
320
321 glMatrixMode(GL_MODELVIEW);
322 glLoadIdentity();
323 glTranslatef(0.0, 0.0, -40.0);
324 }
325 else {
326 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
327 }
Brian Paul355da232001-03-23 22:46:26 +0000328
329 glPushMatrix();
330 glRotatef(view_rotx, 1.0, 0.0, 0.0);
331 glRotatef(view_roty, 0.0, 1.0, 0.0);
332 glRotatef(view_rotz, 0.0, 0.0, 1.0);
333
334 glPushMatrix();
335 glTranslatef(-3.0, -2.0, 0.0);
336 glRotatef(angle, 0.0, 0.0, 1.0);
337 glCallList(gear1);
338 glPopMatrix();
339
340 glPushMatrix();
341 glTranslatef(3.1, -2.0, 0.0);
342 glRotatef(-2.0 * angle - 9.0, 0.0, 0.0, 1.0);
343 glCallList(gear2);
344 glPopMatrix();
345
346 glPushMatrix();
347 glTranslatef(-3.1, 4.2, 0.0);
348 glRotatef(-2.0 * angle - 25.0, 0.0, 0.0, 1.0);
349 glCallList(gear3);
350 glPopMatrix();
351
352 glPopMatrix();
353}
354
355
356/* new window size or exposure */
357static void
358reshape(int width, int height)
359{
Brian Paul785774d2003-05-30 15:30:16 +0000360 aspect = (GLfloat) height / (GLfloat) width;
Brian Paul355da232001-03-23 22:46:26 +0000361
Brian Paul785774d2003-05-30 15:30:16 +0000362
Brian Paul355da232001-03-23 22:46:26 +0000363 glViewport(0, 0, (GLint) width, (GLint) height);
364 glMatrixMode(GL_PROJECTION);
365 glLoadIdentity();
Brian Paul785774d2003-05-30 15:30:16 +0000366
367 glFrustum(-1.0, 1.0, -aspect, aspect, 5.0, 60.0);
Brian Paul355da232001-03-23 22:46:26 +0000368 glMatrixMode(GL_MODELVIEW);
369 glLoadIdentity();
370 glTranslatef(0.0, 0.0, -40.0);
371}
372
373
374static void
375init(void)
376{
377 static GLfloat pos[4] = { 5.0, 5.0, 10.0, 0.0 };
378 static GLfloat red[4] = { 0.8, 0.1, 0.0, 1.0 };
379 static GLfloat green[4] = { 0.0, 0.8, 0.2, 1.0 };
380 static GLfloat blue[4] = { 0.2, 0.2, 1.0, 1.0 };
381
382 glLightfv(GL_LIGHT0, GL_POSITION, pos);
383 glEnable(GL_CULL_FACE);
384 glEnable(GL_LIGHTING);
385 glEnable(GL_LIGHT0);
386 glEnable(GL_DEPTH_TEST);
387
388 /* make the gears */
389 gear1 = glGenLists(1);
390 glNewList(gear1, GL_COMPILE);
391 glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, red);
392 gear(1.0, 4.0, 1.0, 20, 0.7);
393 glEndList();
394
395 gear2 = glGenLists(1);
396 glNewList(gear2, GL_COMPILE);
397 glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, green);
398 gear(0.5, 2.0, 2.0, 10, 0.7);
399 glEndList();
400
401 gear3 = glGenLists(1);
402 glNewList(gear3, GL_COMPILE);
403 glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, blue);
404 gear(1.3, 2.0, 0.5, 10, 0.7);
405 glEndList();
406
407 glEnable(GL_NORMALIZE);
408}
409
410
411/*
412 * Create an RGB, double-buffered window.
413 * Return the window and context handles.
414 */
415static void
416make_window( Display *dpy, const char *name,
417 int x, int y, int width, int height,
418 Window *winRet, GLXContext *ctxRet)
419{
420 int attrib[] = { GLX_RGBA,
421 GLX_RED_SIZE, 1,
422 GLX_GREEN_SIZE, 1,
423 GLX_BLUE_SIZE, 1,
424 GLX_DOUBLEBUFFER,
425 GLX_DEPTH_SIZE, 1,
426 None };
427 int scrnum;
428 XSetWindowAttributes attr;
429 unsigned long mask;
430 Window root;
431 Window win;
432 GLXContext ctx;
433 XVisualInfo *visinfo;
434
435 scrnum = DefaultScreen( dpy );
436 root = RootWindow( dpy, scrnum );
437
438 visinfo = glXChooseVisual( dpy, scrnum, attrib );
439 if (!visinfo) {
440 printf("Error: couldn't get an RGB, Double-buffered visual\n");
441 exit(1);
442 }
443
444 /* window attributes */
445 attr.background_pixel = 0;
446 attr.border_pixel = 0;
447 attr.colormap = XCreateColormap( dpy, root, visinfo->visual, AllocNone);
448 attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask;
449 mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
450
451 win = XCreateWindow( dpy, root, 0, 0, width, height,
452 0, visinfo->depth, InputOutput,
453 visinfo->visual, mask, &attr );
454
455 /* set hints and properties */
456 {
457 XSizeHints sizehints;
458 sizehints.x = x;
459 sizehints.y = y;
460 sizehints.width = width;
461 sizehints.height = height;
462 sizehints.flags = USSize | USPosition;
463 XSetNormalHints(dpy, win, &sizehints);
464 XSetStandardProperties(dpy, win, name, name,
465 None, (char **)NULL, 0, &sizehints);
466 }
467
468 ctx = glXCreateContext( dpy, visinfo, NULL, True );
469 if (!ctx) {
470 printf("Error: glXCreateContext failed\n");
471 exit(1);
472 }
473
474 XFree(visinfo);
475
476 *winRet = win;
477 *ctxRet = ctx;
478}
479
480
481static void
482event_loop(Display *dpy, Window win)
483{
Brian Paul785774d2003-05-30 15:30:16 +0000484 float frame_usage = 0.0;
485
Brian Paul355da232001-03-23 22:46:26 +0000486 while (1) {
487 while (XPending(dpy) > 0) {
488 XEvent event;
489 XNextEvent(dpy, &event);
490 switch (event.type) {
491 case Expose:
492 /* we'll redraw below */
493 break;
494 case ConfigureNotify:
495 reshape(event.xconfigure.width, event.xconfigure.height);
496 break;
497 case KeyPress:
498 {
499 char buffer[10];
500 int r, code;
501 code = XLookupKeysym(&event.xkey, 0);
502 if (code == XK_Left) {
503 view_roty += 5.0;
504 }
505 else if (code == XK_Right) {
506 view_roty -= 5.0;
507 }
508 else if (code == XK_Up) {
509 view_rotx += 5.0;
510 }
511 else if (code == XK_Down) {
512 view_rotx -= 5.0;
513 }
514 else {
515 r = XLookupString(&event.xkey, buffer, sizeof(buffer),
516 NULL, NULL);
517 if (buffer[0] == 27) {
518 /* escape */
519 return;
520 }
521 }
522 }
523 }
524 }
525
526 /* next frame */
527 angle += 2.0;
528
529 draw();
Brian Paul785774d2003-05-30 15:30:16 +0000530 if ( get_frame_usage != NULL ) {
531 GLfloat temp;
532
533 (*get_frame_usage)( dpy, win, & temp );
534 frame_usage += temp;
535 }
536
Brian Paul355da232001-03-23 22:46:26 +0000537 glXSwapBuffers(dpy, win);
538
539 /* calc framerate */
540 {
541 static int t0 = -1;
542 static int frames = 0;
543 int t = current_time();
544
545 if (t0 < 0)
546 t0 = t;
547
548 frames++;
549
550 if (t - t0 >= 5.0) {
551 GLfloat seconds = t - t0;
552 GLfloat fps = frames / seconds;
Brian Paul785774d2003-05-30 15:30:16 +0000553 if ( get_frame_usage != NULL ) {
554 printf("%d frames in %3.1f seconds = %6.3f FPS (%3.1f%% usage)\n",
555 frames, seconds, fps,
556 (frame_usage * 100.0) / (float) frames );
557 }
558 else {
559 printf("%d frames in %3.1f seconds = %6.3f FPS\n",
560 frames, seconds, fps);
561 }
562
Brian Paul355da232001-03-23 22:46:26 +0000563 t0 = t;
564 frames = 0;
Brian Paul785774d2003-05-30 15:30:16 +0000565 frame_usage = 0.0;
Brian Paul355da232001-03-23 22:46:26 +0000566 }
567 }
568 }
569}
570
571
Brian Paulad802332003-04-09 21:47:19 +0000572/**
573 * Display the refresh rate of the display using the GLX_OML_sync_control
574 * extension.
575 */
576
577static void
578show_refresh_rate( Display * dpy )
579{
580#ifdef GLX_OML_sync_control
581 PFNGLXGETMSCRATEOMLPROC get_msc_rate;
582 int32_t n;
583 int32_t d;
584
585 get_msc_rate = (PFNGLXGETMSCRATEOMLPROC) glXGetProcAddressARB( (const GLubyte *) "glXGetMscRateOML" );
586 if ( get_msc_rate != NULL ) {
587 (*get_msc_rate)( dpy, glXGetCurrentDrawable(), &n, &d );
588 printf( "refresh rate: %.1fHz\n", (float) n / d );
589 return;
590 }
591#endif
592 printf( "glXGetMscRateOML not supported.\n" );
593}
594
595
596/**
597 * Fill in the table of extension strings from a supplied extensions string
598 * (as returned by glXQueryExtensionsString).
599 *
600 * \param string String of GLX extensions.
601 * \sa is_extension_supported
602 */
603
604static void
605make_extension_table( const char * string )
606{
607 char ** string_tab;
608 unsigned num_strings;
609 unsigned base;
610 unsigned idx;
611 unsigned i;
612
613 /* Count the number of spaces in the string. That gives a base-line
614 * figure for the number of extension in the string.
615 */
616
617 num_strings = 1;
618 for ( i = 0 ; string[i] != NUL ; i++ ) {
619 if ( string[i] == ' ' ) {
620 num_strings++;
621 }
622 }
623
624 string_tab = (char **) malloc( sizeof( char * ) * num_strings );
625 if ( string_tab == NULL ) {
626 return;
627 }
628
629 base = 0;
630 idx = 0;
631
632 while ( string[ base ] != NUL ) {
633 /* Determine the length of the next extension string.
634 */
635
636 for ( i = 0
637 ; (string[ base + i ] != NUL) && (string[ base + i ] != ' ')
638 ; i++ ) {
639 /* empty */ ;
640 }
641
642 if ( i > 0 ) {
643 /* If the string was non-zero length, add it to the table. We
644 * can get zero length strings if there is a space at the end of
645 * the string or if there are two (or more) spaces next to each
646 * other in the string.
647 */
648
649 string_tab[ idx ] = malloc( sizeof( char ) * (i + 1) );
650 if ( string_tab[ idx ] == NULL ) {
651 return;
652 }
653
654 (void) memcpy( string_tab[ idx ], & string[ base ], i );
655 string_tab[ idx ][i] = NUL;
656 idx++;
657 }
658
659
660 /* Skip to the start of the next extension string.
661 */
662
663 for ( base += i
664 ; (string[ base ] == ' ') && (string[ base ] != NUL)
665 ; base++ ) {
666 /* empty */ ;
667 }
668 }
669
670 extension_table = string_tab;
671 num_extensions = idx;
672}
673
674
675/**
676 * Determine of an extension is supported. The extension string table
677 * must have already be initialized by calling \c make_extension_table.
678 *
679 * \praram ext Extension to be tested.
680 * \return GL_TRUE of the extension is supported, GL_FALSE otherwise.
681 * \sa make_extension_table
682 */
683
684static GLboolean
685is_extension_supported( const char * ext )
686{
687 unsigned i;
688
689 for ( i = 0 ; i < num_extensions ; i++ ) {
690 if ( strcmp( ext, extension_table[i] ) == 0 ) {
691 return GL_TRUE;
692 }
693 }
694
695 return GL_FALSE;
696}
697
698
Brian Paul355da232001-03-23 22:46:26 +0000699int
700main(int argc, char *argv[])
701{
702 Display *dpy;
703 Window win;
704 GLXContext ctx;
705 char *dpyName = ":0";
Brian Paulad802332003-04-09 21:47:19 +0000706 int swap_interval = 1;
707 GLboolean do_swap_interval = GL_FALSE;
708 GLboolean force_get_rate = GL_FALSE;
Brian Paul355da232001-03-23 22:46:26 +0000709 GLboolean printInfo = GL_FALSE;
710 int i;
Brian Paulad802332003-04-09 21:47:19 +0000711 PFNGLXSWAPINTERVALMESAPROC set_swap_interval = NULL;
712 PFNGLXGETSWAPINTERVALMESAPROC get_swap_interval = NULL;
713
Brian Paul355da232001-03-23 22:46:26 +0000714
715 for (i = 1; i < argc; i++) {
Brian Paulad802332003-04-09 21:47:19 +0000716 if (strcmp(argv[i], "-display") == 0 && i + 1 < argc) {
Brian Paul355da232001-03-23 22:46:26 +0000717 dpyName = argv[i+1];
718 i++;
719 }
720 else if (strcmp(argv[i], "-info") == 0) {
721 printInfo = GL_TRUE;
722 }
Brian Paulad802332003-04-09 21:47:19 +0000723 else if (strcmp(argv[i], "-swap") == 0 && i + 1 < argc) {
724 swap_interval = atoi( argv[i+1] );
725 do_swap_interval = GL_TRUE;
726 i++;
727 }
728 else if (strcmp(argv[i], "-forcegetrate") == 0) {
729 /* This option was put in because some DRI drivers don't support the
730 * full GLX_OML_sync_control extension, but they do support
731 * glXGetMscRateOML.
732 */
733 force_get_rate = GL_TRUE;
734 }
Brian Paul785774d2003-05-30 15:30:16 +0000735 else if (strcmp(argv[i], "-ztrick") == 0) {
736 use_ztrick = GL_TRUE;
737 }
Brian Paulad802332003-04-09 21:47:19 +0000738 else if (strcmp(argv[i], "-help") == 0) {
739 printf("Usage:\n");
740 printf(" gears [options]\n");
741 printf("Options:\n");
742 printf(" -help Print this information\n");
743 printf(" -display displayName Specify X display\n");
744 printf(" -info Display GL information\n");
745 printf(" -swap N Swap no more than once per N vertical refreshes\n");
Brian Paulebab6312003-04-09 22:50:52 +0000746 printf(" -forcegetrate Try to use glXGetMscRateOML function\n");
Brian Paulad802332003-04-09 21:47:19 +0000747 return 0;
748 }
Brian Paul355da232001-03-23 22:46:26 +0000749 }
750
751 dpy = XOpenDisplay(dpyName);
752 if (!dpy) {
753 printf("Error: couldn't open display %s\n", dpyName);
754 return -1;
755 }
756
757 make_window(dpy, "glxgears", 0, 0, 300, 300, &win, &ctx);
758 XMapWindow(dpy, win);
759 glXMakeCurrent(dpy, win, ctx);
760
Brian Paulad802332003-04-09 21:47:19 +0000761 make_extension_table( (char *) glXQueryExtensionsString(dpy,DefaultScreen(dpy)) );
762 has_OML_sync_control = is_extension_supported( "GLX_OML_sync_control" );
763 has_SGI_swap_control = is_extension_supported( "GLX_SGI_swap_control" );
764 has_MESA_swap_control = is_extension_supported( "GLX_MESA_swap_control" );
Brian Paul785774d2003-05-30 15:30:16 +0000765 has_MESA_swap_frame_usage = is_extension_supported( "GLX_MESA_swap_frame_usage" );
Brian Paulad802332003-04-09 21:47:19 +0000766
767 if ( has_MESA_swap_control ) {
768 set_swap_interval = (PFNGLXSWAPINTERVALMESAPROC) glXGetProcAddressARB( (const GLubyte *) "glXSwapIntervalMESA" );
769 get_swap_interval = (PFNGLXGETSWAPINTERVALMESAPROC) glXGetProcAddressARB( (const GLubyte *) "glXGetSwapIntervalMESA" );
770 }
771 else if ( has_SGI_swap_control ) {
772 set_swap_interval = (PFNGLXSWAPINTERVALMESAPROC) glXGetProcAddressARB( (const GLubyte *) "glXSwapIntervalSGI" );
773 }
774
775
Brian Paul785774d2003-05-30 15:30:16 +0000776 if ( has_MESA_swap_frame_usage ) {
777 get_frame_usage = (PFNGLXGETFRAMEUSAGEMESAPROC) glXGetProcAddressARB( (const GLubyte *) "glXGetFrameUsageMESA" );
778 }
779
780
Brian Paul355da232001-03-23 22:46:26 +0000781 if (printInfo) {
782 printf("GL_RENDERER = %s\n", (char *) glGetString(GL_RENDERER));
783 printf("GL_VERSION = %s\n", (char *) glGetString(GL_VERSION));
784 printf("GL_VENDOR = %s\n", (char *) glGetString(GL_VENDOR));
785 printf("GL_EXTENSIONS = %s\n", (char *) glGetString(GL_EXTENSIONS));
Brian Paulad802332003-04-09 21:47:19 +0000786 if ( has_OML_sync_control || force_get_rate ) {
787 show_refresh_rate( dpy );
788 }
789
790 if ( get_swap_interval != NULL ) {
791 printf("Default swap interval = %d\n", (*get_swap_interval)() );
792 }
793 }
794
795 if ( do_swap_interval ) {
796 if ( set_swap_interval != NULL ) {
797 if ( ((swap_interval == 0) && !has_MESA_swap_control)
798 || (swap_interval < 0) ) {
799 printf( "Swap interval must be non-negative or greater than zero "
800 "if GLX_MESA_swap_control is not supported.\n" );
801 }
802 else {
803 (*set_swap_interval)( swap_interval );
804 }
805
806 if ( printInfo && (get_swap_interval != NULL) ) {
807 printf("Current swap interval = %d\n", (*get_swap_interval)() );
808 }
809 }
810 else {
811 printf("Unable to set swap-interval. Neither GLX_SGI_swap_control "
812 "nor GLX_MESA_swap_control are supported.\n" );
813 }
Brian Paul355da232001-03-23 22:46:26 +0000814 }
815
816 init();
817
818 event_loop(dpy, win);
819
820 glXDestroyContext(dpy, ctx);
821 XDestroyWindow(dpy, win);
822 XCloseDisplay(dpy);
823
824 return 0;
825}