/*****************************************************************************
*   An SGI 4D driver using Open GL.					     *
*									     *
* Written by:  Gershon Elber			     Ver 0.1, October 1994.  *
*****************************************************************************/

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/cursorfont.h>
#include <X11/Xresource.h>

#include <GL/glx.h>
#include <GL/gl.h>
#include <unistd.h>

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
#ifdef _AIX
#    include <fcntl.h>
#else
#    include <sys/fcntl.h>
#endif
#include "irit_sm.h"
#include "genmat.h"
#include "iritprsr.h"
#include "allocate.h"
#include "attribut.h"
#include "ip_cnvrt.h"
#include "cagd_lib.h"
#include "symb_lib.h"
#include "iritgrap.h"

/* Interactive menu setup structure: */
#define INTERACT_NUM_OF_STRINGS		4
#define INTERACT_NUM_OF_SUB_WNDWS	16

typedef struct InteractString {
    RealType X, Y;
    int Color;
    char *Str;
} InteractString;
typedef struct InteractSubWindow {
    RealType X, Y;					   /* Center points. */
    int Color;
    IGGraphicEventType Event;
    int TextInside; /* If TRUE, Str will be in window, otherwise left to it. */
    char *Str;
} InteractSubWindow;
typedef struct InteractWindowStruct {	 /* The interactive menu structures. */
    /* Rotate, Translate, Scale strings: */
    InteractString Strings[INTERACT_NUM_OF_STRINGS];
    InteractSubWindow SubWindows[INTERACT_NUM_OF_SUB_WNDWS];
} InteractWindowStruct;

#define INTERACT_SUB_WINDOW_WIDTH  0.8		 /* Relative to window size. */
#define INTERACT_SUB_WINDOW_HEIGHT 0.04

static int
    GlblStateMenu = 0;

static long
    TransWinWidth = 100,
    TransWinWidth2 = 50,
    TransWinHeight = 100,
    TransWinLow = 0,
    TransWinLeft = 0,
    ViewWinWidth = 100,
    ViewWinWidth2 = 50,
    ViewWinHeight = 100,
    ViewWinHeight2 = 50,
    ViewWinLow = 0,
    ViewWinLeft = 0;

static GLXContext ViewCx, TransCx;
static Display *XDisplay;
static Window ViewWin, TransWin,
    ObjScrTglWndw, PersOrthoTglWndw, PersOrthoZWndw,
    RotateXWndw, RotateYWndw, RotateZWndw, TranslateXWndw,
    TranslateYWndw, TranslateZWndw, ScaleWndw, DepthCueWndw,
    SaveMatrixWndw, PushMatrixWndw, PopMatrixWndw, QuitWndw;

/* Interactive mode menu set up structure is define below: */
static InteractWindowStruct InteractMenu = {
    { { 0.5, 0.81, IG_IRIT_RED,		"Rotate" },
      { 0.5, 0.65, IG_IRIT_GREEN,	"Translate" },
      { 0.5, 0.49, IG_IRIT_CYAN,	"Scale" },
      { 0.5, 0.41, IG_IRIT_LIGHTGREEN,	"Clip Plane" },
    },
    { { 0.5, 0.93, IG_IRIT_YELLOW, IG_EVENT_SCR_OBJ_TGL,	TRUE,  "Screen Coords." },
      { 0.5, 0.87, IG_IRIT_BLUE,   IG_EVENT_PERS_ORTHO_TGL,TRUE,  "Perspective" },
      { 0.5, 0.83, IG_IRIT_BLUE,   IG_EVENT_PERS_ORTHO_Z,	FALSE, "Z" },
      { 0.5, 0.75, IG_IRIT_RED,    IG_EVENT_ROTATE_X,		FALSE, "X" }, /* Rot */
      { 0.5, 0.71, IG_IRIT_RED,    IG_EVENT_ROTATE_Y,		FALSE, "Y" },
      { 0.5, 0.67, IG_IRIT_RED,    IG_EVENT_ROTATE_Z,		FALSE, "Z" },
      { 0.5, 0.59, IG_IRIT_GREEN,  IG_EVENT_TRANSLATE_X,	FALSE, "X" }, /* Trans */
      { 0.5, 0.55, IG_IRIT_GREEN,  IG_EVENT_TRANSLATE_Y,	FALSE, "Y" },
      { 0.5, 0.51, IG_IRIT_GREEN,  IG_EVENT_TRANSLATE_Z,	FALSE, "Z" },
      { 0.5, 0.43, IG_IRIT_CYAN,   IG_EVENT_SCALE,		FALSE, "" },  /* Scale */
      { 0.5, 0.35, IG_IRIT_LIGHTGREEN, IG_EVENT_NEAR_CLIP,	FALSE,  "" },
      { 0.5, 0.31, IG_IRIT_LIGHTGREEN, IG_EVENT_FAR_CLIP,	FALSE,  "" },

      { 0.5, 0.20, IG_IRIT_YELLOW, IG_EVENT_SAVE_MATRIX,	TRUE,  "Save Matrix" },
      { 0.5, 0.13, IG_IRIT_YELLOW, IG_EVENT_PUSH_MATRIX,	TRUE,  "Push Matrix" },
      { 0.5, 0.09, IG_IRIT_YELLOW, IG_EVENT_POP_MATRIX,		TRUE,  "Pop Matrix" },
      { 0.5, 0.03, IG_IRIT_WHITE,  IG_EVENT_QUIT,		TRUE,  "Quit" },
    }
};

static short Colors[IG_MAX_COLOR + 1][3] =
{
    { 0,   0,   0   },  /* 0. BLACK */
    { 0,   0,   170 },  /* 1. BLUE */
    { 0,   170, 0   },  /* 2. GREEN */
    { 0,   170, 170 },  /* 3. CYAN */
    { 170, 0,   0   },  /* 4. RED */
    { 170, 0,   170 },  /* 5. MAGENTA */
    { 170, 170, 0   },  /* 6. BROWN */
    { 170, 170, 170 },  /* 7. LIGHTGREY */
    { 85,  85,  85  },  /* 8. DARKGRAY */
    { 85,  85,  255 },  /* 9. LIGHTBLUE */
    { 85,  255, 85  },  /* 10. LIGHTGREEN */
    { 85,  255, 255 },  /* 11. LIGHTCYAN */
    { 255, 85,  85  },  /* 12. LIGHTRED */
    { 255, 85,  255 },  /* 13. LIGHTMAGENTA */
    { 255, 255, 85  },  /* 14. YELLOW */
    { 255, 255, 255 }   /* 15. WHITE */
};

static void SetColorIndex(int c);
static void SetColorRGB(int Color[3]);
static void ClearViewArea(void);
static void SetTransformWindow(int argc, cahr **argv);
static void RedrawTransformWindow(void);
static void SetViewWindow(void);
static IGGraphicEventType GetGraphicEvent(RealType *ChangeFactor);
static void DrawText(char *Str, long PosX, long PosY);

/****************************************************************************
* Pop up all windows, read input and display.				    *
****************************************************************************/
void main(int argc, char **argv)
{
    RealType ChangeFactor;
    IGGraphicEventType Event;
    IPObjectStruct *PObjects;

    IGConfigureGlobals("xogldrvs", argc, argv);

    XDisplay = XOpenDisplay(0);

    SetViewWindow();
    IGRedrawViewWindow();
    SetTransformWindow(argc, argv);
    RedrawTransformWindow();

    IGCreateStateMenu();

    while ((Event = GetGraphicEvent(&ChangeFactor)) != IG_EVENT_QUIT) {
	if (IGProcessEvent(Event, ChangeFactor * IGGlblChangeFactor))
	    IGRedrawViewWindow();
    }
}

/*****************************************************************************
* Construct global pop up menu.						     *
*****************************************************************************/
void IGCreateStateMenu(void)
{
#ifdef TTTTT
    if (GlblStateMenu) 
        freepup(GlblStateMenu);

    GlblStateMenu = newpup();

    addtopup(GlblStateMenu, " Set Up %t", 0);
    addtopup(GlblStateMenu, "Oops!%l", 0);
    addtopup(GlblStateMenu, "More Sensitive", 0);
    addtopup(GlblStateMenu, "Less Sensitive%l", 0);
    addtopup(GlblStateMenu, IGGlblTransformMode == IG_TRANS_SCREEN ?
				"Screen Trans." : "Object Trans", 0);
    addtopup(GlblStateMenu, IGGlblViewMode == IG_VIEW_PERSPECTIVE ?
				"Perspective" : "Orthographic", 0);
    addtopup(GlblStateMenu,
	     IGGlblDepthCue ? "Depth Cue" : "No Depth Cue", 0);
    addtopup(GlblStateMenu,
	     IGGlblDrawSolid ? "Draw Solid" : "Draw Wireframe", 0);
    addtopup(GlblStateMenu,
	     IGGlblDoDoubleBuffer ? "Double Buffer" : "Single Buffer", 0);
    addtopup(GlblStateMenu,
	     IGGlblAntiAliasing ? "Anti Aliasing" : "No Anti Aliasing", 0);
    addtopup(GlblStateMenu,
	     IGGlblDrawInternal ? "Draw Internal Edges" : "No Internal Edges", 0);
    addtopup(GlblStateMenu,
	     IGGlblDrawVNormal ? "Draw Vrtx Normals" :  "No Vrtx Normals", 0);
    addtopup(GlblStateMenu,
	     IGGlblDrawPNormal ? "Draw Poly Normals" :  "No Poly Normals", 0);
    addtopup(GlblStateMenu,
	     IGGlblDrawSurfaceMesh ? "Draw Surface Mesh" : "No Surface Mesh", 0);
    addtopup(GlblStateMenu,
	     IGGlblDrawSurfacePoly ? "Surface Polygons" : "Surface Isolines", 0);
    addtopup(GlblStateMenu,
	     IGGlblFourPerFlat ? "Four Per Flat%l" : "Two Per Flat%l", 0);
    addtopup(GlblStateMenu, "More Isolines", 0);
    addtopup(GlblStateMenu, "Less Isolines%l", 0);
    addtopup(GlblStateMenu, "Finer approx.", 0);
    addtopup(GlblStateMenu, "Coarser approx.%l", 0);
    addtopup(GlblStateMenu, "Longer Vectors.", 0);
    addtopup(GlblStateMenu, "Shorter Vectors", 0);
    addtopup(GlblStateMenu, "Wider Lines", 0);
    addtopup(GlblStateMenu, "Narrow Lines", 0);
    addtopup(GlblStateMenu, "Front View", 0);
    addtopup(GlblStateMenu, "Size View", 0);
    addtopup(GlblStateMenu, "Top View", 0);
    addtopup(GlblStateMenu, "Isometry View", 0);
    addtopup(GlblStateMenu, "Clear View Area", 0);
#endif
}

/****************************************************************************
* Draw a Point/Vector object using current modes and transformations.	    *
****************************************************************************/
void IGDrawPtVec(IPObjectStruct *PObj)
{
    int i;
    PointType Ends[6], Zero;
    RealType
	*Pt = PObj -> U.Pt;

    for (i = 0; i < 6; i++)
	PT_COPY(Ends[i], Pt);

    Ends[0][0] -= IG_POINT_WIDTH;
    Ends[1][0] += IG_POINT_WIDTH;
    Ends[2][1] -= IG_POINT_WIDTH;
    Ends[3][1] += IG_POINT_WIDTH;
    Ends[4][2] -= IG_POINT_WIDTH;
    Ends[5][2] += IG_POINT_WIDTH;

    for (i = 0; i < 6; i += 2) {
	glBegin(GL_LINES);
	glVertex3dv(Ends[i]);
	glVertex3dv(Ends[i+1]);
	glEnd();
    }

    if (IP_IS_VEC_OBJ(PObj)) {
	glBegin(GL_LINES);
	glVertex3dv(Pt);
	Zero[0] = Zero[1] = Zero[2] = 0.0;
	glVertex3dv(Zero);
	glEnd();
    }
}

/****************************************************************************
* Draw a Poly object using current modes and transformations.		    *
****************************************************************************/
void IGDrawPoly(IPObjectStruct *PObj)
{
    IPVertexStruct *V;
    IPPolygonStruct
	*Pl = PObj -> U.Pl;

    if (IP_IS_POLYLINE_OBJ(PObj)) {
	for (; Pl != NULL; Pl = Pl -> Pnext) {
	    glBegin(GL_LINE_STRIP);
	    for (V = Pl -> PVertex; V != NULL; V = V -> Pnext)
	        glVertex3dv(V -> Coord);
	    glEnd();
	}
    }
    else if (IP_IS_POINTLIST_OBJ(PObj)) {
	for (; Pl != NULL; Pl = Pl -> Pnext) {
	    for (V = Pl -> PVertex; V != NULL; V = V -> Pnext) {
		int i;
		PointType Ends[6];
		RealType
		    *Pt = V -> Coord;

		for (i = 0; i < 6; i++)
		    PT_COPY(Ends[i], Pt);

		Ends[0][0] -= IG_POINT_WIDTH;
		Ends[1][0] += IG_POINT_WIDTH;
		Ends[2][1] -= IG_POINT_WIDTH;
		Ends[3][1] += IG_POINT_WIDTH;
		Ends[4][2] -= IG_POINT_WIDTH;
		Ends[5][2] += IG_POINT_WIDTH;

		for (i = 0; i < 6; i += 2) {
		    glBegin(GL_LINES);
		    glVertex3dv(Ends[i]);
		    glVertex3dv(Ends[i+1]);
		    glEnd();
		}
	    }
	}
    }
    else if (IP_IS_POLYGON_OBJ(PObj)) {
	int i, j,
	    NumOfVertices;
	PointType PNormal, VNormal;

	for (; Pl != NULL; Pl = Pl -> Pnext) {
	    if (IGGlblDrawPNormal) {
		NumOfVertices = 0;
		PNormal[0] = PNormal[1] = PNormal[2] = 0.0;
	    }

	    if (IGGlblDrawSolid) {
		glBegin(GL_POLYGON);
		for (V = Pl -> PVertex; V != NULL; V = V -> Pnext) {
		    glNormal3dv(V -> Normal);
		    glVertex3dv(V -> Coord);

		    if (IGGlblDrawPNormal) {
			for (j = 0; j < 3; j++)
			    PNormal[j] += V -> Coord[j];
			NumOfVertices++;
		    }
		}
		glEnd();
	    }
	    else {
		glBegin(GL_LINES);
		for (V = Pl -> PVertex; V != NULL; V = V -> Pnext) {
		    glVertex3dv(V -> Coord);
		    if (IP_IS_INTERNAL_VRTX(V) && !IGGlblDrawInternal) {
			glEnd();
			glBegin(GL_LINE_STRIP);
		    }

		    if (IGGlblDrawPNormal) {
			for (j = 0; j < 3; j++)
			    PNormal[j] += V -> Coord[j];
			NumOfVertices++;
		    }
		}
		glVertex3dv(Pl -> PVertex -> Coord);
		glEnd();
	    }

	    if (IGGlblDrawPNormal && IP_HAS_PLANE_POLY(Pl)) {
		glBegin(GL_LINES);
		for (i = 0; i < 3; i++)
		    PNormal[i] /= NumOfVertices;
		glVertex3dv(PNormal);
		for (i = 0; i < 3; i++)
		    PNormal[i] += Pl -> Plane[i] * IGGlblNormalLen;
		glVertex3dv(PNormal);
		glEnd();
	    }

	    if (IGGlblDrawVNormal) {
		for (V = Pl -> PVertex; V != NULL; V = V -> Pnext) {
		    if (IP_HAS_NORMAL_VRTX(V)) {
			for (j = 0; j < 3; j++)
			    VNormal[j] = V -> Coord[j] +
				         V -> Normal[j] * IGGlblNormalLen;
			glBegin(GL_LINES);
			glVertex3dv(V ->Coord);
			glVertex3dv(VNormal);
			glEnd();
		    }
		}
	    }
	}
    }
}

/****************************************************************************
* Routine to set the color according to the given object's color.	    *
****************************************************************************/
void IGSetColorObj(IPObjectStruct *PObj)
{
    int c, Color[3];

    if (AttrGetObjectRGBColor(PObj, &Color[0], &Color[1], &Color[2])) {
	SetColorRGB(Color);
    }
    else if ((c = AttrGetObjectColor(PObj)) != IP_ATTR_NO_COLOR) {
	SetColorIndex(c);
    }
    else {
	/* Use white as default color: */
	SetColorIndex(IG_IRIT_WHITE);
    }
}

/****************************************************************************
* Routine to set the line width to draw the given object, in pixels.        *
****************************************************************************/
void IGSetWidthObj(int Width)
{
    glLineWidth(Width);
}

/****************************************************************************
* Routine to set the color according to the given color index.		    *
****************************************************************************/
static void SetColorIndex(int c)
{
    int Color[3];

    if (c < 0 || c > IG_MAX_COLOR)
        c = IG_IRIT_WHITE;

    Color[0] = Colors[c][0];
    Color[1] = Colors[c][1];
    Color[2] = Colors[c][2];

    SetColorRGB(Color);
}

/****************************************************************************
* Routine to set the color according to the given RGB values.		    *
****************************************************************************/
static void SetColorRGB(int Color[3])
{
    int i;

    if (IGGlblDrawSolid) {
	GLfloat MatAmbient[4], MatDiffuse[4], MatSpecular[4];
	static GLfloat
	    MatShininess[] = { 15.0 };

	for (i = 0; i < 3; i++) {
	    MatAmbient[i] = 0.2 * Color[0] / 255.0;
	    MatDiffuse[i] = 0.4 * Color[i] / 255.0;
	    MatSpecular[i] = Color[i] / 255.0;
	}
	MatAmbient[3] = MatDiffuse[3] = MatSpecular[3] = 1.0;

	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, MatAmbient);
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, MatDiffuse);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, MatSpecular);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, MatShininess);
    }
    else
	glColor3f(Color[0] / 255.0, Color[1] / 255.0, Color[2] / 255.0);

    if (IGGlblDepthCue) {
	GLfloat fogColor[4];

	for (i = 0; i < 3; i++)
	    fogColor[i] = (float) IGGlblBackGroundColor[i] / 255.0;
	fogColor[3] = (float) 1.0;

	glEnable(GL_FOG);
	glFogi(GL_FOG_MODE, GL_LINEAR);
	glHint(GL_FOG_HINT, GL_NICEST);
	glFogf(GL_FOG_START, (float) IGGlblZMinClip);
	glFogf(GL_FOG_END, (float) IGGlblZMaxClip);
	glFogfv(GL_FOG_COLOR, fogColor);
    }
    else
	glDisable(GL_FOG);
}

/*****************************************************************************
* Set up and draw a transformation window.				     *
*****************************************************************************/
static void SetTransformWindow(int argc, char **argv)
{
    int SubTransPosX, SubTransPosY, SubTransWidth, SubTransHeight;
    long ValueMask;
    XSizeHints Hints;
    XSetWindowAttributes SetWinAttr;

    SetWinAttr.background_pixel = TransBackGroundPixel;
    SetWinAttr.border_pixel = TransBorderPixel;
    ValueMask = CWBackPixel | CWBorderPixel;

    Hints.flags = PMinSize | PMaxSize;
    Hints.x = Hints.y = 1;
    Hints.min_width = 100;
    Hints.max_width = 1000;
    Hints.min_height = 200;
    Hints.max_height = 1000;
    if (TransHasSize) {
	Hints.flags |= PSize;
	if (TransWidth < Hints.min_width)
	    TransWidth = Hints.min_width;
	if (TransWidth > Hints.max_width)
	    TransWidth = Hints.max_width;
	if (TransHeight < Hints.min_height)
	    TransHeight = Hints.min_height;
	if (TransHeight > Hints.max_height)
	    TransHeight = Hints.max_height;
	Hints.width = TransWidth;
	Hints.height = TransHeight;
    }
    else {
	Hints.flags |= PSize;
	Hints.width = TransWidth = DEFAULT_TRANS_WIDTH;
	Hints.height = TransHeight = DEFAULT_TRANS_HEIGHT;
    }
    if (TransHasPos) {
	Hints.flags |= USPosition;
	Hints.x = TransPosX;
	Hints.y = TransPosY;
    }

    TransformWndw = XCreateWindow(XDisplay, XRoot,
				  TransPosX, TransPosY,
				  TransWidth, TransHeight,
			          1, 0, CopyFromParent, CopyFromParent,
			          ValueMask, &SetWinAttr);

    XSetStandardProperties(XDisplay, TransformWndw,
			   RESOURCE_NAME, RESOURCE_NAME, None,
			   argv, argc,
			   &Hints);

    /* Set our own cursor: */
    XCursor = XCreateFontCursor(XDisplay, XC_hand1);
    XDefineCursor(XDisplay, TransformWndw, XCursor);
    if (TransCursorColor != NULL)
	XRecolorCursor(XDisplay, XCursor, TransCursorColor, &BlackColor);

    /* Now lets create the sub windows inside. Note we do not place them yet. */
    SubTransPosX = 0;
    SubTransPosY = TransHeight;
    SubTransWidth = TransWidth - SubTransPosX * 2;
    SubTransHeight = TransHeight / 25;

    /* OBJECT/SCREEN space toggle: */
    ObjScrTglWndw = SetTransformSubWindow(SubTransPosX, SubTransPosY,
					  SubTransWidth, SubTransHeight);

    /* PERSPECTIVE/ORTHOGRPHIC toggle: */
    PersOrthoTglWndw = SetTransformSubWindow(SubTransPosX, SubTransPosY,
					     SubTransWidth, SubTransHeight);
    PersOrthoZWndw = SetTransformSubWindow(SubTransPosX, SubTransPosY,
					     SubTransWidth, SubTransHeight);

    /* ROTATE: */
    RotateXWndw = SetTransformSubWindow(SubTransPosX, SubTransPosY,
					SubTransWidth, SubTransHeight);
    RotateYWndw = SetTransformSubWindow(SubTransPosX, SubTransPosY,
					SubTransWidth, SubTransHeight);
    RotateZWndw = SetTransformSubWindow(SubTransPosX, SubTransPosY,
					SubTransWidth, SubTransHeight);
    /* TRANSLATE: */
    TranslateXWndw = SetTransformSubWindow(SubTransPosX, SubTransPosY,
					   SubTransWidth, SubTransHeight);
    TranslateYWndw = SetTransformSubWindow(SubTransPosX, SubTransPosY,
					   SubTransWidth, SubTransHeight);
    TranslateZWndw = SetTransformSubWindow(SubTransPosX, SubTransPosY,
					   SubTransWidth, SubTransHeight);
    /* SCALE: */
    ScaleWndw = SetTransformSubWindow(SubTransPosX, SubTransPosY,
				      SubTransWidth, SubTransHeight);

    /* DEPTH CUE: */
    DepthCueWndw = SetTransformSubWindow(SubTransPosX, SubTransPosY,
					 SubTransWidth, SubTransHeight);

    /* SAVE MATRIX: */
    SaveMatrixWndw = SetTransformSubWindow(SubTransPosX, SubTransPosY,
					   SubTransWidth, SubTransHeight);

    /* PUSH MATRIX: */
    PushMatrixWndw = SetTransformSubWindow(SubTransPosX, SubTransPosY,
					   SubTransWidth, SubTransHeight);

    /* POP MATRIX: */
    PopMatrixWndw = SetTransformSubWindow(SubTransPosX, SubTransPosY,
					  SubTransWidth, SubTransHeight);

    /* QUIT: */
    QuitWndw = SetTransformSubWindow(SubTransPosX, SubTransPosY,
				     SubTransWidth, SubTransHeight);

    XSelectInput(XDisplay, TransformWndw, ExposureMask);
}

/*****************************************************************************
* Redraw a transformation window (after exposure or resize events).	     *
*****************************************************************************/
static void RedrawTransformWindow(void)
{
    int SubTransPosX, SubTransPosY,
        SizeChanged = FALSE;
    unsigned long SubTransWidth, SubTransHeight;
    long ValueMask;
    XSizeHints Hints;
    XWindowAttributes TransWindowAttr;
    XSetWindowAttributes SetWinAttr;

    XClearWindow(XDisplay, TransformWndw);

    /* Get the window attributes, and see if it is the same size or not. */
    XGetWindowAttributes(XDisplay, TransformWndw, &TransWindowAttr);
    if (TransWindowAttr.width != TransWidth ||
	TransWindowAttr.height != TransHeight) {
	SizeChanged = TRUE;
	TransWidth = TransWindowAttr.width;
	TransHeight = TransWindowAttr.height;
    }

    /* Now lets update the sub windows inside: */
    SubTransPosX = MIN(TransWidth / 10, 20);
    SubTransPosY =  TransHeight / 28;
    SubTransWidth = TransWidth - SubTransPosX * 2;
    SubTransHeight = TransHeight / 28;

    /* OBJECT/SCREEN space toggle: */
    RedrawTransformSubWindow(ObjScrTglWndw, SubTransPosX, SubTransPosY,
			     SubTransWidth, SubTransHeight, FALSE,
			     IGGlblTransformMode == IG_TRANS_OBJECT ?
			     "Object" : "Screen");
    SubTransPosY += SubTransHeight * 2;

    /* PERSPECTIVE/ORTHOGRAPHIC toggle: */
    RedrawTransformSubWindow(PersOrthoTglWndw, SubTransPosX, SubTransPosY,
			     SubTransWidth, SubTransHeight, FALSE,
			     IGGlblViewMode == IG_VIEW_ORTHOGRAPHIC ?
			     "Orthographic" : "Perspective");
    SubTransPosY += SubTransHeight;
    RedrawTransformSubWindow(PersOrthoZWndw, SubTransPosX, SubTransPosY,
			     SubTransWidth, SubTransHeight, TRUE, NULL);
    SubTransPosY += SubTransHeight;
    DrawText(TransformWndw, "Z", SubTransPosX / 2,
	     SubTransPosY - SubTransHeight / 4, TransTextPixel);
    SubTransPosY += SubTransHeight;

    /* ROTATE: */
    DrawText(TransformWndw, "Rotate", TransWidth / 2, SubTransPosY,
	     TransTextPixel);
    SubTransPosY += SubTransHeight / 2;
    RedrawTransformSubWindow(RotateXWndw, SubTransPosX, SubTransPosY,
			     SubTransWidth, SubTransHeight, TRUE, NULL);
    SubTransPosY += SubTransHeight;
    DrawText(TransformWndw, "X", SubTransPosX / 2,
	     SubTransPosY - SubTransHeight / 4, TransTextPixel);
    RedrawTransformSubWindow(RotateYWndw, SubTransPosX, SubTransPosY,
			     SubTransWidth, SubTransHeight, TRUE, NULL);
    SubTransPosY += SubTransHeight;
    DrawText(TransformWndw, "Y", SubTransPosX / 2,
	     SubTransPosY - SubTransHeight / 4, TransTextPixel);
    RedrawTransformSubWindow(RotateZWndw, SubTransPosX, SubTransPosY,
			     SubTransWidth, SubTransHeight, TRUE, NULL);
    SubTransPosY += SubTransHeight;
    DrawText(TransformWndw, "Z", SubTransPosX / 2,
	     SubTransPosY - SubTransHeight / 4, TransTextPixel);

    /* TRANSLATE: */
    SubTransPosY += SubTransHeight;
    DrawText(TransformWndw, "Translate", TransWidth / 2, SubTransPosY,
	     TransTextPixel);
    SubTransPosY += SubTransHeight / 2;
    RedrawTransformSubWindow(TranslateXWndw, SubTransPosX, SubTransPosY,
			     SubTransWidth, SubTransHeight, TRUE, NULL);
    SubTransPosY += SubTransHeight;
    DrawText(TransformWndw, "X", SubTransPosX / 2,
	     SubTransPosY - SubTransHeight / 4, TransTextPixel);
    RedrawTransformSubWindow(TranslateYWndw, SubTransPosX, SubTransPosY,
			     SubTransWidth, SubTransHeight, TRUE, NULL);
    SubTransPosY += SubTransHeight;
    DrawText(TransformWndw, "Y", SubTransPosX / 2,
	     SubTransPosY - SubTransHeight / 4, TransTextPixel);
    RedrawTransformSubWindow(TranslateZWndw, SubTransPosX, SubTransPosY,
			     SubTransWidth, SubTransHeight, TRUE, NULL);
    SubTransPosY += SubTransHeight;
    DrawText(TransformWndw, "Z", SubTransPosX / 2,
	     SubTransPosY - SubTransHeight / 4, TransTextPixel);

    /* SCALE: */
    SubTransPosY += SubTransHeight;
    DrawText(TransformWndw, "Scale", TransWidth / 2, SubTransPosY,
	     TransTextPixel);
    SubTransPosY += SubTransHeight / 2;
    RedrawTransformSubWindow(ScaleWndw, SubTransPosX, SubTransPosY,
			     SubTransWidth, SubTransHeight, TRUE, NULL);

    /* DEPTH CUE: */
    SubTransPosY += SubTransHeight * 2;
    RedrawTransformSubWindow(DepthCueWndw, SubTransPosX, SubTransPosY,
			     SubTransWidth, SubTransHeight, FALSE,
			     IGGlblDepthCue ? "Depth Cue" : "No Depth Cue");

    /* SAVE MATRIX: */
    SubTransPosY += SubTransHeight * 2;
    RedrawTransformSubWindow(SaveMatrixWndw, SubTransPosX, SubTransPosY,
			     SubTransWidth, SubTransHeight, FALSE,
			     "Save Matrix");

    /* PUSH MATRIX: */
    SubTransPosY += SubTransHeight + SubTransHeight / 2;
    RedrawTransformSubWindow(PushMatrixWndw, SubTransPosX, SubTransPosY,
			     SubTransWidth, SubTransHeight, FALSE,
			     "Push Matrix");

    /* POP MATRIX: */
    SubTransPosY += SubTransHeight + SubTransHeight / 2;
    RedrawTransformSubWindow(PopMatrixWndw, SubTransPosX, SubTransPosY,
			     SubTransWidth, SubTransHeight, FALSE,
			     "Pop Matrix");

    /* QUIT: */
    SubTransPosY += SubTransHeight * 3;
    RedrawTransformSubWindow(QuitWndw, SubTransPosX, SubTransPosY,
			     SubTransWidth, SubTransHeight, FALSE, "Quit" );

    /* Save half of the window width so we can refer to the zero point on X */
    /* axes, which is the vertical line in the middle of the window:	    */
    SubWindowWidthState2 = SubTransWidth / 2;
    SubWindowHeightState2 = SubTransHeight / 2;

    GraphicFlush();
}

/*****************************************************************************
* Set up a transformation sub window.					     *
*****************************************************************************/
static Window SetTransformSubWindow(int SubTransPosX, int SubTransPosY,
			unsigned int SubTransWidth, unsigned int SubTransHeight)
{
    long ValueMask;
    XSetWindowAttributes SetWinAttr;
    Window Win;

    SetWinAttr.background_pixel = TransSubWinBackPixel;
    SetWinAttr.border_pixel = TransSubWinBorderPixel;
    SetWinAttr.bit_gravity = SetWinAttr.win_gravity = CenterGravity;
    ValueMask = CWBackPixel | CWBorderPixel | CWBitGravity | CWWinGravity;

    Win = XCreateWindow(XDisplay, TransformWndw,
			SubTransPosX, SubTransPosY,
			SubTransWidth, SubTransHeight,
			1, 0, CopyFromParent, CopyFromParent,
			ValueMask, &SetWinAttr);

    XSelectInput(XDisplay, Win, ButtonPressMask | Button1MotionMask);

    XMapWindow(XDisplay, Win);

    return Win;
}

/*****************************************************************************
* Redraw a transformation sub window.					     *
*****************************************************************************/
static void RedrawTransformSubWindow(Window Win,
    int SubTransPosX, int SubTransPosY,
    unsigned int SubTransWidth, unsigned int SubTransHeight,
    int DrawMiddleVertLine, char *DrawString)
{
    XGCValues values;

    XMoveResizeWindow(XDisplay, Win, SubTransPosX, SubTransPosY,
		                               SubTransWidth, SubTransHeight);
    if (DrawMiddleVertLine) {
	values.foreground = TransSubWinBorderPixel;
	XChangeGC(XDisplay, XTransGraphContext, GCForeground, &values);

	XDrawLine(XDisplay, Win, XTransGraphContext,
		  SubTransWidth / 2, 0, SubTransWidth / 2, SubTransHeight);
    }
    if (DrawString != NULL) {
	DrawText(Win, DrawString, SubTransWidth / 2, SubTransHeight / 2,
		 TransTextPixel);
    }
}

/*****************************************************************************
* Set up and draw a view window.				             *
*****************************************************************************/
static void SetViewWindow(void)
{
    static int AttributeList[] = { GLX_RGBA, None };
    int HasPos = FALSE;
    long PrefPos[4];
    XVisualInfo	*Vi;
    Colormap CMap;
    XSetWindowAttributes WinAttr;
    Window Win;
    XEvent event;

    if (sscanf(IGGlblViewPrefPos, "%ld,%ld,%ld,%ld",
	       &PrefPos[0], &PrefPos[1], &PrefPos[2], &PrefPos[3]) == 4)
	HasPos = TRUE;
    if (!HasPos) {
	PrefPos[0] = PrefPos[2] = 0;
	PrefPos[1] = PrefPos[3] = 500;
    }
    else {
	PrefPos[1] -= PrefPos[0];
	PrefPos[3] -= PrefPos[2];
    }

    Vi = glXChooseVisual(XDisplay, DefaultScreen(XDisplay), AttributeList);
    ViewCx = glXCreateContext(XDisplay, Vi, 0, GL_TRUE);
    CMap = XCreateColormap(XDisplay, RootWindow(XDisplay, Vi -> screen),
			   Vi -> visual, AllocNone);
    WinAttr.colormap = CMap;
    WinAttr.border_pixel = 0;
    WinAttr.event_mask = StructureNotifyMask;
    ViewWin = XCreateWindow(XDisplay, RootWindow(XDisplay, Vi -> screen),
			    PrefPos[0], PrefPos[2], PrefPos[1], PrefPos[3],
			    0, Vi -> depth, InputOutput, Vi -> visual,
			    CWBorderPixel|CWColormap|CWEventMask, &WinAttr);
    XMapWindow(XDisplay, ViewWin);
    XIfEvent(XDisplay, &event, WaitForNotify, (char*) ViewWin);
}

/*****************************************************************************
* Set up and draw a viewing window.					     *
*****************************************************************************/
void IGRedrawViewWindow(void)
{
    IPObjectStruct *PObj;
    GLdouble CrntView[16];
    int i, j, k;

    glXMakeCurrent(XDisplay, ViewWin, ViewCx);

    glDrawBuffer(IGGlblDoDoubleBuffer ? GL_BACK : GL_FRONT);

    /* Clear viewing area. */
    glClear(GL_COLOR_BUFFER_BIT |
	    (IGGlblDrawSolid ? GL_DEPTH_BUFFER_BIT : 0));

    /* activate zbuffer only if we are in solid drawing mode. */
    if (IGGlblDrawSolid) {
	static int
	    UpdateLightPos = FALSE;
	static GLfloat
	    LightPosition[4] = { 1.0, 2.0, 10.0, 0.0 },
	    LightAmbient[] = { 1.0, 1.0, 1.0, 1.0 },
	    LightDiffuse[] = { 1.0, 1.0, 1.0, 1.0 },
	    LightSpecular[] = { 1.0, 1.0, 1.0, 1.0 },
	    LModelAmbient[] = { 0.2, 0.2, 0.2, 1.0 };

	if (!UpdateLightPos) {
	    int i;
	    
	    for (i = 0; i < 4; i++)
		LightPosition[i] = IGGlblLightSrcPos[i];
	    UpdateLightPos = TRUE;
	}

	glLightfv(GL_LIGHT0, GL_POSITION, LightPosition);
	glLightfv(GL_LIGHT0, GL_AMBIENT, LightAmbient);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, LightDiffuse);
	glLightfv(GL_LIGHT0, GL_SPECULAR, LightSpecular);
	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, LModelAmbient);

	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
	glDepthFunc(GL_LESS);
	glEnable(GL_DEPTH_TEST);
	glClear(GL_DEPTH_BUFFER_BIT);
    }
    else {
	glDisable(GL_LIGHTING);
	glDisable(GL_LIGHT0);
	glDisable(GL_DEPTH_TEST);
    }

    if (IGGlblViewMode == IG_VIEW_PERSPECTIVE) {
	for (i = 0; i < 4; i++)
	    for (j = 0; j < 4; j++) {
		CrntView[i * 4 + j] = 0;
		for (k = 0; k < 4; k++)
		    CrntView[i * 4 + j] += IritPrsrViewMat[i][k] *
			                   IritPrsrPrspMat[k][j];
	    }
    }
    else {
	for (i = 0; i < 4; i++)
	    for (j = 0; j < 4; j++)
		CrntView[i * 4 + j] = IritPrsrViewMat[i][j];
    }
    glLoadMatrixd(CrntView);
    if (APX_EQ(IGGlblZMinClip, IGGlblZMaxClip))
        IGGlblZMaxClip += EPSILON;
    glOrtho(-1.0, 1.0, -1.0, 1.0, IGGlblZMinClip, IGGlblZMaxClip);

    for (PObj = IGGlblDisplayList; PObj != NULL; PObj = PObj -> Pnext)
	IGDrawObject(PObj);
    glFlush();

    if (IGGlblDoDoubleBuffer)
	glXSwapBuffers(XDisplay, ViewWin);
}

/******************************************************************************
* Handle X events							      *
******************************************************************************/
static IGGraphicEventType GetGraphicEvent(RealType *ChangeFactor)
{
    static int LastX;
    int Dx;
    XEvent Event;
    XWindowAttributes WinAttr;

    XMapWindow(XDisplay, TransformWndw);

    while (TRUE) {
	/* Maybe we have something in communication socket. */
	if (!IGGlblStandAlone &&
	    IGReadObjectsFromSocket(IGGlblViewMode, &IGGlblDisplayList))
	    IGRedrawViewWindow();

	if (XPending(XDisplay)) {
	    XNextEvent(XDisplay, &Event);

	    switch (Event.type) {
		case Expose:
	            /* Get rid of all Expose events in the queue. */
	            while (XCheckWindowEvent(XDisplay, Event.xbutton.window,
					     ExposureMask, &Event));
		    if (Event.xbutton.window == TransformWndw)
			RedrawTransformWindow();
		    else if (Event.xbutton.window == ViewWndw) {
			XGetWindowAttributes(XDisplay, ViewWndw, &WinAttr);
			ViewWidth = WinAttr.width;
			ViewHeight = WinAttr.height;
			IGRedrawViewWindow();
		    }
		    break;
		case ButtonPress:
		    LastX = Event.xbutton.x;
		    *ChangeFactor =
			((RealType) (LastX - SubWindowWidthState2)) /
			             SubWindowWidthState2;

		    if (Event.xbutton.window == ObjScrTglWndw) {
			XClearWindow(XDisplay, ObjScrTglWndw);
			IGGlblTransformMode =
			    IGGlblTransformMode == IG_TRANS_OBJECT ?
						   IG_TRANS_SCREEN :
						   IG_TRANS_OBJECT;
			DrawText(ObjScrTglWndw,
			    IGGlblTransformMode == IG_TRANS_OBJECT ? "Object" :
								     "Screen",
			    SubWindowWidthState2, SubWindowHeightState2,
			    TransTextPixel);
			return IG_EVENT_SCR_OBJ_TGL;
		    }
		    else if (Event.xbutton.window == PersOrthoTglWndw) {
			XClearWindow(XDisplay, PersOrthoTglWndw);
			IGGlblViewMode =
			    IGGlblViewMode == IG_VIEW_PERSPECTIVE ?
					      IG_VIEW_ORTHOGRAPHIC :
					      IG_VIEW_PERSPECTIVE;
			DrawText(PersOrthoTglWndw,
				 IGGlblViewMode == IG_VIEW_PERSPECTIVE ?
				     "Perspective" : "Orthographic",
				 SubWindowWidthState2, SubWindowHeightState2,
				 TransTextPixel);
			return IG_EVENT_PERS_ORTHO_TGL;
		    }
		    else if (Event.xbutton.window == PersOrthoZWndw) {
			return IG_EVENT_PERS_ORTHO_Z;
		    }
		    else if (Event.xbutton.window == RotateXWndw) {
			return IG_EVENT_ROTATE_X;
		    }
		    else if (Event.xbutton.window == RotateYWndw) {
			return IG_EVENT_ROTATE_Y;
		    }
		    else if (Event.xbutton.window == RotateZWndw) {
			return IG_EVENT_ROTATE_Z;
		    }
		    else if (Event.xbutton.window == TranslateXWndw) {
			return IG_EVENT_TRANSLATE_X;
		    }
		    else if (Event.xbutton.window == TranslateYWndw) {
			return IG_EVENT_TRANSLATE_Y;
		    }
		    else if (Event.xbutton.window == TranslateZWndw) {
			return IG_EVENT_TRANSLATE_Z;
		    }
		    else if (Event.xbutton.window == ScaleWndw) {
			return IG_EVENT_SCALE;
		    }
		    else if (Event.xbutton.window == DepthCueWndw) {
			XClearWindow(XDisplay, DepthCueWndw);
			IGGlblDepthCue = !IGGlblDepthCue;
			DrawText(DepthCueWndw,
				 IGGlblDepthCue ? "Depth Cue" : "No Depth Cue",
				 SubWindowWidthState2, SubWindowHeightState2,
				 TransTextPixel);
			return IG_EVENT_DEPTH_CUE;
		    }
		    else if (Event.xbutton.window == SaveMatrixWndw) {
			return IG_EVENT_SAVE_MATRIX;
		    }
		    else if (Event.xbutton.window == PushMatrixWndw) {
			return IG_EVENT_PUSH_MATRIX;
		    }
		    else if (Event.xbutton.window == PopMatrixWndw) {
			return IG_EVENT_POP_MATRIX;
		    }
		    else if (Event.xbutton.window == QuitWndw) {
			GraphicFlush();
			return IG_EVENT_QUIT;
		    }
		    break;
		case MotionNotify:
		    /* We may get events of movement in Y which are ignored. */
		    if (Event.xbutton.x - LastX == 0)
			break;

		    *ChangeFactor = ((RealType) (Event.xbutton.x - LastX)) /
							SubWindowWidthState2;
		    LastX = Event.xbutton.x;

		    if (Event.xbutton.window == PersOrthoZWndw) {
			return IG_EVENT_PERS_ORTHO_Z;
		    }
		    else if (Event.xbutton.window == RotateXWndw) {
			return IG_EVENT_ROTATE_X;
		    }
		    else if (Event.xbutton.window == RotateYWndw) {
			return IG_EVENT_ROTATE_Y;
		    }
		    else if (Event.xbutton.window == RotateZWndw) {
			return IG_EVENT_ROTATE_Z;
		    }
		    else if (Event.xbutton.window == TranslateXWndw) {
			return IG_EVENT_TRANSLATE_X;
		    }
		    else if (Event.xbutton.window == TranslateYWndw) {
			return IG_EVENT_TRANSLATE_Y;
		    }
		    else if (Event.xbutton.window == TranslateZWndw) {
			return IG_EVENT_TRANSLATE_Z;
		    }
		    else if (Event.xbutton.window == ScaleWndw) {
			return IG_EVENT_SCALE;
		    }
		    break;
		default:
		    fprintf(stderr,
			    "x11drvs: undefined event type %d.\n", Event.type);
	    }
	}
	IritSleep(10);
    }
}

/******************************************************************************
* Handle the event of a pop up window.					      *
******************************************************************************/
int IGHandleState(int State, int Refresh)
{
    int UpdateView = TRUE;

    switch (State) {
	case IG_STATE_SCR_OBJ_TGL:
	    IGGlblTransformMode = IGGlblTransformMode == IG_TRANS_OBJECT ?
							 IG_TRANS_SCREEN :
							 IG_TRANS_OBJECT;
	    RedrawTransformWindow();
	    UpdateView = FALSE;
	    break;
	case IG_STATE_PERS_ORTHO_TGL:
	    IGGlblViewMode = IGGlblViewMode == IG_VIEW_PERSPECTIVE ?
					       IG_VIEW_ORTHOGRAPHIC :
					       IG_VIEW_PERSPECTIVE;
	    RedrawTransformWindow();
	    break;
	case IG_STATE_DRAW_SOLID:
	    IGGlblDrawSolid = !IGGlblDrawSolid;
	    /* And fall thru to disable the depth cueing. */
	    IGGlblDepthCue = TRUE; 
	case IG_STATE_DEPTH_CUE:
	    IGGlblDepthCue = !IGGlblDepthCue;
	    break;
	case IG_STATE_DOUBLE_BUFFER:
	    IGGlblDoDoubleBuffer = !IGGlblDoDoubleBuffer;
	    break;
	case IG_STATE_ANTI_ALIASING:
	    IGGlblAntiAliasing = !IGGlblAntiAliasing;
	    break;
	default:
	    UpdateView = IGDefaultStateHandler(State, Refresh);
	    break;
    }

    IGCreateStateMenu();

    return UpdateView;
}

/******************************************************************************
* Draw text centered at the given position.				      *
******************************************************************************/
static void DrawText(char *Str, long PosX, long PosY)
{
#ifdef TTTT
    long Width = strwidth(Str);

    cmov2s(PosX - Width / 2, PosY - (getheight() / 2 - getdescender()));
    charstr(Str);
#endif
}

/****************************************************************************
* Make noise function.							    *
****************************************************************************/
void IGIritBeep(void)
{
}
