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 * See usage() below for command line options. 27 */ 28 29 30#include <math.h> 31#include <stdlib.h> 32#include <stdio.h> 33#include <string.h> 34#include <X11/Xlib.h> 35#include <X11/keysym.h> 36#include <GL/gl.h> 37#include <GL/glx.h> 38#include <GL/glxext.h> 39 40#ifndef GLX_MESA_swap_control 41#define GLX_MESA_swap_control 1 42typedef int (*PFNGLXGETSWAPINTERVALMESAPROC)(void); 43#endif 44 45 46#define BENCHMARK 47 48#ifdef BENCHMARK 49 50/* XXX this probably isn't very portable */ 51 52#include <sys/time.h> 53#include <unistd.h> 54 55/* return current time (in seconds) */ 56static double 57current_time(void) 58{ 59 struct timeval tv; 60#ifdef __VMS 61 (void) gettimeofday(&tv, NULL ); 62#else 63 struct timezone tz; 64 (void) gettimeofday(&tv, &tz); 65#endif 66 return (double) tv.tv_sec + tv.tv_usec / 1000000.0; 67} 68 69#else /*BENCHMARK*/ 70 71/* dummy */ 72static double 73current_time(void) 74{ 75 /* update this function for other platforms! */ 76 static double t = 0.0; 77 static int warn = 1; 78 if (warn) { 79 fprintf(stderr, "Warning: current_time() not implemented!!\n"); 80 warn = 0; 81 } 82 return t += 1.0; 83} 84 85#endif /*BENCHMARK*/ 86 87 88 89#ifndef M_PI 90#define M_PI 3.14159265 91#endif 92 93 94/** Event handler results: */ 95#define NOP 0 96#define EXIT 1 97#define DRAW 2 98 99static GLfloat view_rotx = 20.0, view_roty = 30.0, view_rotz = 0.0; 100static GLint gear1, gear2, gear3; 101static GLfloat angle = 0.0; 102 103static GLboolean fullscreen = GL_FALSE; /* Create a single fullscreen window */ 104static GLboolean stereo = GL_FALSE; /* Enable stereo. */ 105static GLint samples = 0; /* Choose visual with at least N samples. */ 106static GLboolean animate = GL_TRUE; /* Animation */ 107static GLfloat eyesep = 5.0; /* Eye separation. */ 108static GLfloat fix_point = 40.0; /* Fixation point distance. */ 109static GLfloat left, right, asp; /* Stereo frustum params. */ 110 111 112/* 113 * 114 * Draw a gear wheel. You'll probably want to call this function when 115 * building a display list since we do a lot of trig here. 116 * 117 * Input: inner_radius - radius of hole at center 118 * outer_radius - radius at center of teeth 119 * width - width of gear 120 * teeth - number of teeth 121 * tooth_depth - depth of tooth 122 */ 123static void 124gear(GLfloat inner_radius, GLfloat outer_radius, GLfloat width, 125 GLint teeth, GLfloat tooth_depth) 126{ 127 GLint i; 128 GLfloat r0, r1, r2; 129 GLfloat angle, da; 130 GLfloat u, v, len; 131 132 r0 = inner_radius; 133 r1 = outer_radius - tooth_depth / 2.0; 134 r2 = outer_radius + tooth_depth / 2.0; 135 136 da = 2.0 * M_PI / teeth / 4.0; 137 138 glShadeModel(GL_FLAT); 139 140 glNormal3f(0.0, 0.0, 1.0); 141 142 /* draw front face */ 143 glBegin(GL_QUAD_STRIP); 144 for (i = 0; i <= teeth; i++) { 145 angle = i * 2.0 * M_PI / teeth; 146 glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5); 147 glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5); 148 if (i < teeth) { 149 glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5); 150 glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), 151 width * 0.5); 152 } 153 } 154 glEnd(); 155 156 /* draw front sides of teeth */ 157 glBegin(GL_QUADS); 158 da = 2.0 * M_PI / teeth / 4.0; 159 for (i = 0; i < teeth; i++) { 160 angle = i * 2.0 * M_PI / teeth; 161 162 glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5); 163 glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width * 0.5); 164 glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), 165 width * 0.5); 166 glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), 167 width * 0.5); 168 } 169 glEnd(); 170 171 glNormal3f(0.0, 0.0, -1.0); 172 173 /* draw back face */ 174 glBegin(GL_QUAD_STRIP); 175 for (i = 0; i <= teeth; i++) { 176 angle = i * 2.0 * M_PI / teeth; 177 glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5); 178 glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5); 179 if (i < teeth) { 180 glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), 181 -width * 0.5); 182 glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5); 183 } 184 } 185 glEnd(); 186 187 /* draw back sides of teeth */ 188 glBegin(GL_QUADS); 189 da = 2.0 * M_PI / teeth / 4.0; 190 for (i = 0; i < teeth; i++) { 191 angle = i * 2.0 * M_PI / teeth; 192 193 glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), 194 -width * 0.5); 195 glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), 196 -width * 0.5); 197 glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width * 0.5); 198 glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5); 199 } 200 glEnd(); 201 202 /* draw outward faces of teeth */ 203 glBegin(GL_QUAD_STRIP); 204 for (i = 0; i < teeth; i++) { 205 angle = i * 2.0 * M_PI / teeth; 206 207 glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5); 208 glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5); 209 u = r2 * cos(angle + da) - r1 * cos(angle); 210 v = r2 * sin(angle + da) - r1 * sin(angle); 211 len = sqrt(u * u + v * v); 212 u /= len; 213 v /= len; 214 glNormal3f(v, -u, 0.0); 215 glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width * 0.5); 216 glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width * 0.5); 217 glNormal3f(cos(angle), sin(angle), 0.0); 218 glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), 219 width * 0.5); 220 glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), 221 -width * 0.5); 222 u = r1 * cos(angle + 3 * da) - r2 * cos(angle + 2 * da); 223 v = r1 * sin(angle + 3 * da) - r2 * sin(angle + 2 * da); 224 glNormal3f(v, -u, 0.0); 225 glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), 226 width * 0.5); 227 glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), 228 -width * 0.5); 229 glNormal3f(cos(angle), sin(angle), 0.0); 230 } 231 232 glVertex3f(r1 * cos(0), r1 * sin(0), width * 0.5); 233 glVertex3f(r1 * cos(0), r1 * sin(0), -width * 0.5); 234 235 glEnd(); 236 237 glShadeModel(GL_SMOOTH); 238 239 /* draw inside radius cylinder */ 240 glBegin(GL_QUAD_STRIP); 241 for (i = 0; i <= teeth; i++) { 242 angle = i * 2.0 * M_PI / teeth; 243 glNormal3f(-cos(angle), -sin(angle), 0.0); 244 glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5); 245 glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5); 246 } 247 glEnd(); 248} 249 250 251static void 252draw(void) 253{ 254 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 255 256 glPushMatrix(); 257 glRotatef(view_rotx, 1.0, 0.0, 0.0); 258 glRotatef(view_roty, 0.0, 1.0, 0.0); 259 glRotatef(view_rotz, 0.0, 0.0, 1.0); 260 261 glPushMatrix(); 262 glTranslatef(-3.0, -2.0, 0.0); 263 glRotatef(angle, 0.0, 0.0, 1.0); 264 glCallList(gear1); 265 glPopMatrix(); 266 267 glPushMatrix(); 268 glTranslatef(3.1, -2.0, 0.0); 269 glRotatef(-2.0 * angle - 9.0, 0.0, 0.0, 1.0); 270 glCallList(gear2); 271 glPopMatrix(); 272 273 glPushMatrix(); 274 glTranslatef(-3.1, 4.2, 0.0); 275 glRotatef(-2.0 * angle - 25.0, 0.0, 0.0, 1.0); 276 glCallList(gear3); 277 glPopMatrix(); 278 279 glPopMatrix(); 280} 281 282 283static void 284draw_gears(void) 285{ 286 if (stereo) { 287 /* First left eye. */ 288 glDrawBuffer(GL_BACK_LEFT); 289 290 glMatrixMode(GL_PROJECTION); 291 glLoadIdentity(); 292 glFrustum(left, right, -asp, asp, 5.0, 60.0); 293 294 glMatrixMode(GL_MODELVIEW); 295 296 glPushMatrix(); 297 glTranslated(+0.5 * eyesep, 0.0, 0.0); 298 draw(); 299 glPopMatrix(); 300 301 /* Then right eye. */ 302 glDrawBuffer(GL_BACK_RIGHT); 303 304 glMatrixMode(GL_PROJECTION); 305 glLoadIdentity(); 306 glFrustum(-right, -left, -asp, asp, 5.0, 60.0); 307 308 glMatrixMode(GL_MODELVIEW); 309 310 glPushMatrix(); 311 glTranslated(-0.5 * eyesep, 0.0, 0.0); 312 draw(); 313 glPopMatrix(); 314 } 315 else { 316 draw(); 317 } 318} 319 320 321/** Draw single frame, do SwapBuffers, compute FPS */ 322static void 323draw_frame(Display *dpy, Window win) 324{ 325 static int frames = 0; 326 static double tRot0 = -1.0, tRate0 = -1.0; 327 double dt, t = current_time(); 328 329 if (tRot0 < 0.0) 330 tRot0 = t; 331 dt = t - tRot0; 332 tRot0 = t; 333 334 if (animate) { 335 /* advance rotation for next frame */ 336 angle += 70.0 * dt; /* 70 degrees per second */ 337 if (angle > 3600.0) 338 angle -= 3600.0; 339 } 340 341 draw_gears(); 342 glXSwapBuffers(dpy, win); 343 344 frames++; 345 346 if (tRate0 < 0.0) 347 tRate0 = t; 348 if (t - tRate0 >= 5.0) { 349 GLfloat seconds = t - tRate0; 350 GLfloat fps = frames / seconds; 351 printf("%d frames in %3.1f seconds = %6.3f FPS\n", frames, seconds, 352 fps); 353 fflush(stdout); 354 tRate0 = t; 355 frames = 0; 356 } 357} 358 359 360/* new window size or exposure */ 361static void 362reshape(int width, int height) 363{ 364 glViewport(0, 0, (GLint) width, (GLint) height); 365 366 if (stereo) { 367 GLfloat w; 368 369 asp = (GLfloat) height / (GLfloat) width; 370 w = fix_point * (1.0 / 5.0); 371 372 left = -5.0 * ((w - 0.5 * eyesep) / fix_point); 373 right = 5.0 * ((w + 0.5 * eyesep) / fix_point); 374 } 375 else { 376 GLfloat h = (GLfloat) height / (GLfloat) width; 377 378 glMatrixMode(GL_PROJECTION); 379 glLoadIdentity(); 380 glFrustum(-1.0, 1.0, -h, h, 5.0, 60.0); 381 } 382 383 glMatrixMode(GL_MODELVIEW); 384 glLoadIdentity(); 385 glTranslatef(0.0, 0.0, -40.0); 386} 387 388 389 390static void 391init(void) 392{ 393 static GLfloat pos[4] = { 5.0, 5.0, 10.0, 0.0 }; 394 static GLfloat red[4] = { 0.8, 0.1, 0.0, 1.0 }; 395 static GLfloat green[4] = { 0.0, 0.8, 0.2, 1.0 }; 396 static GLfloat blue[4] = { 0.2, 0.2, 1.0, 1.0 }; 397 398 glLightfv(GL_LIGHT0, GL_POSITION, pos); 399 glEnable(GL_CULL_FACE); 400 glEnable(GL_LIGHTING); 401 glEnable(GL_LIGHT0); 402 glEnable(GL_DEPTH_TEST); 403 404 /* make the gears */ 405 gear1 = glGenLists(1); 406 glNewList(gear1, GL_COMPILE); 407 glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, red); 408 gear(1.0, 4.0, 1.0, 20, 0.7); 409 glEndList(); 410 411 gear2 = glGenLists(1); 412 glNewList(gear2, GL_COMPILE); 413 glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, green); 414 gear(0.5, 2.0, 2.0, 10, 0.7); 415 glEndList(); 416 417 gear3 = glGenLists(1); 418 glNewList(gear3, GL_COMPILE); 419 glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, blue); 420 gear(1.3, 2.0, 0.5, 10, 0.7); 421 glEndList(); 422 423 glEnable(GL_NORMALIZE); 424} 425 426 427/** 428 * Remove window border/decorations. 429 */ 430static void 431no_border( Display *dpy, Window w) 432{ 433 static const unsigned MWM_HINTS_DECORATIONS = (1 << 1); 434 static const int PROP_MOTIF_WM_HINTS_ELEMENTS = 5; 435 436 typedef struct 437 { 438 unsigned long flags; 439 unsigned long functions; 440 unsigned long decorations; 441 long inputMode; 442 unsigned long status; 443 } PropMotifWmHints; 444 445 PropMotifWmHints motif_hints; 446 Atom prop, proptype; 447 unsigned long flags = 0; 448 449 /* setup the property */ 450 motif_hints.flags = MWM_HINTS_DECORATIONS; 451 motif_hints.decorations = flags; 452 453 /* get the atom for the property */ 454 prop = XInternAtom( dpy, "_MOTIF_WM_HINTS", True ); 455 if (!prop) { 456 /* something went wrong! */ 457 return; 458 } 459 460 /* not sure this is correct, seems to work, XA_WM_HINTS didn't work */ 461 proptype = prop; 462 463 XChangeProperty( dpy, w, /* display, window */ 464 prop, proptype, /* property, type */ 465 32, /* format: 32-bit datums */ 466 PropModeReplace, /* mode */ 467 (unsigned char *) &motif_hints, /* data */ 468 PROP_MOTIF_WM_HINTS_ELEMENTS /* nelements */ 469 ); 470} 471 472 473/* 474 * Create an RGB, double-buffered window. 475 * Return the window and context handles. 476 */ 477static void 478make_window( Display *dpy, const char *name, 479 int x, int y, int width, int height, 480 Window *winRet, GLXContext *ctxRet, VisualID *visRet) 481{ 482 int attribs[64]; 483 int i = 0; 484 485 int scrnum; 486 XSetWindowAttributes attr; 487 unsigned long mask; 488 Window root; 489 Window win; 490 GLXContext ctx; 491 XVisualInfo *visinfo; 492 493 /* Singleton attributes. */ 494 attribs[i++] = GLX_RGBA; 495 attribs[i++] = GLX_DOUBLEBUFFER; 496 if (stereo) 497 attribs[i++] = GLX_STEREO; 498 499 /* Key/value attributes. */ 500 attribs[i++] = GLX_RED_SIZE; 501 attribs[i++] = 1; 502 attribs[i++] = GLX_GREEN_SIZE; 503 attribs[i++] = 1; 504 attribs[i++] = GLX_BLUE_SIZE; 505 attribs[i++] = 1; 506 attribs[i++] = GLX_DEPTH_SIZE; 507 attribs[i++] = 1; 508 if (samples > 0) { 509 attribs[i++] = GLX_SAMPLE_BUFFERS; 510 attribs[i++] = 1; 511 attribs[i++] = GLX_SAMPLES; 512 attribs[i++] = samples; 513 } 514 515 attribs[i++] = None; 516 517 scrnum = DefaultScreen( dpy ); 518 root = RootWindow( dpy, scrnum ); 519 520 visinfo = glXChooseVisual(dpy, scrnum, attribs); 521 if (!visinfo) { 522 printf("Error: couldn't get an RGB, Double-buffered"); 523 if (stereo) 524 printf(", Stereo"); 525 if (samples > 0) 526 printf(", Multisample"); 527 printf(" visual\n"); 528 exit(1); 529 } 530 531 /* window attributes */ 532 attr.background_pixel = 0; 533 attr.border_pixel = 0; 534 attr.colormap = XCreateColormap( dpy, root, visinfo->visual, AllocNone); 535 attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask; 536 /* XXX this is a bad way to get a borderless window! */ 537 mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask; 538 539 win = XCreateWindow( dpy, root, x, y, width, height, 540 0, visinfo->depth, InputOutput, 541 visinfo->visual, mask, &attr ); 542 543 if (fullscreen) 544 no_border(dpy, win); 545 546 /* set hints and properties */ 547 { 548 XSizeHints sizehints; 549 sizehints.x = x; 550 sizehints.y = y; 551 sizehints.width = width; 552 sizehints.height = height; 553 sizehints.flags = USSize | USPosition; 554 XSetNormalHints(dpy, win, &sizehints); 555 XSetStandardProperties(dpy, win, name, name, 556 None, (char **)NULL, 0, &sizehints); 557 } 558 559 ctx = glXCreateContext( dpy, visinfo, NULL, True ); 560 if (!ctx) { 561 printf("Error: glXCreateContext failed\n"); 562 exit(1); 563 } 564 565 *winRet = win; 566 *ctxRet = ctx; 567 *visRet = visinfo->visualid; 568 569 XFree(visinfo); 570} 571 572 573/** 574 * Determine whether or not a GLX extension is supported. 575 */ 576static int 577is_glx_extension_supported(Display *dpy, const char *query) 578{ 579 const int scrnum = DefaultScreen(dpy); 580 const char *glx_extensions = NULL; 581 const size_t len = strlen(query); 582 const char *ptr; 583 584 if (glx_extensions == NULL) { 585 glx_extensions = glXQueryExtensionsString(dpy, scrnum); 586 } 587 588 ptr = strstr(glx_extensions, query); 589 return ((ptr != NULL) && ((ptr[len] == ' ') || (ptr[len] == '\0'))); 590} 591 592 593/** 594 * Attempt to determine whether or not the display is synched to vblank. 595 */ 596static void 597query_vsync(Display *dpy, GLXDrawable drawable) 598{ 599 int interval = 0; 600 601#if defined(GLX_EXT_swap_control) 602 if (is_glx_extension_supported(dpy, "GLX_EXT_swap_control")) { 603 unsigned int tmp = -1; 604 glXQueryDrawable(dpy, drawable, GLX_SWAP_INTERVAL_EXT, &tmp); 605 interval = tmp; 606 } else 607#endif 608 if (is_glx_extension_supported(dpy, "GLX_MESA_swap_control")) { 609 PFNGLXGETSWAPINTERVALMESAPROC pglXGetSwapIntervalMESA = 610 (PFNGLXGETSWAPINTERVALMESAPROC) 611 glXGetProcAddressARB((const GLubyte *) "glXGetSwapIntervalMESA"); 612 613 interval = (*pglXGetSwapIntervalMESA)(); 614 } else if (is_glx_extension_supported(dpy, "GLX_SGI_swap_control")) { 615 /* The default swap interval with this extension is 1. Assume that it 616 * is set to the default. 617 * 618 * Many Mesa-based drivers default to 0, but all of these drivers also 619 * export GLX_MESA_swap_control. In that case, this branch will never 620 * be taken, and the correct result should be reported. 621 */ 622 interval = 1; 623 } 624 625 626 if (interval > 0) { 627 printf("Running synchronized to the vertical refresh. The framerate should be\n"); 628 if (interval == 1) { 629 printf("approximately the same as the monitor refresh rate.\n"); 630 } else if (interval > 1) { 631 printf("approximately 1/%d the monitor refresh rate.\n", 632 interval); 633 } 634 } 635} 636 637/** 638 * Handle one X event. 639 * \return NOP, EXIT or DRAW 640 */ 641static int 642handle_event(Display *dpy, Window win, XEvent *event) 643{ 644 (void) dpy; 645 (void) win; 646 647 switch (event->type) { 648 case Expose: 649 return DRAW; 650 case ConfigureNotify: 651 reshape(event->xconfigure.width, event->xconfigure.height); 652 break; 653 case KeyPress: 654 { 655 char buffer[10]; 656 int code; 657 code = XLookupKeysym(&event->xkey, 0); 658 if (code == XK_Left) { 659 view_roty += 5.0; 660 } 661 else if (code == XK_Right) { 662 view_roty -= 5.0; 663 } 664 else if (code == XK_Up) { 665 view_rotx += 5.0; 666 } 667 else if (code == XK_Down) { 668 view_rotx -= 5.0; 669 } 670 else { 671 XLookupString(&event->xkey, buffer, sizeof(buffer), 672 NULL, NULL); 673 if (buffer[0] == 27) { 674 /* escape */ 675 return EXIT; 676 } 677 else if (buffer[0] == 'a' || buffer[0] == 'A') { 678 animate = !animate; 679 } 680 } 681 return DRAW; 682 } 683 } 684 return NOP; 685} 686 687 688static void 689event_loop(Display *dpy, Window win) 690{ 691 while (1) { 692 int op; 693 while (!animate || XPending(dpy) > 0) { 694 XEvent event; 695 XNextEvent(dpy, &event); 696 op = handle_event(dpy, win, &event); 697 if (op == EXIT) 698 return; 699 else if (op == DRAW) 700 break; 701 } 702 703 draw_frame(dpy, win); 704 } 705} 706 707 708static void 709usage(void) 710{ 711 printf("Usage:\n"); 712 printf(" -display <displayname> set the display to run on\n"); 713 printf(" -stereo run in stereo mode\n"); 714 printf(" -samples N run in multisample mode with at least N samples\n"); 715 printf(" -fullscreen run in fullscreen mode\n"); 716 printf(" -info display OpenGL renderer info\n"); 717 printf(" -geometry WxH+X+Y window geometry\n"); 718} 719 720 721int 722main(int argc, char *argv[]) 723{ 724 unsigned int winWidth = 300, winHeight = 300; 725 int x = 0, y = 0; 726 Display *dpy; 727 Window win; 728 GLXContext ctx; 729 char *dpyName = NULL; 730 GLboolean printInfo = GL_FALSE; 731 VisualID visId; 732 int i; 733 734 for (i = 1; i < argc; i++) { 735 if (strcmp(argv[i], "-display") == 0) { 736 dpyName = argv[i+1]; 737 i++; 738 } 739 else if (strcmp(argv[i], "-info") == 0) { 740 printInfo = GL_TRUE; 741 } 742 else if (strcmp(argv[i], "-stereo") == 0) { 743 stereo = GL_TRUE; 744 } 745 else if (i < argc-1 && strcmp(argv[i], "-samples") == 0) { 746 samples = strtod(argv[i+1], NULL ); 747 ++i; 748 } 749 else if (strcmp(argv[i], "-fullscreen") == 0) { 750 fullscreen = GL_TRUE; 751 } 752 else if (i < argc-1 && strcmp(argv[i], "-geometry") == 0) { 753 XParseGeometry(argv[i+1], &x, &y, &winWidth, &winHeight); 754 i++; 755 } 756 else { 757 usage(); 758 return -1; 759 } 760 } 761 762 dpy = XOpenDisplay(dpyName); 763 if (!dpy) { 764 printf("Error: couldn't open display %s\n", 765 dpyName ? dpyName : getenv("DISPLAY")); 766 return -1; 767 } 768 769 if (fullscreen) { 770 int scrnum = DefaultScreen(dpy); 771 772 x = 0; y = 0; 773 winWidth = DisplayWidth(dpy, scrnum); 774 winHeight = DisplayHeight(dpy, scrnum); 775 } 776 777 make_window(dpy, "glxgears", x, y, winWidth, winHeight, &win, &ctx, &visId); 778 XMapWindow(dpy, win); 779 glXMakeCurrent(dpy, win, ctx); 780 query_vsync(dpy, win); 781 782 if (printInfo) { 783 printf("GL_RENDERER = %s\n", (char *) glGetString(GL_RENDERER)); 784 printf("GL_VERSION = %s\n", (char *) glGetString(GL_VERSION)); 785 printf("GL_VENDOR = %s\n", (char *) glGetString(GL_VENDOR)); 786 printf("GL_EXTENSIONS = %s\n", (char *) glGetString(GL_EXTENSIONS)); 787 printf("VisualID %d, 0x%x\n", (int) visId, (int) visId); 788 } 789 790 init(); 791 792 /* Set initial projection/viewing transformation. 793 * We can't be sure we'll get a ConfigureNotify event when the window 794 * first appears. 795 */ 796 reshape(winWidth, winHeight); 797 798 event_loop(dpy, win); 799 800 glDeleteLists(gear1, 1); 801 glDeleteLists(gear2, 1); 802 glDeleteLists(gear3, 1); 803 glXMakeCurrent(dpy, None, NULL); 804 glXDestroyContext(dpy, ctx); 805 XDestroyWindow(dpy, win); 806 XCloseDisplay(dpy); 807 808 return 0; 809} 810