ftp://ftp.linux.ncsu.edu/mirror/ftp.redhat.com/pub/redhat/linux/enterprise/4/en/os...
[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 static inline void
187 normalize(GLfloat v[3])
188 {
189   GLfloat d = (GLfloat) sqrt((double) (v[0] * v[0] +
190                                        v[1] * v[1] +
191                                        v[2] * v[2]));
192   if (d != 0)
193     {
194       v[0] /= d;
195       v[1] /= d;
196       v[2] /= d;
197         }
198   else
199     {
200       v[0] = v[1] = v[2] = 0;
201         }
202 }
203
204
205 /* Set up and enable texturing on our object */
206 static void
207 setup_xpm_texture (ModeInfo *mi, char **xpm_data)
208 {
209   XImage *image = xpm_to_ximage (MI_DISPLAY (mi), MI_VISUAL (mi),
210                                   MI_COLORMAP (mi), xpm_data);
211   char buf[1024];
212   clear_gl_error();
213   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
214                image->width, image->height, 0,
215                GL_RGBA,
216                /* GL_UNSIGNED_BYTE, */
217                GL_UNSIGNED_INT_8_8_8_8_REV,
218                image->data);
219   sprintf (buf, "builtin texture (%dx%d)", image->width, image->height);
220   check_gl_error(buf);
221
222   /* setup parameters for texturing */
223   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
224   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
225   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
226   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
227   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
228   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
229 }
230
231
232 static void
233 setup_file_texture (ModeInfo *mi, char *filename)
234 {
235   Display *dpy = mi->dpy;
236   Visual *visual = mi->xgwa.visual;
237   char buf[1024];
238
239   Colormap cmap = mi->xgwa.colormap;
240   XImage *image = xpm_file_to_ximage (dpy, visual, cmap, filename);
241
242   clear_gl_error();
243   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
244                image->width, image->height, 0,
245                GL_RGBA,
246                /* GL_UNSIGNED_BYTE, */
247                GL_UNSIGNED_INT_8_8_8_8_REV,
248                image->data);
249   sprintf (buf, "texture: %.100s (%dx%d)",
250            filename, image->width, image->height);
251   check_gl_error(buf);
252
253   /* setup parameters for texturing */
254   glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
255   glPixelStorei(GL_UNPACK_ROW_LENGTH, image->width);
256
257   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
258   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
259   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
260   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
261   glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
262 }
263
264
265 static void
266 setup_texture(ModeInfo * mi)
267 {
268 /*  planetstruct *gp = &planets[MI_SCREEN(mi)];*/
269
270   glEnable(GL_TEXTURE_2D);
271
272   if (!which_image ||
273           !*which_image ||
274           !strcmp(which_image, "BUILTIN"))
275         setup_xpm_texture (mi, earth_xpm);
276   else
277         setup_file_texture (mi, which_image);
278
279   check_gl_error("texture initialization");
280
281   /* Need to flip the texture top for bottom for some reason. */
282   glMatrixMode (GL_TEXTURE);
283   glScalef (1, -1, 1);
284   glMatrixMode (GL_MODELVIEW);
285 }
286
287
288 void
289 init_stars (ModeInfo *mi)
290 {
291   int i, j;
292   GLfloat max_size = 3;
293   GLfloat inc = 0.5;
294   int steps = max_size / inc;
295   int width  = MI_WIDTH(mi);
296   int height = MI_HEIGHT(mi);
297
298   planetstruct *gp = &planets[MI_SCREEN(mi)];
299   Bool wire = MI_IS_WIREFRAME(mi);
300   
301   if (!wire)
302     glEnable (GL_POINT_SMOOTH);
303
304   gp->starlist = glGenLists(1);
305   glNewList(gp->starlist, GL_COMPILE);
306
307   glScalef (1.0/width, 1.0/height, 1);
308
309   for (j = 1; j <= steps; j++)
310     {
311       glPointSize(inc * j);
312       glBegin(GL_POINTS);
313       for (i = 0 ; i < NUM_STARS / steps; i++)
314         {
315           glColor3f (0.6 + frand(0.3),
316                      0.6 + frand(0.3),
317                      0.6 + frand(0.3));
318           glVertex2f ((GLfloat) (random() % width),
319                       (GLfloat) (random() % height));
320         }
321       glEnd();
322     }
323   glEndList();
324
325   check_gl_error("stars initialization");
326 }
327
328
329 void
330 draw_stars (ModeInfo * mi)
331 {
332   int width  = MI_WIDTH(mi);
333   int height = MI_HEIGHT(mi);
334
335   planetstruct *gp = &planets[MI_SCREEN(mi)];
336   
337   /* Sadly, this causes a stall of the graphics pipeline (as would the
338      equivalent calls to glGet*.)  But there's no way around this, short
339      of having each caller set up the specific display matrix we need
340      here, which would kind of defeat the purpose of centralizing this
341      code in one file.
342    */
343   glPushAttrib(GL_TRANSFORM_BIT |  /* for matrix contents */
344                GL_ENABLE_BIT |     /* for various glDisable calls */
345                GL_CURRENT_BIT |    /* for glColor3f() */
346                GL_LIST_BIT);       /* for glListBase() */
347   {
348     check_gl_error ("glPushAttrib");
349
350     /* disable lighting and texturing when drawing stars!
351        (glPopAttrib() restores these.)
352      */
353     glDisable(GL_TEXTURE_2D);
354     glDisable(GL_LIGHTING);
355     glDisable(GL_DEPTH_TEST);
356
357     /* glPopAttrib() does not restore matrix changes, so we must
358        push/pop the matrix stacks to be non-intrusive there.
359      */
360     glMatrixMode(GL_PROJECTION);
361     glPushMatrix();
362     {
363       check_gl_error ("glPushMatrix");
364       glLoadIdentity();
365
366       /* Each matrix mode has its own stack, so we need to push/pop
367          them separately. */
368       glMatrixMode(GL_MODELVIEW);
369       glPushMatrix();
370       {
371         check_gl_error ("glPushMatrix");
372         glLoadIdentity();
373
374         gluOrtho2D (0, width, 0, height);
375         check_gl_error ("gluOrtho2D");
376
377         /* Draw the stars */
378         glScalef (width, height, 1);
379         glCallList(gp->starlist);
380         check_gl_error ("drawing stars");
381       }
382       glPopMatrix();
383     }
384     glMatrixMode(GL_PROJECTION);
385     glPopMatrix();
386
387   }
388   /* clean up after our state changes */
389   glPopAttrib();
390   check_gl_error ("glPopAttrib");
391 }
392
393
394
395 /* Set up lighting */
396 static void
397 init_sun (ModeInfo * mi)
398 {
399   planetstruct *gp = &planets[MI_SCREEN(mi)];
400
401   GLfloat lamb[4] = { 0.1, 0.1, 0.1, 1.0 };
402   GLfloat ldif[4] = { 1.0, 1.0, 1.0, 1.0 };
403   GLfloat spec[4] = { 1.0, 1.0, 1.0, 1.0 };
404
405   GLfloat mamb[4] = { 0.5, 0.5, 0.5, 1.0 };
406   GLfloat mdif[4] = { 1.0, 1.0, 1.0, 1.0 };
407   GLfloat mpec[4] = { 1.0, 1.0, 1.0, 1.0 };
408   GLfloat shiny = .4;
409
410   {
411     double h =  0.1 + frand(0.8);   /* east-west position - screen-side. */
412     double v = -0.3 + frand(0.6);   /* north-south position */
413
414     if (h > 0.3 && h < 0.8)         /* avoid having the sun at the camera */
415       h += (h > 0.5 ? 0.2 : -0.2);
416
417     gp->sunpos[0] = cos(h * M_PI);
418     gp->sunpos[1] = sin(h * M_PI);
419     gp->sunpos[2] = sin(v * M_PI);
420     gp->sunpos[3] =  0.00;
421   }
422
423   glEnable(GL_LIGHTING);
424   glEnable(GL_LIGHT0);
425
426   glLightfv (GL_LIGHT0, GL_POSITION, gp->sunpos);
427   glLightfv (GL_LIGHT0, GL_AMBIENT,  lamb);
428   glLightfv (GL_LIGHT0, GL_DIFFUSE,  ldif);
429   glLightfv (GL_LIGHT0, GL_SPECULAR, spec);
430
431   glMaterialfv (GL_FRONT, GL_AMBIENT,  mamb);
432   glMaterialfv (GL_FRONT, GL_DIFFUSE,  mdif);
433   glMaterialfv (GL_FRONT, GL_SPECULAR, mpec);
434   glMaterialf  (GL_FRONT, GL_SHININESS, shiny);
435
436
437   glEnable(GL_BLEND);
438   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
439   glShadeModel(GL_SMOOTH);
440
441   check_gl_error("lighting");
442 }
443
444
445 #define RANDSIGN() ((random() & 1) ? 1 : -1)
446
447 void
448 reshape_planet (ModeInfo *mi, int width, int height)
449 {
450   GLfloat h = (GLfloat) height / (GLfloat) width;
451
452   glViewport(0, 0, (GLint) width, (GLint) height);
453   glMatrixMode(GL_PROJECTION);
454   glLoadIdentity();
455   glFrustum(-1.0, 1.0, -h, h, 5.0, 100.0);
456   glMatrixMode(GL_MODELVIEW);
457   glLoadIdentity();
458   glTranslatef(0.0, 0.0, -DIST);
459
460   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
461 }
462
463
464 Bool
465 planet_handle_event (ModeInfo *mi, XEvent *event)
466 {
467   planetstruct *gp = &planets[MI_SCREEN(mi)];
468
469   if (event->xany.type == ButtonPress &&
470       event->xbutton.button & Button1)
471     {
472       gp->button_down_p = True;
473       gltrackball_start (gp->trackball,
474                          event->xbutton.x, event->xbutton.y,
475                          MI_WIDTH (mi), MI_HEIGHT (mi));
476       return True;
477     }
478   else if (event->xany.type == ButtonRelease &&
479            event->xbutton.button & Button1)
480     {
481       gp->button_down_p = False;
482       return True;
483     }
484   else if (event->xany.type == MotionNotify &&
485            gp->button_down_p)
486     {
487       gltrackball_track (gp->trackball,
488                          event->xmotion.x, event->xmotion.y,
489                          MI_WIDTH (mi), MI_HEIGHT (mi));
490       return True;
491     }
492
493   return False;
494 }
495
496
497 void
498 init_planet (ModeInfo * mi)
499 {
500   planetstruct *gp;
501   int screen = MI_SCREEN(mi);
502   Bool wire = MI_IS_WIREFRAME(mi);
503
504   if (planets == NULL) {
505         if ((planets = (planetstruct *) calloc(MI_NUM_SCREENS(mi),
506                                                                                   sizeof (planetstruct))) == NULL)
507           return;
508   }
509   gp = &planets[screen];
510
511   if ((gp->glx_context = init_GL(mi)) != NULL) {
512         reshape_planet(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
513   }
514
515   {
516         char *f = get_string_resource("imageForeground", "Foreground");
517         char *b = get_string_resource("imageBackground", "Background");
518         char *s;
519         if (!f) f = strdup("white");
520         if (!b) b = strdup("black");
521         
522         for (s = f + strlen(f)-1; s > f; s--)
523           if (*s == ' ' || *s == '\t')
524                 *s = 0;
525         for (s = b + strlen(b)-1; s > b; s--)
526           if (*s == ' ' || *s == '\t')
527                 *s = 0;
528
529     if (!XParseColor(mi->dpy, mi->xgwa.colormap, f, &gp->fg))
530       {
531                 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
532                 exit(1);
533       }
534     if (!XParseColor(mi->dpy, mi->xgwa.colormap, b, &gp->bg))
535       {
536                 fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
537                 exit(1);
538       }
539
540         free (f);
541         free (b);
542   }
543
544   {
545     double spin_speed   = 0.5;
546     double wander_speed = 0.02;
547     gp->rot = make_rotator (do_roll ? spin_speed : 0,
548                             do_roll ? spin_speed : 0,
549                             0, 1,
550                             do_wander ? wander_speed : 0,
551                             True);
552     gp->z = frand (1.0);
553     gp->trackball = gltrackball_init ();
554   }
555
556   if (wire)
557     {
558       do_texture = False;
559       do_light = False;
560       glEnable (GL_LINE_SMOOTH);
561     }
562
563   if (do_texture)
564     setup_texture (mi);
565
566   if (do_light)
567         init_sun (mi);
568
569   if (do_stars)
570     init_stars (mi);
571
572   glEnable(GL_DEPTH_TEST);
573   glEnable(GL_CULL_FACE);
574   glCullFace(GL_BACK); 
575
576   /* construct the polygons of the planet
577    */
578   gp->platelist = glGenLists(1);
579   glNewList (gp->platelist, GL_COMPILE);
580   glColor3f (1,1,1);
581   glPushMatrix ();
582   glScalef (RADIUS, RADIUS, RADIUS);
583   glRotatef (90, 1, 0, 0);
584   unit_sphere (resolution, resolution, wire);
585   mi->polygon_count += resolution*resolution;
586   glPopMatrix ();
587   glEndList();
588
589   /* construct the polygons of the latitude/longitude/axis lines.
590    */
591   gp->latlonglist = glGenLists(1);
592   glNewList (gp->latlonglist, GL_COMPILE);
593   glPushMatrix ();
594   if (do_texture) glDisable (GL_TEXTURE_2D);
595   if (do_light)   glDisable (GL_LIGHTING);
596   glColor3f (0.1, 0.3, 0.1);
597   glScalef (RADIUS, RADIUS, RADIUS);
598   glScalef (1.01, 1.01, 1.01);
599   glRotatef (90, 1, 0, 0);
600   unit_sphere (12, 24, 1);
601   glBegin(GL_LINES);
602   glVertex3f(0, -2, 0);
603   glVertex3f(0,  2, 0);
604   glEnd();
605   if (do_light)   glEnable(GL_LIGHTING);
606   if (do_texture) glEnable(GL_TEXTURE_2D);
607   glPopMatrix ();
608   glEndList();
609 }
610
611 void
612 draw_planet (ModeInfo * mi)
613 {
614   planetstruct *gp = &planets[MI_SCREEN(mi)];
615   Display    *display = MI_DISPLAY(mi);
616   Window      window = MI_WINDOW(mi);
617   double x, y, z;
618
619   if (!gp->glx_context)
620         return;
621
622   glDrawBuffer(GL_BACK);
623   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
624
625   glXMakeCurrent (display, window, *(gp->glx_context));
626
627   if (do_stars)
628     draw_stars (mi);
629
630   glPushMatrix();
631
632   get_position (gp->rot, &x, &y, &z, !gp->button_down_p);
633   glTranslatef((x - 0.5) * 15,
634                (y - 0.5) * 15,
635                (z - 0.5) * 8);
636
637   gltrackball_rotate (gp->trackball);
638
639   glRotatef (90,1,0,0);
640
641   if (do_roll)
642     {
643       get_rotation (gp->rot, &x, &y, 0, !gp->button_down_p);
644       glRotatef (x * 360, 1.0, 0.0, 0.0);
645       glRotatef (y * 360, 0.0, 1.0, 0.0);
646     }
647
648   glLightfv (GL_LIGHT0, GL_POSITION, gp->sunpos);
649
650   glRotatef (gp->z * 360, 0.0, 0.0, 1.0);
651   if (do_rotate && !gp->button_down_p)
652     {
653       gp->z -= 0.005;     /* the sun sets in the west */
654       if (gp->z < 0) gp->z += 1;
655     }
656
657   glCallList (gp->platelist);
658   if (gp->button_down_p)
659     glCallList (gp->latlonglist);
660   glPopMatrix();
661
662   if (mi->fps_p) do_fps (mi);
663   glFinish();
664   glXSwapBuffers(display, window);
665 }
666
667
668 void
669 release_planet (ModeInfo * mi)
670 {
671   if (planets != NULL) {
672         int screen;
673
674         for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
675           planetstruct *gp = &planets[screen];
676
677           if (gp->glx_context) {
678                 /* Display lists MUST be freed while their glXContext is current. */
679                 glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));
680
681                 if (glIsList(gp->platelist))
682                   glDeleteLists(gp->platelist, 1);
683                 if (glIsList(gp->starlist))
684                   glDeleteLists(gp->starlist, 1);
685           }
686         }
687         (void) free((void *) planets);
688         planets = NULL;
689   }
690   FreeAllGL(mi);
691 }
692
693
694 #endif
695