/*****************************************************************************
*   An X11 driver using only libx11.a.					     *
******************************************************************************
* (C) Gershon Elber, Technion, Israel Institute of Technology                *
******************************************************************************
* Written by:  Gershon Elber				Ver 0.1, June 1993.  *
*****************************************************************************/

#ifdef __hpux
typedef char *caddr_t;	       /* Awful kludge. Let me know of a better way. */
#endif /* __hpux */

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

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <ctype.h>

#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"
#include "x11drvs.h"

#define X11_FONT_NAME		"8x13"

#define DEFAULT_TRANS_WIDTH	200
#define DEFAULT_TRANS_HEIGHT	500

/* If your X does not recognizes XrmGetDatabase, uncomment the following */
/* and try again:							 */
/* #define XrmGetDatabase(XDisplay) ((XDisplay)->db)			 */

/* X global specific staff goes here: */
int ViewHasSize = FALSE,
    ViewHasPos = FALSE,
    ViewPosX = 0,
    ViewPosY = 0,
    XScreen;
unsigned int
    MaxColors = IG_MAX_COLOR,
    ViewBackGroundPixel,
    ViewBorderPixel,
    ViewTextPixel,
    ViewBorderWidth = 1,
    ViewWidth = DEFAULT_VIEW_WIDTH,
    ViewHeight = DEFAULT_VIEW_HEIGHT;
Colormap XColorMap;
XColor BlackColor,
    *ViewCursorColor = NULL;
Display *XDisplay;
Window XRoot, ViewWndw;
GC XViewGraphContext;

static GC XTransGraphContext;
static XFontStruct *XLoadedFont;
static XColor
    *TransCursorColor = NULL;
static unsigned long
    TransBackGroundPixel,
    TransBorderPixel,
    TransTextPixel,
    TransSubWinBackPixel,
    TransSubWinBorderPixel;
static int
    XFontYOffsetToCenter = 0,
    TransHasSize = FALSE,
    TransHasPos = FALSE,
    TransPosX = 0,
    TransPosY = 0;
static unsigned int
    TransWidth = DEFAULT_TRANS_WIDTH,
    TransHeight = DEFAULT_TRANS_HEIGHT;

/* X Colors to be used for viewed object (see also iritgrap.h): */
static int XViewColorDefs[IG_MAX_COLOR + 1][3] =
{
    {     0,     0,     0 },  /* 0. IG_IRIT_BLACK */
    {     0,     0, 43350 },  /* 1. IG_IRIT_BLUE */
    {     0, 43350,     0 },  /* 2. IG_IRIT_GREEN */
    {     0, 43350, 43350 },  /* 3. IG_IRIT_CYAN */
    { 43350,     0,     0 },  /* 4. IG_IRIT_RED */
    { 43350,     0, 43350 },  /* 5. IG_IRIT_MAGENTA */
    { 43350, 43350,     0 },  /* 6. IG_IRIT_BROWN */
    { 43350, 43350, 43350 },  /* 7. IG_IRIT_LIGHTGREY */
    { 21675, 21675, 21675 },  /* 8. IG_IRIT_DARKGRAY */
    { 21675, 21675, 65535 },  /* 9. IG_IRIT_LIGHTBLUE */
    { 21675, 65535, 21675 },  /* 10. IG_IRIT_LIGHTGREEN */
    { 21675, 65535, 65535 },  /* 11. IG_IRIT_LIGHTCYAN */
    { 65535, 21675, 21675 },  /* 12. IG_IRIT_LIGHTRED */
    { 65535, 21675, 65535 },  /* 13. IG_IRIT_LIGHTMAGENTA */
    { 65535, 65535, 21675 },  /* 14. IG_IRIT_YELLOW */
    { 65535, 65535, 65535 }   /* 15. IG_IRIT_WHITE */
};
XColor XViewColorsHigh[IG_MAX_COLOR + 1];
XColor XViewColorsLow[IG_MAX_COLOR + 1];

/* X transformation window staff goes here: */
static Window TransformWndw;
static Window ObjScrTglWndw;
static Window PersOrthoTglWndw, PersOrthoZWndw;
static Window RotateXWndw, RotateYWndw, RotateZWndw;
static Window TranslateXWndw, TranslateYWndw, TranslateZWndw;
static Window ScaleWndw;
static Window DepthCueWndw;
static Window AnimationWndw;
static Window SaveMatrixWndw;
static Window PushMatrixWndw;
static Window PopMatrixWndw;
static Window QuitWndw;

/* Viewing state variables: */
static int
    SubWindowWidthState2 = 1,
    SubWindowHeightState2 = 1;

static char *ReadOneXDefault(char *Entry);
static void ReadXDefaults(void);
static void SetTransformWindow(int argc, char **argv);
static void RedrawTransformWindow(void);
static Window SetTransformSubWindow(int SubTransPosX,
				    int SubTransPosY,
				    unsigned int SubTransWidth,
				    unsigned int SubTransHeight);
static void RedrawTransformSubWindow(Window Win,
				     int SubTransPosX,
				     int SubTransPosY,
				     unsigned int SubTransWidth,
				     unsigned int SubTransHeight,
				     int DrawMiddleVertLine,
				     char *DrawString);
static IGGraphicEventType GetGraphicEvent(RealType *ChangeFactor);
static void DrawText(Window Win,
		     char *Str,
		     int PosX,
		     int PosY,
		     unsigned long Color);

/*****************************************************************************
* DESCRIPTION:                                                               M
* Main module of x11drvs - X11 graphics driver of IRIT.             	     M
*                                                                            *
* PARAMETERS:                                                                M
*   argc, argv:  Command line.                                               M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:         Exit code.                                                  M
*                                                                            *
* KEYWORDS:                                                                  M
*   WinMain                                                                  M
*****************************************************************************/
void main(int argc, char **argv)
{
    int i;
    XGCValues values;
    RealType ChangeFactor[2];
    IGGraphicEventType Event;

    IGConfigureGlobals("x11drvs", argc, argv);

    /* Lets see if we can get access to the X server before we even start: */
    if ((XDisplay = (Display *) XOpenDisplay(NULL)) == NULL) {
	fprintf(stderr, "x11drvs: Failed to access X server, abored.\n");
        exit(-1);
    }
    if ((XLoadedFont = XLoadQueryFont(XDisplay, X11_FONT_NAME)) == NULL) {
	fprintf(stderr,
		"x11drvs: Failed to load required X font \"%s\", aborted.\n",
		X11_FONT_NAME);
	exit(-1);
    }
    XFontYOffsetToCenter = (XLoadedFont -> ascent - XLoadedFont -> descent + 1)
									/ 2;

    XScreen = DefaultScreen(XDisplay);
    XRoot = RootWindow(XDisplay, XScreen);
    XColorMap = DefaultColormap(XDisplay, XScreen);
    values.foreground = WhitePixel(XDisplay, XScreen);
    values.background = BlackPixel(XDisplay, XScreen);
    values.font = XLoadedFont -> fid;
    XTransGraphContext = XCreateGC(XDisplay, XRoot,
			      GCForeground | GCBackground | GCFont, &values);
    XViewGraphContext = XCreateGC(XDisplay, XRoot,
			      GCForeground | GCBackground, &values);
    
    if (XrmGetDatabase(XDisplay) == NULL)
	XGetDefault(XDisplay, "", "");
    ReadXDefaults();

    for (i = 0; i <= IG_MAX_COLOR; i++) {
	XViewColorsHigh[i].red   = XViewColorDefs[i][0];
	XViewColorsHigh[i].green = XViewColorDefs[i][1];
	XViewColorsHigh[i].blue  = XViewColorDefs[i][2];

	/* If fails to allocate the color - take WHITE instead. */
	if (!XAllocColor(XDisplay, XColorMap, &XViewColorsHigh[i]))
	    XViewColorsHigh[i].pixel = WhitePixel(XDisplay, XScreen);

	XViewColorsLow[i].red   = XViewColorDefs[i][0] / 2;
	XViewColorsLow[i].green = XViewColorDefs[i][1] / 2;
	XViewColorsLow[i].blue  = XViewColorDefs[i][2] / 2;

	/* If fails to allocate the color - take WHITE instead. */
	if (!XAllocColor(XDisplay, XColorMap, &XViewColorsLow[i]))
	    XViewColorsLow[i].pixel = WhitePixel(XDisplay, XScreen);
    }

    values.line_width = IGGlblLineWidth;
    XChangeGC(XDisplay, XViewGraphContext, GCLineWidth, &values);

    IGCreateStateMenu();

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

    sleep(1); /* Some systems get confused if we draw immediately. */

    XFlush(XDisplay);

    while ((Event = GetGraphicEvent(ChangeFactor)) != IG_EVENT_QUIT) {
	ChangeFactor[0] *= IGGlblChangeFactor;
	ChangeFactor[1] *= IGGlblChangeFactor;

	if (IGProcessEvent(Event, ChangeFactor))
	    IGRedrawViewWindow();
    }

    XFreeGC(XDisplay, XViewGraphContext);
    XFreeGC(XDisplay, XTransGraphContext);
    XUnloadFont(XDisplay, XLoadedFont -> fid);
    XCloseDisplay(XDisplay);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Optionally construct a state pop up menu for the driver, if has one.       M
*                                                                            *
* PARAMETERS:                                                                M
*   None                                                                     *
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IGCreateStateMenu                                                        M
*****************************************************************************/
void IGCreateStateMenu(void)
{
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Reads one default from X resource data base.				     *
*                                                                            *
* PARAMETERS:                                                                *
*   Entry:    String name of the defau;t to read.                            *
*                                                                            *
* RETURN VALUE:                                                              *
*   char *:   Value f found, NULL otherwise.                                 *
*****************************************************************************/
static char *ReadOneXDefault(char *Entry)
{
    XrmString Type;
    XrmValue Result;
    char Line[LINE_LEN_LONG];

    sprintf(Line, "%s.%s", RESOURCE_NAME, Entry);
    if (XrmGetResource(XrmGetDatabase(XDisplay), Line,
		       "Program.Name", &Type, &Result))
	return Result.addr;
    else
	return NULL;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Reads Defaults from X data base.					     *
*                                                                            *
* PARAMETERS:                                                                *
*   None                                                                     *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void ReadXDefaults(void)
{
    int i;
    XColor Color;
    char *TransBackGroundColor = ReadOneXDefault("Trans.BackGround"),
         *TransBorderColor = ReadOneXDefault("Trans*BorderColor"),
         *TransBorderWidthStr = ReadOneXDefault("Trans*BorderWidth"),
         *TransTextColor = ReadOneXDefault("Trans.TextColor"),
         *TransSubWinBackColor = ReadOneXDefault("Trans.SubWin.BackGround"),
         *TransSubWinBorderColor = ReadOneXDefault("Trans.SubWin.BorderColor"),
         *TransSubWinBorderWidthStr = ReadOneXDefault("Trans.SubWin.BorderWidth"),
         *TransGeometry = ReadOneXDefault("Trans.Geometry"),
         *TransCursorColorStr = ReadOneXDefault("Trans.CursorColor"),
         *ViewBackGroundColor = ReadOneXDefault("View.BackGround"),
         *ViewTextColor = ReadOneXDefault("View.TextColor"),
         *ViewBorderColor = ReadOneXDefault("View.BorderColor"),
         *ViewBorderWidthStr = ReadOneXDefault("View.BorderWidth"),
         *ViewGeometry = ReadOneXDefault("View.Geometry"),
         *ViewCursorColorStr = ReadOneXDefault("View.CursorColor"),
         *MaxColorsStr = ReadOneXDefault("MaxColors");

    if (XParseColor(XDisplay, XColorMap, "Black", &BlackColor))
	XAllocColor(XDisplay, XColorMap, &BlackColor);

    if (TransBackGroundColor != NULL &&
	XParseColor(XDisplay, XColorMap, TransBackGroundColor, &Color) &&
	XAllocColor(XDisplay, XColorMap, &Color))
	TransBackGroundPixel = Color.pixel;
    else
	TransBackGroundPixel = BlackPixel(XDisplay, XScreen);

    if (TransBorderColor != NULL &&
	XParseColor(XDisplay, XColorMap, TransBorderColor, &Color) &&
	XAllocColor(XDisplay, XColorMap, &Color))
	TransBorderPixel = Color.pixel;
    else
	TransBorderPixel = WhitePixel(XDisplay, XScreen);

    if (TransTextColor != NULL &&
	XParseColor(XDisplay, XColorMap, TransTextColor, &Color) &&
	XAllocColor(XDisplay, XColorMap, &Color))
	TransTextPixel = Color.pixel;
    else
	TransTextPixel = WhitePixel(XDisplay, XScreen);

    if (TransSubWinBackColor != NULL &&
	XParseColor(XDisplay, XColorMap, TransSubWinBackColor, &Color) &&
	XAllocColor(XDisplay, XColorMap, &Color))
	TransSubWinBackPixel = Color.pixel;
    else
	TransSubWinBackPixel = BlackPixel(XDisplay, XScreen);

    if (TransSubWinBorderColor != NULL &&
	XParseColor(XDisplay, XColorMap, TransSubWinBorderColor, &Color) &&
	XAllocColor(XDisplay, XColorMap, &Color))
	TransSubWinBorderPixel = Color.pixel;
    else
	TransSubWinBorderPixel = WhitePixel(XDisplay, XScreen);

    if (IGGlblTransPrefPos &&
	sscanf(IGGlblTransPrefPos, "%d,%d,%d,%d",
	       &TransPosX, &TransWidth, &TransPosY, &TransHeight) == 4) {
	TransWidth -= TransPosX;
	TransHeight -= TransPosY;
	TransHasSize = TransHasPos = TRUE;
    }
    else if ((IGGlblTransPrefPos == NULL || strlen(IGGlblViewPrefPos) == 0) &&
	     TransGeometry) {
	i = XParseGeometry(TransGeometry, &TransPosX, &TransPosY,
		                          &TransWidth, &TransHeight);
	TransHasPos = i & XValue && i & YValue;
	TransHasSize =  i & WidthValue && i & HeightValue;
    }
    else
        TransHasSize = TransHasPos = FALSE;

    if (TransCursorColorStr != NULL &&
	XParseColor(XDisplay, XColorMap, TransCursorColorStr, &Color) &&
	XAllocColor(XDisplay, XColorMap, &Color)) {
	TransCursorColor = (XColor *) IritMalloc(sizeof(XColor));
	*TransCursorColor = Color;
    }
    else
	TransCursorColor = NULL;

    if (ViewBackGroundColor &&
	XParseColor(XDisplay, XColorMap, ViewBackGroundColor, &Color) &&
	XAllocColor(XDisplay, XColorMap, &Color))
	ViewBackGroundPixel = Color.pixel;
    else
	ViewBackGroundPixel = BlackPixel(XDisplay, XScreen);

    if (ViewBorderColor &&
	XParseColor(XDisplay, XColorMap, ViewBorderColor, &Color) &&
	XAllocColor(XDisplay, XColorMap, &Color))
	ViewBorderPixel = Color.pixel;
    else
	ViewBorderPixel = WhitePixel(XDisplay, XScreen);

    if (ViewTextColor != NULL &&
	XParseColor(XDisplay, XColorMap, ViewTextColor, &Color) &&
	XAllocColor(XDisplay, XColorMap, &Color))
	ViewTextPixel = Color.pixel;
    else
	ViewTextPixel = WhitePixel(XDisplay, XScreen);

    if (ViewBorderWidthStr)
	ViewBorderWidth = atoi(ViewBorderWidthStr);
    else
	ViewBorderWidth = 1;

    if (IGGlblViewPrefPos &&
	sscanf(IGGlblViewPrefPos, "%d,%d,%d,%d",
	       &ViewPosX, &ViewWidth, &ViewPosY, &ViewHeight) == 4) {
	ViewWidth -= ViewPosX;
	ViewHeight -= ViewPosY;
	ViewHasSize = ViewHasPos = TRUE;
    }
    else if ((IGGlblViewPrefPos == NULL || strlen(IGGlblViewPrefPos) == 0) &&
	     ViewGeometry) {
	i = XParseGeometry(ViewGeometry, &ViewPosX, &ViewPosY,
		                         &ViewWidth, &ViewHeight);
	ViewHasPos = i & XValue && i & YValue;
	ViewHasSize = i & WidthValue && i & HeightValue;
    }
    else
	ViewHasSize = ViewHasPos = FALSE;

    if (ViewCursorColorStr != NULL &&
	XParseColor(XDisplay, XColorMap, ViewCursorColorStr, &Color) &&
	XAllocColor(XDisplay, XColorMap, &Color)) {
	ViewCursorColor = (XColor *) IritMalloc(sizeof(XColor));
	*ViewCursorColor = Color;
    }
    else
	ViewCursorColor = NULL;

    if (MaxColorsStr)
	MaxColors = atoi(MaxColorsStr);
    else
	MaxColors = IG_MAX_COLOR;

}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Sets up and draw a transformation window.				     *
*                                                                            *
* PARAMETERS:                                                                *
*   argc, argv:   Command line.						     *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
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 = 2000;
    Hints.min_height = 200;
    Hints.max_height = 2000;
    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);

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

    /* ANIMATION: */
    AnimationWndw = 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);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Redraws the transformation window.					     M
*                                                                            *
* PARAMETERS:                                                                M
*   None                                                                     M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   RedrawTransformWindow                                                    M
*****************************************************************************/
static void RedrawTransformWindow(void)
{
    int SubTransPosX, SubTransPosY, SubTransWidth, SubTransHeight;
    XWindowAttributes TransWindowAttr;

    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) {
	TransWidth = TransWindowAttr.width;
	TransHeight = TransWindowAttr.height;
    }

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

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

    /* ANIMATION: */
    SubTransPosY += SubTransHeight * 2;
    RedrawTransformSubWindow(AnimationWndw, SubTransPosX, SubTransPosY,
			     SubTransWidth, SubTransHeight, FALSE,
			     "Animation");

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

    XFlush(XDisplay);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Sets up a transformation sub window.					     *
*                                                                            *
* PARAMETERS:                                                                *
*   SubTransPosX, SubTransPosY:      Location of the subwindows.	     *
*   SubTransWidth, SubTransHeight:   Dimensions of the subwindow.	     *
*                                                                            *
* RETURN VALUE:                                                              *
*   Window:      Handle on constructed 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;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Redraws a transformation sub window.					     *
*                                                                            *
* PARAMETERS:                                                                *
*   Win:			     A handle on transformation window.      *
*   SubTransPosX, SubTransPosY:      Location of the subwindows.	     *
*   SubTransWidth, SubTransHeight:   Dimensions of the subwindow.	     *
*   DrawMiddleVertLine:              Do we need a vertical middle line?      *
*   DrawString:                      Any string to draw inside?              *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
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);
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Handles input events                                                       *
*                                                                            *
* PARAMETERS:                                                                *
*   ChangeFactor:        A continuous numeric value between -1 and 1. This   *
*			 value will be used to set amount of event such as   *
*			 rotation or translation. In some events it can      *
*			 contain a vector value.			     *
*                                                                            *
* RETURN VALUE:                                                              *
*   IGGraphicEventType:  Type of new event.                                  *
*****************************************************************************/
static IGGraphicEventType GetGraphicEvent(RealType *ChangeFactor)
{
    static int LastX, LastY;
    static IGGraphicEventType LastEvent;
    XEvent Event;
    XWindowAttributes WinAttr;

    ChangeFactor[0] = ChangeFactor[1] = 0.0;

    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 == ViewWndw) {
			LastY = Event.xbutton.y;
			ChangeFactor[0] = ChangeFactor[1] = 0.0;
			switch (Event.xbutton.button) {
			    case 1:
			        return (LastEvent = IG_EVENT_ROTATE);
			    case 3:
			        return (LastEvent = IG_EVENT_TRANSLATE);
			}
		    }
		    else 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 == AnimationWndw) { 
			XClearWindow(XDisplay, AnimationWndw);
			DrawText(AnimationWndw, "Animation",
				 SubWindowWidthState2, SubWindowHeightState2,
				 TransTextPixel);
			return IG_EVENT_ANIMATION;
                    }
		    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) {
			XFlush(XDisplay);
			return IG_EVENT_QUIT;
		    }
		    break;
		case MotionNotify:
		    if (Event.xbutton.window == ViewWndw) {
			if (fabs(Event.xbutton.x - LastX) > 3 ||
			    fabs(Event.xbutton.y - LastY) > 3) {
			    ChangeFactor[0] = (Event.xbutton.x - LastX) / 50.0;
			    ChangeFactor[1] = (LastY - Event.xbutton.y) / 50.0;
			    return LastEvent;
			}
			break;
		    }

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

/*****************************************************************************
* DESCRIPTION:                                                               M
* Handles the events of the pop up window.                                   M
*                                                                            *
* PARAMETERS:                                                                M
*   State:      Event to handle.                                             M
*   Refresh:    Do we need to refresh the screen according to what we know   M
*		on entry.						     M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:        TRUE, if we need to refresh the screen.                      M
*                                                                            *
* KEYWORDS:                                                                  M
*   IGHandleState                                                            M
*****************************************************************************/
int IGHandleState(int State, int Refresh)
{
    int UpdateView = TRUE;

    switch (State) {
	case IG_STATE_DEPTH_CUE:
	    XClearWindow(XDisplay, DepthCueWndw);
	    IGGlblDepthCue = !IGGlblDepthCue;
	    DrawText(DepthCueWndw,
		     IGGlblDepthCue ? "Depth Cue" : "No Depth Cue",
		     SubWindowWidthState2, SubWindowHeightState2,
		     TransTextPixel);

	    break;
	case IG_STATE_DOUBLE_BUFFER:
	    IGGlblDoDoubleBuffer = !IGGlblDoDoubleBuffer;
	    break;
	default:
	    UpdateView = IGDefaultStateHandler(State, Refresh);
	    break;
    }

    IGCreateStateMenu();

    return UpdateView;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Draws text centered at the given position.				     *
*                                                                            *
* PARAMETERS:                                                                *
*   Win:        A handle on window to draw on.                               *
*   Str:        String to draw.                                              *
*   PosX, PosY: Location to draw at.                                         *
*   Color:      Of drawn text.                                               *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void DrawText(Window Win,
		     char *Str,
		     int PosX,
		     int PosY,
		     unsigned long Color)
{
    int Len = strlen(Str),
        Width = XTextWidth(XLoadedFont, Str, Len);
    XGCValues values;

    values.foreground = Color;
    XChangeGC(XDisplay, XTransGraphContext, GCForeground, &values);

    XDrawString(XDisplay, Win, XTransGraphContext, PosX - Width / 2,
		PosY + XFontYOffsetToCenter, Str, Len);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Make some sound.                                                           M
*                                                                            *
* PARAMETERS:                                                                M
*   None                                                                     M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IGIritBeep                                                               M
*****************************************************************************/
void IGIritBeep(void)
{
    XBell(XDisplay, 0);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Should we stop this animation. Senses the event queue of X11.            M
*                                                                            *
* PARAMETERS:                                                                M
*   Anim:     The animation to abort.                                        M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:      TRUE if we need to abort, FALSE otherwise.                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   AnimCheckInterrupt                                                       M
*****************************************************************************/
int AnimCheckInterrupt(AnimationStruct *Anim)
{
    if (XPending(XDisplay)) {
	Anim -> StopAnim = TRUE;
	fprintf(stderr, "\nAnimation was interrupted by the user.\n");
	return TRUE;
    }
    else
        return FALSE;
}
