/*****************************************************************************
*   An X11 driver using only libx11.a.					     *
*									     *
* 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 */
#ifdef sgi
#define FUNCPROTO
#endif /* sgi */

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

#define X11_FONT_NAME		"8x13"

#define RESOURCE_NAME		"irit"

#define DEFAULT_TRANS_WIDTH	200
#define DEFAULT_TRANS_HEIGHT	500
#define DEFAULT_VIEW_WIDTH	400
#define DEFAULT_VIEW_HEIGHT	400

#define X11_MAP_X_COORD(x) (((int) ((x + 1.0) * ViewWidth)) / 2)
#define X11_MAP_Y_COORD(y) (((int) ((1.0 - y) * ViewHeight)) / 2)

/* X global specific staff goes here: */
static Display *XDisplay;
static int XScreen;
static Window XRoot;
static Colormap XColorMap;
static GC XTransGraphContext;
static GC XViewGraphContext;
static Visual *XVisual;
static XImage *XImageBuffer;
static Pixmap XIcon;
static Cursor XCursor;
static XColor BlackColor;
static XFontStruct *XLoadedFont;
static XGCValues CrntColorHighIntensity, CrntColorLowIntensity;
static XColor
    *TransCursorColor = NULL,
    *ViewCursorColor = NULL;
static unsigned long
    TransBackGroundPixel,
    TransBorderPixel,
    TransTextPixel,
    TransSubWinBackPixel,
    TransSubWinBorderPixel,
    ViewBackGroundPixel,
    ViewBorderPixel,
    ViewTextPixel;
static int
    XFontYOffsetToCenter = 0,
    TransHasSize = FALSE,
    TransHasPos = FALSE,
    TransPosX = 0,
    TransPosY = 0,
    ViewHasSize = FALSE,
    ViewHasPos = FALSE,
    ViewPosX = 0,
    ViewPosY = 0,
    CurrentXPosition = 0,
    CurrentYPosition = 0;
static unsigned int
    TransBorderWidth = 1,
    TransSubWinBorderWidth = 1,
    TransWidth = DEFAULT_TRANS_WIDTH,
    TransHeight = DEFAULT_TRANS_HEIGHT,
    ViewBorderWidth = 1,
    ViewWidth = DEFAULT_VIEW_WIDTH,
    ViewHeight = DEFAULT_VIEW_HEIGHT,
    MaxColors = IG_MAX_COLOR;

/* 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 Viewing window staff goes here: */
static Window ViewWndw;

/* 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 SaveMatrixWndw;
static Window PushMatrixWndw;
static Window PopMatrixWndw;
static Window QuitWndw;

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

static void SetColorIndex(int c);
static void SetColorRGB(int Color[3]);
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 void SetViewWindow(int argc, char **argv);
static void RedrawViewWindow(void);
static void GraphicFlush(void);
static IGGraphicEventType GetGraphicEvent(RealType *ChangeFactor);
static void DrawText(Window Win, char *Str, int PosX, int PosY,
		     unsigned long Color);

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

    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);
    XVisual = DefaultVisual(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 (XDisplay->db == 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);
    }

    IGCreateStateMenu();

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

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

    GraphicFlush();

    if (!IGGlblStandAlone)
	SocClientCreateSocket();

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

    if (!IGGlblStandAlone)
	SocClientCloseSocket();

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

/*****************************************************************************
* Construct global pop up menu.						     *
*****************************************************************************/
void IGCreateStateMenu(void)
{
}

/****************************************************************************
* Routine to move in 2D normalized (-1..1) view space.			    *
****************************************************************************/
void IGMoveTo2D(RealType X, RealType Y)
{
    CurrentXPosition = X11_MAP_X_COORD(X);
    CurrentYPosition = X11_MAP_Y_COORD(Y);
}

/****************************************************************************
* Routine to draw in 2D normalized (-1..1) view space.			    *
****************************************************************************/
void IGLineTo2D(RealType X, RealType Y)
{
    int NewX, NewY;

    XDrawLine(XDisplay, ViewWndw, XViewGraphContext,
	      CurrentXPosition,
	      CurrentYPosition,
	      NewX = X11_MAP_X_COORD(X),
	      NewY = X11_MAP_Y_COORD(Y));

    CurrentXPosition = NewX;
    CurrentYPosition = NewY;
}

/****************************************************************************
* Routine to set the intensity of a color (high or low).		    *
****************************************************************************/
void IGSetColorIntensity(int High)
{
    XChangeGC(XDisplay, XViewGraphContext, GCForeground,
	      High ? &CrntColorHighIntensity : &CrntColorLowIntensity);
    IGGlblIntensityHighState = High;
}

/****************************************************************************
* 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 color according to the given color index.		    *
****************************************************************************/
static void SetColorIndex(int color)
{
    if (color >= MaxColors)
	color = IG_IRIT_WHITE;

    CrntColorHighIntensity.foreground = XViewColorsHigh[color].pixel;
    CrntColorLowIntensity.foreground = XViewColorsLow[color].pixel;
    XChangeGC(XDisplay, XViewGraphContext, GCForeground,
	      &CrntColorHighIntensity);
    IGGlblIntensityHighState = TRUE;
}

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

    XClr.red   = (Color[0] << 8);
    XClr.green = (Color[1] << 8);
    XClr.blue  = (Color[2] << 8);

    /* If fails to allocate the color - take WHITE instead. */
    if (!XAllocColor(XDisplay, XColorMap, &XClr)) {
	fprintf(stderr,
		"x11drvs: Failed to allocate color, selected WHITE instead\n");
	XClr.pixel = WhitePixel(XDisplay, XScreen);
    }
    CrntColorHighIntensity.foreground = XClr.pixel;

    XClr.red   = (Color[0] << 7);
    XClr.green = (Color[1] << 7);
    XClr.blue  = (Color[2] << 7);

    /* If fails to allocate the color - take WHITE instead. */
    if (!XAllocColor(XDisplay, XColorMap, &XClr)) {
	fprintf(stderr,
		"x11drvs: Failed to allocate color, selected WHITE instead\n");
	XClr.pixel = WhitePixel(XDisplay, XScreen);
    }
    CrntColorLowIntensity.foreground = XClr.pixel;

    XChangeGC(XDisplay, XViewGraphContext, GCForeground,
	      &CrntColorHighIntensity);
    IGGlblIntensityHighState = TRUE;
}

/*****************************************************************************
* Read one default from X resource data base.				     *
*****************************************************************************/
static char *ReadOneXDefault(char *Entry)
{
    XrmString Type;
    XrmValue Result;
    char Line[LINE_LEN_LONG];

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

/*****************************************************************************
* Read Defaults from X data base.					     *
*****************************************************************************/
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 (TransBorderWidthStr)
	TransBorderWidth = atoi(TransBorderWidthStr);
    else
	TransBorderWidth = 1;

    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 (TransSubWinBorderWidthStr)
	TransSubWinBorderWidth = atoi(TransSubWinBorderWidthStr);
    else
	TransSubWinBorderWidth = 1;

    if (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 (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;

}

/*****************************************************************************
* 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 a view window.							     *
*****************************************************************************/
static void SetViewWindow(int argc, char **argv)
{
    long ValueMask;
    XSizeHints Hints;
    XSetWindowAttributes SetWinAttr;

    SetWinAttr.background_pixel = ViewBackGroundPixel;
    SetWinAttr.border_pixel = ViewBorderPixel;
    ValueMask = CWBackPixel | CWBorderPixel;

    Hints.flags = PMinSize | PMaxSize;
    Hints.x = Hints.y = 1;
    Hints.min_width = 50;
    Hints.max_width = 1000;
    Hints.min_height = 50;
    Hints.max_height = 1000;
    if (ViewHasSize) {
	Hints.flags |= PSize;
	if (ViewWidth < Hints.min_width)
	    ViewWidth = Hints.min_width;
	if (ViewWidth > Hints.max_width)
	    ViewWidth = Hints.max_width;
	if (ViewHeight < Hints.min_height)
	    ViewHeight = Hints.min_height;
	if (ViewHeight > Hints.max_height)
	    ViewHeight = Hints.max_height;
	Hints.width = ViewWidth;
	Hints.height = ViewHeight;
    }
    else {
	Hints.flags |= PSize;
	Hints.width = ViewWidth = DEFAULT_VIEW_WIDTH;
	Hints.height = ViewHeight = DEFAULT_VIEW_HEIGHT;
    }
    if (ViewHasPos) {
	Hints.flags |= USPosition;
	Hints.x = ViewPosX;
	Hints.y = ViewPosY;
    }

    ViewWndw = XCreateWindow(XDisplay, XRoot,
			     ViewPosX, ViewPosY,
			     ViewWidth, ViewHeight,
			     1, 0, CopyFromParent, CopyFromParent,
			     ValueMask, &SetWinAttr);

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

    /* Set our own cursor: */
    XCursor = XCreateFontCursor(XDisplay, XC_iron_cross);
    XDefineCursor(XDisplay, ViewWndw, XCursor);
    if (ViewCursorColor != NULL)
	XRecolorCursor(XDisplay, XCursor, ViewCursorColor, &BlackColor);

    XSelectInput(XDisplay, ViewWndw, ExposureMask | ButtonPressMask);
    
    XMapWindow(XDisplay, ViewWndw);
}

/*****************************************************************************
* Redraw the view window.						     *
*****************************************************************************/
static void RedrawViewWindow(void)
{
    IPObjectStruct *PObj;

    XClearWindow(XDisplay, ViewWndw);

    switch (IGGlblViewMode) {		 /* Update the current view. */
	case IG_VIEW_ORTHOGRAPHIC:
	    GEN_COPY(IGGlblCrntViewMat, IritPrsrViewMat, sizeof(MatrixType));
	    break;
	case IG_VIEW_PERSPECTIVE:
	    MatMultTwo4by4(IGGlblCrntViewMat, IritPrsrViewMat,
							IritPrsrPrspMat);
	    break;
    }

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

/******************************************************************************
* Flush output of graphic command.					      *
******************************************************************************/
static void GraphicFlush(void)
{
    XFlush(XDisplay);
}

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

	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;
			RedrawViewWindow();
		    }
		    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;
    XGCValues values;

    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;
	case IG_STATE_WIDER_LINES:
	    IGGlblLineWidth *= 2;
	    values.line_width = IGGlblLineWidth;
	    XChangeGC(XDisplay, XViewGraphContext, GCLineWidth, &values);
	    break;
	case IG_STATE_NARROW_LINES:
	    IGGlblLineWidth /= 2;
	    if (IGGlblLineWidth < 1)
		IGGlblLineWidth = 1;
	    values.line_width = IGGlblLineWidth;
	    XChangeGC(XDisplay, XViewGraphContext, GCLineWidth, &values);
	    break;
	default:
	    UpdateView = IGDefaultStateHandler(State, Refresh);
	    break;
    }

    IGCreateStateMenu();

    return UpdateView;
}

/******************************************************************************
* Draw text centered at the given position.				      *
******************************************************************************/
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);
}

/*****************************************************************************
* Routine to make some sound.						     *
*****************************************************************************/
void IGIritBeep(void)
{
    XBell(XDisplay, 0);
}
