/* -*- Mode: C; tab-width: 4 -*- */
/* glplanet --- 3D rotating planet, e.g., Earth. */

#if 0
static const char sccsid[] = "@(#)glplanet.c	5.01 01/04/13 xlockmore";
#endif

/*-
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted,
 * provided that the above copyright notice appear in all copies and that
 * both that copyright notice and this permission notice appear in
 * supporting documentation.
 *
 * This file is provided AS IS with no warranties of any kind.  The author
 * shall have no liability with respect to the infringement of copyrights,
 * trade secrets or any patents by this file or any part thereof.  In no
 * event will the author be liable for any lost revenue or profits or
 * other special, indirect and consequential damages.
 *
 * Revision History:
 *
 * 13-Apr-01: rolf@groppe.de Did this on the xlockmore version
 * (21-Mar-01: jwz AT jwz.org   Broke sphere routine out into its own file.
 *                           Done on the xscreensaver version)
 *
 * 5-Apr-01:  rolf@groppe.de    Ported this mode from xscreensaver to xlock.
 *                              Made smoother roll & rotational movement
 *                                   configurable by -count and -cycles.
 *                              Renamed the parameter -image to -pimage
 *
 * 9-Oct-98:  dek@cgl.ucsf.edu  Added stars.
 *
 * 8-Oct-98:  jwz AT jwz.org   Made the 512x512x1 xearth image be built in.
 *                          Made it possible to load XPM or XBM files.
 *                          Made the planet bounce and roll around.
 *
 * 8-Oct-98: Released initial version of "glplanet"
 * (David Konerding, dek@cgl.ucsf.edu)
 *
 * BUGS:
 * -bounce is broken
 *
 *   For even more spectacular results, grab the images from the "SSysten"
 *   package (http://www.msu.edu/user/kamelkev/) and do this:
 *
 *     cd ssystem-1.4/hires/
 *     foreach f ( *.jpg )
 *       djpeg $f | ppmquant 254 | ppmtoxpm > /tmp/$f:r.xpm
 *     end
 *
 *     cd /tmp
 *     foreach f ( *.xpm )
 *       xlock -mode glplanet -pimage $f
 *     end
 */


#ifdef VMS
#include <types.h>
#endif

#ifdef STANDALONE
# define MODE_glplanet
#define DEFAULTS	"*delay:			15000   \n"	\
					"*showFPS:			False   \n" \
					"*wireframe:		False	\n"	\
					"*imageForeground:	Green	\n" \
					"*imageBackground:	Blue	\n"

#define free_glplanet 0
#define reshape_glplanet 0
#define glplanet_handle_event 0
# include "xlockmore.h"			/* from the xscreensaver distribution */
#else  /* !STANDALONE */
# include "xlock.h"			/* from the xlockmore distribution */
# include "visgl.h"
#endif /* !STANDALONE */

#ifdef MODE_glplanet /* whole file */

#ifdef HAVE_XPM
#include <X11/xpm.h>
# ifndef PIXEL_ALREADY_TYPEDEFED
#  define PIXEL_ALREADY_TYPEDEFED /* Sigh, Xmu/Drawing.h needs this... */
# endif
#endif

#ifdef USE_XMU
# ifdef VMS
#  include <Xmu/Drawing.h>
#else  /* !VMS */
#  include <X11/Xmu/Drawing.h>
# endif /* !VMS */
#endif

#include "sphere.h"

#include <GL/glu.h>

#define DEF_ROTATE  "True"
#define DEF_ROLL    "True"
#define DEF_BOUNCE  "True"
#define DEF_TEXTURE "True"
#define DEF_STARS "True"
#define DEF_LIGHT   "True"
#define DEF_IMAGE   "BUILTIN"
#define MINROLL 1
#define MINROT 2

#undef countof
#define countof(x) (sizeof((x))/sizeof((*x)))

static int do_rotate;
static int do_roll;
static int do_bounce;
static int do_texture;
static int do_stars;
static int do_light;
static char *which_image;
static XrmOptionDescRec opts[] = {
  {(char *) "-rotate", (char *) ".glplanet.rotate",  XrmoptionNoArg, (caddr_t) "on" },
  {(char *) "+rotate", (char *) ".glplanet.rotate",  XrmoptionNoArg, (caddr_t) "off" },
  {(char *) "-roll", (char *) ".glplanet.roll",    XrmoptionNoArg, (caddr_t) "on" },
  {(char *) "+roll", (char *) ".glplanet.roll",    XrmoptionNoArg, (caddr_t) "off" },
  {(char *) "-bounce", (char *) ".glplanet.bounce",  XrmoptionNoArg, (caddr_t) "on" },
  {(char *) "+bounce", (char *) ".glplanet.bounce",  XrmoptionNoArg, (caddr_t) "off" },
  {(char *) "-texture", (char *) ".glplanet.texture", XrmoptionNoArg, (caddr_t) "on" },
  {(char *) "+texture", (char *) ".glplanet.texture", XrmoptionNoArg, (caddr_t) "off" },
  {(char *) "-stars", (char *) ".glplanet.stars",   XrmoptionNoArg, (caddr_t) "on" },
  {(char *) "+stars", (char *) ".glplanet.stars",   XrmoptionNoArg, (caddr_t) "off" },
  {(char *) "-light", (char *) ".glplanet.light",   XrmoptionNoArg, (caddr_t) "on" },
  {(char *) "+light", (char *) ".glplanet.light",   XrmoptionNoArg, (caddr_t) "off" },
  {(char *) "-pimage", (char *) ".glplanet.pimage",  XrmoptionSepArg, (caddr_t) 0 }
};

static argtype vars[] = {
  {(void *) &do_rotate,   (char *) "rotate",  (char *) "Rotate",
    (char *) DEF_ROTATE,  t_Bool},
  {(void *) &do_roll,     (char *) "roll",    (char *) "Roll",
    (char *) DEF_ROLL,    t_Bool},
  {(void *) &do_bounce,   (char *) "bounce",  (char *) "Bounce",
    (char *) DEF_BOUNCE,  t_Bool},
  {(void *) &do_texture,  (char *) "texture", (char *) "Texture",
    (char *) DEF_TEXTURE, t_Bool},
  {(void *) &do_stars,    (char *) "stars",   (char *) "Stars",
    (char *) DEF_STARS,   t_Bool},
  {(void *) &do_light,    (char *) "light",   (char *) "Light",
    (char *) DEF_LIGHT,   t_Bool},
  {(void *) &which_image, (char *) "pimage",  (char *) "Pimage",
    (char *) DEF_IMAGE,   t_String},
};

static OptionStruct desc[] = {
  {(char *) "-/+rotate", (char *) "turn on/off rotatation"},
  {(char *) "-/+roll", (char *) "turn on/off rolling"},
  {(char *) "-/+bounce", (char *) "turn on/off bouncing"},
  {(char *) "-/+texture", (char *) "turn on/off texture mapping"},
  {(char *) "-/+stars", (char *) "turn on/off stars"},
  {(char *) "-/+light", (char *) "turn on/off light source"},
  {(char *) "-pimage", (char *) "set image"},
};

ENTRYPOINT ModeSpecOpt glplanet_opts =
{countof(opts), opts, countof(vars), vars, desc};

#ifdef USE_MODULES
ModStruct   glplanet_description =
{"glplanet", "init_glplanet", "draw_glplanet", "release_glplanet",
 "draw_glplanet", "init_glplanet", (char *) NULL, &glplanet_opts,
 15000, 1, 2, 1, 64, 1.0, "",
 "Animates texture mapped sphere (planet)", 0, NULL};
#endif

#define FLOATRAND(a) (((double)LRAND() / (double)MAXRAND) * a)

#include "bitmaps/earth.xbm"
#include "xpm-ximage.h"


/*-
 * slices and stacks are used in the sphere parameterization routine.
 * more slices and stacks will increase the quality of the sphere,
 * at the expense of rendering speed
 */

#define NUM_STARS 1000
#define SLICES 32
#define STACKS 32

/* radius of the sphere- fairly arbitrary */
#define RADIUS 4

/* distance away from the sphere model */
#define DIST 40



/* structure for holding the planet data */
typedef struct {
#ifdef WIN32
  HGLRC glx_context;
#else
  GLXContext *glx_context;
#endif
  Window window;
  GLuint platelist;
  GLuint starlist;
  int screen_width, screen_height;

  XColor fg, bg;

  GLfloat tx, ty, tz;
  GLfloat dtx, dty, dtz;
  GLfloat xpos, ypos, zpos;
  GLfloat dx, dy, dz;
  GLfloat box_width, box_height, box_depth;
  int roll_div, rot_div;

} planetstruct;


static planetstruct *planets = (planetstruct *) NULL;

#if 0
static inline void
normalize(GLfloat v[3])
{
	GLfloat     d = (GLfloat) sqrt((double) (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]));

	if (d != 0) {
		v[0] /= d;
		v[1] /= d;
		v[2] /= d;
	} else {
		v[0] = v[1] = v[2] = 0;
	}
}
#endif

/* Set up and enable texturing on our object */
static Bool
setup_xbm_texture (unsigned char *bits, int width, int height,
				   XColor *fgc, XColor *bgc)
{
  unsigned int fg = (((fgc->red  >> 8) << 16) |
					 ((fgc->green >> 8) << 8) |
					 ((fgc->blue >> 8)));
  unsigned int bg = (((bgc->red  >> 8) << 16) |
					 ((bgc->green >> 8) << 8) |
					 ((bgc->blue >> 8)));

  unsigned char *data = (unsigned char *)
	malloc ((width * height * 24) / 8);
  unsigned char *out = data;
  int x, y;

  for (y = 0; y < height; y++)
	for (x = 0; x < width; x++)
	  {
		unsigned char byte = bits [(y * (width / 8) + (x / 8))];
		unsigned char bit = (byte & (1 << (x % 8))) >> (x % 8);
		unsigned int word = (bit ? bg : fg);
		*out++ = (word & 0xFF0000) >> 16;
		*out++ = (word & 0x00FF00) >> 8;
		*out++ = (word & 0x0000FF);
	  }

  clear_gl_error();
  glTexImage2D(GL_TEXTURE_2D, 0, 3, width, height, 0,
			   GL_RGB, GL_UNSIGNED_BYTE, data);
#ifndef STANDALONE
  if (check_gl_error("texture")) {
	if (data != NULL) {
		free(data);
	}
	return False;
  }
#endif

  /* setup parameters for texturing */
  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

  if (data != NULL) {
	free(data);
  }
	return True;
}


static Bool
setup_file_texture (ModeInfo *mi, char *filename)
{
#ifdef HAVE_XPM
  {
	char **xpm_data = 0;
	int result = XpmReadFileToData (filename, &xpm_data);
	switch (result) {
	case XpmSuccess:
	  {
		XImage *image = xpm_to_ximage (MI_DISPLAY(mi), MI_VISUAL(mi),
			 MI_COLORMAP(mi), xpm_data);

		clear_gl_error();
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
					 image->width, image->height, 0,
					 GL_RGBA, GL_UNSIGNED_BYTE, image->data);
		if (check_gl_error("texture A")) {
			return False;
		}

		/* setup parameters for texturing */
		glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
		glPixelStorei(GL_UNPACK_ROW_LENGTH, image->width);

		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		return True;
	  }

	case XpmOpenFailed:
	  (void) fprintf (stderr, "Glplanet: file %s doesn't exist.\n", filename);
	  return False;

	case XpmFileInvalid:
	  /* Fall through and try it as an XBM. */
	  break;

	case XpmNoMemory:
	  (void) fprintf (stderr, "Glplanet: XPM: out of memory\n");
	  return False;

	default:
	  (void) fprintf (stderr, "Glplanet: XPM: unknown error code %d\n", result);
	  return False;
	}
  }
#endif /* HAVE_XPM */

#ifdef USE_XMU
  {
	planetstruct *gp = &planets[MI_SCREEN(mi)];
	unsigned int width = 0;
	unsigned int height = 0;
	unsigned char *data = 0;
	int xhot, yhot;
	int status = XmuReadBitmapDataFromFile (filename, &width, &height, &data,
											&xhot, &yhot);
	if (status != Success)
	  {
#ifdef HAVE_XPM
		(void) fprintf (stderr, "Glplanet: not an XPM file: %s\n", filename);
# endif
		(void) fprintf (stderr, "Glplanet: not an XBM file: %s\n", filename);
		return False;
	  }

	if (!setup_xbm_texture ((unsigned char *) data, width, height,
			&gp->fg, &gp->bg))
		return False;
  }
  return True;
#else  /* !XMU */

#ifdef HAVE_XPM
  (void) fprintf (stderr, "Glplanet: not an XPM file: %s\n", filename);
# endif
  (void) fprintf (stderr, "Glplanet: your vendor doesn't ship the standard Xmu library.\n");
  (void) fprintf (stderr, "Glplanet: we can't load XBM files without it.\n");
  return False;
#endif /* !XMU */
}


static Bool
setup_texture(ModeInfo * mi)
{
  planetstruct *gp = &planets[MI_SCREEN(mi)];
  if (!which_image ||
	  !*which_image ||
	  !strcmp(which_image, "BUILTIN")) {
	if (!setup_xbm_texture (earth_bits, earth_width, earth_height,
					   &gp->fg, &gp->bg))
		return False;

  } else {
	if (!setup_file_texture (mi, which_image))
		return False;
  }
  return True;
}


/* Set up and enable lighting */
static void
setup_light(void)
{
  /* set a number of parameters which make the scene look much nicer */
  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  glShadeModel(GL_SMOOTH);
}


/* Set up and enable face culling so we don't see the inside of the sphere */
static void
setup_face(void)
{
  glEnable(GL_CULL_FACE);
  glCullFace(GL_BACK);
}

/* lame way to generate some random stars */
static void
generate_stars(ModeInfo * mi, int width, int height)
{
  int i;
/*  GLfloat size_range[2], size;*/
  GLfloat x, y;

  planetstruct *gp = &planets[MI_SCREEN(mi)];

/*    glGetFloatv(GL_POINT_SIZE_RANGE, size_range); */

/*    (void) printf("size range: %f\t%f\n", size_range[0], size_range[1]); */
  gp->starlist = glGenLists(1);
  glNewList(gp->starlist, GL_COMPILE);

  /* this hackery makes the viewport map one-to-one with Vertex arguments */
  glMatrixMode(GL_PROJECTION);
  glPushMatrix();
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluOrtho2D(0, width, 0, height);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();

  /* disable depth testing for the stars, so they don't obscure the planet */
  glDisable(GL_DEPTH_TEST);
  glEnable(GL_POINT_SMOOTH);
  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

  glBegin(GL_POINTS);
  for(i = 0 ; i < NUM_STARS ; i++)
	{
/*   	  size = ((random()%size_range[0])) * size_range[1]/2.; */
/*    glPointSize(size); */
	  x = random() % width;
	  y = random() % height;
	  glVertex2f(x,y);
	}
  glEnd();

  /* return to original PROJECT and MODELVIEW */
  glMatrixMode(GL_PROJECTION);
  glPopMatrix();
  glMatrixMode(GL_MODELVIEW);


  glEndList();

}

/* Initialization function for screen saver */
static void
pinit(ModeInfo * mi)
{
  Bool wire = MI_IS_WIREFRAME(mi);
  planetstruct *gp = &planets[MI_SCREEN(mi)];

  if (wire) {
	glEnable(GL_LINE_SMOOTH);
	do_texture = False;
  }

  /* turn on various options we like */
  if (do_texture) {
	if (!setup_texture(mi))
		do_texture = False;
  }
  if (do_light)
	setup_light();

  setup_face();

  if (do_stars) {
	glEnable(GL_POINT_SMOOTH);
	generate_stars(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
  }

  gp->platelist=glGenLists(1);
  glNewList(gp->platelist, GL_COMPILE);
  glPushMatrix ();
  glScalef (RADIUS, RADIUS, RADIUS);
  unit_sphere (STACKS, SLICES, wire);
  glPopMatrix ();
  glEndList();
 }

static void
draw_sphere_glp(ModeInfo * mi)
{
  planetstruct *gp = &planets[MI_SCREEN(mi)];

  glEnable(GL_DEPTH_TEST);

  /* turn on the various attributes for making the sphere look nice */
  if (do_texture)
	glEnable(GL_TEXTURE_2D);

  if (do_light)
	{
	  glEnable(GL_LIGHTING);
	  glEnable(GL_LIGHT0);
	  glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
	  glEnable(GL_COLOR_MATERIAL);
	}

  glCallList(gp->platelist);

}


#define RANDSIGN() ((random() & 1) ? 1 : -1)

static void
pick_velocity (ModeInfo * mi)
{
  float roll1,roll2,rot1,rot2;
  planetstruct *gp = &planets[MI_SCREEN(mi)];

  gp->roll_div = MI_COUNT(mi);
  if (gp->roll_div <= MINROLL) gp->roll_div = MINROLL * 10;
  gp->rot_div = MI_CYCLES(mi);
  if (gp->rot_div <= MINROT) gp->rot_div = MINROT * 50;

  roll1 = gp->roll_div / 100.0;
  roll2 = roll1 * 0.75;
  rot1  = gp->rot_div  / 100.0;
  rot2  = rot1;
  gp->box_width =  15.0;
  gp->box_height = 15.0;
  gp->box_depth =  5.0;

  gp->tx = 0.0;
  gp->ty = 0.0;
  gp->tz = FLOATRAND(360);

/* following values changed for smoother rotation */
  gp->dtx = (FLOATRAND(roll1) + FLOATRAND(roll2)) * RANDSIGN();
  gp->dty = (FLOATRAND(roll1) + FLOATRAND(roll2)) * RANDSIGN();
  gp->dtz = (FLOATRAND(rot1) + FLOATRAND(rot2));  /* the sun sets in the west */

/* following values are unused
  gp->dx = (FLOATRAND(0.2) + FLOATRAND(0.2)) * RANDSIGN();
  gp->dy = (FLOATRAND(0.2) + FLOATRAND(0.2)) * RANDSIGN();
  gp->dz = (FLOATRAND(0.2) + FLOATRAND(0.2)) * RANDSIGN(); */
}


static void
rotate_and_move (ModeInfo * mi)
{
  planetstruct *gp = &planets[MI_SCREEN(mi)];

  if (do_roll)
	{
	  gp->tx += gp->dtx;
	  while (gp->tx < 0)   gp->tx += 360;
	  while (gp->tx > 360) gp->tx -= 360;

	  gp->ty += gp->dty;
	  while (gp->ty < 0)   gp->ty += 360;
	  while (gp->ty > 360) gp->ty -= 360;
	}

  if (do_rotate)
	{
	  gp->tz += gp->dtz;
	  while (gp->tz < 0)   gp->tz += 360;
	  while (gp->tz > 360) gp->tz -= 360;
	}

  if (do_bounce)
	{
      static int frame = 0;
/*
#     define SINOID(SCALE,SIZE) \
        ((((1 + sin((frame * (SCALE)) / 16 * M_PI)) / 2.0) * (SIZE)) - (SIZE)/2)
      gp->xpos = SINOID(0.031, gp->box_width);
      gp->ypos = SINOID(0.023, gp->box_height);
      gp->zpos = SINOID(0.017, gp->box_depth);
      frame++;
*/
/* Original function replaced for faster execution */
#     define SINOID(SCALE,SIZE) \
        (sin(frame * (SCALE)) / 2.0) * (SIZE)
      gp->xpos = SINOID(0.006086836, gp->box_width);
      gp->ypos = SINOID(0.004516039, gp->box_height);
      gp->zpos = SINOID(0.003337942, gp->box_depth);
      frame++;

	}
}


/* Standard reshape function */
ENTRYPOINT void
reshape_planet(ModeInfo *mi, int width, int height)
{
  GLfloat light[4];
  GLfloat h = (GLfloat) height / (GLfloat) width;

  light[0] = -1;
  light[1] = (int) (((random() % 3) & 0xFF) - 1);
  light[2] = (int) (((random() % 3) & 0xFF) - 1);
  light[3] = 0;

  glViewport(0, 0, (GLint) width, (GLint) height);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glFrustum(-1.0, 1.0, -h, h, 5.0, 100.0);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  glTranslatef(0.0, 0.0, -DIST);
  glLightfv(GL_LIGHT0, GL_POSITION, light);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

}


ENTRYPOINT void
init_glplanet(ModeInfo * mi)
{
  int         screen = MI_SCREEN(mi);

  planetstruct *gp;

  MI_INIT(mi, planets);
  gp = &planets[screen];

  pick_velocity (mi);

   {
/*	char *f = get_string_resource("imageForeground", "Foreground");
	char *b = get_string_resource("imageBackground", "Background"); */
        char *f = (char *) strdup("Green");
	char *b = (char *) strdup("Blue");
	char *s;
	if (!f) f = (char *) strdup("white");
	if (!b) b = (char *) strdup("black");

	for (s = f + strlen(f)-1; s > f; s--)
	  if (*s == ' ' || *s == '\t')
		*s = 0;
	for (s = b + strlen(b)-1; s > b; s--)
	  if (*s == ' ' || *s == '\t')
		*s = 0;

    if (!XParseColor(MI_DISPLAY(mi), MI_COLORMAP(mi), f, &gp->fg))
      {
		(void) fprintf(stderr, "Glplanet: unparsable color: \"%s\"\n", f);
		exit(1);
      }
    if (!XParseColor(MI_DISPLAY(mi), MI_COLORMAP(mi), b, &gp->bg))
      {
		(void) fprintf(stderr, "Glplanet: unparsable color: \"%s\"\n", f);
		exit(1);
      }

	free(f);
	free(b);
  }


  gp->window = MI_WINDOW(mi);
  if ((gp->glx_context = init_GL(mi)) != NULL) {
	reshape_planet(mi, MI_WIDTH(mi), MI_HEIGHT(mi));
	pinit(mi);
  } else {
	MI_CLEARWINDOW(mi);
  }
}

ENTRYPOINT void
draw_glplanet(ModeInfo * mi)
{
  planetstruct *gp = &planets[MI_SCREEN(mi)];
  Display    *display = MI_DISPLAY(mi);
  Window      window = MI_WINDOW(mi);

  if (!gp->glx_context)
	return;

  MI_IS_DRAWN(mi) = True;

  glDrawBuffer(GL_BACK);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
#ifdef WIN32
  wglMakeCurrent(hdc, gp->glx_context);
#else
  glXMakeCurrent(display, window, *(gp->glx_context));
#endif

  if (do_stars) {
	/* protect our modelview matrix and attributes */
	glPushMatrix();
	glPushAttrib(GL_ALL_ATTRIB_BITS);
	{
	  glColor3f(1,1,1);
	  /* draw the star field. */
	  glCallList(gp->starlist);

	}
	glPopMatrix();
	glPopAttrib();
  }

  /* protect our modelview matrix and attributes */
  glPushMatrix();
  glPushAttrib(GL_ALL_ATTRIB_BITS);
  {
	/* this pair of rotations seem to be necessary to orient the earth correctly */
	glRotatef(90,0,0,1);
	glRotatef(90,0,1,0);

	glTranslatef(gp->xpos, gp->ypos, gp->zpos);
	glRotatef(gp->tx, 1, 0, 0);
	glRotatef(gp->ty, 0, 1, 0);
	glRotatef(gp->tz, 0, 0, 1);
	/* draw the sphere */
	draw_sphere_glp(mi);
  }
  glPopMatrix();
  glPopAttrib();



  if (MI_IS_FPS(mi)) do_fps (mi);
  glFinish();
  glXSwapBuffers(display, window);

  rotate_and_move (mi);
}

ENTRYPOINT void
release_glplanet(ModeInfo * mi)
{
  if (planets != NULL) {
	int         screen;

	for (screen = 0; screen < MI_NUM_SCREENS(mi); screen++) {
	  planetstruct *gp = &planets[screen];

	  if (gp->glx_context) {
		/* Display lists MUST be freed while their glXContext is current. */
#ifdef WIN32
		wglMakeCurrent(hdc, gp->glx_context);
#else
		glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));
#endif

		if (glIsList(gp->platelist))
		  glDeleteLists(gp->platelist, 1);
		if (glIsList(gp->starlist))
		  glDeleteLists(gp->starlist, 1);
	  }
	}
	free(planets);
	planets = (planetstruct *) NULL;
  }
  FreeAllGL(mi);
}

XSCREENSAVER_MODULE ("Glplanet", glplanet)

#endif /* MODE_glplanet */
