/*****************************************************************************
*   An SGI 4D driver using GL.						     *
*									     *
* 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 "iritgrap.h"
#include "irit_soc.h"

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

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

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

static int
    GlblStateMenu = 0;

static long
    TransWinID = 0,
    TransWinWidth = 100,
    TransWinWidth2 = 50,
    TransWinHeight = 100,
    TransWinLow = 0,
    TransWinLeft = 0,
    ViewWinID = 0,
    ViewWinWidth = 100,
    ViewWinWidth2 = 50,
    ViewWinHeight = 100,
    ViewWinHeight2 = 50,
    ViewWinLow = 0,
    ViewWinLeft = 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 c);
static void SetColorRGB(int Color[3]);
static void ClearViewArea(void);
static void SetTransformWindow(void);
static void RedrawTransformWindow(void);
static void SetViewWindow(void);
static void RedrawViewWindow(void);
static IGGraphicEventType GetGraphicEvent(RealType *ChangeFactor);
static void DrawText(char *Str, long PosX, long PosY);

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

    IGConfigureGlobals("xgldrvs", argc, argv);

    SetViewWindow();
    RedrawViewWindow();
    SetTransformWindow();
    RedrawTransformWindow();

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

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

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

    IGCreateStateMenu();

    if (!IGGlblStandAlone)
	SocClientCreateSocket();

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

    if (!IGGlblStandAlone)
	SocClientCloseSocket();
}

/*****************************************************************************
* Construct global pop up menu.						     *
*****************************************************************************/
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,
	     IGGlblDrawSolid ? "Draw Solid" : "Draw Wireframe", 0);
    addtopup(GlblStateMenu,
	     IGGlblDoDoubleBuffer ? "Double Buffer" : "Single Buffer", 0);
    addtopup(GlblStateMenu,
	     IGGlblAntiAliasing ? "Anti Aliasing" : "No Anti Aliasing", 0);
    addtopup(GlblStateMenu,
	     IGGlblDrawInternal ? "Draw Internal Edges" : "No Internal Edges", 0);
    addtopup(GlblStateMenu,
	     IGGlblDrawVNormal ? "Draw Vrtx Normals" :  "No Vrtx Normals", 0);
    addtopup(GlblStateMenu,
	     IGGlblDrawPNormal ? "Draw Poly Normals" :  "No Poly Normals", 0);
    addtopup(GlblStateMenu,
	     IGGlblDrawSurfaceMesh ? "Draw Surface Mesh" : "No Surface Mesh", 0);
    addtopup(GlblStateMenu,
	     IGGlblDrawSurfacePoly ? "Surface Polygons" : "Surface Isolines", 0);
    addtopup(GlblStateMenu,
	     IGGlblFourPerFlat ? "Four Per Flat%l" : "Two Per Flat%l", 0);
    addtopup(GlblStateMenu, "More Isolines", 0);
    addtopup(GlblStateMenu, "Less Isolines%l", 0);
    addtopup(GlblStateMenu, "Finer approx.", 0);
    addtopup(GlblStateMenu, "Coarser approx.%l", 0);
    addtopup(GlblStateMenu, "Longer Vectors.", 0);
    addtopup(GlblStateMenu, "Shorter Vectors", 0);
}

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

    IGSetColorObj(PObj);

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

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

    for (i = 0; i < 6; i += 2) {
	bgnline();
	v3d(Ends[i]);
	v3d(Ends[i+1]);
	endline();
    }

    if (IP_IS_VEC_OBJ(PObj)) {
	bgnline();
	v3d(Pt);
	Zero[0] = Zero[1] = Zero[2] = 0.0;
	v3d(Zero);
	endline();
    }
}

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

    IGSetColorObj(PObj);

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

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

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

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

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

	    if (IGGlblDrawSolid) {
		bgnpolygon();
		for (V = Pl -> PVertex; V != NULL; V = V -> Pnext) {
		    float n[3];

		    n[0] = V -> Normal[0];
		    n[1] = V -> Normal[1];
		    n[2] = V -> Normal[2];
		    n3f(n);
		    v3d(V -> Coord);

		    if (IGGlblDrawPNormal) {
			for (j = 0; j < 3; j++)
			    PNormal[j] += V -> Coord[j];
			NumOfVertices++;
		    }
		}
		endpolygon();
	    }
	    else {
		bgnline();
		for (V = Pl -> PVertex; V != NULL; V = V -> Pnext) {
		    v3d(V -> Coord);
		    if (IP_IS_INTERNAL_VRTX(V) && !IGGlblDrawInternal) {
			endline();
			bgnline();
		    }

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

	    if (IGGlblDrawPNormal && IP_HAS_PLANE_POLY(Pl)) {
		bgnline();
		for (i = 0; i < 3; i++)
		    PNormal[i] /= NumOfVertices;
		v3d(PNormal);
		for (i = 0; i < 3; i++)
		    PNormal[i] += Pl -> Plane[i] * IGGlblNormalLen;
		v3d(PNormal);
		endline();
	    }

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

/****************************************************************************
* 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 c)
{
    int Color[3];

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

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

    SetColorRGB(Color);
}

/****************************************************************************
* Routine to set the color according to the given RGB values.		    *
****************************************************************************/
static void SetColorRGB(int Color[3])
{
    int i;
    static float
	Ambient = 0.25,
	Diffuse = 0.75,
	Specular = 1.0;
    static float
	Material[] = {
	    AMBIENT,  0.25, 0.25, 0.25,
	    DIFFUSE,  0.75, 0.75, 0.75,
	    SPECULAR, 1.00, 1.00, 1.00,
	    SHININESS, 50,
	    LMNULL
	};

    c3i((long *) Color);
    if (IGGlblDepthCue)
	lRGBrange(0, 0, 0, Color[0], Color[1], Color[2], 0x0, 0x7fffff);

    /* Prepare material structure in this color and select it. */
    for (i = 0; i < 3; i++) {
	Material[1 + i] = Ambient * Color[i] / 255.0;
	Material[5 + i] = Diffuse * Color[i] / 255.0;
	Material[9 + i] = Specular * Color[i] / 255.0;
    }
    lmdef(DEFMATERIAL, 1, sizeof(Material) / sizeof(float), Material);
}

/*****************************************************************************
* Set up and draw a transformation window.				     *
*****************************************************************************/
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);
}

/*****************************************************************************
* Set up and draw a transformation window.				     *
*****************************************************************************/
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. */
}

/*****************************************************************************
*   Routine to clear the viewing area.					     *
*****************************************************************************/
static void ClearViewArea(void)
{
    SetColorRGB(IGGlblBackGroundColor);
    clear();
    mmode(MVIEWING);
    linewidth(IGGlblLineWidth);

    if (APX_EQ(IGGlblZMinClip, IGGlblZMaxClip))
        IGGlblZMaxClip += EPSILON;
    ortho(-1.0, 1.0, -1.0, 1.0, IGGlblZMinClip, IGGlblZMaxClip);

    if (winget() == ViewWinID) {
	/* activate zbuffer only if we are in solid drawing mode. */
	if (IGGlblDrawSolid) {
	    /* Define necessary staff for Lighting. */
	    lmbind(MATERIAL, 1);
	    lmbind(LMODEL, 1);
	    lmbind(LIGHT1, 1);
	    lmbind(LIGHT2, 2);

	    zbuffer(TRUE);
	    zclear();
	}
	else {
	    zbuffer(FALSE);
	}
    }
}

/*****************************************************************************
* Set up and draw a view window.				             *
*****************************************************************************/
static void SetViewWindow(void)
{
    long PrefPos[4];
    static float Light1[] = {
	AMBIENT, 0.25, 0.25, 0.25,
	POSITION, 0.1, 0.5, -1.0, 0.0,
	LMNULL
    };
    static float Light2[] = {
	AMBIENT, 0.25, 0.25, 0.25,
	POSITION, 0.5, -0.5, -1.0, 0.0,
	LMNULL
    };

#ifndef _AIX
    foreground();
#endif

    if (sscanf(IGGlblViewPrefPos, "%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(IGGlblViewPrefPos, "%ld, %ld",
		    &PrefPos[0], &PrefPos[1]) == 2)
	prefsize(PrefPos[0], PrefPos[1]);
    winopen("Poly3dView");
    winconstraints();
    wintitle("xGLdrvs");
    if (IGGlblDoDoubleBuffer)
	doublebuffer();
    else
        singlebuffer();
    RGBmode();
    if (IGGlblDepthCue) {
#	ifndef _AIX
	    glcompat(GLC_ZRANGEMAP, 1);
#	endif /* _AIX */
	lRGBrange(0, 0, 0, 255, 255, 255, 0x0, 0x7fffff);
	depthcue(IGGlblDepthCue);
    }
    else
        depthcue(FALSE);
    if (IGGlblAntiAliasing) {
#	ifndef _AIX
	    if (getgdesc(GD_PNTSMOOTH_CMODE) == 0 ||
		getgdesc(GD_BITS_NORM_DBL_CMODE) < 8)
		IGGlblAntiAliasing = FALSE;
	    else {
		subpixel(TRUE);
		pntsmooth(SMP_ON);
	    }
#	endif /* _AIX */
    }
    gconfig();
    getorigin(&ViewWinLeft, &ViewWinLow);
    getsize(&ViewWinWidth, &ViewWinHeight);
    ViewWinWidth2 = ViewWinWidth / 2;
    ViewWinHeight2 = ViewWinHeight / 2;
    
    ViewWinID = winget();

    concave(TRUE);

    /* Define necessary staff for Lighting. */
    lmdef(DEFMATERIAL, 1, 0, NULL);
    lmdef(DEFLMODEL, 1, 0, NULL);
    lmdef(DEFLIGHT, 1, sizeof(Light1) / sizeof(float), Light1);
    lmdef(DEFLIGHT, 2, sizeof(Light2) / sizeof(float), Light2);

    ClearViewArea();
    if (IGGlblDoDoubleBuffer)
	swapbuffers();

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

/*****************************************************************************
* Set up and draw a viewing window.					     *
*****************************************************************************/
static void RedrawViewWindow(void)
{
    IPObjectStruct *PObj;
    Matrix CrntView;
    int i, j, k;

    ClearViewArea();

    if (IGGlblViewMode == IG_VIEW_PERSPECTIVE) {
	for (i = 0; i < 4; i++)
	    for (j = 0; j < 4; j++) {
		CrntView[i][j] = 0;
		for (k = 0; k < 4; k++)
		    CrntView[i][j] += IritPrsrViewMat[i][k] *
				      IritPrsrPrspMat[k][j];
	    }
    }
    else {
	for (i = 0; i < 4; i++)
	    for (j = 0; j < 4; j++)
		CrntView[i][j] = IritPrsrViewMat[i][j];
    }
    loadmatrix(CrntView);

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

    if (IGGlblDoDoubleBuffer)
	swapbuffers();
}

/******************************************************************************
* Handle gl events							      *
******************************************************************************/
static IGGraphicEventType GetGraphicEvent(RealType *ChangeFactor)
{
    static IGGraphicEventType
	LastEvent = IG_EVENT_NONE;
    static long
	LastX = -1;
    int i, TransformRequest,
        LeftButtonIsPressed = getbutton(LEFTMOUSE) == 1;
    IGGraphicEventType
	RetVal = IG_EVENT_NONE;
    short data;
    long x, y, dev;
    RealType XPos, YPos;

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

    if (LeftButtonIsPressed) {
	/* Allow leaving the Trans 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.          */
	while((x = getvaluator(MOUSEX) - TransWinLeft) == LastX &&
	      getbutton(LEFTMOUSE) == 1);
	if (x != LastX) {
	    *ChangeFactor = (((RealType) x) - LastX) / TransWinWidth2;
	    LastX = x;
	    return LastEvent;
	}
	else
	    LeftButtonIsPressed = FALSE;
    }

    LastEvent = IG_EVENT_NONE;

    while (RetVal == IG_EVENT_NONE) {
	/* 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 (dev = qread(&data)) {
		    case REDRAW:
			if (data == ViewWinID) {
			    getorigin(&ViewWinLeft, &ViewWinLow);
			    getsize(&ViewWinWidth, &ViewWinHeight);
			    ViewWinWidth2 = ViewWinWidth / 2;
			    ViewWinHeight2 = ViewWinHeight / 2;
			    reshapeviewport();
			    ortho2(-0.5, ViewWinWidth - 0.5,
				   -0.5, ViewWinHeight - 0.5);
			    RedrawViewWindow();
			}
			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 &&
			    IGHandleState(dopup(GlblStateMenu), FALSE)) {
			    *ChangeFactor = 0.0;
			    return IG_EVENT_STATE;
			}
			break;
		    case LEFTMOUSE:
			TransformRequest = data;
			break;
		}
		continue;
	    }

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

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

	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 the 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;
	    case IG_EVENT_QUIT:
		break;
	}

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

    LastEvent = RetVal;
    LastX = x;

    return RetVal;
}

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

    switch (State) {
	case IG_STATE_SCR_OBJ_TGL:
	    IGGlblTransformMode = IGGlblTransformMode == IG_TRANS_OBJECT ?
							 IG_TRANS_SCREEN :
							 IG_TRANS_OBJECT;
	    RedrawTransformWindow();
	    UpdateView = FALSE;
	    break;
	case IG_STATE_PERS_ORTHO_TGL:
	    IGGlblViewMode = IGGlblViewMode == IG_VIEW_PERSPECTIVE ?
					       IG_VIEW_ORTHOGRAPHIC :
					       IG_VIEW_PERSPECTIVE;
	    RedrawTransformWindow();
	    break;
	case IG_STATE_DRAW_SOLID:
	    IGGlblDrawSolid = !IGGlblDrawSolid;
	    /* And fall thru to disable the depth cueing. */
	    IGGlblDepthCue = TRUE; 
	case IG_STATE_DEPTH_CUE:
	    IGGlblDepthCue = !IGGlblDepthCue;
	    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;
	case IG_STATE_WIDER_LINES:
	    IGGlblLineWidth *= 2;
	    linewidth(IGGlblLineWidth);
	    break;
	case IG_STATE_NARROW_LINES:
	    IGGlblLineWidth /= 2;
	    if (IGGlblLineWidth < 1)
		IGGlblLineWidth = 1;
	    linewidth(IGGlblLineWidth);
	default:
	    UpdateView = IGDefaultStateHandler(State, Refresh);
	    break;
    }

    IGCreateStateMenu();

    return UpdateView;
}

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

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

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