http://ftp.ksu.edu.tw/FTP/FreeBSD/distfiles/xscreensaver-4.20.tar.gz
[xscreensaver] / hacks / glx / glplanet.c
1 /* -*- Mode: C; tab-width: 4 -*- */
2 /* glplanet --- 3D rotating planet, e.g., Earth.
3  *
4  * Permission to use, copy, modify, and distribute this software and its
5  * documentation for any purpose and without fee is hereby granted,
6  * provided that the above copyright notice appear in all copies and that
7  * both that copyright notice and this permission notice appear in
8  * supporting documentation.
9  *
10  * This file is provided AS IS with no warranties of any kind.  The author
11  * shall have no liability with respect to the infringement of copyrights,
12  * trade secrets or any patents by this file or any part thereof.  In no
13  * event will the author be liable for any lost revenue or profits or
14  * other special, indirect and consequential damages.
15  *
16  * Revision History:
17  *
18  * 16-Jan-02: jwz@jwz.org   gdk_pixbuf support.
19  * 21-Mar-01: jwz@jwz.org   Broke sphere routine out into its own file.
20  *
21  * 9-Oct-98:  dek@cgl.ucsf.edu  Added stars.
22  *
23  * 8-Oct-98:  jwz@jwz.org   Made the 512x512x1 xearth image be built in.
24  *                          Made it possible to load XPM or XBM files.
25  *                          Made the planet bounce and roll around.
26  *
27  * 8-Oct-98: Released initial version of "glplanet"
28  * (David Konerding, dek@cgl.ucsf.edu)
29  *
30  * BUGS:
31  * -bounce is broken
32  * 
33  *   For even more spectacular results, grab the images from the "SSystem"
34  *   package (http://www.msu.edu/user/kamelkev/) and use its JPEGs!
35  */
36
37
38 #ifdef STANDALONE
39 # define PROGCLASS                                              "Planet"
40 # define HACK_INIT                                              init_planet
41 # define HACK_DRAW                                              draw_planet
42 # define HACK_RESHAPE                                   reshape_planet
43 # define HACK_HANDLE_EVENT                              planet_handle_event
44 # define EVENT_MASK                                             PointerMotionMask
45 # define planet_opts                                    xlockmore_opts
46 #define DEFAULTS        "*delay:                        20000   \n"     \
47                                         "*showFPS:                      False   \n" \
48                     "*rotate:           True    \n" \
49                     "*roll:             True    \n" \
50                     "*wander:           True    \n" \
51                                         "*wireframe:            False   \n"     \
52                                         "*light:                        True    \n"     \
53                                         "*texture:                      True    \n" \
54                                         "*stars:                        True    \n" \
55                                         "*image:                        BUILTIN \n" \
56                                         "*imageForeground:      Green   \n" \
57                                         "*imageBackground:      Blue    \n"
58
59 # include "xlockmore.h"                         /* from the xscreensaver distribution */
60 #else  /* !STANDALONE */
61 # include "xlock.h"                                     /* from the xlockmore distribution */
62 #endif /* !STANDALONE */
63
64 #ifdef USE_GL /* whole file */
65
66 #include "sphere.h"
67
68 #ifdef HAVE_XMU
69 # ifndef VMS
70 #  include <X11/Xmu/Drawing.h>
71 #else  /* VMS */
72 #  include <Xmu/Drawing.h>
73 # endif /* VMS */
74 #endif
75
76
77 #include <GL/glu.h>
78
79 #define DEF_ROTATE  "True"
80 #define DEF_ROLL    "True"
81 #define DEF_WANDER  "True"
82 #define DEF_TEXTURE "True"
83 #define DEF_STARS   "True"
84 #define DEF_LIGHT   "True"
85 #define DEF_RESOLUTION "128"
86 #define DEF_IMAGE   "BUILTIN"
87
88 #undef countof
89 #define countof(x) (sizeof((x))/sizeof((*x)))
90
91 static int do_rotate;
92 static int do_roll;
93 static int do_wander;
94 static int do_texture;
95 static int do_stars;
96 static int do_light;
97 static char *which_image;
98 static int resolution;
99
100 static XrmOptionDescRec opts[] = {
101   {"-rotate",  ".glplanet.rotate",  XrmoptionNoArg, "true" },
102   {"+rotate",  ".glplanet.rotate",  XrmoptionNoArg, "false" },
103   {"-roll",    ".glplanet.roll",    XrmoptionNoArg, "true" },
104   {"+roll",    ".glplanet.roll",    XrmoptionNoArg, "false" },
105   {"-wander",  ".glplanet.wander",  XrmoptionNoArg, "true" },
106   {"+wander",  ".glplanet.wander",  XrmoptionNoArg, "false" },
107   {"-texture", ".glplanet.texture", XrmoptionNoArg, "true" },
108   {"+texture", ".glplanet.texture", XrmoptionNoArg, "false" },
109   {"-stars",   ".glplanet.stars",   XrmoptionNoArg, "true" },
110   {"+stars",   ".glplanet.stars",   XrmoptionNoArg, "false" },
111   {"-light",   ".glplanet.light",   XrmoptionNoArg, "true" },
112   {"+light",   ".glplanet.light",   XrmoptionNoArg, "false" },
113   {"-image",   ".glplanet.image",  XrmoptionSepArg, 0 },
114   {"-resolution", ".glplanet.resolution", XrmoptionSepArg, 0 },
115 };
116
117 static argtype vars[] = {
118   {&do_rotate,   "rotate",  "Rotate",  DEF_ROTATE,  t_Bool},
119   {&do_roll,     "roll",    "Roll",    DEF_ROLL,    t_Bool},
120   {&do_wander,   "wander",  "Wander",  DEF_WANDER,  t_Bool},
121   {&do_texture,  "texture", "Texture", DEF_TEXTURE, t_Bool},
122   {&do_stars,    "stars",   "Stars",   DEF_STARS,   t_Bool},
123   {&do_light,    "light",   "Light",   DEF_LIGHT,   t_Bool},
124   {&which_image, "image",   "Image",   DEF_IMAGE,   t_String},
125   {&resolution,  "resolution","Resolution", DEF_RESOLUTION, t_Int},
126 };
127
128 ModeSpecOpt planet_opts = {countof(opts), opts, countof(vars), vars, NULL};
129
130 #ifdef USE_MODULES
131 ModStruct   planet_description =
132 {"planet", "init_planet", "draw_planet", "release_planet",
133  "draw_planet", "init_planet", NULL, &planet_opts,
134  1000, 1, 2, 1, 4, 1.0, "",
135  "Animates texture mapped sphere (planet)", 0, NULL};
136 #endif
137
138 # ifdef __GNUC__
139   __extension__  /* don't warn about "string length is greater than the length
140                     ISO C89 compilers are required to support" when including
141                     the following XPM file... */
142 # endif
143 #include "../images/earth.xpm"
144
145 #include "xpm-ximage.h"
146 #include "rotator.h"
147 #include "gltrackball.h"
148
149
150 /*-
151  * slices and stacks are used in the sphere parameterization routine.
152  * more slices and stacks will increase the quality of the sphere,
153  * at the expense of rendering speed
154  */
155
156 #define NUM_STARS 1000
157
158 /* radius of the sphere- fairly arbitrary */
159 #define RADIUS 4
160
161 /* distance away from the sphere model */
162 #define DIST 40
163
164
165
166 /* structure for holding the planet data */
167 typedef struct {
168   GLuint platelist;
169   GLuint latlonglist;
170   GLuint starlist;
171   int screen_width, screen_height;
172   GLXContext *glx_context;
173   Window window;
174   XColor fg, bg;
175   GLfloat sunpos[4];
176   double z;
177   rotator *rot;
178   trackball_state *trackball;
179   Bool button_down_p;
180 } planetstruct;
181
182
183 static planetstruct *planets = NULL;
184
185
186 /* Set up and enable texturing on our object */
187 static void
188 setup_xpm_texture (ModeInfo *mi, char **xpm_data)
189 {
190   XImage *image = xpm_to_ximage (MI_DISPLAY (mi), MI_VISUAL (mi),
191                                   MI_COLORMAP (mi), xpm_data);
192   char buf[1024];
193   clear_gl_error();
194   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
195                image->width, image->height, 0,
196                GL_RGBA,
197                /* GL_UNSIGNED_BYTE, */
198                GL_UNSIGNED_INT_8_8_8_8_REV,
199                image->data);
200   sprintf (buf, "builtin texture (%dx%d)", image->width, image->height);
201   check_gl_error(buf);
202
203   /* setup parameters for texturing */
204   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
205   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
206   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
207   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
208   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
209   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
210 }
211
212
213 static void
214 setup_file_texture (ModeInfo *mi, char *filename)
215 {
216   Display *dpy = mi->dpy;
217   Visual *visual = mi->xgwa.visual;
218   char buf[1024];
219
220   Colormap cmap = mi->xgwa.colormap;
221   XImage *image = xpm_file_to_ximage (dpy, visual, cmap, filename);
222
223   clear_gl_error();
224   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
225                image->width, image->height, 0,
226                GL_RGBA,
227                /* GL_UNSIGNED_BYTE, */
228                GL_UNSIGNED_INT_8_8_8_8_REV,
229                image->data);
230   sprintf (buf, "texture: %.100s (%dx%d)",
231            filename, image->width, image->height);
232   check_gl_error(buf);
233
234   /* setup parameters for texturing */
235   glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
236   glPixelStorei(GL_UNPACK_ROW_LENGTH, image->width);
237
238   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
239   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
240   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
241   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
242   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
243 }
244
245
246 static void
247 setup_texture(ModeInfo * mi)
248 {
249 /*  planetstruct *gp = &planets[MI_SCREEN(mi)];*/
250
251   glEnable(GL_TEXTURE_2D);
252
253   if (!which_image ||
254           !*which_image ||
255           !strcmp(which_image, "BUILTIN"))
256         setup_xpm_texture (mi, earth_xpm);
257   else
258         setup_file_texture (mi, which_image);
259
260   check_gl_error("texture initialization");
261
262   /* Need to flip the texture top for bottom for some reason. */
263   glMatrixMode (GL_TEXTURE);
264   glScalef (1, -1, 1);
265   glMatrixMode (GL_MODELVIEW);
266 }
267
268
269 void
270 init_stars (ModeInfo *mi)
271 {
272   int i, j;
273   GLfloat max_size = 3;
274   GLfloat inc = 0.5;
275   int steps = max_size / inc;
276   int width  = MI_WIDTH(mi);
277   int height = MI_HEIGHT(mi);
278
279   planetstruct *gp = &planets[MI_SCREEN(mi)];
280   Bool wire = MI_IS_WIREFRAME(mi);
281   
282   if (!wire)
283     glEnable (GL_POINT_SMOOTH);
284
285   gp->starlist = glGenLists(1);
286   glNewList(gp->starlist, GL_COMPILE);
287
288   glScalef (1.0/width, 1.0/height, 1);
289
290   for (j = 1; j <= steps; j++)
291     {
292       glPointSize(inc * j);
293       glBegin(GL_POINTS);
294       for (i = 0 ; i < NUM_STARS / steps; i++)
295         {
296           glColor3f (0.6 + frand(0.3),
297                      0.6 + frand(0.3),
298                      0.6 + frand(0.3));
299           glVertex2f ((GLfloat) (random() % width),
300                       (GLfloat) (random() % height));
301         }
302       glEnd();
303     }
304   glEndList();
305
306   check_gl_error("stars initialization");
307 }
308
309
310 void
311 draw_stars (ModeInfo * mi)
312 {
313   int width  = MI_WIDTH(mi);
314   int height = MI_HEIGHT(mi);
315
316   planetstruct *gp = &planets[MI_SCREEN(mi)];
317   
318   /* Sadly, this causes a stall of the graphics pipeline (as would the
319      equivalent calls to glGet*.)  But there's no way around this, short
320      of having each caller set up the specific display matrix we need
321      here, which would kind of defeat the purpose of centralizing this
322      code in one file.
323    */
324   glPushAttrib(GL_TRANSFORM_BIT |  /* for matrix contents */
325                GL_ENABLE_BIT |     /* for various glDisable calls */
326                GL_CURRENT_BIT |    /* for glColor3f() */
327                GL_LIST_BIT);       /* for glListBase() */
328   {
329     check_gl_error ("glPushAttrib");
330
331     /* disable lighting and texturing when drawing stars!
332        (glPopAttrib() restores these.)
333      */
334     glDisable(GL_TEXTURE_2D);
335     glDisable(GL_LIGHTING);
336     glDisable(GL_DEPTH_TEST);
337
338     /* glPopAttrib() does not restore matrix changes, so we must
339        push/pop the matrix stacks to be non-intrusive there.
340      */
341     glMatrixMode(GL_PROJECTION);
342     glPushMatrix();
343     {
344       check_gl_error ("glPushMatrix");
345       glLoadIdentity();
346
347       /* Each matrix mode has its own stack, so we need to push/pop
348          them separately. */
349       glMatrixMode(GL_MODELVIEW);
350       glPushMatrix();
351       {
352         check_gl_error ("glPushMatrix");
353         glLoadIdentity();
354
355         gluOrtho2D (0, width, 0, height);
356         check_gl_error ("gluOrtho2D");
357
358         /* Draw the stars */
359         glScalef (width, height, 1);
360         glCallList(gp->starlist);
361         check_gl_error ("drawing stars");
362       }
363       glPopMatrix();
364     }
365     glMatrixMode(GL_PROJECTION);
366     glPopMatrix();
367
368   }
369   /* clean up after our state changes */
370   glPopAttrib();
371   check_gl_error ("glPopAttrib");
372 }
373
374
375
376 /* Set up lighting */
377 static void
378 init_sun (ModeInfo * mi)
379 {
380   planetstruct *gp = &planets[MI_SCREEN(mi)];
381
382   GLfloat lamb[4] = { 0.1, 0.1, 0.1, 1.0 };
383   GLfloat ldif[4] = { 1.0, 1.0, 1.0, 1.0 };
384   GLfloat spec[4] = { 1.0, 1.0, 1.0, 1.0 };
385
386   GLfloat mamb[4] = { 0.5, 0.5, 0.5, 1.0 };
387   GLfloat mdif[4] = { 1.0, 1.0, 1.0, 1.0 };
388   GLfloat mpec[4] = { 1.0, 1.0, 1.0, 1.0 };
389   GLfloat shiny = .4;
390
391   {
392     double h =  0.1 + frand(0.8);   /* east-west position - screen-side. */
393     double v = -0.3 + frand(0.6);   /* north-south position */
394
395     if (h > 0.3 && h < 0.8)         /* avoid having the sun at the camera */
396       h += (h > 0.5 ? 0.2 : -0.2);
397
398     gp->sunpos[0] = cos(h * M_PI);
399     gp->sunpos[1] = sin(h * M_PI);
400     gp->sunpos[2] = sin(v * M_PI);
401     gp->sunpos[3] =  0.00;
402   }
403
404   glEnable(GL_LIGHTING);
405   glEnable(GL_LIGHT0);
406
407   glLightfv (GL_LIGHT0, GL_POSITION, gp->sunpos);
408   glLightfv (GL_LIGHT0, GL_AMBIENT,  lamb);
409   glLightfv (GL_LIGHT0, GL_DIFFUSE,  ldif);
410   glLightfv (GL_LIGHT0, GL_SPECULAR, spec);
411
412   glMaterialfv (GL_FRONT, GL_AMBIENT,  mamb);
413   glMaterialfv (GL_FRONT, GL_DIFFUSE,  mdif);
414   glMaterialfv (GL_FRONT, GL_SPECULAR, mpec);
415   glMaterialf  (GL_FRONT, GL_SHININESS, shiny);
416
417
418   glEnable(GL_BLEND);
419   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
420   glShadeModel(GL_SMOOTH);
421
422   check_gl_error("lighting");
423 }
424
425
426 #define RANDSIGN() ((random() & 1) ? 1 : -1)
427
428 void
429 reshape_planet (ModeInfo *mi, int width, int height)
430 {
431   GLfloat h = (GLfloat) height / (GLfloat) width;
432
433   glViewport(0, 0, (GLint) width, (GLint) height);
434   glMatrixMode(GL_PROJECTION);
435   glLoadIdentity();
436   glFrustum(-1.0, 1.0, -h, h, 5.0, 100.0);
437   glMatrixMode(GL_MODELVIEW);
438   glLoadIdentity();
439   glTranslatef(0.0, 0.0, -DIST);
440
441   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
442 }
443
444
445 Bool
446 planet_handle_event (ModeInfo *mi, XEvent *event)
447 {
448   planetstruct *gp = &planets[MI_SCREEN(mi)];
449
450   if (event->xany.type == ButtonPress &&
451       event->xbutton.button == Button1)
452     {
453       gp->button_down_p = True;
454       gltrackball_start (gp->trackball,
455                          event->xbutton.x, event->xbutton.y,
456                          MI_WIDTH (mi), MI_HEIGHT (mi));
457       return True;
458     }
459   else if (event->xany.type == ButtonRelease &&
460            event->xbutton.button == Button1)
461     {
462       gp->button_down_p = False;
463       return True;
464     }
465   else if (event->xany.type == ButtonPress &&
466            (event->xbutton.button == Button4 ||
467             event->xbutton.button == Button5))
468     {
469       gltrackball_mousewheel (gp->trackball, event->xbutton.button, 10,
470                               !!event->xbutton.state);
471       return True;
472     }
473   else if (event->xany.type == MotionNotify &&
474            gp->button_down_p)
475     {
476       gltrackball_track (gp->trackball,
477                          event->xmotion.x, event->xmotion.y,
478                          MI_WIDTH (mi), MI_HEIGHT (mi));
479       return True;
480     }
481
482   return False;
483 }
484
485
486 void
487 init_planet (ModeInfo * mi)
488 {
489   planetstruct *gp;
490   int screen = MI_SCREEN(mi);
491   Bool wire = MI_IS_WIREFRAME(mi);
492
493   if (planets == NULL) {
494         if ((planets = (planetstruct *) calloc(MI_NUM_SCREENS(mi),
495                                                                                   sizeof (planetstruct))) == NULL)
496           return;
497   }
498   gp = &planets[screen];
499
500   if ((gp->glx_context = init_GL(mi)) != NULL) {
501         reshape_planet(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
502   }
503
504   {
505         char *f = get_string_resource("imageForeground", "Foreground");
506         char *b = get_string_resource("imageBackground", "Background");
507         char *s;
508         if (!f) f = strdup("white");
509         if (!b) b = strdup("black");
510         
511         for (s = f + strlen(f)-1; s > f; s--)
512           if (*s == ' ' || *s == '\t')
513                 *s = 0;
514         for (s = b + strlen(b)-1; s > b; s--)
515           if (*s == ' ' || *s == '\t')
516                 *s = 0;
517
518     if (!XParseColor(mi->dpy, mi->xgwa.colormap, f, &gp->fg))
519       {
520                 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
521                 exit(1);
522       }
523     if (!XParseColor(mi->dpy, mi->xgwa.colormap, b, &gp->bg))
524       {
525                 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
526                 exit(1);
527       }
528
529         free (f);
530         free (b);
531   }
532
533   {
534     double spin_speed   = 0.5;
535     double wander_speed = 0.02;
536     gp->rot = make_rotator (do_roll ? spin_speed : 0,
537                             do_roll ? spin_speed : 0,
538                             0, 1,
539                             do_wander ? wander_speed : 0,
540                             True);
541     gp->z = frand (1.0);
542     gp->trackball = gltrackball_init ();
543   }
544
545   if (wire)
546     {
547       do_texture = False;
548       do_light = False;
549       glEnable (GL_LINE_SMOOTH);
550     }
551
552   if (do_texture)
553     setup_texture (mi);
554
555   if (do_light)
556         init_sun (mi);
557
558   if (do_stars)
559     init_stars (mi);
560
561   glEnable(GL_DEPTH_TEST);
562   glEnable(GL_CULL_FACE);
563   glCullFace(GL_BACK); 
564
565   /* construct the polygons of the planet
566    */
567   gp->platelist = glGenLists(1);
568   glNewList (gp->platelist, GL_COMPILE);
569   glColor3f (1,1,1);
570   glPushMatrix ();
571   glScalef (RADIUS, RADIUS, RADIUS);
572   glRotatef (90, 1, 0, 0);
573   unit_sphere (resolution, resolution, wire);
574   mi->polygon_count += resolution*resolution;
575   glPopMatrix ();
576   glEndList();
577
578   /* construct the polygons of the latitude/longitude/axis lines.
579    */
580   gp->latlonglist = glGenLists(1);
581   glNewList (gp->latlonglist, GL_COMPILE);
582   glPushMatrix ();
583   if (do_texture) glDisable (GL_TEXTURE_2D);
584   if (do_light)   glDisable (GL_LIGHTING);
585   glColor3f (0.1, 0.3, 0.1);
586   glScalef (RADIUS, RADIUS, RADIUS);
587   glScalef (1.01, 1.01, 1.01);
588   glRotatef (90, 1, 0, 0);
589   unit_sphere (12, 24, 1);
590   glBegin(GL_LINES);
591   glVertex3f(0, -2, 0);
592   glVertex3f(0,  2, 0);
593   glEnd();
594   if (do_light)   glEnable(GL_LIGHTING);
595   if (do_texture) glEnable(GL_TEXTURE_2D);
596   glPopMatrix ();
597   glEndList();
598 }
599
600 void
601 draw_planet (ModeInfo * mi)
602 {
603   planetstruct *gp = &planets[MI_SCREEN(mi)];
604   Display    *display = MI_DISPLAY(mi);
605   Window      window = MI_WINDOW(mi);
606   double x, y, z;
607
608   if (!gp->glx_context)
609         return;
610
611   glDrawBuffer(GL_BACK);
612   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
613
614   glXMakeCurrent (display, window, *(gp->glx_context));
615
616   if (do_stars)
617     draw_stars (mi);
618
619   glPushMatrix();
620
621   get_position (gp->rot, &x, &y, &z, !gp->button_down_p);
622   glTranslatef((x - 0.5) * 15,
623                (y - 0.5) * 15,
624                (z - 0.5) * 8);
625
626   gltrackball_rotate (gp->trackball);
627
628   glRotatef (90,1,0,0);
629
630   if (do_roll)
631     {
632       get_rotation (gp->rot, &x, &y, 0, !gp->button_down_p);
633       glRotatef (x * 360, 1.0, 0.0, 0.0);
634       glRotatef (y * 360, 0.0, 1.0, 0.0);
635     }
636
637   glLightfv (GL_LIGHT0, GL_POSITION, gp->sunpos);
638
639   glRotatef (gp->z * 360, 0.0, 0.0, 1.0);
640   if (do_rotate && !gp->button_down_p)
641     {
642       gp->z -= 0.005;     /* the sun sets in the west */
643       if (gp->z < 0) gp->z += 1;
644     }
645
646   glCallList (gp->platelist);
647   if (gp->button_down_p)
648     glCallList (gp->latlonglist);
649   glPopMatrix();
650
651   if (mi->fps_p) do_fps (mi);
652   glFinish();
653   glXSwapBuffers(display, window);
654 }
655
656
657 void
658 release_planet (ModeInfo * mi)
659 {
660   if (planets != NULL) {
661         int screen;
662
663         for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
664           planetstruct *gp = &planets[screen];
665
666           if (gp->glx_context) {
667                 /* Display lists MUST be freed while their glXContext is current. */
668                 glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));
669
670                 if (glIsList(gp->platelist))
671                   glDeleteLists(gp->platelist, 1);
672                 if (glIsList(gp->starlist))
673                   glDeleteLists(gp->starlist, 1);
674           }
675         }
676         (void) free((void *) planets);
677         planets = NULL;
678   }
679   FreeAllGL(mi);
680 }
681
682
683 #endif
684