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

#if !defined( lint ) && !defined( SABER )
static const char sccsid[] = "@(#)plate.c	4.07 97/11/24 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:
 * 8-Oct-98:  jwz@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)
 *
 * TODO:
 * 1) stars
 * 3) better earth image
 * 4) "exploding" planet mode-- the surface will expand and explode
 * 5) Fix bug with annoying triangles moving on surface
 *
 *
 *   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 )
 *       glplanet -image $f
 *     end
 */


/*-
 * due to a Bug/feature in VMS X11/Intrinsic.h has to be placed before xlock.
 * otherwise caddr_t is not defined correctly
 */

#include <X11/Intrinsic.h>

#ifdef STANDALONE
# define PROGCLASS						"Planet"
# define HACK_INIT						init_planet
# define HACK_DRAW						draw_planet
# define planet_opts					xlockmore_opts
#define DEFAULTS	"*delay:			15000   \n"	\
                    "*rotate:           True    \n" \
                    "*roll:             True    \n" \
                    "*bounce:           True    \n" \
					"*wireframe:		False	\n"	\
					"*light:			True	\n"	\
					"*texture:			True	\n" \
					"*stipple:			False	\n" \
					"*image:			BUILTIN	\n" \
					"*imageForeground:	Green	\n" \
					"*imageBackground:	Blue	\n"

# include "xlockmore.h"				/* from the xscreensaver distribution */
#else  /* !STANDALONE */
# include "xlock.h"					/* from the xlockmore distribution */
#endif /* !STANDALONE */

#ifdef USE_GL /* 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 HAVE_XMU
# ifndef VMS
#  include <X11/Xmu/Drawing.h>
#else  /* VMS */
#  include <Xmu/Drawing.h>
# endif /* VMS */
#endif


#include <GL/glu.h>

#define DEF_ROTATE  "True"
#define DEF_ROLL    "True"
#define DEF_BOUNCE  "True"
#define DEF_TEXTURE "True"
#define DEF_LIGHT   "True"
#define DEF_STIPPLE "False"
#define DEF_IMAGE   "BUILTIN"

#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_light;
static int do_stipple;
static char *which_image;
static XrmOptionDescRec opts[] = {
  {"-rotate",  ".glplanet.rotate",  XrmoptionNoArg, (caddr_t) "true" },
  {"+rotate",  ".glplanet.rotate",  XrmoptionNoArg, (caddr_t) "false" },
  {"-roll",    ".glplanet.roll",    XrmoptionNoArg, (caddr_t) "true" },
  {"+roll",    ".glplanet.roll",    XrmoptionNoArg, (caddr_t) "false" },
  {"-bounce",  ".glplanet.bounce",  XrmoptionNoArg, (caddr_t) "true" },
  {"+bounce",  ".glplanet.bounce",  XrmoptionNoArg, (caddr_t) "false" },
  {"-texture", ".glplanet.texture", XrmoptionNoArg, (caddr_t) "true" },
  {"+texture", ".glplanet.texture", XrmoptionNoArg, (caddr_t) "false" },
  {"-light",   ".glplanet.light",   XrmoptionNoArg, (caddr_t) "true" },
  {"+light",   ".glplanet.light",   XrmoptionNoArg, (caddr_t) "false" },
  {"-stipple", ".glplanet.stipple", XrmoptionNoArg, (caddr_t) "true" },
  {"+stipple", ".glplanet.stipple", XrmoptionNoArg, (caddr_t) "false" },
  {"-image",   ".glplanet.image",  XrmoptionSepArg, (caddr_t) 0 },
};

static argtype vars[] = {
  {(caddr_t *) &do_rotate,   "rotate",  "Rotate",  DEF_ROTATE,  t_Bool},
  {(caddr_t *) &do_roll,     "roll",    "Roll",    DEF_ROLL,    t_Bool},
  {(caddr_t *) &do_bounce,   "bounce",  "Bounce",  DEF_BOUNCE,  t_Bool},
  {(caddr_t *) &do_texture,  "texture", "Texture", DEF_TEXTURE, t_Bool},
  {(caddr_t *) &do_light,    "light",   "Light",   DEF_LIGHT,   t_Bool},
  {(caddr_t *) &do_stipple,  "stipple", "Stipple", DEF_STIPPLE, t_Bool},
  {(caddr_t *) &which_image, "image",   "Image",   DEF_IMAGE,   t_String},
};

ModeSpecOpt planet_opts = {countof(opts), opts, countof(vars), vars, NULL};

#ifdef USE_MODULES
ModStruct   planet_description =
{"planet", "init_planet", "draw_planet", "release_planet",
 "draw_planet", "init_planet", NULL, &planet_opts,
 1000, 1, 2, 1, 4, 1.0, "",
 "Animates texture mapped sphere (planet)", 0, NULL};
#endif

#include "../images/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 SLICES 25
#define STACKS 25
#define NUM_PLATES (STACKS * (SLICES+1))

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



/*-
 * structure for holding the data for an individual plate.
 * RotationRate, Angle, Vector, Translation and Color
 * are not currently used, but may be used in the future
 */
typedef struct {
  GLfloat RotationRate;
  GLfloat Angle[4];
  GLfloat Vector[3];
  GLfloat Translation[3];
  GLfloat Color[3];
  GLuint platelist;
} plate;

/* structure for holding the planet data */
typedef struct {
  plate plates[NUM_PLATES];
  GLXContext *glx_context;
  Window window;

  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;

} planetstruct;


static planetstruct *planets = NULL;


/* Set up and enable texturing on our object */
static void
setup_xbm_texture (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);
	  }

  glEnable(GL_TEXTURE_2D);
  glTexImage2D(GL_TEXTURE_2D, 0, 3, width, height, 0,
			   GL_RGB, GL_UNSIGNED_BYTE, data);

  /* 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_NEAREST);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
}


static void
setup_file_texture (ModeInfo *mi, char *filename)
{
  Display *dpy = mi->dpy;
  Visual *visual = mi->xgwa.visual;
  Colormap cmap = mi->xgwa.colormap;

#ifdef HAVE_XPM
  {
	char **xpm_data = 0;
	int result = XpmReadFileToData (filename, &xpm_data);
	switch (result) {
	case XpmSuccess:
	  {
		XImage *image = xpm_to_ximage (dpy, visual, cmap, xpm_data);

		glEnable(GL_TEXTURE_2D);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
					 image->width, image->height, 0,
					 GL_RGBA, GL_UNSIGNED_BYTE, image->data);

		/* 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_NEAREST);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		return;
	  }
	  break;

	case XpmOpenFailed:
	  fprintf (stderr, "%s: file %s doesn't exist.\n", progname, filename);
	  exit (-1);
	  break;

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

	case XpmNoMemory:
	  fprintf (stderr, "%s: XPM: out of memory\n", progname);
	  exit (-1);
	  break;

	default:
	  fprintf (stderr, "%s: XPM: unknown error code %d\n", progname, result);
	  exit (-1);
	  break;
	}
  }
#endif /* HAVE_XPM */

#ifdef HAVE_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
		fprintf (stderr, "%s: not an XPM file: %s\n", progname, filename);
# endif
		fprintf (stderr, "%s: not an XBM file: %s\n", progname, filename);
		exit (1);
	  }

	setup_xbm_texture (data, width, height, &gp->fg, &gp->bg);
  }
#else  /* !XMU */

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


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


/* Set up and enable lighting */
static void
setup_light(void)
{

  glEnable(GL_DEPTH_TEST);
  glEnable(GL_AUTO_NORMAL);
  glEnable(GL_NORMALIZE);
  glShadeModel(GL_SMOOTH);

  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  glEnable(GL_LINE_SMOOTH);
  glEnable(GL_BLEND);

  glEnable(GL_LIGHTING);
  glEnable(GL_LIGHT0);

  glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
  glEnable(GL_COLOR_MATERIAL);

}


/* a stipple pattern */
static GLubyte halftone[] =
{
  0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
  0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
  0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
  0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
  0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
  0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
  0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
  0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
  0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
  0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
  0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
  0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
  0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
  0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
  0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
  0xAA, 0xAA, 0xAA, 0xAA, 0x55, 0x55, 0x55, 0x55,
};

/* Set up and enable stippling */
static void
setup_stipple(void)
{
  glEnable(GL_POLYGON_STIPPLE);
  glPolygonStipple(halftone);
}


/* 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); 
}


/* Function for determining points on the surface of the sphere */
void ParametricSphere(float theta, float rho, float *vector)
{
    vector[0] = -sin(theta) * sin(rho);
    vector[1] = cos(theta) * sin(rho);
    vector[2] = cos(rho);
	return;
}


/* Initialization function for screen saver */
static void
pinit(ModeInfo * mi)
{
  Bool wire = MI_IS_WIREFRAME(mi);
  planetstruct *gp = &planets[MI_SCREEN(mi)];
  int i, j, list, dllist;
  int stacks=STACKS, slices=SLICES;
  float radius=RADIUS;

  float drho, dtheta;
  float rho, theta;
  float vector[3];
  float ds, dt, t, s;;

  if (wire)
	do_texture = False;

  /* turn on various options we like */
  if (do_texture)
	setup_texture(mi);
  if (do_light)
	setup_light();
  if (do_stipple)
	setup_stipple();

  setup_face();

  dllist=glGenLists(NUM_PLATES);

  drho = M_PI / stacks;
  dtheta = 2.0 * M_PI / slices;
  ds = 1.0 / slices;
  dt = 1.0 / stacks;
  t = 0.0 ;
  

  /*-
   * Generate a huge sphere with quadrilaterals.
   * Each quad is stored in its own display list; this is so we can
   * move the quads around later (not yet done).
   * Quad vertices are determined using a parametric sphere function.
   * For fun, you could generate practically any parameteric surface and
   * map an image onto it. 
   */

  list = 0;
  for(i=0; i<stacks; i++) {
	rho = i * drho;
	s = 0.0;
	for(j=0; j<slices+1; j++) {
	  theta = j * dtheta;

	  gp->plates[i].Translation[0] = 0.;
	  gp->plates[i].Translation[1] = 0.;
	  gp->plates[i].Translation[2] = 0.;

	  gp->plates[i].RotationRate = 0.;
	  gp->plates[i].Angle[0] = 0.;
	  gp->plates[i].Angle[1] = 0.;
	  gp->plates[i].Angle[2] = 0.;
	  gp->plates[i].Angle[3] = 0.;

	  gp->plates[i].Color[0] = 1.;
	  gp->plates[i].Color[1] = 1.;
	  gp->plates[i].Color[2] = 1.;

	  gp->plates[list].platelist = dllist+list;
	  glNewList(gp->plates[list].platelist, GL_COMPILE);
	  glBegin( wire ? GL_LINE_LOOP : GL_QUADS );

	  glColor3f(gp->plates[i].Color[0], gp->plates[i].Color[1], gp->plates[i].Color[2]);

	  glTexCoord2f(s,t);
	  ParametricSphere(theta, rho, vector);
	  glNormal3fv(vector);
	  glVertex3f( vector[0]*radius, vector[1]*radius, vector[2]*radius );

	  glTexCoord2f(s,t+dt);
	  ParametricSphere(theta, rho+drho, vector);
	  glNormal3fv(vector);
	  glVertex3f( vector[0]*radius, vector[1]*radius, vector[2]*radius );

	  glTexCoord2f(s+ds,t+dt);
	  ParametricSphere(theta + dtheta, rho+drho, vector);
	  glNormal3fv(vector);
	  glVertex3f( vector[0]*radius, vector[1]*radius, vector[2]*radius );

	  glTexCoord2f(s+ds, t);
	  ParametricSphere(theta + dtheta, rho, vector);
	  glNormal3fv(vector);
	  glVertex3f( vector[0]*radius, vector[1]*radius, vector[2]*radius );

	  glEnd();
	  s = s + ds;

	  glEndList();

	  list++;
	}
	t = t + dt;
  }


 }

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

  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  for (i=0; i < NUM_PLATES; i++) {
	glPushMatrix();
	/* currently, the angle and translation are 0, but later this can
	 * help us move the surface around */
#if 0
	glRotatef(gp->plates[i].Angle[0],
			  gp->plates[i].Angle[1],
			  gp->plates[i].Angle[2],
			  gp->plates[i].Angle[3]); 
	glTranslatef(gp->plates[i].Translation[0],
				 gp->plates[i].Translation[1],
				 gp->plates[i].Translation[2]);
#endif
	glCallList(gp->plates[i].platelist);
	glPopMatrix();
#if 0
	gp->plates[i].Angle[0] += gp->plates[i].RotationRate;
#endif
  }
}


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

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

  gp->box_width =  15.0;
  gp->box_height = 15.0;
  gp->box_depth =  60.0;

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

  gp->dtx = (frand(0.4) + frand(0.3)) * RANDSIGN();
  gp->dty = (frand(0.4) + frand(0.3)) * RANDSIGN();
  gp->dtz = (frand(5.0) + frand(5.0));  /* the sun sets in the west */

  gp->dx = (frand(0.2) + frand(0.2)) * RANDSIGN();
  gp->dy = (frand(0.2) + frand(0.2)) * RANDSIGN();
  gp->dz = (frand(0.2) + frand(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)
	{
	  /* Move in the direction we had been moving in. */
	  gp->xpos += gp->dx;
	  gp->ypos += gp->dy;
	  gp->zpos += gp->dz;

	  /* Bounce. */
	  if (gp->xpos > gp->box_depth)
		gp->xpos = gp->box_depth, gp->dx = -gp->dx;
	  else if (gp->xpos < 0)
		gp->xpos = 0, gp->dx = -gp->dx;

	  if (gp->ypos > gp->box_width/2)
		gp->ypos = gp->box_width/2, gp->dy = -gp->dy;
	  else if (gp->ypos < -gp->box_width/2)
		gp->ypos = -gp->box_width/2, gp->dy = -gp->dy;

	  if (gp->zpos > gp->box_height/2)
		gp->zpos = gp->box_height/2, gp->dz = -gp->dz;
	  else if (gp->zpos < -gp->box_height/2)
		gp->zpos = -gp->box_height/2, gp->dz = -gp->dz;
	}
}


/* Standard reshape function */
#define DIST 40
static void
reshape(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);
  /* some messiness for orienting the earth normally */
  glRotatef(90,0,0,1);
  glRotatef(90,0,1,0);
  glLightfv(GL_LIGHT0, GL_POSITION, light);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

}


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

  planetstruct *gp;

  if (planets == NULL) {
	if ((planets = (planetstruct *) calloc(MI_NUM_SCREENS(mi),
										  sizeof (planetstruct))) == NULL)
	  return;
  }
  gp = &planets[screen];

  pick_velocity (mi);

  {
	char *f = get_string_resource("imageForeground", "Foreground");
	char *b = get_string_resource("imageBackground", "Background");
	char *s;
	if (!f) f = strdup("white");
	if (!b) b = 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->dpy, mi->xgwa.colormap, f, &gp->fg))
      {
		fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
		exit(1);
      }
    if (!XParseColor(mi->dpy, mi->xgwa.colormap, b, &gp->bg))
      {
		fprintf(stderr, "%s: unparsable color: \"%s\"\n", progname, f);
		exit(1);
      }

	free (f);
	free (b);
  }


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

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

  if (!gp->glx_context)
	return;

  glDrawBuffer(GL_BACK);

  glXMakeCurrent(display, window, *(gp->glx_context));

  glPushMatrix();
  {
	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(mi);
  }
  glPopMatrix();

  glFinish();
  glXSwapBuffers(display, window);

  rotate_and_move (mi);
}

void
release_planet(ModeInfo * mi)
{
  int i;
  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. */
		glXMakeCurrent(MI_DISPLAY(mi), gp->window, *(gp->glx_context));

		for (i=0; i < NUM_PLATES; i++) {
		  if (glIsList(gp->plates[i].platelist))
			glDeleteLists(gp->plates[i].platelist, 1);
		}
	  }
	}
	(void) free((void *) planets);
	planets = NULL;
  }
  FreeAllGL(mi);
}


#endif

