/*****************************************************************************
*   An SGI 4D driver using GL.						     *
******************************************************************************
* (C) Gershon Elber, Technion, Israel Institute of Technology                *
******************************************************************************
* Written by:  Gershon Elber				Ver 0.1, June 1993.  *
*****************************************************************************/

#include <gl/gl.h>
#include <gl/device.h>

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

/* Interactive menu setup structure: */
#define INTERACT_NUM_OF_STRINGS		4
#define INTERACT_NUM_OF_SUB_WNDWS	16
#define IN_VIEW_WINDOW(x, y)	((x) >= ViewWinLeft && \
				 (y) >= ViewWinLow && \
				 (x) <= ViewWinLeft + ViewWinWidth && \
				 (y) <= ViewWinLow + ViewWinHeight)
#define IN_TRANS_WINDOW(x, y)   ((x) >= TransWinLeft && \
				 (y) >= TransWinLow && \
				 (x) <= TransWinLeft + TransWinWidth && \
				 (y) <= TransWinLow + TransWinHeight)

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

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

static int
    GlblStateMenu = 0;
static long
    TransWinID = 0,
    TransWinWidth = 100,
    TransWinWidth2 = 50,
    TransWinHeight = 100,
    TransWinLow = 0,
    TransWinLeft = 0;

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

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

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

static void SetColorIndex(int color);
static void SetColorRGB(int Color[3]);
static void SetTransformWindow(void);
static void RedrawTransformWindow(void);
static IGGraphicEventType GetGraphicEvent(RealType *ChangeFactor,
					  int BlockForEvent);
static void DrawText(char *Str, long PosX, long PosY);

/*****************************************************************************
* DESCRIPTION:                                                               M
* Main module of xgldrvs - SGI's GL graphics driver of IRIT.          	     M
*                                                                            *
* PARAMETERS:                                                                M
*   argc, argv:  Command line.                                               M
*                                                                            *
* RETURN VALUE:                                                              M
*   int:         Exit code.                                                  M
*                                                                            *
* KEYWORDS:                                                                  M
*   main                                                                     M
*****************************************************************************/
void main(int argc, char **argv)
{
    RealType ChangeFactor[2];
    IGGraphicEventType Event;

    IGConfigureGlobals("xgldrvs", argc, argv);

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

    qdevice(LEFTMOUSE);
    qdevice(MIDDLEMOUSE);
    qdevice(RIGHTMOUSE);

    /* The default drawing window is the view window. */
    winset(ViewWinID);

    setbell(1);						   /* Make it short. */

    IGCreateStateMenu();

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

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

/*****************************************************************************
* 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)
{
    if (GlblStateMenu) 
        freepup(GlblStateMenu);

    GlblStateMenu = newpup();

    addtopup(GlblStateMenu, " Set Up %t", 0);
    addtopup(GlblStateMenu, "Oops!%l", 0);
    addtopup(GlblStateMenu, "More Sensitive", 0);
    addtopup(GlblStateMenu, "Less Sensitive%l", 0);
    addtopup(GlblStateMenu, IGGlblTransformMode == IG_TRANS_SCREEN ?
				"Screen Trans." : "Object Trans", 0);
    addtopup(GlblStateMenu, IGGlblViewMode == IG_VIEW_PERSPECTIVE ?
				"Perspective" : "Orthographic", 0);
    addtopup(GlblStateMenu,
	     IGGlblDepthCue ? "Depth Cue" : "No Depth Cue", 0);
    addtopup(GlblStateMenu,
	     IGGlblCacheGeom ? "Cache Geom" : "No Geom Cache", 0);
    addtopup(GlblStateMenu,
	     IGGlblDrawSolid ? "Draw Solid" : "Draw Wireframe", 0);
    switch (IGGlblShadingModel) {
	case IG_SHADING_NONE:
	     addtopup(GlblStateMenu, "No Shading");
	    break;
	case IG_SHADING_FLAT:
	     addtopup(GlblStateMenu, "Flat Shading");
	    break;
	case IG_SHADING_GOURAUD:
	     addtopup(GlblStateMenu, "Gouraud Shading");
	    break;
	case IG_SHADING_PHONG:
	     addtopup(GlblStateMenu, "Phong Shading");
	    break;
    }
    addtopup(GlblStateMenu,
	     IGGlblBackFaceCull ? "Cull Back Face" : "No Cull Back Face", 0);
    addtopup(GlblStateMenu,
	     IGGlblDoDoubleBuffer ? "Double Buffer" : "Single Buffer", 0);
    addtopup(GlblStateMenu,
	     IGGlblAntiAliasing ? "Anti Aliasing" : "No Anti Aliasing", 0);
    addtopup(GlblStateMenu,
	     IGGlblDrawInternal ? "Draw Internal Edges" : "No Internal Edges", 0);
    addtopup(GlblStateMenu,
	     IGGlblDrawVNormal ? "Draw Vrtx Normals" :  "No Vrtx Normals", 0);
    addtopup(GlblStateMenu,
	     IGGlblDrawPNormal ? "Draw Poly Normals" :  "No Poly Normals", 0);
    addtopup(GlblStateMenu,
	     IGGlblDrawSurfaceMesh ? "Draw Surface Mesh" : "No Surface Mesh", 0);
    addtopup(GlblStateMenu,
	     IGGlblDrawSurfacePoly ? "Surface Polygons" : "Surface Isolines", 0);
    addtopup(GlblStateMenu,
	     IGGlblFourPerFlat ? "Four Per Flat%l" : "Two Per Flat%l", 0);
    addtopup(GlblStateMenu, "More Isolines", 0);
    addtopup(GlblStateMenu, "Less Isolines%l", 0);
    addtopup(GlblStateMenu, "Finer approx.", 0);
    addtopup(GlblStateMenu, "Coarser approx.%l", 0);
    addtopup(GlblStateMenu, "Longer Vectors", 0);
    addtopup(GlblStateMenu, "Shorter Vectors%l", 0);
    addtopup(GlblStateMenu, "Wider Lines", 0);
    addtopup(GlblStateMenu, "Narrow Lines%l", 0);
    addtopup(GlblStateMenu, "Wider Points", 0);
    addtopup(GlblStateMenu, "Narrow Points%l", 0);
    addtopup(GlblStateMenu, "Front View", 0);
    addtopup(GlblStateMenu, "Side View", 0);
    addtopup(GlblStateMenu, "Top View", 0);
    addtopup(GlblStateMenu, "Isometry View%l", 0);
    addtopup(GlblStateMenu, "Clear View Area%l", 0);
    addtopup(GlblStateMenu, "Animation", 0);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Sets the color according to the given color index.		    	     *
*                                                                            *
* PARAMETERS:                                                                *
*   color:     Index of color to use. Must be between 0 and IG_MAX_COLOR.    *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void SetColorIndex(int color)
{
    int Color[3];

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

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

    SetColorRGB(Color);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Sets the color according to the given RGB values.			     *
*                                                                            *
* PARAMETERS:                                                                *
*   Color:      An RGB vector of integer values between 0 and 255.           *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void SetColorRGB(int Color[3])
{
    c3i((long *) Color);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Sets up and draw a transformation window.				     *
*                                                                            *
* PARAMETERS:                                                                *
*   None                                                                     *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void SetTransformWindow(void)
{
    long PrefPos[4];

#ifndef _AIX
    foreground();
#endif

    if (sscanf(IGGlblTransPrefPos, "%ld,%ld,%ld,%ld",
	       &PrefPos[0], &PrefPos[1], &PrefPos[2], &PrefPos[3]) == 4)
	prefposition(PrefPos[0], PrefPos[1], PrefPos[2], PrefPos[3]);
    else if (sscanf(IGGlblTransPrefPos, "%ld,%ld",
		    &PrefPos[0], &PrefPos[1]) == 2)
	prefsize(PrefPos[0], PrefPos[1]);
    winopen("Poly3dTrans");
    winconstraints();
    wintitle("xGLdrvs");
    RGBmode();
    gconfig();
    getorigin(&TransWinLeft, &TransWinLow);
    getsize(&TransWinWidth, &TransWinHeight);
    TransWinWidth2 = TransWinWidth / 2;
    TransWinID = winget();

    SetColorRGB(IGGlblBackGroundColor);
    clear();

    /* This is wierd. without the sleep the gl get mixed up between the two  */
    /* windows. If you have any idea why, let me know...		     */
    sleep(1);
}

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

    /* Make sure the menu is consistent with internatal data. */
    InteractMenu.SubWindows[0].Str =
	IGGlblTransformMode == IG_TRANS_OBJECT ? "Object Coords."
					     : "Screen Coords.";
    InteractMenu.SubWindows[1].Str =
	IGGlblViewMode == IG_VIEW_PERSPECTIVE ? "Perspective" : "Orthographic";

    winset(TransWinID);                /* Draw in the transformation window. */

    SubTransWidth = (int) (TransWinWidth * INTERACT_SUB_WINDOW_WIDTH);
    SubTransHeight = (int) (TransWinHeight * INTERACT_SUB_WINDOW_HEIGHT);
    SubTransPosX = (TransWinWidth - SubTransWidth) / 2;

    SetColorRGB(IGGlblBackGroundColor);
    clear();

    for (i = 0; i < INTERACT_NUM_OF_SUB_WNDWS; i++) {
	SetColorIndex(InteractMenu.SubWindows[i].Color);
	SubTransPosY = (int) (TransWinHeight * InteractMenu.SubWindows[i].Y);

	move2i(SubTransPosX, SubTransPosY);
	draw2i(SubTransPosX + SubTransWidth, SubTransPosY);
	draw2i(SubTransPosX + SubTransWidth, SubTransPosY + SubTransHeight);
	draw2i(SubTransPosX, SubTransPosY + SubTransHeight);
	draw2i(SubTransPosX, SubTransPosY);
	if (InteractMenu.SubWindows[i].TextInside) {
	    DrawText(InteractMenu.SubWindows[i].Str,
		     TransWinWidth / 2,
		     SubTransPosY + SubTransHeight / 2);
	}
	else {
	    DrawText(InteractMenu.SubWindows[i].Str,
		     (TransWinWidth - SubTransWidth) / 3,
		     SubTransPosY + SubTransHeight / 2);
	    move2i(SubTransPosX + SubTransWidth / 2, SubTransPosY);
	    draw2i(SubTransPosX + SubTransWidth / 2,
		   SubTransPosY + SubTransHeight);
	}
    }

    for (i = 0; i < INTERACT_NUM_OF_STRINGS; i++) {
	SetColorIndex(InteractMenu.Strings[i].Color);
	DrawText(InteractMenu.Strings[i].Str,
		 (int) (InteractMenu.Strings[i].X * TransWinWidth),
		 (int) (InteractMenu.Strings[i].Y * TransWinHeight));
    }

    winset(ViewWinID);             /* Go back to the default drawing window. */
}

/*****************************************************************************
* 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.			     *
*   BlockForEvent:	 If TRUE, blocks until event is recieved.	     *
*                                                                            *
* RETURN VALUE:                                                              *
*   IGGraphicEventType:  Type of new event.                                  *
*****************************************************************************/
static IGGraphicEventType GetGraphicEvent(RealType *ChangeFactor,
					  int BlockForEvent)
{
    static IGGraphicEventType
	LastEvent = IG_EVENT_NONE;
    static long
	LastX = -1,
	LastY = -1;
    int i, TransformRequest,
        LeftButtonIsPressed = getbutton(LEFTMOUSE) == 1,
        RightButtonIsPressed = getbutton(RIGHTMOUSE) == 1;
    IGGraphicEventType
	RetVal = IG_EVENT_NONE;
    short data;
    long x, y;
    RealType XPos, YPos;

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

    /* Allow continuous drag on following events only: */
    if (LeftButtonIsPressed && !IG_IS_DRAG_EVENT(LastEvent)) {
	while (getbutton(LEFTMOUSE) == 1);
	LeftButtonIsPressed = FALSE;
    }
    if (RightButtonIsPressed && !IG_IS_DRAG_EVENT(LastEvent)) {
	while (getbutton(RIGHTMOUSE) == 1);
	RightButtonIsPressed = FALSE;
    }

    if (LeftButtonIsPressed) {
	/* Allow leaving the window if still pressed, and use last event     */
	/* as the returned event. Note we wait until current position is     */
	/* different from last one to make sure we do something.             */
	switch (LastEvent) {
	    case IG_EVENT_PERS_ORTHO_Z:
	    case IG_EVENT_ROTATE_X:
	    case IG_EVENT_ROTATE_Y:
	    case IG_EVENT_ROTATE_Z:
	    case IG_EVENT_TRANSLATE_X:
	    case IG_EVENT_TRANSLATE_Y:
	    case IG_EVENT_TRANSLATE_Z:
	    case IG_EVENT_SCALE:
	    case IG_EVENT_NEAR_CLIP:
	    case IG_EVENT_FAR_CLIP:
	        while ((x = getvaluator(MOUSEX)) == LastX &&
		       getbutton(LEFTMOUSE) == 1);

	        if (x != LastX) {
		    *ChangeFactor = (((RealType) x) - LastX) / TransWinWidth2;
		    LastX = x;
		    return LastEvent;
		}
		else
		    LeftButtonIsPressed = FALSE;
		break;
	    case IG_EVENT_ROTATE:
	        while ((x = getvaluator(MOUSEX)) == LastX &&
		       (y = getvaluator(MOUSEY)) == LastY &&
		       getbutton(LEFTMOUSE) == 1);
		x = getvaluator(MOUSEX);
		y = getvaluator(MOUSEY);

	        if (x != LastX || y != LastY) {
		    ChangeFactor[0] = (x - LastX);
		    ChangeFactor[1] = (y - LastY);

		    LastX = x;
		    LastY = y;
		    return LastEvent;
		}
		else
		    LeftButtonIsPressed = FALSE;
		break;
	}
    }

    if (RightButtonIsPressed) {
	/* Allow leaving the window if still pressed, and use last event     */
	/* as the returned event. Note we wait until current position is     */
	/* different from last one to make sure we do something.             */
	switch (LastEvent) {
	    case IG_EVENT_TRANSLATE:
	        while ((x = getvaluator(MOUSEX)) == LastX &&
		       (y = getvaluator(MOUSEY)) == LastY &&
		       getbutton(RIGHTMOUSE) == 1);
		x = getvaluator(MOUSEX);
		y = getvaluator(MOUSEY);

		if (x != LastX || y != LastY) {
		    ChangeFactor[0] = (x - LastX);
		    ChangeFactor[1] = (y - LastY);

		    LastX = x;
		    LastY = y;
		    return LastEvent;
		}
		else
		    RightButtonIsPressed = FALSE;
	}
    }

    LastEvent = IG_EVENT_NONE;

    do {
	/* Wait for left button to be pressed in the Trans window. Note this */
	/* is the loop we are going to cycle in idle time.		     */
	for (TransformRequest = FALSE; !TransformRequest; ) {
	    x = getvaluator(MOUSEX);
	    y = getvaluator(MOUSEY);

	    if (qtest()) {	              /* Any external event occured? */
		switch (qread(&data)) {
		    case REDRAW:
			if (data == ViewWinID) {
			    getorigin(&ViewWinLeft, &ViewWinLow);
			    getsize(&ViewWinWidth, &ViewWinHeight);
			    ViewWinWidth2 = ViewWinWidth / 2;
			    ViewWinHeight2 = ViewWinHeight / 2;
			    if (ViewWinWidth > ViewWinHeight) {
				int i = (ViewWinWidth - ViewWinHeight) / 2;

				viewport((Screencoord) 0,
					 (Screencoord) ViewWinWidth,
					 (Screencoord) -i,
					 (Screencoord) (ViewWinWidth - i));
			    }
			    else {
				int i = (ViewWinHeight - ViewWinWidth) / 2;

				viewport((Screencoord) -i,
					 (Screencoord) (ViewWinHeight - i),
					 (Screencoord) 0,
					 (Screencoord) ViewWinHeight);
			    }
			    ortho2(-0.5, ViewWinWidth - 0.5,
				   -0.5, ViewWinHeight - 0.5);
			    IGRedrawViewWindow();
			}
			else if (data == TransWinID) {
			    winset(TransWinID);
			    getorigin(&TransWinLeft, &TransWinLow);
			    getsize(&TransWinWidth, &TransWinHeight);
			    reshapeviewport();
			    ortho2(-0.5, TransWinWidth - 0.5,
				   -0.5, TransWinHeight - 0.5);
			    TransWinWidth2 = TransWinWidth / 2;
			    RedrawTransformWindow();
			    winset(ViewWinID);
			}
			break;
		    case RIGHTMOUSE:
			if (data) {			/* Mouse press down. */
			    if (IN_TRANS_WINDOW(x, y)) {
				if (IGHandleState(dopup(GlblStateMenu),
						  FALSE)) {
				    *ChangeFactor = 0.0;
				    return IG_EVENT_STATE;
				}
			    }
			    else if (IN_VIEW_WINDOW(x, y)) {
				RetVal = IG_EVENT_TRANSLATE;
				TransformRequest = TRUE;
			    }				
			}
			break;
		    case LEFTMOUSE:
			if (data) {			/* Mouse press down. */
			    TransformRequest = TRUE;

			    if (IN_VIEW_WINDOW(x, y)) {
				RetVal = IG_EVENT_ROTATE;
			    }				
			}
			break;
		}
		continue;
	    }

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

	    if (BlockForEvent)
		IritSleep(10);	       /* So we do not use all CPU on idle. */
	    else
		break;
	}

	if (!TransformRequest && !BlockForEvent)
	    return RetVal;

	LastX = x;
	LastY = y;

	if (IN_TRANS_WINDOW(x, y)) {
	    x -= TransWinLeft;
	    y -= TransWinLow;

	    XPos = ((RealType) x) / TransWinWidth;
	    YPos = ((RealType) y) / TransWinHeight;

	    /* Make sure we are in bound in the X direction. */
	    if (XPos < (1.0 - INTERACT_SUB_WINDOW_WIDTH) / 2.0 ||
		XPos > 1.0 - (1.0 - INTERACT_SUB_WINDOW_WIDTH) / 2.0)
	        continue;

	    /* Now search the sub window the event occured in. */
	    for (i = 0; i < INTERACT_NUM_OF_SUB_WNDWS; i++) {
		if (InteractMenu.SubWindows[i].Y <= YPos &&
		    InteractMenu.SubWindows[i].Y +
		         INTERACT_SUB_WINDOW_HEIGHT >= YPos) {
		    RetVal = InteractMenu.SubWindows[i].Event;
		    break;
		}
	    }
	    if (i == INTERACT_NUM_OF_SUB_WNDWS)
	        continue;

	    /* Take care of special cases in which window should be updated. */
	    switch (RetVal) {
		case IG_EVENT_SCR_OBJ_TGL:
		    IGGlblTransformMode =
		        IGGlblTransformMode == IG_TRANS_OBJECT ?
					       IG_TRANS_SCREEN :
					       IG_TRANS_OBJECT;
		    RedrawTransformWindow();
		    break;
		case IG_EVENT_PERS_ORTHO_TGL:
		    IGGlblViewMode = IGGlblViewMode == IG_VIEW_PERSPECTIVE ?
						       IG_VIEW_ORTHOGRAPHIC :
						       IG_VIEW_PERSPECTIVE;
		    RedrawTransformWindow();
		    break;
		default:
		    break;
	    }

	    *ChangeFactor = (((RealType) x) - TransWinWidth2) / TransWinWidth2;
	}
    }
    while (RetVal == IG_EVENT_NONE);

    LastEvent = RetVal;

    return RetVal;
}

/*****************************************************************************
* 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_SCR_OBJ_TGL:
	    IGGlblTransformMode = IGGlblTransformMode == IG_TRANS_OBJECT ?
							 IG_TRANS_SCREEN :
							 IG_TRANS_OBJECT;
	    RedrawTransformWindow();
	    UpdateView = FALSE;
	    break;
	case IG_STATE_PERS_ORTHO_TGL:
	    IGGlblViewMode = IGGlblViewMode == IG_VIEW_PERSPECTIVE ?
					       IG_VIEW_ORTHOGRAPHIC :
					       IG_VIEW_PERSPECTIVE;
	    RedrawTransformWindow();
	    break;
	case IG_STATE_DRAW_SOLID:
	    IGGlblDrawSolid = !IGGlblDrawSolid;
	    /* And fall thru to disable the depth cueing. */
	    IGGlblDepthCue = TRUE; 
	case IG_STATE_DEPTH_CUE:
	    IGGlblDepthCue = !IGGlblDepthCue;
	    if (IGGlblDepthCue) {
#		ifndef _AIX
		    glcompat(GLC_ZRANGEMAP, 1);
#		endif /* _AIX */
		lRGBrange(0, 0, 0, 255, 255, 255,
			  0x0, 0x7fffff);
		depthcue(IGGlblDepthCue);
	    }
	    else
		depthcue(FALSE);
	    break;
	case IG_STATE_DOUBLE_BUFFER:
	    IGGlblDoDoubleBuffer = !IGGlblDoDoubleBuffer;
	    winset(ViewWinID);
	    if (IGGlblDoDoubleBuffer)
		doublebuffer();
	    else
		singlebuffer();
	    gconfig();
	    break;
	case IG_STATE_ANTI_ALIASING:
	    IGGlblAntiAliasing = !IGGlblAntiAliasing;
#	    ifndef _AIX
		if (getgdesc(GD_PNTSMOOTH_CMODE) == 0 ||
		    getgdesc(GD_BITS_NORM_DBL_CMODE) < 8)
		    IGGlblAntiAliasing = FALSE;
		else
		    subpixel(IGGlblAntiAliasing);
#	    endif /* _AIX */
	    break;
	default:
	    UpdateView = IGDefaultStateHandler(State, Refresh);
	    break;
    }

    IGCreateStateMenu();

    return UpdateView;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Draws text centered at the given position.				     *
*                                                                            *
* PARAMETERS:                                                                *
*   Str:        String to draw.                                              *
*   PosX, PosY: Location to draw at.                                         *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void DrawText(char *Str, long PosX, long PosY)
{
    long Width = strwidth(Str);

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

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

/*****************************************************************************
* 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 (qtest()) {
	short data;
	long
	    dev = qread(&data);

	if (dev == RIGHTMOUSE) {
	    fprintf(stderr, "\nAnimation was interrupted by the user.\n");
	    Anim -> StopAnim = TRUE;
	}
	else if (dev == LEFTMOUSE) {
	    IGGraphicEventType Event;
	    RealType ChangeFactor[2];

	    qenter((Device) dev, data);

	    winset(TransWinID);
	    Event = GetGraphicEvent(ChangeFactor, FALSE);
	    ChangeFactor[0] *= IGGlblChangeFactor;
	    ChangeFactor[1] *= IGGlblChangeFactor;
	    winset(ViewWinID);

	    if (Event != IG_EVENT_NONE) {
		if (IGProcessEvent(Event, ChangeFactor))
		    IGRedrawViewWindow();
	    }
	}
    }

    return Anim -> StopAnim;
}
