ftp://ftp.krokus.ru/pub/OpenBSD/distfiles/xscreensaver-4.21.tar.gz
[xscreensaver] / hacks / glx / boing.c
1 /* boing, Copyright (c) 2005 Jamie Zawinski <jwz@jwz.org>
2  *
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 
9  * implied warranty.
10  *
11  * A clone of the Amiga 1000 "Boing" demo.  This was the first graphics demo
12  * for the Amiga, written by Dale Luck and RJ Mical during a break at the 1984
13  * Consumer Electronics Show (or so the legend goes.)  The boing ball was
14  * briefly the official logo of Amiga Inc., until they were bought by
15  * Commodore later that year.
16  *
17  * With no arguments, this program looks a lot like the original Amiga demo.
18  * With "-smooth -lighting", it looks... less old.
19  *
20  * The amiga version made noise when the ball hit the walls.  This version
21  * does not, obviously.
22  */
23
24 #include <X11/Intrinsic.h>
25
26 extern XtAppContext app;
27
28 #define PROGCLASS       "Boing"
29 #define HACK_INIT       init_boing
30 #define HACK_DRAW       draw_boing
31 #define HACK_RESHAPE    reshape_boing
32 #define HACK_HANDLE_EVENT boing_handle_event
33 #define EVENT_MASK      PointerMotionMask
34 #define sws_opts        xlockmore_opts
35
36 #define DEF_SPIN        "True"
37 #define DEF_LIGHTING    "False"
38 #define DEF_SMOOTH      "False"
39 #define DEF_SCANLINES   "False"
40 #define DEF_SPEED       "1.0"
41 #define DEF_SIZE        "0.5"
42 #define DEF_ANGLE       "15"
43 #define DEF_MERIDIANS   "16"
44 #define DEF_PARALLELS   "8"
45 #define DEF_TILES       "12"
46 #define DEF_THICKNESS   "0.05"
47
48 #define DEF_BALL_COLOR1  "#CC1919"
49 #define DEF_BALL_COLOR2  "#F2F2F2"
50 #define DEF_GRID_COLOR   "#991999"
51 #define DEF_SHADOW_COLOR "#303030"
52 #define DEF_BACKGROUND   "#8C8C8C"
53
54 #define DEFAULTS        "*delay:        30000            \n" \
55                         "*showFPS:      False            \n" \
56                         "*wireframe:    False            \n" \
57
58 #undef countof
59 #define countof(x) (sizeof((x))/sizeof((*x)))
60
61 #include "xlockmore.h"
62 #include "gltrackball.h"
63 #include <ctype.h>
64
65 #ifdef USE_GL /* whole file */
66
67 #include <GL/glu.h>
68
69 typedef struct { GLfloat x, y, z; } XYZ;
70
71 typedef struct {
72   GLXContext *glx_context;
73   trackball_state *trackball;
74   Bool button_down_p;
75
76   GLuint ball_list;
77   double ball_x,   ball_y,   ball_z,   ball_th;
78   double ball_dx,  ball_dy,  ball_dz,  ball_dth;
79   double ball_ddx, ball_ddy, ball_ddz;
80
81   GLfloat ball_color1[4], ball_color2[4], grid_color[4];
82   GLfloat bg_color[4], shadow_color[4];
83   GLfloat lightpos[4];
84
85 } boing_configuration;
86
87 static boing_configuration *bps = NULL;
88
89 static Bool spin;
90 static Bool lighting_p;
91 static Bool smooth_p;
92 static Bool scanlines_p;
93 static GLfloat speed;
94 static int angle;
95 static GLfloat ball_size;
96 static unsigned int meridians;
97 static unsigned int parallels;
98 static unsigned int tiles;
99 static GLfloat thickness;
100 static char *ball_color1_str, *ball_color2_str, *grid_color_str,
101   *shadow_str, *bg_str;
102
103 static XrmOptionDescRec opts[] = {
104   { "-spin",       ".spin",      XrmoptionNoArg,  "True"  },
105   { "+spin",       ".spin",      XrmoptionNoArg,  "False" },
106   { "-lighting",   ".lighting",  XrmoptionNoArg,  "True"  },
107   { "+lighting",   ".lighting",  XrmoptionNoArg,  "False" },
108   { "-smooth",     ".smooth",    XrmoptionNoArg,  "True"  },
109   { "+smooth",     ".smooth",    XrmoptionNoArg,  "False" },
110   { "-scanlines",  ".scanlines", XrmoptionNoArg,  "True"  },
111   { "+scanlines",  ".scanlines", XrmoptionNoArg,  "False" },
112   { "-speed",      ".speed",     XrmoptionSepArg, 0 },
113   { "-angle",      ".angle",     XrmoptionSepArg, 0 },
114   { "-size",       ".ballSize",  XrmoptionSepArg, 0 },
115   { "-meridians",  ".meridians", XrmoptionSepArg, 0 },
116   { "-parallels",  ".parallels", XrmoptionSepArg, 0 },
117   { "-tiles",      ".tiles",     XrmoptionSepArg, 0 },
118   { "-thickness",  ".thickness", XrmoptionSepArg, 0 },
119   { "-ball-color1",".ballColor1",XrmoptionSepArg, 0 },
120   { "-ball-color2",".ballColor2",XrmoptionSepArg, 0 },
121   { "-grid-color", ".gridColor", XrmoptionSepArg, 0 },
122   { "-shadow-color",".shadowColor",XrmoptionSepArg, 0 },
123 };
124
125 static argtype vars[] = {
126   {&spin,      "spin",      "Spin",       DEF_SPIN,      t_Bool},
127   {&lighting_p,"lighting",  "Lighting",   DEF_LIGHTING,  t_Bool},
128   {&smooth_p,  "smooth",    "Smooth",     DEF_SMOOTH,    t_Bool},
129   {&scanlines_p,"scanlines","Scanlines",  DEF_SCANLINES, t_Bool},
130   {&speed,     "speed",     "Speed",      DEF_SPEED,     t_Float},
131   {&angle,     "angle",     "Angle",      DEF_ANGLE,     t_Int},
132   {&ball_size, "ballSize",  "BallSize",   DEF_SIZE,      t_Float},
133   {&meridians, "meridians", "meridians",  DEF_MERIDIANS, t_Int},
134   {&parallels, "parallels", "parallels",  DEF_PARALLELS, t_Int},
135   {&tiles,     "tiles",     "Tiles",      DEF_TILES,     t_Int},
136   {&thickness, "thickness", "Thickness",  DEF_THICKNESS, t_Float},
137   {&ball_color1_str, "ballColor1", "BallColor1", DEF_BALL_COLOR1, t_String},
138   {&ball_color2_str, "ballColor2", "BallColor2", DEF_BALL_COLOR2, t_String},
139   {&grid_color_str,  "gridColor",  "GridColor",  DEF_GRID_COLOR,  t_String},
140   {&shadow_str,      "shadowColor","ShadowColor",DEF_SHADOW_COLOR,t_String},
141   /* dammit, -background is too magic... */
142   {&bg_str,        "boingBackground", "Background", DEF_BACKGROUND, t_String},
143 };
144
145 ModeSpecOpt sws_opts = {countof(opts), opts, countof(vars), vars, NULL};
146
147 static void
148 parse_color (ModeInfo *mi, const char *name, const char *s, GLfloat *a)
149 {
150   XColor c;
151   a[3] = 1.0;  /* alpha */
152
153   if (! XParseColor (MI_DISPLAY(mi), MI_COLORMAP(mi), s, &c))
154     {
155       fprintf (stderr, "%s: can't parse %s color %s", progname, name, s);
156       exit (1);
157     }
158   a[0] = c.red   / 65536.0;
159   a[1] = c.green / 65536.0;
160   a[2] = c.blue  / 65536.0;
161 }
162
163
164 static void
165 draw_grid (ModeInfo *mi)
166 {
167   boing_configuration *bp = &bps[MI_SCREEN(mi)];
168   int x, y;
169   GLfloat t2  = (GLfloat) tiles / 2;
170   GLfloat s = 1.0 / (tiles + thickness);
171   GLfloat z = 0;
172
173   GLfloat lw = MI_HEIGHT(mi) * 0.06 * thickness;
174
175   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, bp->grid_color);
176   glColor3fv (bp->grid_color);
177
178   glPushMatrix();
179   glScalef(s, s, s);
180   glTranslatef (-t2, -t2, 0);
181
182   glLineWidth (lw);
183   glBegin (GL_LINES);
184   for (y = 0; y <= tiles; y++)
185     {
186       glVertex3f (0,     y, z);
187       glVertex3f (tiles, y, z);
188       /*mi->polygon_count++;*/
189     }
190   for (x = 0; x <= tiles; x++)
191     {
192       glVertex3f (x, tiles, z);
193       glVertex3f (x, 0,     z);
194       /*mi->polygon_count++;*/
195     }
196
197   glEnd();
198   glPopMatrix();
199 }
200
201
202 static void
203 draw_box (ModeInfo *mi)
204 {
205   /* boing_configuration *bp = &bps[MI_SCREEN(mi)]; */
206   glPushMatrix();
207   glTranslatef (0, 0, -0.5);
208   glFrontFace (GL_CCW);
209   draw_grid (mi);
210   glPopMatrix();
211
212   glPushMatrix();
213   glRotatef (90, 1, 0, 0);
214   glTranslatef (0, 0, 0.5);
215   glFrontFace (GL_CW);
216   draw_grid (mi);
217   glPopMatrix();
218 }
219
220
221 static void
222 draw_ball (ModeInfo *mi)
223 {
224   boing_configuration *bp = &bps[MI_SCREEN(mi)];
225   int wire = MI_IS_WIREFRAME(mi);
226   int x, y;
227   int xx = meridians;
228   int yy = parallels;
229   int scale = (smooth_p ? 5 : 1);
230
231   if (lighting_p && !wire)
232     glEnable (GL_LIGHTING);
233
234   if (parallels < 3)
235     scale *= 2;
236
237   xx *= scale;
238   yy *= scale;
239
240   glFrontFace (GL_CW);
241
242   glPushMatrix();
243   glTranslatef (bp->ball_x, bp->ball_y, bp->ball_z);
244   glScalef (ball_size, ball_size, ball_size);
245   glRotatef (-angle,      0, 0, 1);
246   glRotatef (bp->ball_th, 0, 1, 0);
247
248   for (y = 0; y < yy; y++)
249     {
250       GLfloat thy0 = y     * (M_PI * 2) / (yy * 2) + M_PI_2;
251       GLfloat thy1 = (y+1) * (M_PI * 2) / (yy * 2) + M_PI_2;
252
253       for (x = 0; x < xx; x++)
254         {
255           GLfloat thx0 = x     * (M_PI * 2) / xx;
256           GLfloat thx1 = (x+1) * (M_PI * 2) / xx;
257           XYZ p;
258           Bool bgp = ((x/scale) & 1) ^ ((y/scale) & 1);
259
260           if (wire && bgp) continue;
261
262           glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE,
263                         (bgp ? bp->ball_color2 : bp->ball_color1));
264           glColor3fv (bgp ? bp->ball_color2 : bp->ball_color1);
265
266           glBegin (wire ? GL_LINE_LOOP : GL_QUADS);
267
268           if (!smooth_p)
269             {
270               p.x = cos((thy0+thy1)/2) * cos((thx0+thx1)/2);
271               p.y = sin((thy0+thy1)/2);
272               p.z = cos((thy0+thy1)/2) * sin((thx0+thx1)/2);
273               glNormal3f (-p.x, -p.y, -p.z);
274             }
275
276           p.x = cos(thy0) * cos(thx0) / 2;
277           p.y = sin(thy0)             / 2;
278           p.z = cos(thy0) * sin(thx0) / 2;
279           if (smooth_p)
280             glNormal3f (-p.x, -p.y, -p.z);
281           glVertex3f (p.x, p.y, p.z);
282
283           p.x = cos(thy1) * cos(thx0) / 2;
284           p.y = sin(thy1)             / 2;
285           p.z = cos(thy1) * sin(thx0) / 2;
286           if (smooth_p)
287             glNormal3f (-p.x, -p.y, -p.z);
288           glVertex3f (p.x, p.y, p.z);
289
290           p.x = cos(thy1) * cos(thx1) / 2;
291           p.y = sin(thy1)             / 2;
292           p.z = cos(thy1) * sin(thx1) / 2;
293           if (smooth_p)
294             glNormal3f (-p.x, -p.y, -p.z);
295           glVertex3f (p.x, p.y, p.z);
296
297           p.x = cos(thy0) * cos(thx1) / 2;
298           p.y = sin(thy0)             / 2;
299           p.z = cos(thy0) * sin(thx1) / 2;
300           if (smooth_p)
301             glNormal3f (-p.x, -p.y, -p.z);
302           glVertex3f (p.x, p.y, p.z);
303
304           glEnd ();
305           mi->polygon_count++;
306         }
307     }
308
309   glPopMatrix();
310
311   if (lighting_p && !wire)
312     glDisable(GL_LIGHTING);
313 }
314
315
316 static void
317 draw_shadow (ModeInfo *mi)
318 {
319   boing_configuration *bp = &bps[MI_SCREEN(mi)];
320   int wire = MI_IS_WIREFRAME(mi);
321   GLfloat xoff = 0.14;
322   GLfloat yoff = 0.07;
323   int y;
324   int yy = parallels;
325   int scale = (smooth_p ? 5 : 1);
326
327   if (lighting_p && !wire)
328     glEnable (GL_BLEND);
329
330   if (parallels < 3)
331     scale *= 2;
332
333   yy *= scale;
334
335   glPushMatrix();
336   glTranslatef (bp->ball_x + xoff, bp->ball_y + yoff, -0.49);
337   glScalef (ball_size, ball_size, ball_size);
338   glRotatef (-angle, 0, 0, 1);
339
340   glMaterialfv (GL_FRONT, GL_AMBIENT_AND_DIFFUSE, bp->shadow_color);
341   glColor4fv (bp->shadow_color);
342
343   glFrontFace (GL_CCW);
344   glNormal3f (0, 0, 1);
345   glBegin (wire ? GL_LINE_LOOP : GL_TRIANGLE_FAN);
346   if (!wire) glVertex3f (0, 0, 0);
347
348   for (y = 0; y < yy*2+1; y++)
349     {
350       GLfloat thy0 = y * (M_PI * 2) / (yy * 2) + M_PI_2;
351       glVertex3f (cos(thy0) / 2, sin(thy0) / 2, 0);
352       mi->polygon_count++;
353     }
354
355   glEnd ();
356
357   glPopMatrix();
358
359   if (lighting_p && !wire)
360     glDisable (GL_BLEND);
361 }
362
363
364 static void
365 draw_scanlines (ModeInfo *mi)
366 {
367   /* boing_configuration *bp = &bps[MI_SCREEN(mi)]; */
368   int wire = MI_IS_WIREFRAME(mi);
369   int w = MI_WIDTH(mi);
370   int h = MI_HEIGHT(mi);
371
372   if (h <= 300) return;
373
374   if (!wire)
375     {
376       glEnable (GL_BLEND);
377       glDisable (GL_DEPTH_TEST);
378     }
379
380   glMatrixMode(GL_PROJECTION);
381   glPushMatrix();
382   {
383     glLoadIdentity();
384     glMatrixMode(GL_MODELVIEW);
385     glPushMatrix();
386     {
387       int lh, ls;
388       int y;
389       glLoadIdentity();
390       gluOrtho2D (0, w, 0, h);
391
392       if      (h > 500) lh = 4, ls = 4;
393       else if (h > 300) lh = 2, ls = 1;
394       else              lh = 1, ls = 1;
395
396       if (lh == 1)
397         glDisable (GL_BLEND);
398
399       glLineWidth (lh);
400       glColor4f (0, 0, 0, 0.3);
401
402       glBegin(GL_LINES);
403       for (y = 0; y < h; y += lh + ls)
404         {
405           glVertex3f (0, y, 0);
406           glVertex3f (w, y, 0);
407         }
408       glEnd();
409     }
410     glPopMatrix();
411   }
412   glMatrixMode(GL_PROJECTION);
413   glPopMatrix();
414   glMatrixMode(GL_MODELVIEW);
415
416   if (!wire)
417     {
418       glDisable (GL_BLEND);
419       glEnable (GL_DEPTH_TEST);
420     }
421 }
422
423
424
425 static void
426 tick_physics (ModeInfo *mi)
427 {
428   boing_configuration *bp = &bps[MI_SCREEN(mi)];
429   GLfloat s2 = ball_size / 2;
430   GLfloat max = 0.5 - s2;
431   GLfloat min = -max;
432
433   bp->ball_th += bp->ball_dth;
434   while (bp->ball_th > 360) bp->ball_th -= 360;
435   while (bp->ball_th < 0)   bp->ball_th += 360;
436
437   bp->ball_dx += bp->ball_ddx;
438   bp->ball_x  += bp->ball_dx;
439   if      (bp->ball_x < min) bp->ball_x = min, bp->ball_dx = -bp->ball_dx,
440     bp->ball_dth = -bp->ball_dth,
441     bp->ball_dx += (frand(speed/2) - speed);
442   else if (bp->ball_x > max) bp->ball_x = max, bp->ball_dx = -bp->ball_dx,
443     bp->ball_dth = -bp->ball_dth,
444     bp->ball_dx += (frand(speed/2) - speed);
445
446   bp->ball_dy += bp->ball_ddy;
447   bp->ball_y  += bp->ball_dy;
448   if      (bp->ball_y < min) bp->ball_y = min, bp->ball_dy = -bp->ball_dy;
449   else if (bp->ball_y > max) bp->ball_y = max, bp->ball_dy = -bp->ball_dy;
450
451   bp->ball_dz += bp->ball_ddz;
452   bp->ball_z  += bp->ball_dz;
453   if      (bp->ball_z < min) bp->ball_z = min, bp->ball_dz = -bp->ball_dz;
454   else if (bp->ball_z > max) bp->ball_z = max, bp->ball_dz = -bp->ball_dz;
455 }
456
457
458
459 /* Window management, etc
460  */
461 void
462 reshape_boing (ModeInfo *mi, int width, int height)
463 {
464   GLfloat h = (GLfloat) height / (GLfloat) width;
465
466   h *= 4.0 / 3.0;   /* Back in the caveman days we couldn't even afford
467                        square pixels! */
468
469   glViewport (0, 0, (GLint) width, (GLint) height);
470
471   glMatrixMode(GL_PROJECTION);
472   glLoadIdentity();
473   gluPerspective (8.0, 1/h, 1.0, 10.0);
474
475   glMatrixMode(GL_MODELVIEW);
476   glLoadIdentity();
477   gluLookAt (0.0, 0.0, 8.0,
478              0.0, 0.0, 0.0,
479              0.0, 1.0, 0.0);
480
481   glClear(GL_COLOR_BUFFER_BIT);
482 }
483
484
485 Bool
486 boing_handle_event (ModeInfo *mi, XEvent *event)
487 {
488   boing_configuration *bp = &bps[MI_SCREEN(mi)];
489
490   if (event->xany.type == ButtonPress &&
491       event->xbutton.button == Button1)
492     {
493       bp->button_down_p = True;
494       gltrackball_start (bp->trackball,
495                          event->xbutton.x, event->xbutton.y,
496                          MI_WIDTH (mi), MI_HEIGHT (mi));
497       return True;
498     }
499   else if (event->xany.type == ButtonRelease &&
500            event->xbutton.button == Button1)
501     {
502       bp->button_down_p = False;
503       return True;
504     }
505   else if (event->xany.type == ButtonPress &&
506            (event->xbutton.button == Button4 ||
507             event->xbutton.button == Button5))
508     {
509       gltrackball_mousewheel (bp->trackball, event->xbutton.button, 10,
510                               !!event->xbutton.state);
511       return True;
512     }
513   else if (event->xany.type == MotionNotify &&
514            bp->button_down_p)
515     {
516       gltrackball_track (bp->trackball,
517                          event->xmotion.x, event->xmotion.y,
518                          MI_WIDTH (mi), MI_HEIGHT (mi));
519       return True;
520     }
521
522   return False;
523 }
524
525
526 void 
527 init_boing (ModeInfo *mi)
528 {
529   boing_configuration *bp;
530   int wire = MI_IS_WIREFRAME(mi);
531
532   if (!bps) {
533     bps = (boing_configuration *)
534       calloc (MI_NUM_SCREENS(mi), sizeof (boing_configuration));
535     if (!bps) {
536       fprintf(stderr, "%s: out of memory\n", progname);
537       exit(1);
538     }
539
540     bp = &bps[MI_SCREEN(mi)];
541   }
542
543   bp = &bps[MI_SCREEN(mi)];
544
545   bp->glx_context = init_GL(mi);
546
547   if (tiles < 1) tiles = 1;
548
549   if (smooth_p)
550     {
551       if (meridians < 1) meridians = 1;
552       if (parallels < 1) parallels = 1;
553     }
554   else
555     {
556       if (meridians < 3) meridians = 3;
557       if (parallels < 2) parallels = 2;
558     }
559
560   if (meridians > 1 && meridians & 1) meridians++;  /* odd numbers look bad */
561
562
563   if (thickness <= 0) thickness = 0.001;
564   else if (thickness > 1) thickness = 1;
565
566   reshape_boing (mi, MI_WIDTH(mi), MI_HEIGHT(mi));
567
568   parse_color (mi, "ballColor1",  ball_color1_str,  bp->ball_color1);
569   parse_color (mi, "ballColor2",  ball_color2_str,  bp->ball_color2);
570   parse_color (mi, "gridColor",   grid_color_str,   bp->grid_color);
571   parse_color (mi, "shadowColor", shadow_str,       bp->shadow_color);
572   parse_color (mi, "background",  bg_str,           bp->bg_color);
573
574   bp->shadow_color[3] = 0.9;
575
576   glClearColor (bp->bg_color[0], bp->bg_color[1], bp->bg_color[2], 1);
577
578   if (!wire)
579     {
580       glEnable(GL_DEPTH_TEST);
581       glEnable(GL_CULL_FACE);
582     }
583
584   bp->lightpos[0] = 0.5;
585   bp->lightpos[1] = 0.5;
586   bp->lightpos[2] = -1;
587   bp->lightpos[3] = 0;
588
589   if (lighting_p && !wire)
590     {
591       GLfloat amb[4] = {0, 0, 0, 1};
592       GLfloat dif[4] = {1, 1, 1, 1};
593       GLfloat spc[4] = {1, 1, 1, 1};
594       glEnable(GL_LIGHT0);
595       glLightfv(GL_LIGHT0, GL_AMBIENT,  amb);
596       glLightfv(GL_LIGHT0, GL_DIFFUSE,  dif);
597       glLightfv(GL_LIGHT0, GL_SPECULAR, spc);
598     }
599
600   glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
601
602   speed = speed / 800.0;
603
604   bp->ball_dth = (spin ? -speed * 7 * 360 : 0);
605
606   bp->ball_x   = 0.5 - ((ball_size/2) + frand(1-ball_size));
607   bp->ball_y   = 0.2;
608   bp->ball_dx  = speed * 6 + frand(speed);
609   bp->ball_ddy = -speed;
610
611   bp->ball_dz  = speed * 6 + frand(speed);
612
613   bp->trackball = gltrackball_init ();
614 }
615
616
617 void
618 draw_boing (ModeInfo *mi)
619 {
620   boing_configuration *bp = &bps[MI_SCREEN(mi)];
621   Display *dpy = MI_DISPLAY(mi);
622   Window window = MI_WINDOW(mi);
623
624   if (!bp->glx_context)
625     return;
626
627   mi->polygon_count = 0;
628
629   glShadeModel(GL_SMOOTH);
630
631   glEnable(GL_NORMALIZE);
632   glEnable(GL_CULL_FACE);
633   glEnable (GL_DEPTH_TEST);
634
635   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
636
637   if (! bp->button_down_p)
638     tick_physics (mi);
639
640   glPushMatrix ();
641   gltrackball_rotate (bp->trackball);
642
643   glLightfv (GL_LIGHT0, GL_POSITION, bp->lightpos);
644
645   draw_box (mi);
646   draw_shadow (mi);
647   draw_ball (mi);
648   if (scanlines_p)
649     draw_scanlines (mi);
650
651   glPopMatrix ();
652
653   if (mi->fps_p) do_fps (mi);
654   glFinish();
655
656   glXSwapBuffers(dpy, window);
657 }
658
659 #endif /* USE_GL */