1 /* gears, Copyright (c) 2007-2008 Jamie Zawinski <jwz@jwz.org>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
11 * Originally written by Brian Paul in 1996 or earlier;
12 * rewritten by jwz in Nov 2007.
15 #define DEFAULTS "*delay: 30000 \n" \
17 "*showFPS: False \n" \
18 "*wireframe: False \n" \
20 # define refresh_gears 0
21 # define release_gears 0
23 #define countof(x) (sizeof((x))/sizeof((*x)))
25 #include "xlockmore.h"
30 #include "gltrackball.h"
33 #ifdef USE_GL /* whole file */
36 #define BELLRAND(n) ((frand((n)) + frand((n)) + frand((n))) / 3)
38 #define DEF_SPIN "True"
39 #define DEF_WANDER "True"
40 #define DEF_SPEED "1.0"
43 GLXContext *glx_context;
45 trackball_state *trackball;
52 GLuint armature_dlist;
53 int armature_polygons;
55 struct { GLfloat x1, y1, x2, y2; } bbox;
57 } gears_configuration;
59 static gears_configuration *bps = NULL;
63 static Bool do_wander;
65 static XrmOptionDescRec opts[] = {
66 { "-spin", ".spin", XrmoptionNoArg, "True" },
67 { "+spin", ".spin", XrmoptionNoArg, "False" },
68 { "-speed", ".speed", XrmoptionSepArg, 0 },
69 { "-wander", ".wander", XrmoptionNoArg, "True" },
70 { "+wander", ".wander", XrmoptionNoArg, "False" },
73 static argtype vars[] = {
74 {&do_spin, "spin", "Spin", DEF_SPIN, t_Bool},
75 {&do_wander, "wander", "Wander", DEF_WANDER, t_Bool},
76 {&speed, "speed", "Speed", DEF_SPEED, t_Float},
79 ENTRYPOINT ModeSpecOpt gears_opts = {countof(opts), opts, countof(vars), vars, NULL};
82 /* Window management, etc
85 reshape_gears (ModeInfo *mi, int width, int height)
87 GLfloat h = (GLfloat) height / (GLfloat) width;
89 glViewport (0, 0, (GLint) width, (GLint) height);
91 glMatrixMode(GL_PROJECTION);
93 gluPerspective (30.0, 1/h, 1.0, 100.0);
95 glMatrixMode(GL_MODELVIEW);
97 gluLookAt( 0.0, 0.0, 30.0,
101 glClear(GL_COLOR_BUFFER_BIT);
106 gears_handle_event (ModeInfo *mi, XEvent *event)
108 gears_configuration *bp = &bps[MI_SCREEN(mi)];
110 if (event->xany.type == ButtonPress &&
111 event->xbutton.button == Button1)
113 bp->button_down_p = True;
114 gltrackball_start (bp->trackball,
115 event->xbutton.x, event->xbutton.y,
116 MI_WIDTH (mi), MI_HEIGHT (mi));
119 else if (event->xany.type == ButtonRelease &&
120 event->xbutton.button == Button1)
122 bp->button_down_p = False;
125 else if (event->xany.type == ButtonPress &&
126 (event->xbutton.button == Button4 ||
127 event->xbutton.button == Button5 ||
128 event->xbutton.button == Button6 ||
129 event->xbutton.button == Button7))
131 gltrackball_mousewheel (bp->trackball, event->xbutton.button, 10,
132 !!event->xbutton.state);
135 else if (event->xany.type == MotionNotify &&
138 gltrackball_track (bp->trackball,
139 event->xmotion.x, event->xmotion.y,
140 MI_WIDTH (mi), MI_HEIGHT (mi));
153 glDeleteLists (g->dlist, 1);
158 /* Create and return a new gear sized for placement next to or on top of
159 the given parent gear (if any.) Returns 0 if out of memory.
160 [Mostly lifted from pinion.c]
163 new_gear (ModeInfo *mi, gear *parent)
165 gears_configuration *bp = &bps[MI_SCREEN(mi)];
166 gear *g = (gear *) calloc (1, sizeof (*g));
167 static unsigned long id = 0; /* only used in debugging output */
172 /* Pick the size of the teeth.
174 if (parent) /* adjascent gears need matching teeth */
176 g->tooth_w = parent->tooth_w;
177 g->tooth_h = parent->tooth_h;
178 g->tooth_slope = -parent->tooth_slope;
180 else /* gears that begin trains get any size they want */
182 g->tooth_w = 0.007 * (1.0 + BELLRAND(4.0));
183 g->tooth_h = 0.005 * (1.0 + BELLRAND(8.0));
185 g->tooth_slope = ((random() % 8)
187 : 0.5 + BELLRAND(1));
191 /* Pick the number of teeth, and thus, the radius.
196 if (!parent || bp->ngears > 4)
197 g->nteeth = 5 + BELLRAND (20);
199 g->nteeth = parent->nteeth * (0.5 + BELLRAND(2));
201 c = g->nteeth * g->tooth_w * 2; /* circumference = teeth + gaps */
202 g->r = c / (M_PI * 2); /* c = 2 pi r */
205 g->thickness = g->tooth_w + frand (g->r);
206 g->thickness2 = g->thickness * 0.7;
207 g->thickness3 = g->thickness;
211 g->color[0] = 0.5 + frand(0.5);
212 g->color[1] = 0.5 + frand(0.5);
213 g->color[2] = 0.5 + frand(0.5);
216 g->color2[0] = g->color[0] * 0.85;
217 g->color2[1] = g->color[1] * 0.85;
218 g->color2[2] = g->color[2] * 0.85;
219 g->color2[3] = g->color[3];
222 /* Decide on shape of gear interior:
223 - just a ring with teeth;
224 - that, plus a thinner in-set "plate" in the middle;
225 - that, plus a thin raised "lip" on the inner plate;
226 - or, a wide lip (really, a thicker third inner plate.)
228 if ((random() % 10) == 0)
230 /* inner_r can go all the way in; there's no inset disc. */
231 g->inner_r = (g->r * 0.1) + frand((g->r - g->tooth_h/2) * 0.8);
237 /* inner_r doesn't go in very far; inner_r2 is an inset disc. */
238 g->inner_r = (g->r * 0.5) + frand((g->r - g->tooth_h) * 0.4);
239 g->inner_r2 = (g->r * 0.1) + frand(g->inner_r * 0.5);
242 if (g->inner_r2 > (g->r * 0.2))
244 int nn = (random() % 10);
246 g->inner_r3 = (g->r * 0.1) + frand(g->inner_r2 * 0.2);
247 else if (nn <= 7 && g->inner_r2 >= 0.1)
248 g->inner_r3 = g->inner_r2 - 0.01;
252 /* If we have three discs, sometimes make the middle disc be spokes.
254 if (g->inner_r3 && ((random() % 5) == 0))
256 g->spokes = 2 + BELLRAND (5);
257 g->spoke_thickness = 1 + frand(7.0);
258 if (g->spokes == 2 && g->spoke_thickness < 2)
259 g->spoke_thickness += 1;
262 /* Sometimes add little nubbly bits, if there is room.
267 involute_biggest_ring (g, 0, &size, 0);
268 if (size > g->r * 0.2 && (random() % 5) == 0)
270 g->nubs = 1 + (random() % 16);
271 if (g->nubs > 8) g->nubs = 1;
275 if (g->inner_r3 > g->inner_r2) abort();
276 if (g->inner_r2 > g->inner_r) abort();
277 if (g->inner_r > g->r) abort();
279 /* Decide how complex the polygon model should be.
282 double pix = g->tooth_h * MI_HEIGHT(mi); /* approx. tooth size in pixels */
283 if (pix <= 2.5) g->size = INVOLUTE_SMALL;
284 else if (pix <= 3.5) g->size = INVOLUTE_MEDIUM;
285 else g->size = INVOLUTE_LARGE;
294 /* Given a newly-created gear, place it next to its parent in the scene,
295 with its teeth meshed and the proper velocity. Returns False if it
296 didn't work. (Call this a bunch of times until either it works, or
297 you decide it's probably not going to.)
298 [Mostly lifted from pinion.c]
301 place_gear (ModeInfo *mi, gear *g, gear *parent)
303 gears_configuration *bp = &bps[MI_SCREEN(mi)];
305 /* Compute this gear's velocity.
309 g->ratio = 0.8 + BELLRAND(0.4); /* 0.8-1.2 = 8-12rpm @ 60fps */
310 g->th = 1; /* not 0 */
314 /* Gearing ratio is the ratio of the number of teeth to previous gear
315 (which is also the ratio of the circumferences.)
317 g->ratio = (double) parent->nteeth / (double) g->nteeth;
319 /* Set our initial rotation to match that of the previous gear,
320 multiplied by the gearing ratio. (This is finessed later,
321 once we know the exact position of the gear relative to its
324 g->th = -(parent->th * g->ratio);
326 if (g->nteeth & 1) /* rotate 1/2 tooth-size if odd number of teeth */
328 double off = (180.0 / g->nteeth);
335 /* ratios are cumulative for all gears in the train. */
336 g->ratio *= parent->ratio;
340 if (parent) /* Place the gear next to the parent. */
342 double r_off = parent->r + g->r;
345 angle = (random() % 360) - 180; /* -180 to +180 degrees */
347 g->x = parent->x + (cos ((double) angle * (M_PI / 180)) * r_off);
348 g->y = parent->y + (sin ((double) angle * (M_PI / 180)) * r_off);
351 /* avoid accidentally changing sign of "th" in the math below. */
352 g->th += (g->th > 0 ? 360 : -360);
354 /* Adjust the rotation of the gear so that its teeth line up with its
355 parent, based on the position of the gear and the current rotation
359 double p_c = 2 * M_PI * parent->r; /* circumference of parent */
360 double g_c = 2 * M_PI * g->r; /* circumference of g */
362 double p_t = p_c * (angle/360.0); /* distance travelled along
363 circumference of parent when
364 moving "angle" degrees along
366 double g_rat = p_t / g_c; /* if travelling that distance
367 along circumference of g,
368 ratio of g's circumference
370 double g_th = 360.0 * g_rat; /* that ratio in degrees */
372 g->th += angle + g_th;
376 /* If the position we picked for this gear causes it to overlap
377 with any earlier gear in the train, give up.
382 for (i = bp->ngears-1; i >= 0; i--)
384 gear *og = bp->gears[i];
386 if (og == g) continue;
387 if (og == parent) continue;
388 if (g->z != og->z) continue; /* Ignore unless on same layer */
390 /* Collision detection without sqrt:
391 d = sqrt(a^2 + b^2) d^2 = a^2 + b^2
392 d < r1 + r2 d^2 < (r1 + r2)^2
394 if (((g->x - og->x) * (g->x - og->x) +
395 (g->y - og->y) * (g->y - og->y)) <
396 ((g->r + g->tooth_h + og->r + og->tooth_h) *
397 (g->r + g->tooth_h + og->r + og->tooth_h)))
406 /* Make a new gear, place it next to its parent in the scene,
407 with its teeth meshed and the proper velocity. Returns the gear;
408 or 0 if it didn't work. (Call this a bunch of times until either
409 it works, or you decide it's probably not going to.)
410 [Mostly lifted from pinion.c]
413 place_new_gear (ModeInfo *mi, gear *parent)
415 gears_configuration *bp = &bps[MI_SCREEN(mi)];
422 if (loop_count >= 100)
430 g = new_gear (mi, parent);
431 if (!g) return 0; /* out of memory? */
433 if (place_gear (mi, g, parent))
439 /* We got a gear, and it is properly positioned.
440 Insert it in the scene.
442 bp->gears[bp->ngears++] = g;
449 GLfloat width1, GLfloat height1,
450 GLfloat width2, GLfloat height2,
454 glShadeModel(GL_FLAT);
456 #if 0 /* don't need these - they're embedded in other objects */
459 glNormal3f(-1, 0, 0);
460 glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
461 glVertex3f(-length/2, -width1/2, -height1/2);
462 glVertex3f(-length/2, width1/2, -height1/2);
463 glVertex3f(-length/2, width1/2, height1/2);
464 glVertex3f(-length/2, -width1/2, height1/2);
471 glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
472 glVertex3f(length/2, -width2/2, -height2/2);
473 glVertex3f(length/2, width2/2, -height2/2);
474 glVertex3f(length/2, width2/2, height2/2);
475 glVertex3f(length/2, -width2/2, height2/2);
482 glNormal3f(0, 0, -1);
483 glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
484 glVertex3f(-length/2, -width1/2, -height1/2);
485 glVertex3f(-length/2, width1/2, -height1/2);
486 glVertex3f( length/2, width2/2, -height2/2);
487 glVertex3f( length/2, -width2/2, -height2/2);
494 glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
495 glVertex3f(-length/2, -width1/2, height1/2);
496 glVertex3f(-length/2, width1/2, height1/2);
497 glVertex3f( length/2, width2/2, height2/2);
498 glVertex3f( length/2, -width2/2, height2/2);
504 glNormal3f(0, -1, 0);
505 glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
506 glVertex3f(-length/2, -width1/2, -height1/2);
507 glVertex3f(-length/2, -width1/2, height1/2);
508 glVertex3f( length/2, -width2/2, height2/2);
509 glVertex3f( length/2, -width2/2, -height2/2);
516 glBegin(wire ? GL_LINE_LOOP : GL_QUADS);
517 glVertex3f(-length/2, width1/2, -height1/2);
518 glVertex3f(-length/2, width1/2, height1/2);
519 glVertex3f( length/2, width2/2, height2/2);
520 glVertex3f( length/2, width2/2, -height2/2);
531 ctube (GLfloat diameter, GLfloat width, Bool wire)
536 32, True, True, wire);
541 armature (ModeInfo *mi)
543 gears_configuration *bp = &bps[MI_SCREEN(mi)];
544 int wire = MI_IS_WIREFRAME(mi);
546 static const GLfloat spec[4] = {1.0, 1.0, 1.0, 1.0};
547 GLfloat shiny = 128.0;
550 color[0] = 0.5 + frand(0.5);
551 color[1] = 0.5 + frand(0.5);
552 color[2] = 0.5 + frand(0.5);
555 bp->armature_polygons = 0;
557 bp->armature_dlist = glGenLists (1);
558 if (! bp->armature_dlist)
560 check_gl_error ("glGenLists");
564 glNewList (bp->armature_dlist, GL_COMPILE);
566 glMaterialfv (GL_FRONT, GL_SPECULAR, spec);
567 glMateriali (GL_FRONT, GL_SHININESS, shiny);
568 glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
569 glColor3f (color[0], color[1], color[2]);
574 GLfloat s = bp->gears[0]->r * 2.7;
579 glTranslatef (0, 0, 1.4 + bp->gears[0]->thickness);
580 glRotatef (30, 0, 0, 1);
582 bp->armature_polygons += ctube (0.5, 10, wire); /* center axle */
585 glTranslatef(0.0, 4.2, -1);
586 bp->armature_polygons += ctube (0.5, 3, wire); /* axle 1 */
587 glTranslatef(0, 0, 1.8);
588 bp->armature_polygons += ctube (0.7, 0.7, wire);
592 glRotatef(120, 0.0, 0.0, 1.0);
593 glTranslatef(0.0, 4.2, -1);
594 bp->armature_polygons += ctube (0.5, 3, wire); /* axle 2 */
595 glTranslatef(0, 0, 1.8);
596 bp->armature_polygons += ctube (0.7, 0.7, wire);
600 glRotatef(240, 0.0, 0.0, 1.0);
601 glTranslatef(0.0, 4.2, -1);
602 bp->armature_polygons += ctube (0.5, 3, wire); /* axle 3 */
603 glTranslatef(0, 0, 1.8);
604 bp->armature_polygons += ctube (0.7, 0.7, wire);
607 glTranslatef(0, 0, 1.5); /* center disk */
608 bp->armature_polygons += ctube (1.5, 2, wire);
611 glRotatef(270, 0, 0, 1);
612 glRotatef(-10, 0, 1, 0);
613 glTranslatef(-2.2, 0, 0);
614 bp->armature_polygons += arm (4.0, 1.0, 0.5,
615 2.0, 1.0, wire); /* arm 1 */
619 glRotatef(30, 0, 0, 1);
620 glRotatef(-10, 0, 1, 0);
621 glTranslatef(-2.2, 0, 0);
622 bp->armature_polygons += arm (4.0, 1.0, 0.5,
623 2.0, 1.0, wire); /* arm 2 */
627 glRotatef(150, 0, 0, 1);
628 glRotatef(-10, 0, 1, 0);
629 glTranslatef(-2.2, 0, 0);
630 bp->armature_polygons += arm (4.0, 1.0, 0.5,
631 2.0, 1.0, wire); /* arm 3 */
641 planetary_gears (ModeInfo *mi)
643 gears_configuration *bp = &bps[MI_SCREEN(mi)];
644 gear *g0, *g1, *g2, *g3, *g4;
645 GLfloat distance = 2.02;
647 bp->planetary_p = True;
649 g0 = new_gear (mi, 0);
650 g1 = new_gear (mi, 0);
651 g2 = new_gear (mi, 0);
652 g3 = new_gear (mi, 0);
653 g4 = new_gear (mi, 0);
655 if (! place_gear (mi, g0, 0)) abort();
656 if (! place_gear (mi, g1, 0)) abort();
657 if (! place_gear (mi, g2, 0)) abort();
658 if (! place_gear (mi, g3, 0)) abort();
659 if (! place_gear (mi, g4, 0)) abort();
661 g0->nteeth = 12 + (3 * (random() % 10)); /* must be multiple of 3 */
662 g0->tooth_w = g0->r / g0->nteeth;
663 g0->tooth_h = g0->tooth_w * 2.8;
665 # define COPY(F) g4->F = g3->F = g2->F = g1->F = g0->F
682 g1->x = cos (M_PI * 2 / 3) * g1->r * distance;
683 g1->y = sin (M_PI * 2 / 3) * g1->r * distance;
685 g2->x = cos (M_PI * 4 / 3) * g2->r * distance;
686 g2->y = sin (M_PI * 4 / 3) * g2->r * distance;
688 g3->x = cos (M_PI * 6 / 3) * g3->r * distance;
689 g3->y = sin (M_PI * 6 / 3) * g3->r * distance;
695 /* rotate central gear 1/2 tooth-size if odd number of teeth */
697 g4->th -= (180.0 / g4->nteeth);
699 g0->inverted_p = True;
702 g0->nteeth = g1->nteeth * 3;
703 g0->r = g1->r * 3.05;
704 g0->inner_r = g0->r * 0.8;
707 g0->th = g1->th + (180 / g0->nteeth);
708 g0->ratio = g1->ratio / 3;
713 g0->size = INVOLUTE_LARGE;
715 bp->gears = (gear **) calloc (6, sizeof(**bp->gears));
718 bp->gears[bp->ngears++] = g1;
719 bp->gears[bp->ngears++] = g2;
720 bp->gears[bp->ngears++] = g3;
721 bp->gears[bp->ngears++] = g4;
722 bp->gears[bp->ngears++] = g0;
729 init_gears (ModeInfo *mi)
731 gears_configuration *bp;
732 int wire = MI_IS_WIREFRAME(mi);
736 bps = (gears_configuration *)
737 calloc (MI_NUM_SCREENS(mi), sizeof (gears_configuration));
739 fprintf(stderr, "%s: out of memory\n", progname);
744 bp = &bps[MI_SCREEN(mi)];
746 bp->glx_context = init_GL(mi);
748 reshape_gears (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
752 GLfloat pos[4] = {1.0, 1.0, 1.0, 0.0};
753 GLfloat amb[4] = {0.0, 0.0, 0.0, 1.0};
754 GLfloat dif[4] = {1.0, 1.0, 1.0, 1.0};
755 GLfloat spc[4] = {0.0, 1.0, 1.0, 1.0};
757 glEnable(GL_LIGHTING);
759 glEnable(GL_DEPTH_TEST);
760 glEnable(GL_CULL_FACE);
762 glLightfv(GL_LIGHT0, GL_POSITION, pos);
763 glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
764 glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
765 glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
769 double spin_speed = 0.5;
770 double wander_speed = 0.01;
771 double spin_accel = 0.25;
773 bp->rot = make_rotator (do_spin ? spin_speed : 0,
774 do_spin ? spin_speed : 0,
775 do_spin ? spin_speed : 0,
777 do_wander ? wander_speed : 0,
780 bp->trackball = gltrackball_init ();
785 planetary_gears (mi);
790 int total_gears = MI_COUNT (mi);
792 if (total_gears <= 0)
793 total_gears = 3 + abs (BELLRAND (8) - 4); /* 3 - 7, mostly 3. */
795 bp->gears = (gear **) calloc (total_gears+2, sizeof(**bp->gears));
798 for (i = 0; i < total_gears; i++)
799 g = place_new_gear (mi, g);
803 /* Center gears in scene. */
805 GLfloat minx=99999, miny=99999, maxx=-99999, maxy=-99999;
807 for (i = 0; i < bp->ngears; i++)
809 gear *g = bp->gears[i];
810 if (g->x - g->r < minx) minx = g->x - g->r;
811 if (g->x + g->r > maxx) maxx = g->x + g->r;
812 if (g->y - g->r < miny) miny = g->y - g->r;
813 if (g->y + g->r > maxy) maxy = g->y + g->r;
821 /* Now render each gear into its display list.
823 for (i = 0; i < bp->ngears; i++)
825 gear *g = bp->gears[i];
826 g->dlist = glGenLists (1);
829 check_gl_error ("glGenLists");
833 glNewList (g->dlist, GL_COMPILE);
834 g->polygons += draw_involute_gear (g, wire);
843 draw_gears (ModeInfo *mi)
845 gears_configuration *bp = &bps[MI_SCREEN(mi)];
846 Display *dpy = MI_DISPLAY(mi);
847 Window window = MI_WINDOW(mi);
850 if (!bp->glx_context)
853 glXMakeCurrent(MI_DISPLAY(mi), MI_WINDOW(mi), *(bp->glx_context));
855 glShadeModel(GL_SMOOTH);
857 glEnable(GL_DEPTH_TEST);
858 glEnable(GL_NORMALIZE);
859 glEnable(GL_CULL_FACE);
861 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
867 get_position (bp->rot, &x, &y, &z, !bp->button_down_p);
868 glTranslatef ((x - 0.5) * 4,
872 /* Do it twice because we don't track the device's orientation. */
873 glRotatef( current_device_rotation(), 0, 0, 1);
874 gltrackball_rotate (bp->trackball);
875 glRotatef(-current_device_rotation(), 0, 0, 1);
877 get_rotation (bp->rot, &x, &y, &z, !bp->button_down_p);
879 /* add a little rotation for -no-spin mode */
883 glRotatef (x * 360, 1.0, 0.0, 0.0);
884 glRotatef (y * 360, 0.0, 1.0, 0.0);
885 glRotatef (z * 360, 0.0, 0.0, 1.0);
888 /* Center the scene's bounding box in the window,
892 GLfloat w = bp->bbox.x2 - bp->bbox.x1;
893 GLfloat h = bp->bbox.y2 - bp->bbox.y1;
894 GLfloat s = 10.0 / (w > h ? w : h);
896 glTranslatef (-(bp->bbox.x1 + w/2),
897 -(bp->bbox.y1 + h/2),
901 mi->polygon_count = 0;
903 for (i = 0; i < bp->ngears; i++)
905 gear *g = bp->gears[i];
909 glTranslatef (g->x, g->y, g->z);
910 glRotatef (g->th, 0, 0, 1);
912 glCallList (g->dlist);
913 mi->polygon_count += g->polygons;
920 glCallList (bp->armature_dlist);
921 mi->polygon_count += bp->armature_polygons;
927 if (!bp->button_down_p)
928 for (i = 0; i < bp->ngears; i++)
930 gear *g = bp->gears[i];
931 double off = g->ratio * 5 * speed;
938 if (mi->fps_p) do_fps (mi);
941 glXSwapBuffers(dpy, window);
944 XSCREENSAVER_MODULE ("Gears", gears)