/*****************************************************************************
*   General routines to	handle the graphic calls.			     *
* currently supported devices:						     *
* Input: Keyboard, mouse.						     *
* Output: X11 device.							     *
*									     *
* Written by:  Gershon Elber				Ver 0.1, Mar 1990.   *
*****************************************************************************/

#ifndef __MSDOS__

#define FONT_NAME		"8x13"
#define PROGRAM_NAME		"poly3d"
#define DEFAULT_TRANS_WIDTH	200
#define DEFAULT_TRANS_HEIGHT	500
#define DEFAULT_VIEW_WIDTH	400
#define DEFAULT_VIEW_HEIGHT	400

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/cursorfont.h>

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
#include "program.h"
#include "xgraphic.h"
#include "graphgng.h"
#include "graphgnl.h"
#include "viewobjg.h"

/* 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 XColor
    *TransCursorColor = NULL,
    *ViewCursorColor = NULL;
static unsigned long
    TransBackGroundPixel,
    TransBorderPixel,
    TransTextPixel,
    TransSubWinBackPixel,
    TransSubWinBorderPixel,
    ViewBackGroundPixel,
    ViewBorderPixel,
    ViewTextPixel;
static int
    XFontYOffsetToCenter = 0,
    AbortKeyPressed = FALSE,
    TransBorderWidth = 1,
    TransSubWinBorderWidth = 1,
    TransHasSize = FALSE,
    TransHasPos = FALSE,
    TransPosX = 0,
    TransPosY = 0,
    TransWidth = DEFAULT_TRANS_WIDTH,
    TransHeight = DEFAULT_TRANS_HEIGHT,
    ViewBorderWidth = 1,
    ViewHasSize = FALSE,
    ViewHasPos = FALSE,
    ViewPosX = 0,
    ViewPosY = 0,
    ViewWidth = DEFAULT_VIEW_WIDTH,
    ViewHeight = DEFAULT_VIEW_HEIGHT,
    CurrentXPosition = 0,
    CurrentYPosition = 0,
    InGraphicMode = FALSE;


/* X Colors to be used for viewed object (see also xgraphic.h): */
int XViewColorDefs[MAX_COLOR + 1][3] =
{
    {     0,     0,     0 },  /* 0. BLACK */
    {     0,     0, 43350 },  /* 1. BLUE */
    {     0, 43350,     0 },  /* 2. GREEN */
    {     0, 43350, 43350 },  /* 3. CYAN */
    { 43350,     0,     0 },  /* 4. RED */
    { 43350,     0, 43350 },  /* 5. MAGENTA */
    { 43350, 43350,     0 },  /* 6. BROWN */
    { 43350, 43350, 43350 },  /* 7. LIGHTGREY */
    { 21675, 21675, 21675 },  /* 8. DARKGRAY */
    { 21675, 21675, 65535 },  /* 9. LIGHTBLUE */
    { 21675, 65535, 21675 },  /* 10. LIGHTGREEN */
    { 21675, 65535, 65535 },  /* 11. LIGHTCYAN */
    { 65535, 21675, 21675 },  /* 12. LIGHTRED */
    { 65535, 21675, 65535 },  /* 13. LIGHTMAGENTA */
    { 65535, 65535, 21675 },  /* 14. YELLOW */
    { 65535, 65535, 65535 }   /* 15. WHITE */
};
XColor XViewColors[MAX_COLOR + 1];

/* X Viewing window staff goes here: */
static Window ViewWndw;

/* X transformation window staff goes here: */
static Window ObjScrTglWndw;
static Window PersOrthoTglWndw, PersOrthoZWndw;
static Window TransformWndw;
static Window RotateXWndw, RotateYWndw, RotateZWndw;
static Window TranslateXWndw, TranslateYWndw, TranslateZWndw;
static Window ScaleWndw;
static Window DepthCueWndw;
static Window SavePSWndw;
static Window SaveMatrixWndw;
static Window ResetMatrixWndw;
static Window QuitWndw;

/* Viewing state variables: */
static int
    ObjectSpaceState = FALSE,
    PerspectiveState = FALSE,
    DepthCueState = FALSE,
    SubWindowWidthState2 = 1,
    SubWindowHeightState2 = 1;

static int GGMapX(RealType x);
static int GGMapY(RealType y);
static void ReadXDefaults(void);
static void SetTransformWindow(int argc, char **argv);
static void RedrawTransformWindow(void);
static Window SetTransformSubWindow(int SubTransPosX, int SubTransPosY,
					int SubTransWidth, int SubTransHeight);
static void RedrawTransformSubWindow(Window Win,
    int SubTransPosX, int SubTransPosY, int SubTransWidth, int SubTransHeight,
    int DrawMiddleVertLine, char *DrawString);
static void SetViewWindow(int argc, char **argv);
static void DrawText(Window Win, char *Str, int PosX, int PosY,
		     unsigned long Color);

/****************************************************************************
* Routine to map real -1..1 normalized coordinates to screen X size.        *
****************************************************************************/
static int GGMapX(RealType x)
{
    return ((int) ((x + 1.0) * ViewWidth)) / 2;
}

/****************************************************************************
* Routine to map real -1..1 normalized coordinates to screen Y size.        *
****************************************************************************/
static int GGMapY(RealType y)
{
    return ((int) ((1.0 - y) * ViewHeight)) / 2;
}

/****************************************************************************
* Routine to move to a normalized point	between	-1..1 on both axes :	    *
****************************************************************************/
void GGMyMove(RealType x, RealType y)
{
    CurrentXPosition = GGMapX(x);
    CurrentYPosition = GGMapY(y);
}

/****************************************************************************
* Routine to draw to a normalized point	between	-1..1 on both axes :	    *
****************************************************************************/
void GGMyDraw(RealType x, RealType y)
{
    int NewX, NewY;

    XDrawLine(XDisplay, ViewWndw, XViewGraphContext,
	      CurrentXPosition, CurrentYPosition,
	      NewX = GGMapX(x), NewY = GGMapY(y));

    CurrentXPosition = NewX;
    CurrentYPosition = NewY;
}

/****************************************************************************
* Routine to set line style.						    *
****************************************************************************/
void GGMySetLineStyle(int Style)
{
    switch (Style) {
	case FULL_LINE_STYLE:
	    XSetLineAttributes(XDisplay, XViewGraphContext,
			       0, LineSolid, CapRound, JoinRound);
	    break;
	case DOTTED_LINE_STYLE:
	    XSetLineAttributes(XDisplay, XViewGraphContext,
			       0, LineOnOffDash, CapRound, JoinRound);
	    break;
    }
}

/****************************************************************************
* Routine to draw to a normelized point	between	-1..1 on both axes :	    *
****************************************************************************/
void GGMySetColor(int color)
{
    XGCValues values;

    if (color > MAX_COLOR) color = WHITE;

    values.foreground = XViewColors[color].pixel;
    XChangeGC(XDisplay, XViewGraphContext, GCForeground, &values);
}

/****************************************************************************
* Routine to reset all the system to starting condition	:		    *
****************************************************************************/
void GGInitGraph(int argc, char **argv)
{
    int i;
    XGCValues values;

    /* Lets see if we can get access to the X server before we even start: */
    if ((XDisplay = (Display *) XOpenDisplay(NULL)) == NULL) {
	fprintf(stderr, "Failed to access X server, abored.\n");
        MyExit(-1);
    }
    if ((XLoadedFont = XLoadQueryFont(XDisplay, FONT_NAME)) == NULL) {
	fprintf(stderr, "Failed to load required X font \"%s\", aborted.\n",
		FONT_NAME);
	MyExit(-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);
    
    ReadXDefaults();

    for (i=0; i<=MAX_COLOR; i++) {
	XViewColors[i].red   = XViewColorDefs[i][0];
	XViewColors[i].green = XViewColorDefs[i][1];
	XViewColors[i].blue  = XViewColorDefs[i][2];

	/* If fails to allocate the color - take WHITE instead. */
	if (!XAllocColor(XDisplay, XColorMap, &XViewColors[i]))
	    XViewColors[i].pixel = WhitePixel(XDisplay, XScreen);
    }

    SetTransformWindow(argc, argv);
    SetViewWindow(argc, argv);

    GraphicFlush();

    InGraphicMode = TRUE;
}

/****************************************************************************
* Routine to close and shutdown	graphic	mode :				    *
****************************************************************************/
void GGCloseGraph(void)
{
    if (!InGraphicMode) return;

    XFreeGC(XDisplay, XViewGraphContext);
    XFreeGC(XDisplay, XTransGraphContext);
    XUnloadFont(XDisplay, XLoadedFont -> fid);
    XCloseDisplay(XDisplay);

    InGraphicMode = FALSE;
}

/****************************************************************************
* Routine to draw a point on screen as marker +	with a title :		    *
****************************************************************************/
void GGDrawPoint(RealType p[], char title[], int PointColor)
{
    GGMySetColor(PointColor);
    GGMyMove(p[1] + POINT_SIZE, p[2]);
    GGMyDraw(p[1] - POINT_SIZE, p[2]);
    GGMyMove(p[1], p[2] + POINT_SIZE);
    GGMyDraw(p[1], p[2] - POINT_SIZE);

    GGXYPutStr(p[1] + POINT_TITLE, p[2] + POINT_TITLE, title);
}

/*****************************************************************************
* Routine to print an message on the given location:			     *
*****************************************************************************/
void GGXYPutStr(RealType x, RealType y, char *s)
{
    int Len = strlen(s),
        Width = XTextWidth(XLoadedFont, s, Len),
        PosX = GGMapX(x),
        PosY = GGMapY(y);
    XGCValues values;

    XDrawString(XDisplay, ViewWndw, XViewGraphContext, PosX - Width / 2,
		PosY + XFontYOffsetToCenter, s, Len);
}

/*****************************************************************************
*   Routine to clear given window area.					     *
* Note the viewport on exit is the window only!				     *
*****************************************************************************/
void GGClearWindow(RealType XMin, RealType YMin,
		   RealType XMax, RealType YMax, int WindowName)
{
    XClearWindow(XDisplay, ViewWndw);
}

/*****************************************************************************
*   Routine to clear all the screen.					     *
*****************************************************************************/
void GGClearAllScreen(void)
{
    XClearWindow(XDisplay, ViewWndw);
}

/*****************************************************************************
* Routine to make some sound with given Frequency, Time milliseconds:	     *
*****************************************************************************/
void GGTone(int Frequency, int Duration)
{
    XBell(XDisplay, 0);
}

/*****************************************************************************
* Read Defaults from X data base.					     *
*****************************************************************************/
static void ReadXDefaults(void)
{
    int i;
    XColor Color;
    char *TransBackGroundColor = XGetDefault(XDisplay, PROGRAM_NAME,
					                   "Trans.BackGround"),
         *TransBorderColor = XGetDefault(XDisplay, PROGRAM_NAME,
							  "Trans.BorderColor"),
         *TransBorderWidthStr = XGetDefault(XDisplay, PROGRAM_NAME,
							  "Trans.BorderWidth"),
         *TransTextColor = XGetDefault(XDisplay, PROGRAM_NAME,
							    "Trans.TextColor"),
         *TransSubWinBackColor = XGetDefault(XDisplay, PROGRAM_NAME,
						    "Trans.SubWin.BackGround"),
         *TransSubWinBorderColor = XGetDefault(XDisplay, PROGRAM_NAME,
						   "Trans.SubWin.BorderColor"),
         *TransSubWinBorderWidthStr = XGetDefault(XDisplay, PROGRAM_NAME,
						   "Trans.SubWin.BorderWidth"),
         *TransGeometry = XGetDefault(XDisplay, PROGRAM_NAME,
							     "Trans.Geometry"),
         *TransCursorColorStr = XGetDefault(XDisplay, PROGRAM_NAME,
							  "Trans.CursorColor"),
         *ViewBackGroundColor = XGetDefault(XDisplay, PROGRAM_NAME,
							    "View.BackGround"),
         *ViewTextColor = XGetDefault(XDisplay, PROGRAM_NAME,
							    "View.TextColor"),
         *ViewBorderColor = XGetDefault(XDisplay, PROGRAM_NAME,
							   "View.BorderColor"),
         *ViewBorderWidthStr = XGetDefault(XDisplay, PROGRAM_NAME,
							   "View.BorderWidth"),
         *ViewGeometry = XGetDefault(XDisplay, PROGRAM_NAME,
							      "View.Geometry"),
         *ViewCursorColorStr = XGetDefault(XDisplay, PROGRAM_NAME,
							   "View.CursorColor");

    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 *) malloc(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 *) malloc(sizeof(XColor));
	*ViewCursorColor = Color;
    }
    else
	ViewCursorColor = NULL;
}

/*****************************************************************************
* 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,
			   PROGRAM_NAME, PROGRAM_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: */
    SubTransPosX = MIN(TransWidth / 10, 20);
    SubTransPosY =  TransHeight / 25;
    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 PS: */
    SavePSWndw = SetTransformSubWindow(SubTransPosX, SubTransPosY,
					   SubTransWidth, SubTransHeight);

    /* SAVE MATRIX: */
    SaveMatrixWndw = SetTransformSubWindow(SubTransPosX, SubTransPosY,
					   SubTransWidth, SubTransHeight);

    /* RESET MATRIX: */
    ResetMatrixWndw = SetTransformSubWindow(SubTransPosX, SubTransPosY,
					    SubTransWidth, SubTransHeight);

    /* QUIT: */
    QuitWndw = SetTransformSubWindow(SubTransPosX, SubTransPosY,
				     SubTransWidth, SubTransHeight);

    XSelectInput(XDisplay, TransformWndw, ExposureMask);

    ObjectSpaceState = GlblTransformMode == TRANS_OBJECT;
    PerspectiveState = GlblViewMode == VIEW_PERSPECTIVE;
    DepthCueState = GlblDepthCue;
}

/*****************************************************************************
* Redraw a transformation window (after exposure or resize events).	     *
*****************************************************************************/
static void RedrawTransformWindow(void)
{
    int SubTransPosX, SubTransPosY, SubTransWidth, SubTransHeight,
        SizeChanged = FALSE;
    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,
			     GlblTransformMode == TRANS_OBJECT ?
			     "Object" : "Screen");
    SubTransPosY += SubTransHeight * 2;

    /* PERSPECTIVE/ORTHOGRPHIC toggle: */
    RedrawTransformSubWindow(PersOrthoTglWndw, SubTransPosX, SubTransPosY,
			     SubTransWidth, SubTransHeight, FALSE,
			     GlblViewMode == 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,
			     GlblDepthCue ? "Depth Cue" : "No Depth Cue");

    /* SAVE PS: */
    SubTransPosY += SubTransHeight * 2;
    RedrawTransformSubWindow(SavePSWndw, SubTransPosX, SubTransPosY,
			     SubTransWidth, SubTransHeight, FALSE,
			     "Save PS");

    /* SAVE MATRIX: */
    SubTransPosY += SubTransHeight * 2;
    RedrawTransformSubWindow(SaveMatrixWndw, SubTransPosX, SubTransPosY,
			     SubTransWidth, SubTransHeight, FALSE,
			     "Save Matrix");

    /* RESET MATRIX: */
    SubTransPosY += SubTransHeight * 2;
    RedrawTransformSubWindow(ResetMatrixWndw, SubTransPosX, SubTransPosY,
			     SubTransWidth, SubTransHeight, FALSE,
			     "Reset 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;
}

/*****************************************************************************
* Set up a transformation sub window.					     *
*****************************************************************************/
static Window SetTransformSubWindow(int SubTransPosX, int SubTransPosY,
					int SubTransWidth, 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, int SubTransWidth, 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,
			   PROGRAM_NAME, PROGRAM_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);
}

/******************************************************************************
* Returns status of abort key if pressed, and reset it.			      *
******************************************************************************/
int IsAbortKeyPressed(void)
{
    int RetVal = AbortKeyPressed;

    AbortKeyPressed = FALSE;

    return RetVal;
}

/******************************************************************************
* Flush output of graphic command.					      *
******************************************************************************/
int GraphicFlush(void)
{
    XFlush(XDisplay);
}

/******************************************************************************
* Handle X events							      *
******************************************************************************/
GraphicEventType GetGraphicEvent(RealType *ChangeFactor, char **ToggleStr)
{
    static int LastX;
    int Dx;
    XEvent Event;
    XWindowAttributes WinAttr;

    XMapWindow(XDisplay, TransformWndw);

    while (TRUE) {
	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;
		    UpdateInteractHandleInput();
		}
		break;
	    case ButtonPress:
		LastX = Event.xbutton.x;
		*ChangeFactor = ((RealType) (LastX - SubWindowWidthState2)) /
							SubWindowWidthState2;
		if (Event.xbutton.button == 3) {
		    AbortKeyPressed = TRUE;
		    break;
		}

		if (Event.xbutton.window == ObjScrTglWndw) {
		    XClearWindow(XDisplay, ObjScrTglWndw);
		    ObjectSpaceState = !ObjectSpaceState;
		    DrawText(ObjScrTglWndw,
			     *ToggleStr = ObjectSpaceState ? "Object" : "Screen",
			     SubWindowWidthState2, SubWindowHeightState2,
			     TransTextPixel);
		    return EVENT_SCR_OBJ_TGL;
		}
		else
		if (Event.xbutton.window == PersOrthoTglWndw) {
		    XClearWindow(XDisplay, PersOrthoTglWndw);
		    PerspectiveState = !PerspectiveState;
		    DrawText(PersOrthoTglWndw,
			     *ToggleStr = PerspectiveState ? "Perspective" :
			                                     "Orthographic",
			     SubWindowWidthState2, SubWindowHeightState2,
			     TransTextPixel);
		    return EVENT_PERS_ORTHO_TGL;
		}
		else
		if (Event.xbutton.window == PersOrthoZWndw) {
		    return EVENT_PERS_ORTHO_Z;
		}
		else
		if (Event.xbutton.window == RotateXWndw) {
		    return EVENT_ROTATE_X;
		}
		else
		if (Event.xbutton.window == RotateYWndw) {
		    return EVENT_ROTATE_Y;
		}
		else
		if (Event.xbutton.window == RotateZWndw) {
		    return EVENT_ROTATE_Z;
		}
		else
		if (Event.xbutton.window == TranslateXWndw) {
		    return EVENT_TRANSLATE_X;
		}
		else
		if (Event.xbutton.window == TranslateYWndw) {
		    return EVENT_TRANSLATE_Y;
		}
		else
		if (Event.xbutton.window == TranslateZWndw) {
		    return EVENT_TRANSLATE_Z;
		}
		else
		if (Event.xbutton.window == ScaleWndw) {
		    return EVENT_SCALE;
		}
		else
		if (Event.xbutton.window == DepthCueWndw) {
		    XClearWindow(XDisplay, DepthCueWndw);
		    DepthCueState = !DepthCueState;
		    DrawText(DepthCueWndw,
			     *ToggleStr = DepthCueState ? "Depth Cue" :
							  "No Depth Cue",
			     SubWindowWidthState2, SubWindowHeightState2,
			     TransTextPixel);
		    return EVENT_DEPTH_CUE;
		}
		else
		if (Event.xbutton.window == SaveMatrixWndw) {
		    return EVENT_SAVE_MATRIX;
		}
		else
		if (Event.xbutton.window == SavePSWndw) {
		    return EVENT_SAVE_PS;
		}
		else
		if (Event.xbutton.window == ResetMatrixWndw) {
		    return EVENT_RESET_MATRIX;
		}
		else
		if (Event.xbutton.window == QuitWndw) {
		    XUnmapWindow(XDisplay, TransformWndw);
		    GraphicFlush();
		    return 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 EVENT_PERS_ORTHO_Z;
		}
		if (Event.xbutton.window == RotateXWndw) {
		    return EVENT_ROTATE_X;
		}
		else
		if (Event.xbutton.window == RotateYWndw) {
		    return EVENT_ROTATE_Y;
		}
		else
		if (Event.xbutton.window == RotateZWndw) {
		    return EVENT_ROTATE_Z;
		}
		else
		if (Event.xbutton.window == TranslateXWndw) {
		    return EVENT_TRANSLATE_X;
		}
		else
		if (Event.xbutton.window == TranslateYWndw) {
		    return EVENT_TRANSLATE_Y;
		}
		else
		if (Event.xbutton.window == TranslateZWndw) {
		    return EVENT_TRANSLATE_Z;
		}
		else
		if (Event.xbutton.window == ScaleWndw) {
		    return EVENT_SCALE;
		}
		break;
	    default:
		fprintf(stderr, "undefined event type %d.\n", Event.type);
	}
    }
}

/******************************************************************************
* 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);
}

#endif /* __MSDOS__ */
