glxswapcontrol.c revision 32001f49
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 * 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 * 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. 28 * 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. 35 */ 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> 44#ifndef __VMS 45/*# include <stdint.h>*/ 46#endif 47# define GLX_GLXEXT_PROTOTYPES 48#include <GL/gl.h> 49#include <GL/glx.h> 50 51#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 60 61#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 66#define BENCHMARK 67 68PFNGLXGETFRAMEUSAGEMESAPROC get_frame_usage = NULL; 69 70#ifdef BENCHMARK 71 72/* XXX this probably isn't very portable */ 73 74#include <sys/time.h> 75#include <unistd.h> 76 77#define NUL '\0' 78 79/* return current time (in seconds) */ 80static int 81current_time(void) 82{ 83 struct timeval tv; 84#ifdef __VMS 85 (void) gettimeofday(&tv, NULL ); 86#else 87 struct timezone tz; 88 (void) gettimeofday(&tv, &tz); 89#endif 90 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 115static GLboolean has_OML_sync_control = GL_FALSE; 116static GLboolean has_SGI_swap_control = GL_FALSE; 117static GLboolean has_MESA_swap_control = GL_FALSE; 118static GLboolean has_MESA_swap_frame_usage = GL_FALSE; 119 120static char ** extension_table = NULL; 121static unsigned num_extensions; 122 123static GLboolean use_ztrick = GL_FALSE; 124static GLfloat aspectX = 1.0f, aspectY = 1.0f; 125 126/* 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); 153 154 glNormal3f(0.0, 0.0, 1.0); 155 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); 162 if (i < teeth) { 163 glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5); 164 glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), 165 width * 0.5); 166 } 167 } 168 glEnd(); 169 170 /* draw front sides of teeth */ 171 glBegin(GL_QUADS); 172 da = 2.0 * M_PI / teeth / 4.0; 173 for (i = 0; i < teeth; i++) { 174 angle = i * 2.0 * M_PI / teeth; 175 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), 179 width * 0.5); 180 glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), 181 width * 0.5); 182 } 183 glEnd(); 184 185 glNormal3f(0.0, 0.0, -1.0); 186 187 /* draw back face */ 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); 193 if (i < teeth) { 194 glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), 195 -width * 0.5); 196 glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5); 197 } 198 } 199 glEnd(); 200 201 /* draw back sides of teeth */ 202 glBegin(GL_QUADS); 203 da = 2.0 * M_PI / teeth / 4.0; 204 for (i = 0; i < teeth; i++) { 205 angle = i * 2.0 * M_PI / teeth; 206 207 glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), 208 -width * 0.5); 209 glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), 210 -width * 0.5); 211 glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width * 0.5); 212 glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5); 213 } 214 glEnd(); 215 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; 220 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); 226 u /= len; 227 v /= len; 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), 233 width * 0.5); 234 glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), 235 -width * 0.5); 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), 240 width * 0.5); 241 glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), 242 -width * 0.5); 243 glNormal3f(cos(angle), sin(angle), 0.0); 244 } 245 246 glVertex3f(r1 * cos(0), r1 * sin(0), width * 0.5); 247 glVertex3f(r1 * cos(0), r1 * sin(0), -width * 0.5); 248 249 glEnd(); 250 251 glShadeModel(GL_SMOOTH); 252 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); 260 } 261 glEnd(); 262} 263 264 265static void 266draw(void) 267{ 268 if ( use_ztrick ) { 269 static GLboolean flip = GL_FALSE; 270 static const GLfloat vert[4][3] = { 271 { -1, -1, -0.999 }, 272 { 1, -1, -0.999 }, 273 { 1, 1, -0.999 }, 274 { -1, 1, -0.999 } 275 }; 276 static const GLfloat col[4][3] = { 277 { 1.0, 0.6, 0.0 }, 278 { 1.0, 0.6, 0.0 }, 279 { 0.0, 0.0, 0.0 }, 280 { 0.0, 0.0, 0.0 }, 281 }; 282 283 if ( flip ) { 284 glDepthRange(0, 0.5); 285 glDepthFunc(GL_LEQUAL); 286 } 287 else { 288 glDepthRange(1.0, 0.4999); 289 glDepthFunc(GL_GEQUAL); 290 } 291 292 flip = !flip; 293 294 /* The famous Quake "Z trick" only works when the whole screen is 295 * re-drawn each frame. 296 */ 297 298 glMatrixMode(GL_MODELVIEW); 299 glLoadIdentity(); 300 glMatrixMode(GL_PROJECTION); 301 glLoadIdentity(); 302 glOrtho(-1, 1, -1, 1, -1, 1); 303 glDisable(GL_LIGHTING); 304 glShadeModel(GL_SMOOTH); 305 306 glEnableClientState( GL_VERTEX_ARRAY ); 307 glEnableClientState( GL_COLOR_ARRAY ); 308 glVertexPointer( 3, GL_FLOAT, 0, vert ); 309 glColorPointer( 3, GL_FLOAT, 0, col ); 310 glDrawArrays( GL_POLYGON, 0, 4 ); 311 glDisableClientState( GL_COLOR_ARRAY ); 312 glDisableClientState( GL_VERTEX_ARRAY ); 313 314 glMatrixMode(GL_PROJECTION); 315 glLoadIdentity(); 316 glFrustum(-aspectX, aspectX, -aspectY, aspectY, 5.0, 60.0); 317 318 glEnable(GL_LIGHTING); 319 320 glMatrixMode(GL_MODELVIEW); 321 glLoadIdentity(); 322 glTranslatef(0.0, 0.0, -45.0); 323 } 324 else { 325 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 326 } 327 328 glPushMatrix(); 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); 332 333 glPushMatrix(); 334 glTranslatef(-3.0, -2.0, 0.0); 335 glRotatef(angle, 0.0, 0.0, 1.0); 336 glCallList(gear1); 337 glPopMatrix(); 338 339 glPushMatrix(); 340 glTranslatef(3.1, -2.0, 0.0); 341 glRotatef(-2.0 * angle - 9.0, 0.0, 0.0, 1.0); 342 glCallList(gear2); 343 glPopMatrix(); 344 345 glPushMatrix(); 346 glTranslatef(-3.1, 4.2, 0.0); 347 glRotatef(-2.0 * angle - 25.0, 0.0, 0.0, 1.0); 348 glCallList(gear3); 349 glPopMatrix(); 350 351 glPopMatrix(); 352} 353 354 355/* new window size or exposure */ 356static void 357reshape(int width, int height) 358{ 359 if (width > height) { 360 aspectX = (GLfloat) width / (GLfloat) height; 361 aspectY = 1.0; 362 } 363 else { 364 aspectX = 1.0; 365 aspectY = (GLfloat) height / (GLfloat) width; 366 } 367 368 glViewport(0, 0, (GLint) width, (GLint) height); 369 glMatrixMode(GL_PROJECTION); 370 glLoadIdentity(); 371 372 glFrustum(-aspectX, aspectX, -aspectY, aspectY, 5.0, 60.0); 373 glMatrixMode(GL_MODELVIEW); 374 glLoadIdentity(); 375 glTranslatef(0.0, 0.0, -45.0); 376} 377 378 379static void 380init(void) 381{ 382 static GLfloat pos[4] = { 5.0, 5.0, 10.0, 0.0 }; 383 static GLfloat red[4] = { 0.8, 0.1, 0.0, 1.0 }; 384 static GLfloat green[4] = { 0.0, 0.8, 0.2, 1.0 }; 385 static GLfloat blue[4] = { 0.2, 0.2, 1.0, 1.0 }; 386 387 glLightfv(GL_LIGHT0, GL_POSITION, pos); 388 glEnable(GL_CULL_FACE); 389 glEnable(GL_LIGHTING); 390 glEnable(GL_LIGHT0); 391 glEnable(GL_DEPTH_TEST); 392 393 /* make the gears */ 394 gear1 = glGenLists(1); 395 glNewList(gear1, GL_COMPILE); 396 glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, red); 397 gear(1.0, 4.0, 1.0, 20, 0.7); 398 glEndList(); 399 400 gear2 = glGenLists(1); 401 glNewList(gear2, GL_COMPILE); 402 glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, green); 403 gear(0.5, 2.0, 2.0, 10, 0.7); 404 glEndList(); 405 406 gear3 = glGenLists(1); 407 glNewList(gear3, GL_COMPILE); 408 glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, blue); 409 gear(1.3, 2.0, 0.5, 10, 0.7); 410 glEndList(); 411 412 glEnable(GL_NORMALIZE); 413} 414 415 416/** 417 * Remove window border/decorations. 418 */ 419static void 420no_border( Display *dpy, Window w) 421{ 422 static const unsigned MWM_HINTS_DECORATIONS = (1 << 1); 423 static const int PROP_MOTIF_WM_HINTS_ELEMENTS = 5; 424 425 typedef struct 426 { 427 unsigned long flags; 428 unsigned long functions; 429 unsigned long decorations; 430 long inputMode; 431 unsigned long status; 432 } PropMotifWmHints; 433 434 PropMotifWmHints motif_hints; 435 Atom prop, proptype; 436 unsigned long flags = 0; 437 438 /* setup the property */ 439 motif_hints.flags = MWM_HINTS_DECORATIONS; 440 motif_hints.decorations = flags; 441 442 /* get the atom for the property */ 443 prop = XInternAtom( dpy, "_MOTIF_WM_HINTS", True ); 444 if (!prop) { 445 /* something went wrong! */ 446 return; 447 } 448 449 /* not sure this is correct, seems to work, XA_WM_HINTS didn't work */ 450 proptype = prop; 451 452 XChangeProperty( dpy, w, /* display, window */ 453 prop, proptype, /* property, type */ 454 32, /* format: 32-bit datums */ 455 PropModeReplace, /* mode */ 456 (unsigned char *) &motif_hints, /* data */ 457 PROP_MOTIF_WM_HINTS_ELEMENTS /* nelements */ 458 ); 459} 460 461 462/* 463 * Create an RGB, double-buffered window. 464 * Return the window and context handles. 465 */ 466static void 467make_window( Display *dpy, const char *name, 468 int x, int y, int width, int height, GLboolean fullscreen, 469 Window *winRet, GLXContext *ctxRet) 470{ 471 int attrib[] = { GLX_RGBA, 472 GLX_RED_SIZE, 1, 473 GLX_GREEN_SIZE, 1, 474 GLX_BLUE_SIZE, 1, 475 GLX_DOUBLEBUFFER, 476 GLX_DEPTH_SIZE, 1, 477 None }; 478 int scrnum; 479 XSetWindowAttributes attr; 480 unsigned long mask; 481 Window root; 482 Window win; 483 GLXContext ctx; 484 XVisualInfo *visinfo; 485 486 scrnum = DefaultScreen( dpy ); 487 root = RootWindow( dpy, scrnum ); 488 489 if (fullscreen) { 490 x = y = 0; 491 width = DisplayWidth( dpy, scrnum ); 492 height = DisplayHeight( dpy, scrnum ); 493 } 494 495 visinfo = glXChooseVisual( dpy, scrnum, attrib ); 496 if (!visinfo) { 497 printf("Error: couldn't get an RGB, Double-buffered visual\n"); 498 exit(1); 499 } 500 501 /* window attributes */ 502 attr.background_pixel = 0; 503 attr.border_pixel = 0; 504 attr.colormap = XCreateColormap( dpy, root, visinfo->visual, AllocNone); 505 attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask; 506 mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; 507 508 win = XCreateWindow( dpy, root, 0, 0, width, height, 509 0, visinfo->depth, InputOutput, 510 visinfo->visual, mask, &attr ); 511 512 /* set hints and properties */ 513 { 514 XSizeHints sizehints; 515 sizehints.x = x; 516 sizehints.y = y; 517 sizehints.width = width; 518 sizehints.height = height; 519 sizehints.flags = USSize | USPosition; 520 XSetNormalHints(dpy, win, &sizehints); 521 XSetStandardProperties(dpy, win, name, name, 522 None, (char **)NULL, 0, &sizehints); 523 } 524 525 if (fullscreen) 526 no_border(dpy, win); 527 528 ctx = glXCreateContext( dpy, visinfo, NULL, True ); 529 if (!ctx) { 530 printf("Error: glXCreateContext failed\n"); 531 exit(1); 532 } 533 534 XFree(visinfo); 535 536 *winRet = win; 537 *ctxRet = ctx; 538} 539 540 541static void 542event_loop(Display *dpy, Window win) 543{ 544 float frame_usage = 0.0; 545 546 while (1) { 547 while (XPending(dpy) > 0) { 548 XEvent event; 549 XNextEvent(dpy, &event); 550 switch (event.type) { 551 case Expose: 552 /* we'll redraw below */ 553 break; 554 case ConfigureNotify: 555 reshape(event.xconfigure.width, event.xconfigure.height); 556 break; 557 case KeyPress: 558 { 559 char buffer[10]; 560 int code; 561 code = XLookupKeysym(&event.xkey, 0); 562 if (code == XK_Left) { 563 view_roty += 5.0; 564 } 565 else if (code == XK_Right) { 566 view_roty -= 5.0; 567 } 568 else if (code == XK_Up) { 569 view_rotx += 5.0; 570 } 571 else if (code == XK_Down) { 572 view_rotx -= 5.0; 573 } 574 else { 575 XLookupString(&event.xkey, buffer, sizeof(buffer), 576 NULL, NULL); 577 if (buffer[0] == 27) { 578 /* escape */ 579 return; 580 } 581 } 582 } 583 } 584 } 585 586 /* next frame */ 587 angle += 2.0; 588 589 draw(); 590 591 glXSwapBuffers(dpy, win); 592 593 if ( get_frame_usage != NULL ) { 594 GLfloat temp; 595 596 (*get_frame_usage)( dpy, win, & temp ); 597 frame_usage += temp; 598 } 599 600 /* calc framerate */ 601 { 602 static int t0 = -1; 603 static int frames = 0; 604 int t = current_time(); 605 606 if (t0 < 0) 607 t0 = t; 608 609 frames++; 610 611 if (t - t0 >= 5.0) { 612 GLfloat seconds = t - t0; 613 GLfloat fps = frames / seconds; 614 if ( get_frame_usage != NULL ) { 615 printf("%d frames in %3.1f seconds = %6.3f FPS (%3.1f%% usage)\n", 616 frames, seconds, fps, 617 (frame_usage * 100.0) / (float) frames ); 618 } 619 else { 620 printf("%d frames in %3.1f seconds = %6.3f FPS\n", 621 frames, seconds, fps); 622 } 623 fflush(stdout); 624 625 t0 = t; 626 frames = 0; 627 frame_usage = 0.0; 628 } 629 } 630 } 631} 632 633 634/** 635 * Display the refresh rate of the display using the GLX_OML_sync_control 636 * extension. 637 */ 638static void 639show_refresh_rate( Display * dpy ) 640{ 641#if defined(GLX_OML_sync_control) && defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) 642 PFNGLXGETMSCRATEOMLPROC get_msc_rate; 643 int32_t n; 644 int32_t d; 645 646 get_msc_rate = (PFNGLXGETMSCRATEOMLPROC) glXGetProcAddressARB( (const GLubyte *) "glXGetMscRateOML" ); 647 if ( get_msc_rate != NULL ) { 648 (*get_msc_rate)( dpy, glXGetCurrentDrawable(), &n, &d ); 649 printf( "refresh rate: %.1fHz\n", (float) n / d ); 650 return; 651 } 652#endif 653 printf( "glXGetMscRateOML not supported.\n" ); 654} 655 656 657/** 658 * Fill in the table of extension strings from a supplied extensions string 659 * (as returned by glXQueryExtensionsString). 660 * 661 * \param string String of GLX extensions. 662 * \sa is_extension_supported 663 */ 664static void 665make_extension_table( const char * string ) 666{ 667 char ** string_tab; 668 unsigned num_strings; 669 unsigned base; 670 unsigned idx; 671 unsigned i; 672 673 /* Count the number of spaces in the string. That gives a base-line 674 * figure for the number of extension in the string. 675 */ 676 677 num_strings = 1; 678 for ( i = 0 ; string[i] != NUL ; i++ ) { 679 if ( string[i] == ' ' ) { 680 num_strings++; 681 } 682 } 683 684 string_tab = (char **) malloc( sizeof( char * ) * num_strings ); 685 if ( string_tab == NULL ) { 686 return; 687 } 688 689 base = 0; 690 idx = 0; 691 692 while ( string[ base ] != NUL ) { 693 /* Determine the length of the next extension string. 694 */ 695 696 for ( i = 0 697 ; (string[ base + i ] != NUL) && (string[ base + i ] != ' ') 698 ; i++ ) { 699 /* empty */ ; 700 } 701 702 if ( i > 0 ) { 703 /* If the string was non-zero length, add it to the table. We 704 * can get zero length strings if there is a space at the end of 705 * the string or if there are two (or more) spaces next to each 706 * other in the string. 707 */ 708 709 string_tab[ idx ] = malloc( sizeof( char ) * (i + 1) ); 710 if ( string_tab[ idx ] == NULL ) { 711 unsigned j = 0; 712 713 for ( j = 0; j < idx; j++ ) { 714 free( string_tab[j] ); 715 } 716 717 free( string_tab ); 718 719 return; 720 } 721 722 (void) memcpy( string_tab[ idx ], & string[ base ], i ); 723 string_tab[ idx ][i] = NUL; 724 idx++; 725 } 726 727 728 /* Skip to the start of the next extension string. 729 */ 730 731 for ( base += i 732 ; (string[ base ] == ' ') && (string[ base ] != NUL) 733 ; base++ ) { 734 /* empty */ ; 735 } 736 } 737 738 extension_table = string_tab; 739 num_extensions = idx; 740} 741 742 743/** 744 * Determine of an extension is supported. The extension string table 745 * must have already be initialized by calling \c make_extension_table. 746 * 747 * \praram ext Extension to be tested. 748 * \return GL_TRUE of the extension is supported, GL_FALSE otherwise. 749 * \sa make_extension_table 750 */ 751static GLboolean 752is_extension_supported( const char * ext ) 753{ 754 unsigned i; 755 756 for ( i = 0 ; i < num_extensions ; i++ ) { 757 if ( strcmp( ext, extension_table[i] ) == 0 ) { 758 return GL_TRUE; 759 } 760 } 761 762 return GL_FALSE; 763} 764 765 766int 767main(int argc, char *argv[]) 768{ 769 Display *dpy; 770 Window win; 771 GLXContext ctx; 772 char *dpyName = NULL; 773 int swap_interval = 1; 774 GLboolean do_swap_interval = GL_FALSE; 775 GLboolean force_get_rate = GL_FALSE; 776 GLboolean fullscreen = GL_FALSE; 777 GLboolean printInfo = GL_FALSE; 778 int i; 779 PFNGLXSWAPINTERVALMESAPROC set_swap_interval = NULL; 780 PFNGLXGETSWAPINTERVALMESAPROC get_swap_interval = NULL; 781 int width = 300, height = 300; 782 783 for (i = 1; i < argc; i++) { 784 if (strcmp(argv[i], "-display") == 0 && i + 1 < argc) { 785 dpyName = argv[i+1]; 786 i++; 787 } 788 else if (strcmp(argv[i], "-info") == 0) { 789 printInfo = GL_TRUE; 790 } 791 else if (strcmp(argv[i], "-swap") == 0 && i + 1 < argc) { 792 swap_interval = atoi( argv[i+1] ); 793 do_swap_interval = GL_TRUE; 794 i++; 795 } 796 else if (strcmp(argv[i], "-forcegetrate") == 0) { 797 /* This option was put in because some DRI drivers don't support the 798 * full GLX_OML_sync_control extension, but they do support 799 * glXGetMscRateOML. 800 */ 801 force_get_rate = GL_TRUE; 802 } 803 else if (strcmp(argv[i], "-fullscreen") == 0) { 804 fullscreen = GL_TRUE; 805 } 806 else if (strcmp(argv[i], "-ztrick") == 0) { 807 use_ztrick = GL_TRUE; 808 } 809 else if (strcmp(argv[i], "-help") == 0) { 810 printf("Usage:\n"); 811 printf(" gears [options]\n"); 812 printf("Options:\n"); 813 printf(" -help Print this information\n"); 814 printf(" -display displayName Specify X display\n"); 815 printf(" -info Display GL information\n"); 816 printf(" -swap N Swap no more than once per N vertical refreshes\n"); 817 printf(" -forcegetrate Try to use glXGetMscRateOML function\n"); 818 printf(" -fullscreen Full-screen window\n"); 819 return 0; 820 } 821 } 822 823 dpy = XOpenDisplay(dpyName); 824 if (!dpy) { 825 printf("Error: couldn't open display %s\n", XDisplayName(dpyName)); 826 return -1; 827 } 828 829 make_window(dpy, "glxgears", 0, 0, width, height, fullscreen, &win, &ctx); 830 XMapWindow(dpy, win); 831 glXMakeCurrent(dpy, win, ctx); 832 833 make_extension_table( (char *) glXQueryExtensionsString(dpy,DefaultScreen(dpy)) ); 834 has_OML_sync_control = is_extension_supported( "GLX_OML_sync_control" ); 835 has_SGI_swap_control = is_extension_supported( "GLX_SGI_swap_control" ); 836 has_MESA_swap_control = is_extension_supported( "GLX_MESA_swap_control" ); 837 has_MESA_swap_frame_usage = is_extension_supported( "GLX_MESA_swap_frame_usage" ); 838 839 if ( has_MESA_swap_control ) { 840 set_swap_interval = (PFNGLXSWAPINTERVALMESAPROC) glXGetProcAddressARB( (const GLubyte *) "glXSwapIntervalMESA" ); 841 get_swap_interval = (PFNGLXGETSWAPINTERVALMESAPROC) glXGetProcAddressARB( (const GLubyte *) "glXGetSwapIntervalMESA" ); 842 } 843 else if ( has_SGI_swap_control ) { 844 set_swap_interval = (PFNGLXSWAPINTERVALMESAPROC) glXGetProcAddressARB( (const GLubyte *) "glXSwapIntervalSGI" ); 845 } 846 847 848 if ( has_MESA_swap_frame_usage ) { 849 get_frame_usage = (PFNGLXGETFRAMEUSAGEMESAPROC) glXGetProcAddressARB( (const GLubyte *) "glXGetFrameUsageMESA" ); 850 } 851 852 853 if (printInfo) { 854 printf("GL_RENDERER = %s\n", (char *) glGetString(GL_RENDERER)); 855 printf("GL_VERSION = %s\n", (char *) glGetString(GL_VERSION)); 856 printf("GL_VENDOR = %s\n", (char *) glGetString(GL_VENDOR)); 857 printf("GL_EXTENSIONS = %s\n", (char *) glGetString(GL_EXTENSIONS)); 858 if ( has_OML_sync_control || force_get_rate ) { 859 show_refresh_rate( dpy ); 860 } 861 862 if ( get_swap_interval != NULL ) { 863 printf("Default swap interval = %d\n", (*get_swap_interval)() ); 864 } 865 } 866 867 if ( do_swap_interval ) { 868 if ( set_swap_interval != NULL ) { 869 if ( ((swap_interval == 0) && !has_MESA_swap_control) 870 || (swap_interval < 0) ) { 871 printf( "Swap interval must be non-negative or greater than zero " 872 "if GLX_MESA_swap_control is not supported.\n" ); 873 } 874 else { 875 (*set_swap_interval)( swap_interval ); 876 } 877 878 if ( printInfo && (get_swap_interval != NULL) ) { 879 printf("Current swap interval = %d\n", (*get_swap_interval)() ); 880 } 881 } 882 else { 883 printf("Unable to set swap-interval. Neither GLX_SGI_swap_control " 884 "nor GLX_MESA_swap_control are supported.\n" ); 885 } 886 } 887 888 init(); 889 890 /* Set initial projection/viewing transformation. 891 * same as glxgears.c 892 */ 893 reshape(width, height); 894 895 event_loop(dpy, win); 896 897 glXDestroyContext(dpy, ctx); 898 XDestroyWindow(dpy, win); 899 XCloseDisplay(dpy); 900 901 return 0; 902} 903