2 * Copyright (C) 1999-2001 Brian Paul All Rights Reserved.
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:
11 * The above copyright notice and this permission notice shall be included
12 * in all copies or substantial portions of the Software.
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.
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
26 * Modified by Ian Romanick <idr@us.ibm.com> 09 April 2003 to support
27 * GLX_{MESA,SGI}_swap_control and GLX_OML_sync_control.
29 * 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.
43 #include <X11/keysym.h>
45 /*# include <stdint.h>*/
47 # define GLX_GLXEXT_PROTOTYPES
51 #ifndef GLX_MESA_swap_control
52 typedef GLint ( * PFNGLXSWAPINTERVALMESAPROC
) (unsigned interval
);
53 typedef GLint ( * PFNGLXGETSWAPINTERVALMESAPROC
) ( void );
56 #if !defined( GLX_OML_sync_control ) && defined( _STDINT_H )
57 #define GLX_OML_sync_control 1
58 typedef Bool ( * PFNGLXGETMSCRATEOMLPROC
) (Display
*dpy
, GLXDrawable drawable
, int32_t *numerator
, int32_t *denominator
);
61 #ifndef GLX_MESA_swap_frame_usage
62 #define GLX_MESA_swap_frame_usage 1
63 typedef int ( * PFNGLXGETFRAMEUSAGEMESAPROC
) (Display
*dpy
, GLXDrawable drawable
, float * usage
);
68 PFNGLXGETFRAMEUSAGEMESAPROC get_frame_usage
= NULL
;
72 /* XXX this probably isn't very portable */
79 /* return current time (in seconds) */
85 (void) gettimeofday(&tv
, NULL
);
88 (void) gettimeofday(&tv
, &tz
);
90 return (int) tv
.tv_sec
;
107 #define M_PI 3.14159265
111 static GLfloat view_rotx
= 20.0, view_roty
= 30.0, view_rotz
= 0.0;
112 static GLint gear1
, gear2
, gear3
;
113 static GLfloat angle
= 0.0;
115 static GLboolean has_OML_sync_control
= GL_FALSE
;
116 static GLboolean has_SGI_swap_control
= GL_FALSE
;
117 static GLboolean has_MESA_swap_control
= GL_FALSE
;
118 static GLboolean has_MESA_swap_frame_usage
= GL_FALSE
;
120 static char ** extension_table
= NULL
;
121 static unsigned num_extensions
;
123 static GLboolean use_ztrick
= GL_FALSE
;
124 static GLfloat aspect
;
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.
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
138 gear(GLfloat inner_radius
, GLfloat outer_radius
, GLfloat width
,
139 GLint teeth
, GLfloat tooth_depth
)
147 r1
= outer_radius
- tooth_depth
/ 2.0;
148 r2
= outer_radius
+ tooth_depth
/ 2.0;
150 da
= 2.0 * M_PI
/ teeth
/ 4.0;
152 glShadeModel(GL_FLAT
);
154 glNormal3f(0.0, 0.0, 1.0);
156 /* draw front face */
157 glBegin(GL_QUAD_STRIP
);
158 for (i
= 0; i
<= teeth
; i
++) {
159 angle
= i
* 2.0 * M_PI
/ teeth
;
160 glVertex3f(r0
* cos(angle
), r0
* sin(angle
), width
* 0.5);
161 glVertex3f(r1
* cos(angle
), r1
* sin(angle
), width
* 0.5);
163 glVertex3f(r0
* cos(angle
), r0
* sin(angle
), width
* 0.5);
164 glVertex3f(r1
* cos(angle
+ 3 * da
), r1
* sin(angle
+ 3 * da
),
170 /* draw front sides of teeth */
172 da
= 2.0 * M_PI
/ teeth
/ 4.0;
173 for (i
= 0; i
< teeth
; i
++) {
174 angle
= i
* 2.0 * M_PI
/ teeth
;
176 glVertex3f(r1
* cos(angle
), r1
* sin(angle
), width
* 0.5);
177 glVertex3f(r2
* cos(angle
+ da
), r2
* sin(angle
+ da
), width
* 0.5);
178 glVertex3f(r2
* cos(angle
+ 2 * da
), r2
* sin(angle
+ 2 * da
),
180 glVertex3f(r1
* cos(angle
+ 3 * da
), r1
* sin(angle
+ 3 * da
),
185 glNormal3f(0.0, 0.0, -1.0);
188 glBegin(GL_QUAD_STRIP
);
189 for (i
= 0; i
<= teeth
; i
++) {
190 angle
= i
* 2.0 * M_PI
/ teeth
;
191 glVertex3f(r1
* cos(angle
), r1
* sin(angle
), -width
* 0.5);
192 glVertex3f(r0
* cos(angle
), r0
* sin(angle
), -width
* 0.5);
194 glVertex3f(r1
* cos(angle
+ 3 * da
), r1
* sin(angle
+ 3 * da
),
196 glVertex3f(r0
* cos(angle
), r0
* sin(angle
), -width
* 0.5);
201 /* draw back sides of teeth */
203 da
= 2.0 * M_PI
/ teeth
/ 4.0;
204 for (i
= 0; i
< teeth
; i
++) {
205 angle
= i
* 2.0 * M_PI
/ teeth
;
207 glVertex3f(r1
* cos(angle
+ 3 * da
), r1
* sin(angle
+ 3 * da
),
209 glVertex3f(r2
* cos(angle
+ 2 * da
), r2
* sin(angle
+ 2 * da
),
211 glVertex3f(r2
* cos(angle
+ da
), r2
* sin(angle
+ da
), -width
* 0.5);
212 glVertex3f(r1
* cos(angle
), r1
* sin(angle
), -width
* 0.5);
216 /* draw outward faces of teeth */
217 glBegin(GL_QUAD_STRIP
);
218 for (i
= 0; i
< teeth
; i
++) {
219 angle
= i
* 2.0 * M_PI
/ teeth
;
221 glVertex3f(r1
* cos(angle
), r1
* sin(angle
), width
* 0.5);
222 glVertex3f(r1
* cos(angle
), r1
* sin(angle
), -width
* 0.5);
223 u
= r2
* cos(angle
+ da
) - r1
* cos(angle
);
224 v
= r2
* sin(angle
+ da
) - r1
* sin(angle
);
225 len
= sqrt(u
* u
+ v
* v
);
228 glNormal3f(v
, -u
, 0.0);
229 glVertex3f(r2
* cos(angle
+ da
), r2
* sin(angle
+ da
), width
* 0.5);
230 glVertex3f(r2
* cos(angle
+ da
), r2
* sin(angle
+ da
), -width
* 0.5);
231 glNormal3f(cos(angle
), sin(angle
), 0.0);
232 glVertex3f(r2
* cos(angle
+ 2 * da
), r2
* sin(angle
+ 2 * da
),
234 glVertex3f(r2
* cos(angle
+ 2 * da
), r2
* sin(angle
+ 2 * da
),
236 u
= r1
* cos(angle
+ 3 * da
) - r2
* cos(angle
+ 2 * da
);
237 v
= r1
* sin(angle
+ 3 * da
) - r2
* sin(angle
+ 2 * da
);
238 glNormal3f(v
, -u
, 0.0);
239 glVertex3f(r1
* cos(angle
+ 3 * da
), r1
* sin(angle
+ 3 * da
),
241 glVertex3f(r1
* cos(angle
+ 3 * da
), r1
* sin(angle
+ 3 * da
),
243 glNormal3f(cos(angle
), sin(angle
), 0.0);
246 glVertex3f(r1
* cos(0), r1
* sin(0), width
* 0.5);
247 glVertex3f(r1
* cos(0), r1
* sin(0), -width
* 0.5);
251 glShadeModel(GL_SMOOTH
);
253 /* draw inside radius cylinder */
254 glBegin(GL_QUAD_STRIP
);
255 for (i
= 0; i
<= teeth
; i
++) {
256 angle
= i
* 2.0 * M_PI
/ teeth
;
257 glNormal3f(-cos(angle
), -sin(angle
), 0.0);
258 glVertex3f(r0
* cos(angle
), r0
* sin(angle
), -width
* 0.5);
259 glVertex3f(r0
* cos(angle
), r0
* sin(angle
), width
* 0.5);
269 static GLboolean flip
= GL_FALSE
;
270 static const GLfloat vert
[4][3] = {
276 static const GLfloat col
[4][3] = {
284 glDepthRange(0, 0.5);
285 glDepthFunc(GL_LEQUAL
);
288 glDepthRange(1.0, 0.4999);
289 glDepthFunc(GL_GEQUAL
);
294 /* The famous Quake "Z trick" only works when the whole screen is
295 * re-drawn each frame.
298 glMatrixMode(GL_MODELVIEW
);
300 glMatrixMode(GL_PROJECTION
);
302 glOrtho(-1, 1, -1, 1, -1, 1);
303 glDisable(GL_LIGHTING
);
304 glShadeModel(GL_SMOOTH
);
306 glEnable( GL_VERTEX_ARRAY
);
307 glEnable( GL_COLOR_ARRAY
);
308 glVertexPointer( 3, GL_FLOAT
, 0, vert
);
309 glColorPointer( 3, GL_FLOAT
, 0, col
);
310 glDrawArrays( GL_POLYGON
, 0, 4 );
311 glDisable( GL_COLOR_ARRAY
);
312 glDisable( GL_VERTEX_ARRAY
);
314 glMatrixMode(GL_PROJECTION
);
316 glFrustum(-1.0, 1.0, -aspect
, aspect
, 5.0, 60.0);
318 glEnable(GL_LIGHTING
);
320 glMatrixMode(GL_MODELVIEW
);
322 glTranslatef(0.0, 0.0, -40.0);
325 glClear(GL_COLOR_BUFFER_BIT
| GL_DEPTH_BUFFER_BIT
);
329 glRotatef(view_rotx
, 1.0, 0.0, 0.0);
330 glRotatef(view_roty
, 0.0, 1.0, 0.0);
331 glRotatef(view_rotz
, 0.0, 0.0, 1.0);
334 glTranslatef(-3.0, -2.0, 0.0);
335 glRotatef(angle
, 0.0, 0.0, 1.0);
340 glTranslatef(3.1, -2.0, 0.0);
341 glRotatef(-2.0 * angle
- 9.0, 0.0, 0.0, 1.0);
346 glTranslatef(-3.1, 4.2, 0.0);
347 glRotatef(-2.0 * angle
- 25.0, 0.0, 0.0, 1.0);
355 /* new window size or exposure */
357 reshape(int width
, int height
)
359 aspect
= (GLfloat
) height
/ (GLfloat
) width
;
362 glViewport(0, 0, (GLint
) width
, (GLint
) height
);
363 glMatrixMode(GL_PROJECTION
);
366 glFrustum(-1.0, 1.0, -aspect
, aspect
, 5.0, 60.0);
367 glMatrixMode(GL_MODELVIEW
);
369 glTranslatef(0.0, 0.0, -40.0);
376 static GLfloat pos
[4] = { 5.0, 5.0, 10.0, 0.0 };
377 static GLfloat red
[4] = { 0.8, 0.1, 0.0, 1.0 };
378 static GLfloat green
[4] = { 0.0, 0.8, 0.2, 1.0 };
379 static GLfloat blue
[4] = { 0.2, 0.2, 1.0, 1.0 };
381 glLightfv(GL_LIGHT0
, GL_POSITION
, pos
);
382 glEnable(GL_CULL_FACE
);
383 glEnable(GL_LIGHTING
);
385 glEnable(GL_DEPTH_TEST
);
388 gear1
= glGenLists(1);
389 glNewList(gear1
, GL_COMPILE
);
390 glMaterialfv(GL_FRONT
, GL_AMBIENT_AND_DIFFUSE
, red
);
391 gear(1.0, 4.0, 1.0, 20, 0.7);
394 gear2
= glGenLists(1);
395 glNewList(gear2
, GL_COMPILE
);
396 glMaterialfv(GL_FRONT
, GL_AMBIENT_AND_DIFFUSE
, green
);
397 gear(0.5, 2.0, 2.0, 10, 0.7);
400 gear3
= glGenLists(1);
401 glNewList(gear3
, GL_COMPILE
);
402 glMaterialfv(GL_FRONT
, GL_AMBIENT_AND_DIFFUSE
, blue
);
403 gear(1.3, 2.0, 0.5, 10, 0.7);
406 glEnable(GL_NORMALIZE
);
411 * Create an RGB, double-buffered window.
412 * Return the window and context handles.
415 make_window( Display
*dpy
, const char *name
,
416 int x
, int y
, int width
, int height
,
417 Window
*winRet
, GLXContext
*ctxRet
)
419 int attrib
[] = { GLX_RGBA
,
427 XSetWindowAttributes attr
;
432 XVisualInfo
*visinfo
;
434 scrnum
= DefaultScreen( dpy
);
435 root
= RootWindow( dpy
, scrnum
);
437 visinfo
= glXChooseVisual( dpy
, scrnum
, attrib
);
439 printf("Error: couldn't get an RGB, Double-buffered visual\n");
443 /* window attributes */
444 attr
.background_pixel
= 0;
445 attr
.border_pixel
= 0;
446 attr
.colormap
= XCreateColormap( dpy
, root
, visinfo
->visual
, AllocNone
);
447 attr
.event_mask
= StructureNotifyMask
| ExposureMask
| KeyPressMask
;
448 mask
= CWBackPixel
| CWBorderPixel
| CWColormap
| CWEventMask
;
450 win
= XCreateWindow( dpy
, root
, 0, 0, width
, height
,
451 0, visinfo
->depth
, InputOutput
,
452 visinfo
->visual
, mask
, &attr
);
454 /* set hints and properties */
456 XSizeHints sizehints
;
459 sizehints
.width
= width
;
460 sizehints
.height
= height
;
461 sizehints
.flags
= USSize
| USPosition
;
462 XSetNormalHints(dpy
, win
, &sizehints
);
463 XSetStandardProperties(dpy
, win
, name
, name
,
464 None
, (char **)NULL
, 0, &sizehints
);
467 ctx
= glXCreateContext( dpy
, visinfo
, NULL
, True
);
469 printf("Error: glXCreateContext failed\n");
481 event_loop(Display
*dpy
, Window win
)
483 float frame_usage
= 0.0;
486 while (XPending(dpy
) > 0) {
488 XNextEvent(dpy
, &event
);
489 switch (event
.type
) {
491 /* we'll redraw below */
493 case ConfigureNotify
:
494 reshape(event
.xconfigure
.width
, event
.xconfigure
.height
);
500 code
= XLookupKeysym(&event
.xkey
, 0);
501 if (code
== XK_Left
) {
504 else if (code
== XK_Right
) {
507 else if (code
== XK_Up
) {
510 else if (code
== XK_Down
) {
514 r
= XLookupString(&event
.xkey
, buffer
, sizeof(buffer
),
516 if (buffer
[0] == 27) {
529 if ( get_frame_usage
!= NULL
) {
532 (*get_frame_usage
)( dpy
, win
, & temp
);
536 glXSwapBuffers(dpy
, win
);
541 static int frames
= 0;
542 int t
= current_time();
550 GLfloat seconds
= t
- t0
;
551 GLfloat fps
= frames
/ seconds
;
552 if ( get_frame_usage
!= NULL
) {
553 printf("%d frames in %3.1f seconds = %6.3f FPS (%3.1f%% usage)\n",
554 frames
, seconds
, fps
,
555 (frame_usage
* 100.0) / (float) frames
);
558 printf("%d frames in %3.1f seconds = %6.3f FPS\n",
559 frames
, seconds
, fps
);
572 * Display the refresh rate of the display using the GLX_OML_sync_control
577 show_refresh_rate( Display
* dpy
)
579 #if defined(GLX_OML_sync_control) && defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
580 PFNGLXGETMSCRATEOMLPROC get_msc_rate
;
584 get_msc_rate
= (PFNGLXGETMSCRATEOMLPROC
) glXGetProcAddressARB( (const GLubyte
*) "glXGetMscRateOML" );
585 if ( get_msc_rate
!= NULL
) {
586 (*get_msc_rate
)( dpy
, glXGetCurrentDrawable(), &n
, &d
);
587 printf( "refresh rate: %.1fHz\n", (float) n
/ d
);
591 printf( "glXGetMscRateOML not supported.\n" );
596 * Fill in the table of extension strings from a supplied extensions string
597 * (as returned by glXQueryExtensionsString).
599 * \param string String of GLX extensions.
600 * \sa is_extension_supported
604 make_extension_table( const char * string
)
607 unsigned num_strings
;
612 /* Count the number of spaces in the string. That gives a base-line
613 * figure for the number of extension in the string.
617 for ( i
= 0 ; string
[i
] != NUL
; i
++ ) {
618 if ( string
[i
] == ' ' ) {
623 string_tab
= (char **) malloc( sizeof( char * ) * num_strings
);
624 if ( string_tab
== NULL
) {
631 while ( string
[ base
] != NUL
) {
632 /* Determine the length of the next extension string.
636 ; (string
[ base
+ i
] != NUL
) && (string
[ base
+ i
] != ' ')
642 /* If the string was non-zero length, add it to the table. We
643 * can get zero length strings if there is a space at the end of
644 * the string or if there are two (or more) spaces next to each
645 * other in the string.
648 string_tab
[ idx
] = malloc( sizeof( char ) * (i
+ 1) );
649 if ( string_tab
[ idx
] == NULL
) {
653 (void) memcpy( string_tab
[ idx
], & string
[ base
], i
);
654 string_tab
[ idx
][i
] = NUL
;
659 /* Skip to the start of the next extension string.
663 ; (string
[ base
] == ' ') && (string
[ base
] != NUL
)
669 extension_table
= string_tab
;
670 num_extensions
= idx
;
675 * Determine of an extension is supported. The extension string table
676 * must have already be initialized by calling \c make_extension_table.
678 * \praram ext Extension to be tested.
679 * \return GL_TRUE of the extension is supported, GL_FALSE otherwise.
680 * \sa make_extension_table
684 is_extension_supported( const char * ext
)
688 for ( i
= 0 ; i
< num_extensions
; i
++ ) {
689 if ( strcmp( ext
, extension_table
[i
] ) == 0 ) {
699 main(int argc
, char *argv
[])
704 char *dpyName
= ":0";
705 int swap_interval
= 1;
706 GLboolean do_swap_interval
= GL_FALSE
;
707 GLboolean force_get_rate
= GL_FALSE
;
708 GLboolean printInfo
= GL_FALSE
;
710 PFNGLXSWAPINTERVALMESAPROC set_swap_interval
= NULL
;
711 PFNGLXGETSWAPINTERVALMESAPROC get_swap_interval
= NULL
;
714 for (i
= 1; i
< argc
; i
++) {
715 if (strcmp(argv
[i
], "-display") == 0 && i
+ 1 < argc
) {
719 else if (strcmp(argv
[i
], "-info") == 0) {
722 else if (strcmp(argv
[i
], "-swap") == 0 && i
+ 1 < argc
) {
723 swap_interval
= atoi( argv
[i
+1] );
724 do_swap_interval
= GL_TRUE
;
727 else if (strcmp(argv
[i
], "-forcegetrate") == 0) {
728 /* This option was put in because some DRI drivers don't support the
729 * full GLX_OML_sync_control extension, but they do support
732 force_get_rate
= GL_TRUE
;
734 else if (strcmp(argv
[i
], "-ztrick") == 0) {
735 use_ztrick
= GL_TRUE
;
737 else if (strcmp(argv
[i
], "-help") == 0) {
739 printf(" gears [options]\n");
740 printf("Options:\n");
741 printf(" -help Print this information\n");
742 printf(" -display displayName Specify X display\n");
743 printf(" -info Display GL information\n");
744 printf(" -swap N Swap no more than once per N vertical refreshes\n");
745 printf(" -forcegetrate Try to use glXGetMscRateOML function\n");
750 dpy
= XOpenDisplay(dpyName
);
752 printf("Error: couldn't open display %s\n", XDisplayName(dpyName
));
756 make_window(dpy
, "glxgears", 0, 0, 300, 300, &win
, &ctx
);
757 XMapWindow(dpy
, win
);
758 glXMakeCurrent(dpy
, win
, ctx
);
760 make_extension_table( (char *) glXQueryExtensionsString(dpy
,DefaultScreen(dpy
)) );
761 has_OML_sync_control
= is_extension_supported( "GLX_OML_sync_control" );
762 has_SGI_swap_control
= is_extension_supported( "GLX_SGI_swap_control" );
763 has_MESA_swap_control
= is_extension_supported( "GLX_MESA_swap_control" );
764 has_MESA_swap_frame_usage
= is_extension_supported( "GLX_MESA_swap_frame_usage" );
766 if ( has_MESA_swap_control
) {
767 set_swap_interval
= (PFNGLXSWAPINTERVALMESAPROC
) glXGetProcAddressARB( (const GLubyte
*) "glXSwapIntervalMESA" );
768 get_swap_interval
= (PFNGLXGETSWAPINTERVALMESAPROC
) glXGetProcAddressARB( (const GLubyte
*) "glXGetSwapIntervalMESA" );
770 else if ( has_SGI_swap_control
) {
771 set_swap_interval
= (PFNGLXSWAPINTERVALMESAPROC
) glXGetProcAddressARB( (const GLubyte
*) "glXSwapIntervalSGI" );
775 if ( has_MESA_swap_frame_usage
) {
776 get_frame_usage
= (PFNGLXGETFRAMEUSAGEMESAPROC
) glXGetProcAddressARB( (const GLubyte
*) "glXGetFrameUsageMESA" );
781 printf("GL_RENDERER = %s\n", (char *) glGetString(GL_RENDERER
));
782 printf("GL_VERSION = %s\n", (char *) glGetString(GL_VERSION
));
783 printf("GL_VENDOR = %s\n", (char *) glGetString(GL_VENDOR
));
784 printf("GL_EXTENSIONS = %s\n", (char *) glGetString(GL_EXTENSIONS
));
785 if ( has_OML_sync_control
|| force_get_rate
) {
786 show_refresh_rate( dpy
);
789 if ( get_swap_interval
!= NULL
) {
790 printf("Default swap interval = %d\n", (*get_swap_interval
)() );
794 if ( do_swap_interval
) {
795 if ( set_swap_interval
!= NULL
) {
796 if ( ((swap_interval
== 0) && !has_MESA_swap_control
)
797 || (swap_interval
< 0) ) {
798 printf( "Swap interval must be non-negative or greater than zero "
799 "if GLX_MESA_swap_control is not supported.\n" );
802 (*set_swap_interval
)( swap_interval
);
805 if ( printInfo
&& (get_swap_interval
!= NULL
) ) {
806 printf("Current swap interval = %d\n", (*get_swap_interval
)() );
810 printf("Unable to set swap-interval. Neither GLX_SGI_swap_control "
811 "nor GLX_MESA_swap_control are supported.\n" );
817 event_loop(dpy
, win
);
819 glXDestroyContext(dpy
, ctx
);
820 XDestroyWindow(dpy
, win
);