/*
 * @(#)MballGL.c
 *
 * Copyright 2023 - 2026  David A. Bagley, bagleyd AT verizon.net
 *
 * Based sphere, Copyright (c) 2002, 2008 Paul Bourke <pbourke@swin.edu.au>
 * Utility function to create a unit sphere in GL.
 * All rights reserved.
 *
 * 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, and that the name of the author not be
 * used in advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.
 *
 * This program is distributed in the hope that it will be "useful",
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 * GL Code for displaying sphere orignally had these updates:
 *  8-Oct-98: dek          Released initial version of "glplanet"
 * 21-Mar-01: jwz AT jwz.org  Broke sphere routine out into its own file.
 * 28-Feb-02: jwz AT jwz.org  New implementation from Paul Bourke:
 *                         http://astronomy.swin.edu.au/~pbourke/opengl/sphere/
 */

#ifdef HAVE_OPENGL

/* Methods file for MballGL */

#include "MballP.h"
#include "MballGLP.h"

#define glTranslate(x,y,z) glTranslatef((float) x, (float) y, (float) z)

static float front_shininess[] =
{60.0};
static float front_specular[] =
{(float) 0.7, (float) 0.7, (float) 0.7, 1.0};
static float ambient[] =
{0.0, 0.0, 0.0, 1.0};
static float diffuse[] =
{1.0, 1.0, 1.0, 1.0};
static float position0[] =
{1.0, 0.0, 0.0, 0.0};
static float position1[] =
{0.0, 0.0, 1.0, 0.0};
static float position2[] =
{-1.0, 0.0, 0.0, 0.0};
static float position3[] =
{0.0, 0.0, -1.0, 0.0};
static float position4[] =
{0.0, 1.0, 0.0, 0.0};
static float position5[] =
{0.0, -1.0, 0.0, 0.0};
static float lmodel_ambient[] =
{0.5, 0.5, 0.5, 1.0};
static float lmodel_twoside[] =
{GL_TRUE};

static float wedge_material[MAX_WEDGES + 2][4] =
{{1.0, 1.0, 1.0, 1.0},
{1.0, 1.0, 1.0, 1.0},
{1.0, 1.0, 1.0, 1.0},
{1.0, 1.0, 1.0, 1.0},
{1.0, 1.0, 1.0, 1.0},
{1.0, 1.0, 1.0, 1.0},
{1.0, 1.0, 1.0, 1.0},
{1.0, 1.0, 1.0, 1.0},
{1.0, 1.0, 1.0, 1.0},
{1.0, 1.0, 1.0, 1.0},
{1.0, 1.0, 1.0, 1.0},
{1.0, 1.0, 1.0, 1.0},
{0.0, 0.0, 0.0, 1.0},
{0.0, 0.0, 0.0, 1.0}};

static Boolean madeCurrent = False;

#ifdef WINVER
static HGLRC hRC = NULL;
#else
static GLXContext *glXContext = (GLXContext *) NULL;
static Boolean setValuesPuzzleGL(Widget current, Widget request, Widget renew);
static void resizePuzzleGL(MballGLWidget w);
static void initializePuzzleGL(Widget request, Widget renew);
static void exposePuzzleGL(Widget renew, XEvent *event, Region region);
static void movePuzzleGLTl(MballGLWidget w,
	XEvent *event, char **args, int nArgs);
static void movePuzzleGLTop(MballGLWidget w,
	XEvent *event, char **args, int nArgs);
static void movePuzzleGLTr(MballGLWidget w,
	XEvent *event, char **args, int nArgs);
static void movePuzzleGLLeft(MballGLWidget w,
	XEvent *event, char **args, int nArgs);
static void movePuzzleGLRight(MballGLWidget w,
	XEvent *event, char **args, int nArgs);
static void movePuzzleGLBl(MballGLWidget w,
	XEvent *event, char **args, int nArgs);
static void movePuzzleGLBottom(MballGLWidget w,
	XEvent *event, char **args, int nArgs);
static void movePuzzleGLBr(MballGLWidget w,
	XEvent *event, char **args, int nArgs);

static char translationsGL[] =
"<KeyPress>q: Quit()\n\
 Ctrl<KeyPress>C: Quit()\n\
 <KeyPress>osfCancel: Hide()\n\
 <KeyPress>Escape: Hide()\n\
 <KeyPress>osfEscape: Hide()\n\
 Ctrl<KeyPress>[: Hide()\n\
 <KeyPress>0x1B: Hide()\n\
 <KeyPress>0x2E: Speed()\n\
 <KeyPress>0x3E: Speed()\n\
 <KeyPress>0x3C: Slow()\n\
 <KeyPress>0x2C: Slow()\n\
 Shift<KeyPress>2: Sound()\n\
 <KeyPress>F11: MoveCcw()\n\
 <KeyPress>KP_Divide: MoveCcw()\n\
 <KeyPress>R5: MoveCcw()\n\
 <KeyPress>Home: MoveTl()\n\
 <KeyPress>KP_7: MoveTl()\n\
 <KeyPress>R7: MoveTl()\n\
 <KeyPress>Up: MoveTop()\n\
 <KeyPress>osfUp: MoveTop()\n\
 <KeyPress>KP_Up: MoveTop()\n\
 <KeyPress>KP_8: MoveTop()\n\
 <KeyPress>R8: MoveTop()\n\
 <KeyPress>Prior: MoveTr()\n\
 <KeyPress>KP_9: MoveTr()\n\
 <KeyPress>R9: MoveTr()\n\
 <KeyPress>Left: MoveLeft()\n\
 <KeyPress>osfLeft: MoveLeft()\n\
 <KeyPress>KP_Left: MoveLeft()\n\
 <KeyPress>KP_4: MoveLeft()\n\
 <KeyPress>R10: MoveLeft()\n\
 <KeyPress>F12: MoveCw()\n\
 <KeyPress>Begin: MoveCw()\n\
 <KeyPress>KP_5: MoveCw()\n\
 <KeyPress>R11: MoveCw()\n\
 <KeyPress>Right: MoveRight()\n\
 <KeyPress>osfRight: MoveRight()\n\
 <KeyPress>KP_Right: MoveRight()\n\
 <KeyPress>KP_6: MoveRight()\n\
 <KeyPress>R12: MoveRight()\n\
 <KeyPress>End: MoveBl()\n\
 <KeyPress>KP_1: MoveBl()\n\
 <KeyPress>R13: MoveBl()\n\
 <KeyPress>Down: MoveBottom()\n\
 <KeyPress>osfDown: MoveBottom()\n\
 <KeyPress>KP_Down: MoveBottom()\n\
 <KeyPress>KP_2: MoveBottom()\n\
 <KeyPress>R14: MoveBottom()\n\
 <KeyPress>Next: MoveBr()\n\
 <KeyPress>KP_3: MoveBr()\n\
 <KeyPress>R15: MoveBr()\n\
 <Btn1Down>: Select()\n\
 <Btn1Up>: Release()\n\
 <Btn2Down>: PracticeMaybe()\n\
 <Btn2Down>(2+): Practice2()\n\
 <Btn3Down>: RandomizeMaybe()\n\
 <Btn3Down>(2+): Randomize2()\n\
 <Btn4Down>: MoveTop()\n\
 <Btn5Down>: MoveBottom()\n\
 <KeyPress>g: Get()\n\
 <KeyPress>w: Write()\n\
 <KeyPress>u: Undo()\n\
 <KeyPress>r: Redo()\n\
 <KeyPress>c: Clear()\n\
 <KeyPress>z: Randomize()\n\
 <KeyPress>s: Solve()\n\
 <KeyPress>p: Practice()\n\
 <KeyPress>i: Increment()\n\
 <KeyPress>d: Decrement()\n\
 <KeyPress>o: Orientize()\n\
 <KeyPress>2: Wedge2()\n\
 <KeyPress>4: Wedge4()\n\
 <KeyPress>6: Wedge6()\n\
 <KeyPress>8: Wedge8()\n\
 <KeyPress>0: Wedge10()\n\
 <KeyPress>=: Wedge12()\n\
 <KeyPress>.: Wedge12()\n\
 <KeyPress>v: View()\n\
 <KeyPress>t: Perspective()\n\
 <EnterWindow>: Enter()\n\
 <LeaveWindow>: Leave()";

static XtActionsRec actionsListGL[] =
{
	{(char *) "Quit", (XtActionProc) quitPuzzle},
	{(char *) "Hide", (XtActionProc) hidePuzzle},
	{(char *) "MoveCcw", (XtActionProc) movePuzzleCcw},
	{(char *) "MoveTl", (XtActionProc) movePuzzleGLTl},
	{(char *) "MoveTop", (XtActionProc) movePuzzleGLTop},
	{(char *) "MoveTr", (XtActionProc) movePuzzleGLTr},
	{(char *) "MoveLeft", (XtActionProc) movePuzzleGLLeft},
	{(char *) "MoveCw", (XtActionProc) movePuzzleCw},
	{(char *) "MoveBr", (XtActionProc) movePuzzleGLBr},
	{(char *) "MoveRight", (XtActionProc) movePuzzleGLRight},
	{(char *) "MoveBl", (XtActionProc) movePuzzleGLBl},
	{(char *) "MoveBottom", (XtActionProc) movePuzzleGLBottom},
	{(char *) "MoveBr", (XtActionProc) movePuzzleGLBr},
	{(char *) "Select", (XtActionProc) selectPuzzle},
	{(char *) "Release", (XtActionProc) releasePuzzle},
	{(char *) "PracticeMaybe", (XtActionProc) practicePuzzleWithQuery},
	{(char *) "Practice2", (XtActionProc) practicePuzzleWithDoubleClick},
	{(char *) "RandomizeMaybe", (XtActionProc) randomizePuzzleWithQuery},
	{(char *) "Randomize2", (XtActionProc) randomizePuzzleWithDoubleClick},
	{(char *) "Get", (XtActionProc) getPuzzle},
	{(char *) "Write", (XtActionProc) writePuzzle},
	{(char *) "Undo", (XtActionProc) undoPuzzle},
	{(char *) "Redo", (XtActionProc) redoPuzzle},
	{(char *) "Clear", (XtActionProc) clearPuzzle},
	{(char *) "Randomize", (XtActionProc) randomizePuzzle},
	{(char *) "Solve", (XtActionProc) solvePuzzle},
	{(char *) "Practice", (XtActionProc) practicePuzzle},
	{(char *) "Increment", (XtActionProc) incrementPuzzle},
	{(char *) "Decrement", (XtActionProc) decrementPuzzle},
	{(char *) "Orientize", (XtActionProc) orientizePuzzle},
	{(char *) "Wedge2", (XtActionProc) wedge2ModePuzzle},
	{(char *) "Wedge4", (XtActionProc) wedge4ModePuzzle},
	{(char *) "Wedge6", (XtActionProc) wedge6ModePuzzle},
	{(char *) "Wedge8", (XtActionProc) wedge8ModePuzzle},
	{(char *) "Wedge10", (XtActionProc) wedge10ModePuzzle},
	{(char *) "Wedge12", (XtActionProc) wedge12ModePuzzle},
	{(char *) "View", (XtActionProc) viewPuzzle},
	{(char *) "Speed", (XtActionProc) speedUpPuzzle},
	{(char *) "Slow", (XtActionProc) slowDownPuzzle},
	{(char *) "Sound", (XtActionProc) toggleSoundPuzzle},
	{(char *) "Perspective", (XtActionProc) perspectivePuzzle},
	{(char *) "Enter", (XtActionProc) enterPuzzle},
	{(char *) "Leave", (XtActionProc) leavePuzzle}
};

static XtResource resourcesGL[] =
{
	{XtNwidth, XtCWidth, XtRDimension, sizeof (Dimension),
	 XtOffset(MballWidget, core.width),
	 XtRString, (caddr_t) "300"},
	{XtNheight, XtCHeight, XtRDimension, sizeof (Dimension),
	 XtOffset(MballWidget, core.height),
	 XtRString, (caddr_t) "400"},
	{XtNmono, XtCMono, XtRBoolean, sizeof (Boolean),
	 XtOffset(MballWidget, mball.mono),
	 XtRString, (caddr_t) "FALSE"},
	{XtNreverseVideo, XtCReverseVideo, XtRBoolean, sizeof (Boolean),
	 XtOffset(MballWidget, mball.reverse),
	 XtRString, (caddr_t) "FALSE"},
	{XtNforeground, XtCForeground, XtRPixel, sizeof (Pixel),
	 XtOffset(MballWidget, mball.foreground),
	 XtRString, (caddr_t) XtDefaultForeground},
	{XtNbackground, XtCBackground, XtRPixel, sizeof (Pixel),
	 XtOffset(MballWidget, mball.background),
	 XtRString, (caddr_t) "#AEB2C3" /*XtDefaultBackground*/},
	{XtNframeColor, XtCColor, XtRPixel, sizeof (Pixel),
	 XtOffset(MballWidget, mball.frameColor),
	 XtRString, (caddr_t) "cyan" /*XtDefaultForeground*/},
	{XtNwedgeColor0, XtCLabel, XtRString, sizeof (String),
	 XtOffset(MballWidget, mball.wedgeName[0]),
	 XtRString, (caddr_t) "yellow"},
	{XtNwedgeColor1, XtCLabel, XtRString, sizeof (String),
	 XtOffset(MballWidget, mball.wedgeName[1]),
	 XtRString, (caddr_t) "green"},
	{XtNwedgeColor2, XtCLabel, XtRString, sizeof (String),
	 XtOffset(MballWidget, mball.wedgeName[2]),
	 XtRString, (caddr_t) "SeaGreen"},
	{XtNwedgeColor3, XtCLabel, XtRString, sizeof (String),
	 XtOffset(MballWidget, mball.wedgeName[3]),
	 XtRString, (caddr_t) "blue"},
	{XtNwedgeColor4, XtCLabel, XtRString, sizeof (String),
	 XtOffset(MballWidget, mball.wedgeName[4]),
	 XtRString, (caddr_t) "cyan"},
	{XtNwedgeColor5, XtCLabel, XtRString, sizeof (String),
	 XtOffset(MballWidget, mball.wedgeName[5]),
	 XtRString, (caddr_t) "magenta"},
	{XtNwedgeColor6, XtCLabel, XtRString, sizeof (String),
	 XtOffset(MballWidget, mball.wedgeName[6]),
	 XtRString, (caddr_t) "red"},
	{XtNwedgeColor7, XtCLabel, XtRString, sizeof (String),
	 XtOffset(MballWidget, mball.wedgeName[7]),
	 XtRString, (caddr_t) "orange"},
	{XtNwedgeColor8, XtCLabel, XtRString, sizeof (String),
	 XtOffset(MballWidget, mball.wedgeName[8]),
	 XtRString, (caddr_t) "pink"},
	{XtNwedgeColor9, XtCLabel, XtRString, sizeof (String),
	 XtOffset(MballWidget, mball.wedgeName[9]),
	 XtRString, (caddr_t) "tan"},
	{XtNwedgeColor10, XtCLabel, XtRString, sizeof (String),
	 XtOffset(MballWidget, mball.wedgeName[10]),
	 XtRString, (caddr_t) "LightSteelBlue"},
	{XtNwedgeColor11, XtCLabel, XtRString, sizeof (String),
	 XtOffset(MballWidget, mball.wedgeName[11]),
	 XtRString, (caddr_t) "IndianRed"},
	{XtNpieceBorder, XtCColor, XtRPixel, sizeof (Pixel),
	 XtOffset(MballWidget, mball.borderColor),
	 XtRString, (caddr_t) "gray25" /*XtDefaultForeground*/},
	{XtNdelay, XtCDelay, XtRInt, sizeof (int),
	 XtOffset(MballWidget, mball.delay),
	 XtRString, (caddr_t) "10"},
	{XtNsound, XtCSound, XtRBoolean, sizeof (Boolean),
	 XtOffset(MballWidget, mball.sound),
	 XtRString, (caddr_t) "FALSE"},
	{XtNmoveSound, XtCMoveSound, XtRString, sizeof (String),
	 XtOffset(MballWidget, mball.moveSound),
	 XtRString, (caddr_t) MOVESOUND},
	{XtNfont, XtCFont, XtRString, sizeof (String),
	 XtOffset(MballWidget, mball.font),
	 XtRString, (caddr_t) "9x15bold"},
	{XtNview, XtCView, XtRInt, sizeof (int),
	 XtOffset(MballWidget, mball.view),
	 XtRString, (caddr_t) "1"},
	{XtNwedges, XtCWedges, XtRInt, sizeof (int),
	 XtOffset(MballWidget, mball.wedges),
	 XtRString, (caddr_t) "8"}, /*DEFAULT_WEDGES */
	{XtNbands, XtCBands, XtRInt, sizeof (int),
	 XtOffset(MballWidget, mball.bands),
	 XtRString, (caddr_t) "4"}, /*DEFAULT_BANDS */
	{XtNorient, XtCOrient, XtRBoolean, sizeof (Boolean),
	 XtOffset(MballWidget, mball.orient),
	 XtRString, (caddr_t) "FALSE"},	/* DEFAULT_ORIENT */
	{XtNpractice, XtCPractice, XtRBoolean, sizeof (Boolean),
	 XtOffset(MballWidget, mball.practice),
	 XtRString, (caddr_t) "TRUE"}, /* DEFAULT_PRACTICE */
	{XtNbase, XtCBase, XtRInt, sizeof (int),
	 XtOffset(MballWidget, mball.base),
	 XtRString, (caddr_t) "16"}, /* DEFAULT_BASE */
	{XtNperspective, XtCPerspective, XtRBoolean, sizeof (Boolean),
	 XtOffset(MballWidget, mball.perspective),
	 XtRString, (caddr_t) "FALSE"}, /* DEFAULT_PERSPECTIVE */
	{XtNuserName, XtCUserName, XtRString, sizeof (String),
	 XtOffset(MballWidget, mball.userName),
	 XtRString, (caddr_t) ""},
	{XtNscoreFile, XtCScoreFile, XtRString, sizeof (String),
	 XtOffset(MballWidget, mball.scoreFile),
	 XtRString, (caddr_t) ""},
	{XtNscoreOnly, XtCBoolean, XtRBoolean, sizeof (Boolean),
	 XtOffset(MballWidget, mball.scoreOnly),
	 XtRString, (caddr_t) "FALSE"},
	{XtNversionOnly, XtCBoolean, XtRBoolean, sizeof (Boolean),
	 XtOffset(MballWidget, mball.versionOnly),
	 XtRString, (caddr_t) "FALSE"},
	{XtNmenu, XtCMenu, XtRInt, sizeof (int),
	 XtOffset(MballWidget, mball.menu),
	 XtRString, (caddr_t) "999"}, /* ACTION_IGNORE */
	{XtNstart, XtCBoolean, XtRBoolean, sizeof (Boolean),
	 XtOffset(MballWidget, mball.started),
	 XtRString, (caddr_t) "FALSE"},
	{XtNcheat, XtCBoolean, XtRBoolean, sizeof (Boolean),
	 XtOffset(MballWidget, mball.cheat),
	 XtRString, (caddr_t) "FALSE"},
	{XtNwedge, XtCWedge, XtRInt, sizeof (int),
	 XtOffset(MballWidget, mball.currentWedge),
	 XtRString, (caddr_t) "-1"},
	{XtNband, XtCBand, XtRInt, sizeof (int),
	 XtOffset(MballWidget, mball.currentBand),
	 XtRString, (caddr_t) "-1"},
	{XtNdirection, XtCDirection, XtRInt, sizeof (int),
	 XtOffset(MballWidget, mball.currentDirection),
	 XtRString, (caddr_t) "-1"},
	{XtNcontrol, XtCControl, XtRInt, sizeof (int),
	 XtOffset(MballWidget, mball.currentControl),
	 XtRString, (caddr_t) "0"},
	{XtNfast, XtCFast, XtRInt, sizeof (int),
	 XtOffset(MballWidget, mball.currentFast),
	 XtRString, (caddr_t) "1"},
	{XtNpixmapSize, XtCPixmapSize, XtRInt, sizeof (int),
	 XtOffset(MballWidget, mball.pixmapSize),
	 XtRString, (caddr_t) "64"},
	{XtNselectCallback, XtCCallback, XtRCallback, sizeof (caddr_t),
	 XtOffset(MballWidget, mball.select),
	 XtRCallback, (caddr_t) NULL}
};

MballGLClassRec mballGLClassRec =
{
	{
		(WidgetClass) & mballClassRec,	/* superclass */
		(char *) "MballGL",	/* class name */
		sizeof (MballGLRec),	/* widget size */
		NULL,		/* class initialize */
		NULL,		/* class part initialize */
		FALSE,		/* class inited */
		(XtInitProc) initializePuzzleGL,	/* initialize */
		NULL,		/* initialize hook */
		XtInheritRealize,	/* realize */
		actionsListGL,	/* actions */
		XtNumber(actionsListGL),	/* num actions */
		resourcesGL,	/* resources */
		XtNumber(resourcesGL),	/* num resources */
		NULLQUARK,	/* xrm class */
		TRUE,		/* compress motion */
		TRUE,		/* compress exposure */
		TRUE,		/* compress enterleave */
		TRUE,		/* visible interest */
		NULL,		/* destroy */
		(XtWidgetProc) resizePuzzleGL,	/* resize */
		(XtExposeProc) exposePuzzleGL,	/* expose */
		(XtSetValuesFunc) setValuesPuzzleGL,	/* set values */
		NULL,		/* set values hook */
		XtInheritSetValuesAlmost,	/* set values almost */
		NULL,		/* get values hook */
		XtInheritAcceptFocus,	/* accept focus */
		XtVersion,	/* version */
		NULL,		/* callback private */
		translationsGL,	/* tm table */
		NULL,		/* query geometry */
		NULL,		/* display accelerator */
		NULL		/* extension */
	},
	{
		0		/* ignore */
	},
	{
		0		/* ignore */
	}
};

WidgetClass mballGLWidgetClass = (WidgetClass) & mballGLClassRec;
#endif

static int
sphereSlices(MballGLWidget w)
{
	return 360 / w->mball.wedges;
}

static int
sphereStacks(MballGLWidget w)
{
	if (w->mball.bands < 360)
		return 360 / w->mball.bands;
	else
		return w->mball.bands;
}

static void
pickColor(MballGLWidget w, int c, int mono, float *material)
{
#ifdef WINVER
	struct tagColor {
		int red, green, blue;
	} color;
#define MAX_INTENSITY 0xFF
#else
	XColor color;
#define MAX_INTENSITY 0xFFFF
#endif

	switch (c) {
	case TOP_WEDGE:
	case TTR_WEDGE:
	case TR_WEDGE:
	case RIGHT_WEDGE:
	case BR_WEDGE:
	case BBR_WEDGE:
	case BOTTOM_WEDGE:
	case BBL_WEDGE:
	case BL_WEDGE:
	case LEFT_WEDGE:
	case TL_WEDGE:
	case TTL_WEDGE:
#ifdef WINVER
		color.red = GetRValue(w->mball.wedgeGC[c]);
		color.green = GetGValue(w->mball.wedgeGC[c]);
		color.blue = GetBValue(w->mball.wedgeGC[c]);
#else
		color.pixel = w->mball.wedgeColor[c];
		XQueryColor(XtDisplay(w), DefaultColormapOfScreen(XtScreen(w)),
			&color);
#endif
		break;
	case BORDER_WEDGE:
#ifdef WINVER
		color.red = GetRValue(w->mball.borderGC);
		color.green = GetGValue(w->mball.borderGC);
		color.blue = GetBValue(w->mball.borderGC);
#else
		color.pixel = w->mball.borderColor;
		XQueryColor(XtDisplay(w), DefaultColormapOfScreen(XtScreen(w)),
			&color);
#endif
		break;
	case NO_WEDGE:
	default:
#ifdef WINVER
		color.red = GetRValue(w->mball.inverseGC);
		color.green = GetGValue(w->mball.inverseGC);
		color.blue = GetBValue(w->mball.inverseGC);
#else
		color.pixel = w->mball.background;
		XQueryColor(XtDisplay(w), DefaultColormapOfScreen(XtScreen(w)),
			&color);
#endif
		break;
	}
#ifdef DEBUG
	(void) printf("%d: i%d %d %d\n", c, color.red, color.green, color.blue);
#endif
	if (mono) {
		/* really GrayScale */
		float intensity = (float) (0.3 * color.red +
			0.59 * color.green + 0.11 * color.blue);
#ifdef DEBUG
		(void) printf("m%g\n", intensity);
#endif
		material[0] = (float) intensity / MAX_INTENSITY;
		material[1] = material[0];
		material[2] = material[0];
	} else {
#if 1	/* Hack: Orange and Pink were getting washed out */
		if (c < BORDER_WEDGE && ((color.red != 0 && color.red != MAX_INTENSITY)
				|| (color.green != 0 && color.green != MAX_INTENSITY)
				|| (color.blue != 0 && color.blue != MAX_INTENSITY))) {

			material[0] = (GLfloat) color.red / (2 * MAX_INTENSITY);
			material[1] = (GLfloat) color.green / (2 * MAX_INTENSITY);
			material[2] = (GLfloat) color.blue / (2 * MAX_INTENSITY);
		} else
#endif
		{
			material[0] = (GLfloat) color.red / MAX_INTENSITY;
			material[1] = (GLfloat) color.green / MAX_INTENSITY;
			material[2] = (GLfloat) color.blue / MAX_INTENSITY;
		}
	}
#ifdef DEBUG
	(void) printf("%d: f%g %g %g\n", c, material[0], material[1], material[2]);
#endif
}

typedef float Matrix[3][3];
typedef float Vector[3];

static void
matrixMatrix(Matrix a, Matrix b, Matrix x)
{
	int i, j;

	for (i = 0; i < 3; i++) {
		for (j = 0; j < 3; j++) {
			x[i][j] = a[i][0] * b[0][j] +
				a[i][1] * b[1][j] +
				a[i][2] * b[2][j];
		}
	}
}

static void
matrixVector(Matrix a, Vector v, Vector x)
{
	int i;

	for (i = 0; i < 3; i++) {
		x[i] = a[i][0] * v[0] +
			a[i][1] * v[1] +
			a[i][2] * v[2];
	}
}

static void
setMatrix(float theta, float x, float y, float z, Matrix a)
{
	float s = (float) sin(theta * M_PI / 180.0);
	float c = (float) cos(theta * M_PI / 180.0);

	if (x != 0.0 && y == 0.0 && z == 0.0) {
		a[0][0] = 1.0, a[0][1] = 0.0; a[0][2] = 0.0;
		a[1][0] = 0.0, a[1][1] = c; a[1][2] = -s;
		a[2][0] = 0.0, a[2][1] = s; a[2][2] = c;
	} else if (y != 0.0 && x == 0.0 && z == 0.0) {
		a[0][0] = c, a[0][1] = 0.0; a[0][2] = s;
		a[1][0] = 0.0, a[1][1] = 1.0; a[1][2] = 0.0;
		a[2][0] = -s, a[2][1] = 0.0; a[2][2] = c;
	} else if (z != 0.0 && x == 0.0 && y == 0.0) {
		a[0][0] = c, a[0][1] = -s; a[0][2] = 0.0;
		a[1][0] = s, a[1][1] = c; a[1][2] = 0.0;
		a[2][0] = 0.0, a[2][1] = 0.0; a[2][2] = 1.0;
	}
}

typedef struct { GLfloat x, y, z; } XYZ;

static int
drawWedge(MballGLWidget w, Boolean wire, int wedge, int band)
{
	int stacks = w->mballGL.stacksPerBand * w->mball.bands;
	int slices = w->mballGL.slicesPerWedge * w->mball.wedges;
	int polys = 0;
	int i, j;
	double theta1, theta2, theta3;
	XYZ e, p;
	XYZ la = {0, 0, 0}, lb = {0, 0, 0};
	XYZ c = {0, 0, 0};	/* center */
	double r = 1.0;		/* radius */
	int stacks2 = stacks * 2;
	int firstStack, firstSlice;
	int lastStack, lastSlice;
	int selectFirstStack, selectFirstSlice;
	int selectLastStack, selectLastSlice;
	int currentBand = -1, currentWedge = -1;

	if (w->mball.currentSector != -1) {
		currentBand = w->mball.currentSector / w->mball.wedges;
		currentWedge = w->mball.currentSector % w->mball.wedges;
		/* draws from bottom up */
		currentBand = w->mball.bands - 1 - currentBand;
	}
	if (r < 0)
		r = -r;
	if (slices < 0)
		slices = -slices;
	if (slices < 4 || stacks < 2 || r <= 0) {
		glBegin(GL_POINTS);
		glVertex3f(c.x, c.y, c.z);
		glEnd();
		return 1;
	}
	glFrontFace(GL_CW);
	firstStack = band * w->mballGL.stacksPerBand;
	lastStack = firstStack + w->mballGL.stacksPerBand - 1;
	selectFirstStack = band * w->mballGL.stacksPerBand +
		w->mballGL.stacksPerBand / 3;
	selectLastStack = firstStack + w->mballGL.stacksPerBand - 1 -
		w->mballGL.stacksPerBand / 3;
	if (wire)
		glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, wedge_material[BORDER_WEDGE]);
	for (j = firstStack; j <= lastStack; j++) {
		theta1 = 2 * j * M_PI / stacks2 - M_PI_2;
		theta2 = 2 * (j + 1) * M_PI / stacks2 - M_PI_2;
		glBegin((wire) ? GL_LINE_LOOP : GL_TRIANGLE_STRIP);
		firstSlice = wedge * w->mballGL.slicesPerWedge;
		lastSlice = firstSlice + w->mballGL.slicesPerWedge;
		selectFirstSlice = wedge * w->mballGL.slicesPerWedge +
			w->mballGL.slicesPerWedge / 3;
		selectLastSlice = firstSlice + w->mballGL.slicesPerWedge -
			w->mballGL.slicesPerWedge / 3;
		for (i = firstSlice; i <= lastSlice; i++) {
#ifdef WEDGE_NO_EDGE
			if (!wire)
#else
#ifdef WEDGE_GAP
			if (i == firstSlice || i == lastSlice || j == firstStack || j == lastStack)
				continue;
#else
			if (i == firstSlice + 1 || i == lastSlice || j == firstStack || j == lastStack)
				glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, wedge_material[BORDER_WEDGE]);
			else
#endif
#endif
				glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, wedge_material[w->mball.mballLoc[wedge][w->mball.bands - 1 - band].wedge]);
			if (band == currentBand && wedge == currentWedge &&
					i >= selectFirstSlice + 1 && i <= selectLastSlice &&
					j >= selectFirstStack && j <= selectLastStack)
				glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, wedge_material[BORDER_WEDGE]);
			theta3 = i * 2 * M_PI / slices;
			if (wire && i != firstSlice) {
				glVertex3f(lb.x, lb.y, lb.z);
				glVertex3f(la.x, la.y, la.z);
			}
			e.x = (float) (cos(theta2) * cos(theta3));
			e.y = (float) sin(theta2);
			e.z = (float) (cos(theta2) * sin(theta3));
			p.x = (float) (c.x + r * e.x);
			p.y = (float) (c.y + r * e.y);
			p.z = (float) (c.z + r * e.z);
			glNormal3f(e.x, e.y, e.z);
			glTexCoord2f((float) (i / (double) slices),
				(float) (2 * (j + 1) / (double) stacks2));
			glVertex3f(p.x, p.y, p.z);
			if (wire)
				la = p;
			e.x = (float) (cos(theta1) * cos(theta3));
			e.y = (float) sin(theta1);
			e.z = (float) (cos(theta1) * sin(theta3));
			p.x = (float) (c.x + r * e.x);
			p.y = (float) (c.y + r * e.y);
			p.z = (float) (c.z + r * e.z);
			glNormal3f(e.x, e.y, e.z);
			glTexCoord2f((float) (i / (double) slices),
				(float) (2 * j / (double) stacks2));
			glVertex3f(p.x, p.y, p.z);
			if (wire)
				lb = p;
			polys++;
		}
		glEnd();
	}
	return polys;
}

static void
drawBand(MballGLWidget w, int band) {
	int wedge;
	for (wedge = 0; wedge < w->mball.wedges; wedge++)
		drawWedge(w, False, wedge, w->mball.bands - 1 - band);
}

static void
drawNotBand(MballGLWidget w, int notBand) {
	int wedge, band;
	for (wedge = 0; wedge < w->mball.wedges; wedge++)
		for (band = 0; band < w->mball.bands; band++)
			if (notBand != band)
				drawWedge(w, False, wedge, w->mball.bands - 1 - band);
}

static void
drawHalf(MballGLWidget w, int offset) {
	int wedge, band;
	for (wedge = 0; wedge < w->mball.wedges / 2; wedge++)
		for (band = 0; band < w->mball.bands; band++)
			drawWedge(w, False, (wedge + offset) % w->mball.wedges,
				 w->mball.bands - 1 - band);
}

static void
drawWedges(MballGLWidget w, int wedge, int band, int direction,
		Boolean use, Boolean all)
{
	int half = w->mball.wedges / 2;
	int whole = w->mball.wedges;

	switch (direction) {
	case TOP:
	case BOTTOM:
		if (all || use) {
			glPushMatrix();
			drawHalf(w, (wedge / half) * half);
			glPopMatrix();
		} else if (!all)
			drawHalf(w, ((wedge / half) * half + half) % whole);
		break;
	case TTR:
	case BBL:
		if (w->mball.wedges % 12 == 0) {
			if (all || use) {
				glPushMatrix();
				drawHalf(w, ((((wedge + 11 * half / 6) % whole) / half) * half + half / 6) % whole);
				glPopMatrix();
			} else if (!all)
				drawHalf(w, ((((wedge + 11 * half / 6) % whole) / half) * half + 7 * half / 6) % whole);
		} else if (w->mball.wedges % 10 == 0) {
			if (all || use) {
				glPushMatrix();
				drawHalf(w, ((((wedge + 9 * half / 5) % whole) / half) * half + half / 5) % whole);
				glPopMatrix();
			} else if (!all)
				drawHalf(w, ((((wedge + 9 * half / 5) % whole) / half) * half + 6 * half / 5) % whole);
		}
		break;
	case TR:
	case BL:
		if (w->mball.wedges % 10 == 0) {
			if (all || use) {
				glPushMatrix();
				drawHalf(w, ((((wedge + 8 * half / 5) % whole) / half) * half + 2 * half / 5) % whole);
				glPopMatrix();
			} else if (!all)
				drawHalf(w, ((((wedge + 8 * half / 5) % whole) / half) * half + 7 * half / 5) % whole);
		} else if (w->mball.wedges % 8 == 0) {
			if (all || use) {
				glPushMatrix();
				drawHalf(w, ((((wedge + 7 * half / 4) % whole) / half) * half + half / 4) % whole);
				glPopMatrix();
			} else if (!all)
				drawHalf(w, ((((wedge + 7 * half / 4) % whole) / half) * half + 5 * half / 4) % whole);
		} else if (w->mball.wedges % 6 == 0) {
			if (all || use) {
				glPushMatrix();
				drawHalf(w, ((((wedge + 5 * half / 3) % whole) / half) * half + half / 3) % whole);
				glPopMatrix();
			} else if (!all)
				drawHalf(w, ((((wedge + 5 * half / 3) % whole) / half) * half + 2 * whole / 3) % whole);
		}
		break;
	case RIGHT:
	case LEFT:
		if (w->mball.wedges % 4 == 0) {
			if (all || use) {
				glPushMatrix();
				drawHalf(w, ((((wedge + 3 * half / 2) % whole) / half) * half + half / 2) % whole);
				glPopMatrix();
			} else if (!all)
				drawHalf(w, ((((wedge + 3 * half / 2) % whole) / half) * half + 3 * half / 2) % whole);
		}
		break;
	case BR:
	case TL:
		if (w->mball.wedges % 10 == 0) {
			if (all || use) {
				glPushMatrix();
				drawHalf(w, ((((wedge + 7 * half / 5) % whole) / half) * half + 3 * half / 5) % whole);
				glPopMatrix();
			} else if (!all)
				drawHalf(w, ((((wedge + 7 * half / 5) % whole) / half) * half + 8 * half / 5) % whole);
		} else if (w->mball.wedges % 8 == 0) {
			if (all || use) {
				glPushMatrix();
				drawHalf(w, ((((wedge + 5 * half / 4) % whole) / half) * half + 3 * half / 4) % whole);
				glPopMatrix();
			} else if (!all)
				drawHalf(w, ((((wedge + 5 * half / 4) % whole) / half) * half + 7 * half / 4) % whole);
		} else if (w->mball.wedges % 6 == 0) {
			if (all || use) {
				glPushMatrix();
				drawHalf(w, ((((wedge + 4 * half / 3) % whole) / half) * half + whole / 3) % whole);
				glPopMatrix();
			} else if (!all)
				drawHalf(w, ((((wedge + 4 * half / 3) % whole) / half) * half + 5 * half / 3) % whole);
		}
		break;
	case BBR:
	case TTL:
		if (w->mball.wedges % 12 == 0) {
			if (all || use) {
				glPushMatrix();
				drawHalf(w, ((((wedge + 13 * half / 6) % whole) / half) * half + 11 * half / 6) % whole);
				glPopMatrix();
			} else if (!all)
				drawHalf(w, ((((wedge + 13 * half / 6) % whole) / half) * half + 5 * half / 6) % whole);
		} else if (w->mball.wedges % 10 == 0) {
			if (all || use) {
				glPushMatrix();
				drawHalf(w, ((((wedge + 11 * half / 5) % whole) / half) * half + 9 * half / 5) % whole);
				glPopMatrix();
			} else if (!all)
				drawHalf(w, ((((wedge + 11 * half / 5) % whole) / half) * half + 4 * half / 5) % whole);
		}
		break;
	case CW:
	case CCW:
		if (all || use) {
			glPushMatrix();
			drawBand(w, band);
			glPopMatrix();
		} else if (!all)
			drawNotBand(w, band);
		break;
	}
}

static void
rotateWedgeSlice(MballGLWidget w, GLfloat rotateStep,
		GLfloat x, GLfloat y, GLfloat z,
		int wedge, int band, int direction)
{
	int b;
	glPushMatrix();
	glRotatef(rotateStep, x, y, z);
	if (w->mballGL.movement.control) {
		if (direction == CW || direction == CCW)
			for (b = 0; b < w->mball.bands; b++)
				drawWedges(w, wedge, b, direction, False, True);
		else
			for (b = 0; b < 2; b++)
				drawWedges(w, b * w->mball.wedges / 2, band, direction, False, True);
	} else
		drawWedges(w, wedge, band, direction, True, False);
	glPopMatrix();
	if (!w->mballGL.movement.control)
		drawWedges(w, wedge, band, direction, False, False);
}

static void
drawSphere(MballGLWidget w)
{
	GLfloat rotateStep = 0.0;
	int wedge = w->mballGL.movement.wedge;
	int band = w->mballGL.movement.band;
	int direction = w->mballGL.movement.direction;

	if (wedge == NO_WEDGE || direction == IGNORE_DIR) {
		drawNotBand(w, -1);
	} else {
		float sqrt3 = (float) 1.7320508075688;
		float tanpidot3 = (float) 1.3763819204711; /* tan(3*pi/10) */
		float tanpidot = (float) 0.3249196962329; /* tan(pi/10) */
		rotateStep = (float) w->mballGL.rotateStep;
		switch(direction) {
		case TOP:
			rotateWedgeSlice(w, rotateStep, 0, 0, -1,
				wedge, band, direction);
			break;
		case TTR:
			if (w->mball.wedges % 12 == 0)
				rotateWedgeSlice(w, rotateStep, 1, 0, -sqrt3,
					wedge, band, direction);
			else if (w->mball.wedges % 10 == 0)
				rotateWedgeSlice(w, rotateStep, 1, 0, -tanpidot3,
					wedge, band, direction);
			break;
		case TR:
			if (w->mball.wedges % 10 == 0)
				rotateWedgeSlice(w, rotateStep, 1, 0, -tanpidot,
					wedge, band, direction);
			else if (w->mball.wedges % 8 == 0)
				rotateWedgeSlice(w, rotateStep, 1, 0, -1,
					wedge, band, direction);
			else if (w->mball.wedges % 6 == 0)
				rotateWedgeSlice(w, rotateStep, sqrt3, 0, -1,
					wedge, band, direction);
			break;
		case RIGHT:
			if (w->mball.wedges % 4 == 0)
				rotateWedgeSlice(w, rotateStep, 1, 0, 0,
					wedge, band, direction);
			break;
		case BR:
			if (w->mball.wedges % 10 == 0)
				rotateWedgeSlice(w, rotateStep, 1, 0, tanpidot,
					wedge, band, direction);
			else if (w->mball.wedges % 8 == 0)
				rotateWedgeSlice(w, rotateStep, 1, 0, 1,
					wedge, band, direction);
			else if (w->mball.wedges % 6 == 0)
				rotateWedgeSlice(w, rotateStep, sqrt3, 0, 1,
					wedge, band, direction);
			break;
		case BBR:
			if (w->mball.wedges % 12 == 0)
				rotateWedgeSlice(w, rotateStep, 1, 0, sqrt3,
					wedge, band, direction);
			else if (w->mball.wedges % 10 == 0)
				rotateWedgeSlice(w, rotateStep, 1, 0, tanpidot3,
					wedge, band, direction);
			break;
		case BOTTOM:
			rotateWedgeSlice(w, rotateStep, 0, 0, 1,
				wedge, band, direction);
			break;
		case BBL:
			if (w->mball.wedges % 12 == 0)
				rotateWedgeSlice(w, rotateStep, -1, 0, sqrt3,
					wedge, band, direction);
			else if (w->mball.wedges % 10 == 0)
				rotateWedgeSlice(w, rotateStep, -1, 0, tanpidot3,
					wedge, band, direction);
			break;
		case BL:
			if (w->mball.wedges % 10 == 0)
				rotateWedgeSlice(w, rotateStep, -1, 0, tanpidot,
					wedge, band, direction);
			else if (w->mball.wedges % 8 == 0)
				rotateWedgeSlice(w, rotateStep, -1, 0, 1,
					wedge, band, direction);
			else if (w->mball.wedges % 6 == 0)
				rotateWedgeSlice(w, rotateStep, -sqrt3, 0, 1,
					wedge, band, direction);
			break;
		case LEFT:
			if (w->mball.wedges % 4 == 0)
				rotateWedgeSlice(w, rotateStep, -1, 0, 0,
					wedge, band, direction);
			break;
		case TL:
			if (w->mball.wedges % 10 == 0)
				rotateWedgeSlice(w, rotateStep, -1, 0, -tanpidot,
					wedge, band, direction);
			else if (w->mball.wedges % 8 == 0)
				rotateWedgeSlice(w, rotateStep, -1, 0, -1,
					wedge, band, direction);
			else if (w->mball.wedges % 6 == 0)
				rotateWedgeSlice(w, rotateStep, -sqrt3, 0, -1,
					wedge, band, direction);
			break;
		case TTL:
			if (w->mball.wedges % 12 == 0)
				rotateWedgeSlice(w, rotateStep, -1, 0, -sqrt3,
					wedge, band, direction);
			else if (w->mball.wedges % 10 == 0)
				rotateWedgeSlice(w, rotateStep, -1, 0, -tanpidot3,
					wedge, band, direction);
			break;
		case CW:
			rotateWedgeSlice(w, rotateStep, 0, -1, 0,
				wedge, band, direction);
			break;
		case CCW:
			rotateWedgeSlice(w, rotateStep, 0, 1, 0,
				wedge, band, direction);
			break;
		}
	}
}

void
movePiecesGL(MballGLWidget w, int wedge, int band, int direction,
		Boolean control, int fast)
{
	int i, f;
	w->mballGL.movement.wedge = wedge;
	w->mballGL.movement.band = band;
	w->mballGL.movement.direction = direction;
	w->mballGL.movement.control = control;
	w->mballGL.rotateStep = 0.0;
	w->mballGL.angleStep = 0.0;
	f = (w->mballGL.numTurnInc < fast) ? 1 : fast;
	for (i = 0; i < w->mballGL.numTurnInc / f; i++) {
		double increment;
		if (direction == CW || direction == CCW)
			increment = (360.0 / w->mball.wedges) * f / w->mballGL.numTurnInc;
		else
			increment = 180.0 * f / w->mballGL.numTurnInc;
		w->mballGL.rotateStep += increment;
		drawAllPiecesGL(w);
		Sleep((unsigned int) ((w->mball.delay /
			(w->mballGL.numTurnInc * f)) * 40.0));
		w->mball.currentWedge = -1;
		w->mball.currentBand = -1;
		w->mball.currentSector = -1;
	}
	w->mballGL.angleStep = 0.0;
	w->mballGL.movement.wedge = NO_WEDGE;
	w->mballGL.movement.band = NO_BAND;
	w->mballGL.movement.direction = IGNORE_DIR;
	w->mballGL.movement.control = False;
}

void
drawAllPiecesGL(MballGLWidget w)
{
#ifdef WINVER
	wglMakeCurrent(w->core.hDC, hRC);
#else
	if (!glXMakeCurrent(XtDisplay(w), XtWindow(w), *glXContext)) {
		DISPLAY_WARNING("Draw All GL error");
	}
#endif
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glPushMatrix();
	glTranslate(0.0, 0.0, -10.0);
	if (w->core.height > w->core.width)
		glScalef(SCALE, (SCALE * (float) w->core.width / (float) w->core.height), SCALE);
	else
		glScalef((SCALE * (float) w->core.height / (float) w->core.width), SCALE, SCALE);
	switch (w->mball.view) {
	case 0: /* v  vertical */
		glRotatef((GLfloat) (-3 * w->mballGL.step) / (GLfloat) 1.0, 0, 1, 0);
		glRotatef((GLfloat) (w->mballGL.step) / (GLfloat) -2.5, 0, 0, 1);
		break;
	case 1: /* ^  vertical */
		glRotatef((GLfloat) (-3 * w->mballGL.step) / (GLfloat) 1.0, 0, 1, 0);
		glRotatef((GLfloat) (w->mballGL.step) / (GLfloat) 2.5, 0, 0, 1);
		break;
	default:
		break;
	}
	drawSphere(w);
	glPopMatrix();
	glFlush();
#ifdef WINVER
	SwapBuffers(w->core.hDC);
#else
	glXSwapBuffers(XtDisplay(w), XtWindow(w));
#endif
}

Boolean
positionToSectorGL(MballGLWidget w, int x, int y,
		int *sector, int *view)
{
	/* Using gluUnProject By: Luke Benstead */
	/* http://nehe.gamedev.net/data/articles/article.asp?article=13 */

	GLint viewport[4];
	GLdouble modelview[16];
	GLdouble projection[16];
	GLfloat winX, winY, winZ;
	GLdouble posX, posY, posZ;
	Matrix a, b, r;
	Vector t, world;
	int wedge = 0, band = 0;

	glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
	glGetDoublev(GL_PROJECTION_MATRIX, projection);
	glGetIntegerv(GL_VIEWPORT, viewport);

	winX = (float) x;
	winY = (float) viewport[3] - (float) y;
	glReadPixels(x, (int) winY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT,
		&winZ);
	gluUnProject(winX, winY, winZ, modelview, projection, viewport,
		&(posX), &(posY), &(posZ));
	t[0] = (float) posX;
	t[1] = (float) posY;
	t[2] = (float) (posZ + 10);
	if (w->core.height > w->core.width)
		t[1] *= (float) w->core.height / (float) w->core.width;
	else
		t[0] *= (float) w->core.width / (float) w->core.height;
	if (winZ == 1) {
		return False;
	}
	*view = w->mball.view;
	switch (*view) {
	case 0: /* v  !vertical */
		setMatrix((float) (-w->mballGL.step) / (float) 2.5, 0.0, 0.0, 1.0, a);
		setMatrix((float) (-3 * w->mballGL.step) / (float) 1.0, 0.0, 1.0, 0.0, b);
		matrixMatrix(a, b, r);
		matrixVector(r, t, world);
		break;
	case 1: /* ^  !vertical */
		setMatrix((float) (w->mballGL.step) / (float) 2.5, 0.0, 0.0, 1.0, a);
		setMatrix((float) (-3 * w->mballGL.step) / (float) 1.0, 0.0, 1.0, 0.0, b);
		matrixMatrix(a, b, r);
		matrixVector(r, t, world);
		break;
	default:
		break;
	}
	wedge = (int) (((atan2(world[2], world[0]) + M_PI ) / (2.0 * M_PI)) * w->mball.wedges);
	band =	w->mball.bands - 1 - (int) (((world[1] + 2.0) / 4.0) * w->mball.bands);
	*sector = wedge + w->mball.wedges * band;
#ifdef DEBUG
	(void) printf("view %d, x %d, y %d\n", w->mball.view, x, y);
	(void) printf(" wx %g, wy %g, wz %g\n", winX, winY, winZ);
	(void) printf("  px %g, py %g, pz %g\n", posX, posY, posZ);
	(void) printf("   tx %g, ty %g, tz %g\n", t[0], t[1], t[2]);
	(void) printf("    rx %g, ry %g, rz %g\n",
		world[0], world[1], world[2]);
	(void) printf("     wedge %d, band %d, view %d\n", wedge, band, *view);
	(void)	printf(" theta %d, phi %d\n", wedge, band);
#endif
	return True;
}

#ifdef WINVER
static Boolean
setupPixelFormat(MballGLWidget w, BYTE type, DWORD flags)
{
	PIXELFORMATDESCRIPTOR pfd;
	int pixelFormat;

	memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
	pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
	pfd.nVersion = 1;
	pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | flags;
	pfd.dwLayerMask = PFD_MAIN_PLANE;
	/*pfd.cColorBits = 8;
	pfd.cDepthBits = 16;*/
	pfd.iPixelType = type;
	pfd.cColorBits = 24;
	pfd.cDepthBits = 32;

	if ((pixelFormat = ChoosePixelFormat(w->core.hDC, &pfd)) == 0) {
		DWORD err;
		char *buf1;

		err = GetLastError();
		/* 6 ERROR_INVALID_HANDLE */
		intCat(&buf1, "ChoosePixelFormat failed ", (int) err);
		DISPLAY_WARNING(buf1);
		free(buf1);
		return FALSE;
	}

	if (SetPixelFormat(w->core.hDC, pixelFormat, &pfd) == FALSE) {
		MessageBox(NULL, "SetPixelFormat failed", "Error", MB_OK);
		return FALSE;
	}
	DescribePixelFormat(w->core.hDC, pixelFormat,
		sizeof(PIXELFORMATDESCRIPTOR), &pfd);
	return TRUE;
}

#else

static Boolean
setValuesPuzzleGL(Widget current, Widget request, Widget renew)
{
	MballGLWidget c = (MballGLWidget) current, w = (MballGLWidget) renew;
	Boolean redraw = False;

	if (w->mball.view != c->mball.view) {
		resizePuzzleGL(w);
		redraw = True;
	}
	if (w->mball.wedges != c->mball.wedges ||
			w->mball.bands != c->mball.bands) {
		resetPieces((MballWidget) w);
		resizePuzzleGL(w);
		redraw = True;
	}
	if (w->mball.delay != c->mball.delay) {
		w->mballGL.numTurnInc = ((w->mball.delay < MAX_TURN_INC) ?
			w->mball.delay + 1 : MAX_TURN_INC);
	}
	return (redraw);
}

static GLXContext *
initGL(MballGLWidget w) {
	XVisualInfo xviIn, *xviOut;
	int numVis;

	/*XGetWindowAttributes(XtDisplay(w), XtWindow(w), &xgwa); */
	xviIn.screen = DefaultScreen(XtDisplay(w));
	xviIn.visualid = XVisualIDFromVisual(DefaultVisual(XtDisplay(w),
		xviIn.screen));
	xviOut = XGetVisualInfo(XtDisplay(w), VisualScreenMask | VisualIDMask,
		&xviIn, &numVis);
	if (!xviOut) {
		XtWarning("Could not get XVisualInfo");
		return (GLXContext *) NULL;
	}
	if (glXContext)
		free(glXContext);
	if ((glXContext = (GLXContext *) malloc(sizeof (GLXContext))) ==
			NULL) {
		DISPLAY_ERROR("Not enough memory for glx info, exiting.");
	}
	/* assertion "glx_dpy" failed on Cygwin */
	if (!glXQueryExtension(XtDisplay(w), NULL, NULL)) {
		XtWarning("Could not find GLX");
		return (GLXContext *) NULL;
	}
	*glXContext = glXCreateContext(XtDisplay(w), xviOut, 0, GL_TRUE);
#ifndef __VMS
	(void) XFree((char *) xviOut);
#endif
	if (!*glXContext) {
		XtWarning("Could not create rendering context");
		return (GLXContext *) NULL;
	}
	return glXContext;
}
#endif

#ifndef WINVER
static
#endif
void
resizePuzzleGL(MballGLWidget w)
{
#ifdef WINVER
	RECT rect;

	/* Determine size of client area */
	(void) GetClientRect(w->core.hWnd, &rect);
	w->core.width = rect.right;
	w->core.height = rect.bottom;
#endif
	glViewport(0, 0, (GLint) w->core.width, (GLint) w->core.height);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glFrustum(-1.0, 1.0, -1.0, 1.0, 5.0, 15.0);
	glMatrixMode(GL_MODELVIEW);
	w->mballGL.slicesPerWedge = sphereSlices(w);
	w->mballGL.stacksPerBand = sphereStacks(w);
}

static void
makeCurrentGL(MballGLWidget w)
{
	GLboolean rgbaMode;

#ifdef WINVER
	wglMakeCurrent(w->core.hDC, hRC);
#else
	if (!glXMakeCurrent(XtDisplay(w), XtWindow(w), *glXContext)) {
		DISPLAY_WARNING("GL error");
	}
#endif
	madeCurrent = True;
	/* True Color junk */
	glGetBooleanv(GL_RGBA_MODE, &rgbaMode);
	if (!rgbaMode) {
#ifdef WINVER
		glClearIndex(0.0);
#else
		glIndexi(WhitePixel(XtDisplay(w),
			DefaultScreen(XtDisplay(w))));
		glClearIndex((float) BlackPixel(XtDisplay(w),
			DefaultScreen(XtDisplay(w))));
#endif
	}
	resizePuzzleGL(w);

	glDrawBuffer(GL_BACK);
	glClearDepth(1.0);
	glClearColor(wedge_material[NO_WEDGE][0], wedge_material[NO_WEDGE][1],
		wedge_material[NO_WEDGE][2], wedge_material[NO_WEDGE][3]);
	glColor3f(1.0, 1.0, 1.0);

	glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
	glLightfv(GL_LIGHT0, GL_POSITION, position0);
	glLightfv(GL_LIGHT1, GL_AMBIENT, ambient);
	glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse);
	glLightfv(GL_LIGHT1, GL_POSITION, position1);
	glLightfv(GL_LIGHT2, GL_AMBIENT, ambient);
	glLightfv(GL_LIGHT2, GL_DIFFUSE, diffuse);
	glLightfv(GL_LIGHT2, GL_POSITION, position2);
	glLightfv(GL_LIGHT3, GL_AMBIENT, ambient);
	glLightfv(GL_LIGHT3, GL_DIFFUSE, diffuse);
	glLightfv(GL_LIGHT3, GL_POSITION, position3);
	glLightfv(GL_LIGHT4, GL_AMBIENT, ambient);
	glLightfv(GL_LIGHT4, GL_DIFFUSE, diffuse);
	glLightfv(GL_LIGHT4, GL_POSITION, position4);
	glLightfv(GL_LIGHT5, GL_AMBIENT, ambient);
	glLightfv(GL_LIGHT5, GL_DIFFUSE, diffuse);
	glLightfv(GL_LIGHT5, GL_POSITION, position5);
	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
	glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE, lmodel_twoside);
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
	glEnable(GL_LIGHT1);
	glEnable(GL_LIGHT2);
	glEnable(GL_LIGHT3);
	glEnable(GL_LIGHT4);
	glEnable(GL_LIGHT5);
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_NORMALIZE);
	glEnable(GL_CULL_FACE);
	glShadeModel(GL_FLAT);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, front_shininess);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, front_specular);

	w->mballGL.step = 90;
	w->mballGL.rotateStep = 0.0;
}

#ifndef WINVER
static void
initializePuzzleGL(Widget request, Widget renew)
{
	MballGLWidget w = (MballGLWidget) renew;
	int i;

	setAllColors((MballWidget) w);
	if (initGL(w) == (GLXContext *) NULL) {
		w->mball.dim = 0;
		return;
	}
	w->mball.dim = 4; /* 2 and 3 already taken */
	for (i = 0; i < MAX_WEDGES + 2; i++)
		pickColor(w, i, w->mball.mono, wedge_material[i]);
	w->mballGL.numTurnInc = ((w->mball.delay < MAX_TURN_INC) ?
		w->mball.delay + 1 : MAX_TURN_INC);
	resizePuzzleGL(w);
}
#endif

#ifndef WINVER
static
#endif
void
exposePuzzleGL(
#ifdef WINVER
MballGLWidget w
#else
Widget renew, XEvent *event, Region region
#endif
)
{
#ifdef WINVER
	if (hRC == NULL) {
		BYTE type;
		int i;
		DWORD flags = PFD_DOUBLEBUFFER;

#if 1
		type = PFD_TYPE_RGBA;
#else
		type = PFD_TYPE_COLORINDEX;
#endif
		(void) setupPixelFormat(w, type, flags);
		hRC = wglCreateContext(w->core.hDC);
		w->mballGL.movement.wedge = NO_WEDGE;
		w->mballGL.movement.band = NO_BAND;
		w->mballGL.movement.direction = IGNORE_DIR;
		w->mballGL.movement.control = False;
		w->mballGL.rotateStep = 0.0;
		w->mballGL.angleStep = 0.0;
		w->mball.dim = 4; /* 2 and 3 already taken */
		for (i = 0; i < MAX_WEDGES + 2; i++)
			pickColor(w, i, w->mball.mono, wedge_material[i]);
		w->mballGL.numTurnInc = ((w->mball.delay < MAX_TURN_INC) ?
			w->mball.delay + 1 : MAX_TURN_INC);
		resizePuzzleGL(w);
	}
#else
	MballGLWidget w = (MballGLWidget) renew;

	if (!w->core.visible)
		return;
	if (w->mball.dim != 4)
		return;
#endif
	if (!madeCurrent) {
		makeCurrentGL(w);
	}
	drawAllPiecesGL(w);
}

#ifndef WINVER
static void
movePuzzleGLTl(MballGLWidget w, XEvent *event, char **args, int nArgs)
{
	movePuzzleInput((MballWidget) w, event->xbutton.x, event->xbutton.y,
		TL, (int) (event->xkey.state & ControlMask));
}

static void
movePuzzleGLTop(MballGLWidget w, XEvent *event, char **args, int nArgs)
{
	movePuzzleInput((MballWidget) w, event->xbutton.x, event->xbutton.y,
		TOP, (int) (event->xkey.state & ControlMask));
}

static void
movePuzzleGLTr(MballGLWidget w, XEvent *event, char **args, int nArgs)
{
	movePuzzleInput((MballWidget) w, event->xbutton.x, event->xbutton.y,
		TR, (int) (event->xkey.state & ControlMask));
}

static void
movePuzzleGLLeft(MballGLWidget w, XEvent *event, char **args, int nArgs)
{
	movePuzzleInput((MballWidget) w, event->xbutton.x, event->xbutton.y,
		LEFT, (int) (event->xkey.state & ControlMask));
}

static void
movePuzzleGLRight(MballGLWidget w, XEvent *event, char **args, int nArgs)
{
	movePuzzleInput((MballWidget) w, event->xbutton.x, event->xbutton.y,
		RIGHT, (int) (event->xkey.state & ControlMask));
}

static void
movePuzzleGLBl(MballGLWidget w, XEvent *event, char **args, int nArgs)
{
	movePuzzleInput((MballWidget) w, event->xbutton.x, event->xbutton.y,
		BL, (int) (event->xkey.state & ControlMask));
}

static void
movePuzzleGLBottom(MballGLWidget w, XEvent *event, char **args, int nArgs)
{
	movePuzzleInput((MballWidget) w, event->xbutton.x, event->xbutton.y,
		BOTTOM, (int) (event->xkey.state & ControlMask));
}

static void
movePuzzleGLBr(MballGLWidget w, XEvent *event, char **args, int nArgs)
{
	movePuzzleInput((MballWidget) w, event->xbutton.x, event->xbutton.y,
		BR, (int) (event->xkey.state & ControlMask));
}
#endif

#else
int x = 0; /* non empty source */
#endif
