/****************************************************************************
*   An OS2 2.x driver.							    *
*									    *
* Written by:  Gershon Elber			     Ver 0.1, October 1993. *
*****************************************************************************
* (C) Gershon Elber, Technion, Israel Institute of Technology               *
****************************************************************************/

#include <stdio.h>

#define INCL_WINSTDFILE
#define INCL_WININPUT
#define INCL_WINFRAMEMGR
#define INCL_WINMENUS
#define INCL_DOSPROCESS
#define INCL_PM
#define INCL_BASE
#include <os2.h>

#include "irit_sm.h"
#include "genmat.h"
#include "iritprsr.h"
#include "allocate.h"
#include "attribut.h"
#include "ip_cnvrt.h"
#include "cagd_lib.h"
#include "symb_lib.h"
#include "iritgrap.h"
#include "os2drvs.h"
#include "irit_soc.h"

#define ID_VIEW		1
#define ID_TRANS	2
#define ID_ANIM		3

#define DEFAULT_TRANS_WIDTH	200
#define DEFAULT_TRANS_HEIGHT	500
#define DEFAULT_VIEW_WIDTH	400
#define DEFAULT_VIEW_HEIGHT	400
                                   
#define ARGCV_LINE_LEN	1000
#define ARGCV_MAX_WORDS	100

#define SIGNED_SHORT1FROMMP(mp)	(SHORT1FROMMP(mp) > 32767 ? \
				     SHORT1FROMMP(mp) - 65536 : \
				     SHORT1FROMMP(mp))
#define SIGNED_SHORT2FROMMP(mp)	(SHORT2FROMMP(mp) > 32767 ? \
				     SHORT2FROMMP(mp) - 65536 : \
				     SHORT2FROMMP(mp))

#define RGB_COLOR(R, G, B)	(((R) << 16) + ((G) << 8) + (B))
#define GPI_MOVE(X, Y)	{ POINTL Pt; Pt.x = X; Pt.y = Y; GpiMove(hps, &Pt); }
#define GPI_LINE(X, Y)	{ POINTL Pt; Pt.x = X; Pt.y = Y; GpiLine(hps, &Pt); }
#define GPI_CHAR_STR_AT(Str, X, Y) { POINTL Pt; int l = strlen(Str); \
				     Pt.x = X - cxChar * (l + 1) / 2; \
				     Pt.y = Y - cyChar / 3; \
				     GpiCharStringAt(hps, &Pt, l, Str); }
#define OS2_MAP_X_COORD(x) ((int) (ViewWidth2 + x * ViewWidth2))
#define OS2_MAP_Y_COORD(y) ((int) (ViewHeight2 + y * ViewWidth2))

#define WIN_CHECK_MENU(Item, Val)	WinSendMsg(hwndMenu, MM_SETITEMATTR, \
						   MPFROM2SHORT(Item, TRUE), \
						   MPFROM2SHORT(MIA_CHECKED, \
								(Val)));
#define WIN_TEXT_MENU(Item, Val)	WinSendMsg(hwndMenu, MM_SETITEMTEXT, \
						   MPFROM2SHORT(Item, TRUE), \
						   MPFROMP(Val));

/* Interactive menu setup structure: */
#define INTERACT_NUM_OF_STRINGS		3
#define INTERACT_NUM_OF_SUB_WNDWS	15
#define INTERACT_SUB_WINDOW_WIDTH  0.8		 /* Relative to window size. */
#define INTERACT_SUB_WINDOW_HEIGHT 0.04

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;

/* Interactive mode menu set up structure is define below: */
static InteractWindowStruct InteractMenu =
{
  {
    { 0.5, 0.76, IG_IRIT_RED,   "Rotate" },
    { 0.5, 0.56, IG_IRIT_GREEN, "Translate" },
    { 0.5, 0.36, IG_IRIT_CYAN,  "Scale" },
  },
  {
    { 0.5, 0.92, IG_IRIT_YELLOW, IG_EVENT_SCR_OBJ_TGL,    TRUE,  "Screen Coords." },
    { 0.5, 0.84, IG_IRIT_BLUE,   IG_EVENT_PERS_ORTHO_TGL, TRUE,  "Perspective" },
    { 0.5, 0.79, IG_IRIT_BLUE,   IG_EVENT_PERS_ORTHO_Z,   FALSE, "Z" },
    { 0.5, 0.69, IG_IRIT_RED,    IG_EVENT_ROTATE_X,       FALSE, "X" },  /* Rot */
    { 0.5, 0.64, IG_IRIT_RED,    IG_EVENT_ROTATE_Y,       FALSE, "Y" },
    { 0.5, 0.59, IG_IRIT_RED,    IG_EVENT_ROTATE_Z,       FALSE, "Z" },
    { 0.5, 0.49, IG_IRIT_GREEN,  IG_EVENT_TRANSLATE_X,    FALSE, "X" },/* Trans */
    { 0.5, 0.44, IG_IRIT_GREEN,  IG_EVENT_TRANSLATE_Y,    FALSE, "Y" },
    { 0.5, 0.39, IG_IRIT_GREEN,  IG_EVENT_TRANSLATE_Z,    FALSE, "Z" },
    { 0.5, 0.29, IG_IRIT_CYAN,   IG_EVENT_SCALE,	  FALSE, "" }, /* Scale */
    { 0.5, 0.21, IG_IRIT_YELLOW, IG_EVENT_DEPTH_CUE,      TRUE,  "Depth Cue" },
    { 0.5, 0.16, IG_IRIT_YELLOW, IG_EVENT_SAVE_MATRIX,    TRUE,  "Save Matrix" },
    { 0.5, 0.12, IG_IRIT_YELLOW, IG_EVENT_PUSH_MATRIX,    TRUE,  "Push Matrix" },
    { 0.5, 0.08, IG_IRIT_YELLOW, IG_EVENT_POP_MATRIX,     TRUE,  "Pop Matrix" },
    { 0.5, 0.03, IG_IRIT_WHITE,  IG_EVENT_QUIT,	          TRUE,  "Quit" },
  }
};

/* Colors to be used for viewed object (see also iritgrap.h):		   */
/* This colors are adjusted so as to give OS2 2.x a better chance at       */
/* selecting these colors as solid.					   */
static short Colors[IG_MAX_COLOR + 1][3] =
{
    { 0,   0,   0   },  /* 0. BLACK */
    { 0,   0,   255 },  /* 1. BLUE */
    { 0,   255, 0   },  /* 2. GREEN */
    { 0,   255, 255 },  /* 3. CYAN */
    { 255, 0,   0   },  /* 4. RED */
    { 255, 0,   255 },  /* 5. MAGENTA */
    { 127, 127, 0   },  /* 6. BROWN */
    { 127, 127, 127 },  /* 7. LIGHTGREY */
    { 63,  63,  63  },  /* 8. DARKGRAY */
    { 63,  63,  255 },  /* 9. LIGHTBLUE */
    { 63,  255, 63  },  /* 10. LIGHTGREEN */
    { 63,  255, 255 },  /* 11. LIGHTCYAN */
    { 255, 63,  63  },  /* 12. LIGHTRED */
    { 255, 63,  255 },  /* 13. LIGHTMAGENTA */
    { 255, 255, 63  },  /* 14. YELLOW */
    { 255, 255, 255 }   /* 15. WHITE */
};

static int
    GlblSliderIncrements = 0;
static unsigned int
    TransWidth = DEFAULT_TRANS_WIDTH,
    TransHeight = DEFAULT_TRANS_HEIGHT,
    TransWidth2 = DEFAULT_TRANS_WIDTH / 2,
    ViewWidth2 = DEFAULT_VIEW_WIDTH / 2,
    ViewHeight2 = DEFAULT_VIEW_HEIGHT / 2;

static LONG CrntColorLowIntensity, CrntColorHighIntensity,
    ColorsLowIntensity[IG_MAX_COLOR + 1],
    ColorsHighIntensity[IG_MAX_COLOR + 1];
static HPS
    CurrentHps = 0;
static HWND
    hwndMenu = 0,
    hwndFrame = 0,
    hwndClient = 0,
    hwndViewFrame = 0,
    hwndTransFrame = 0,
    hwndSlider = 0;
static double
    GlblAnimGetTime = 0.0;

static void GetInputFromSocket(void *Data);
static MRESULT TransWndProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2);
static MRESULT ViewWndProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2);
static MRESULT ClientWndProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2);
static MRESULT AnimWndProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2);
static MRESULT AnimGetTimeWndProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2);

static void OS2DrvsAnimation(void *Data);
static void HandleGetOneNumber(RealType *t, HWND hwnd);
static IGGraphicEventType GetGraphicEvent(RealType *ChangeFactor,
					  int X,
					  int Y);
static void RedrawViewWindow(HWND hwnd);
static void ViewObject(IPObjectStruct *PObj, MatrixType Mat);
static void RedrawTransformationWindow(HWND hwnd);
static void SetColorIndex(int c);
static void SetColorRGB(int Color[3]);

/*****************************************************************************
* DESCRIPTION:                                                               M
* Main module of os2drvs - OS2 2.x 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)
{
    static ULONG flFrameFlags;
    static char
	*szClientClass = "os2iritdrv.Client",
	*szViewClass = "os2iritdrv.View",
	*szTransClass = "os2iritdrv.Trans";
    int i, x1, x2, y1, y2,
	Xmin = 190,
	Ymin = 190,
	Width = 600,
	Height = 400;
    QMSG qMsg;
    HPS hps;
    HAB hab;
    HMQ hmq;
    RECTL rcl, DeskTopRcl;

    IGConfigureGlobals("os2drvs", argc, argv);
    if (getenv("IRIT_DISPLAY_S") != NULL)
	IGGlblStandAlone = FALSE;

    if (IGGlblViewPrefPos &&
	sscanf(IGGlblViewPrefPos, "%d,%d,%d,%d", &x1, &x2, &y1, &y2) == 4) {
	Xmin = x1;
	Ymin = y1;
	Width = x2 - x1;
	Height = y2 - y1;
    }

    hab = WinInitialize(0);
    hmq = WinCreateMsgQueue(hab, 0);

    WinRegisterClass(hab, szClientClass, ClientWndProc, CS_SIZEREDRAW, 0L);
    WinRegisterClass(hab, szViewClass,   ViewWndProc,   CS_SIZEREDRAW, 0L);
    WinRegisterClass(hab, szTransClass,  TransWndProc,  CS_SIZEREDRAW, 0L);

    flFrameFlags = FCF_TITLEBAR      | FCF_SYSMENU  |
                   FCF_SIZEBORDER    | FCF_MINMAX   |
                   FCF_SHELLPOSITION | FCF_TASKLIST |
		   FCF_MENU	     | FCF_ACCELTABLE;

    hwndFrame = WinCreateStdWindow(
                    HWND_DESKTOP,
                    0,
                    &flFrameFlags,
                    szClientClass,
                    NULL,
                    0L,
                    0,
                    ID_OS2DRVS,
                    &hwndClient);

    WinQueryWindowRect(HWND_DESKTOP, &DeskTopRcl);

    if (DeskTopRcl.yTop < Ymin + Height)   /* Make sure top side is visible. */
	Ymin = DeskTopRcl.yTop - Height;

    WinSetWindowPos(hwndFrame, HWND_TOP, Xmin, Ymin, Width, Height,
				SWP_SIZE | SWP_MOVE | SWP_SHOW | SWP_ZORDER);

    WinQueryWindowRect(hwndClient, &rcl);
    flFrameFlags &= (~FCF_TASKLIST & ~FCF_MENU & ~FCF_ACCELTABLE);

    hwndViewFrame = WinCreateWindow(
		    hwndClient,
		    szViewClass,
		    NULL,
		    WS_VISIBLE,
		    5,
		    5,
		    (SHORT) ((rcl.xRight * 4) / 5 - 10),
		    (SHORT) (rcl.yTop - 10),
		    hwndClient,
		    HWND_BOTTOM,
		    ID_VIEW,
		    NULL,
		    NULL);

    hwndTransFrame = WinCreateWindow(
		    hwndClient,
		    szTransClass,
		    NULL,
		    WS_VISIBLE,
		    (SHORT) ((rcl.xRight * 4) / 5),
		    5,
		    (SHORT) (rcl.xRight / 5 - 5),
		    (SHORT) (rcl.yTop - 10),
		    hwndClient,
		    HWND_BOTTOM,
		    ID_TRANS,
		    NULL,
		    NULL);

    if (hwndViewFrame == 0 || hwndTransFrame == 0) {
    	DosBeep(1000, 100);
    	exit(1);
    }

    /* Preallocate the colors that will be used frequently. */
    hps = WinBeginPaint(hwndFrame, 0, NULL);
    for (i = 0; i <= IG_MAX_COLOR; i++) {
	ColorsHighIntensity[i] =
	    GpiQueryColorIndex(hps, 0L,
		RGB_COLOR(Colors[i][0], Colors[i][1], Colors[i][2]));
	ColorsLowIntensity[i] =
	    GpiQueryColorIndex(hps, 0L,
		RGB_COLOR(Colors[i][0] / 2,
			  Colors[i][1] / 2,
			  Colors[i][2] / 2));
    }
    WinEndPaint(hps);

    /* Set up the reading socket as a secondary thread. */
    if (!IGGlblStandAlone) {
	if (_beginthread(GetInputFromSocket, NULL, 128 * 1024, NULL) == -1) {
	    WinMessageBox(HWND_DESKTOP, hwndFrame, "Failed to start thread\n",
			  NULL, 0, MB_OK);
	    exit(1);
	}
    }

    IGCreateStateMenu();	       /* Refresh the menu to current state. */

    while (TRUE) {
	if (WinPeekMsg(hab, &qMsg, 0L, 0, 0, PM_REMOVE))
	    WinDispatchMsg(hab, &qMsg);

	if (IGGlblNewDisplayObjects != NULL) {
	    IGHandleObjectsFromSocket(IGGlblViewMode,
				      IGGlblNewDisplayObjects,
				      &IGGlblDisplayList);
	    IGGlblNewDisplayObjects = NULL;
	    WinInvalidateRect(hwndViewFrame, NULL, FALSE);
	}
	IritSleep(1);				      /* Do not use all CPU. */
    }

    WinDestroyWindow(hwndFrame);
    WinDestroyMsgQueue(hmq);
    WinTerminate(hab);
}

/*****************************************************************************
* 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)
{
    WIN_CHECK_MENU(IDM_TGLS_SCREEN, 
		   IGGlblTransformMode == IG_TRANS_SCREEN ? MIA_CHECKED : 0);
    WIN_CHECK_MENU(IDM_TGLS_PERSP,
		   IGGlblViewMode == IG_VIEW_PERSPECTIVE ? MIA_CHECKED : 0);
    WIN_CHECK_MENU(IDM_TGLS_DEPTH_CUE, IGGlblDepthCue ? MIA_CHECKED : 0);
    WIN_CHECK_MENU(IDM_TGLS_DOUBLE_BUFFER, IGGlblDoDoubleBuffer ? MIA_CHECKED : 0);
    WIN_CHECK_MENU(IDM_TGLS_DRAW_SOLID, IGGlblDrawSolid ? MIA_CHECKED : 0);
    WIN_CHECK_MENU(IDM_TGLS_BFACE_CULL, IGGlblBackFaceCull ? MIA_CHECKED : 0);
    switch (IGGlblShadingModel) {
	case IG_SHADING_NONE:
	    WIN_TEXT_MENU(IDM_TGLS_SHADING_MODES, "No Shading ~Mode");
	    break;
	case IG_SHADING_FLAT:
	    WIN_TEXT_MENU(IDM_TGLS_SHADING_MODES, "Flat Shading ~Mode");
	    break;
	case IG_SHADING_GOURAUD:
	    WIN_TEXT_MENU(IDM_TGLS_SHADING_MODES, "Gouraud Shading ~Mode");
	    break;
	case IG_SHADING_PHONG:
	    WIN_TEXT_MENU(IDM_TGLS_SHADING_MODES, "Phong Shading ~Mode");
	    break;
    }
    WIN_CHECK_MENU(IDM_TGLS_INTERNAL, IGGlblDrawInternal ? MIA_CHECKED : 0);
    WIN_CHECK_MENU(IDM_TGLS_VRTX_NRML, IGGlblDrawVNormal ? MIA_CHECKED : 0);
    WIN_CHECK_MENU(IDM_TGLS_POLY_NRML, IGGlblDrawPNormal ? MIA_CHECKED : 0);
    WIN_CHECK_MENU(IDM_TGLS_CTL_MESH, IGGlblDrawSurfaceMesh ? MIA_CHECKED : 0);
    WIN_CHECK_MENU(IDM_TGLS_SRF_POLYS, IGGlblDrawSurfacePoly ? MIA_CHECKED : 0);
    WIN_CHECK_MENU(IDM_TGLS_4_PER_FLAT, IGGlblFourPerFlat ? MIA_CHECKED : 0);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* A secondary thread that waits for input from socket.			     *
*                                                                            *
* PARAMETERS:                                                                *
*   Data:      Not used.                                                     *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void GetInputFromSocket(void *Data)
{
    while (TRUE) {
	IGReadObjectsFromSocket(IGGlblViewMode, &IGGlblDisplayList);
	IritSleep(10);
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Trans window call back drawing function.				     *
*									     *
* PARAMETERS:                                                                *
*   hwnd:     A handle on the window.                                        *
*   msg:      Type of event to handle.                                       *
*   mp1, mp2: Parameters of event.                                           *
*                                                                            *
* RETURN VALUE:                                                              *
*   MRESULT:  Event state code.                                              *
*****************************************************************************/
static MRESULT TransWndProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
{
    static IGGraphicEventType
	LastEvent = IG_EVENT_NONE;
    static int
	LastX = 0,
	Button1Down = FALSE;
    RealType ChangeFactor[2];
    IGGraphicEventType Event;
    int x, y;
    
    switch (msg) {
    	case WM_CREATE:
	    break;
	case WM_BUTTON1DOWN:
	    LastEvent = IG_EVENT_NONE;
	    x = SIGNED_SHORT1FROMMP(mp1);
	    y = SIGNED_SHORT2FROMMP(mp1);

	    if ((Event = GetGraphicEvent(ChangeFactor, x, y))
							   != IG_EVENT_NONE) {
		ChangeFactor[0] *= IGGlblChangeFactor;
		ChangeFactor[1] *= IGGlblChangeFactor;

		if (Event == IG_EVENT_QUIT) {
		    exit(0);		   /* Not the nicest ways to quit. */
		}
		else {
		    if (IGProcessEvent(Event, ChangeFactor))
		        WinInvalidateRect(hwndViewFrame, NULL, TRUE);

		    /* Save the event in case drag operation is performed. */
		    LastEvent = Event;
		    LastX = x;
		}
	    }
	    Button1Down = TRUE;
	    WinSetCapture(HWND_DESKTOP, hwnd);
	    break;
	case WM_MOUSEMOVE:
	    if (Button1Down && LastEvent != IG_EVENT_NONE) {
		*ChangeFactor = IGGlblChangeFactor *
		      ((x = SIGNED_SHORT1FROMMP(mp1)) - ((RealType) LastX)) /
								   TransWidth2;
		if (IGProcessEvent(LastEvent, ChangeFactor))
		    WinInvalidateRect(hwndViewFrame, NULL, TRUE);
		LastX = x;
	    }
	    break;
	case WM_BUTTON1UP:
	    Button1Down = FALSE;
	    WinSetCapture(HWND_DESKTOP, 0);
	    break;
        case WM_PAINT:
	    RedrawTransformationWindow(hwnd);
	    break;
        case WM_COMMAND:
	    break;
    }
    return WinDefWindowProc(hwnd, msg, mp1, mp2);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* View window call back drawing function.				     *
*									     *
* PARAMETERS:                                                                *
*   hwnd:     A handle on the window.                                        *
*   msg:      Type of event to handle.                                       *
*   mp1, mp2: Parameters of event.                                           *
*                                                                            *
* RETURN VALUE:                                                              *
*   MRESULT:  Event state code.                                              *
*****************************************************************************/
static MRESULT ViewWndProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
{
    static IGGraphicEventType
	LastEvent = IG_EVENT_NONE;
    static int
	LastX = 0,
	LastY = 0,
	ButtonDown = FALSE;
    RealType ChangeFactor[2];
    
    switch (msg) {
        case WM_PAINT:
	    RedrawViewWindow(hwnd);
	    break;
        case WM_COMMAND:
            switch (SHORT1FROMMP(mp1)) {
            }
            break;
	case WM_BUTTON1DOWN:
	    LastEvent = IG_EVENT_ROTATE;
	    LastX = SIGNED_SHORT1FROMMP(mp1);
	    LastY = SIGNED_SHORT2FROMMP(mp1);
	    ButtonDown = TRUE;
	    WinSetCapture(HWND_DESKTOP, hwnd);
	    break;
	case WM_BUTTON2DOWN:
	    LastEvent = IG_EVENT_TRANSLATE;
	    LastX = SIGNED_SHORT1FROMMP(mp1);
	    LastY = SIGNED_SHORT2FROMMP(mp1);
	    ButtonDown = TRUE;
	    WinSetCapture(HWND_DESKTOP, hwnd);
	    break;
	case WM_MOUSEMOVE:
	    if (ButtonDown && LastEvent != IG_EVENT_NONE) {
		ChangeFactor[0] = (SIGNED_SHORT1FROMMP(mp1) - LastX) *
							 IGGlblChangeFactor;
		ChangeFactor[1] = (SIGNED_SHORT2FROMMP(mp1) - LastY) *
							 IGGlblChangeFactor;
		if (IGProcessEvent(LastEvent, ChangeFactor))
		    WinInvalidateRect(hwndViewFrame, NULL, TRUE);
		LastX = SIGNED_SHORT1FROMMP(mp1);
		LastY = SIGNED_SHORT2FROMMP(mp1);
	    }
	    break;
	case WM_BUTTON1UP:
	case WM_BUTTON2UP:
	    WinSetCapture(HWND_DESKTOP, 0);
	    ButtonDown = FALSE;
	    break;
    }
    return WinDefWindowProc(hwnd, msg, mp1, mp2);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Client (Frame) window call back drawing function.			     *
*									     *
* PARAMETERS:                                                                *
*   hwnd:     A handle on the window.                                        *
*   msg:      Type of event to handle.                                       *
*   mp1, mp2: Parameters of event.                                           *
*                                                                            *
* RETURN VALUE:                                                              *
*   MRESULT:  Event state code.                                              *
*****************************************************************************/
static MRESULT ClientWndProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
{
    POINTL Pt;
    RECTL rcl;
    HPS hps;
    int Refresh;
    FILEDLG fild;
    static int
	Resized = FALSE;

    switch (msg) {
	case WM_CREATE:
	    hwndMenu = WinWindowFromID(WinQueryWindow(hwnd, QW_PARENT),
				       FID_MENU);
	    break;
        case WM_SIZE:
	    Resized = TRUE;
	    break;
        case WM_PAINT:
	    hps = WinBeginPaint(hwnd, 0, NULL);

	    WinQueryWindowRect(hwnd, &rcl);
	    GpiSetColor(hps, CLR_WHITE);
	    Pt.x = Pt.y = 0;
	    GpiMove(hps, &Pt);
	    Pt.x = rcl.xRight;
	    Pt.y = rcl.yTop;
	    GpiBox(hps, DRO_OUTLINEFILL, &Pt, 0, 0);

	    WinEndPaint(hps);

	    if (Resized) {
	    	Resized = FALSE;
	    	
	    	if (hwndViewFrame)
		    WinSetWindowPos(hwndViewFrame, HWND_BOTTOM, 5, 5,
				    (SHORT) ((rcl.xRight * 4) / 5 - 10),
				    (SHORT) (rcl.yTop - 10),
				    SWP_SIZE | SWP_MOVE);
		if (hwndTransFrame)
		    WinSetWindowPos(hwndTransFrame, HWND_BOTTOM,
				    (SHORT) (rcl.xRight * 4 / 5), 5,
				    (SHORT) (rcl.xRight / 5 - 5),
				    (SHORT) (rcl.yTop - 10),
				    SWP_SIZE | SWP_MOVE);
	    }
            break;
        case WM_COMMAND:
	    Refresh = FALSE;
            switch (SHORT1FROMMP(mp1)) {
		case IDM_FILE_SAVE:
		    IGSaveCurrentMat(IGGlblViewMode, NULL);
		    break;
		case IDM_FILE_SAVE_AS:
		    memset(&fild, 0, sizeof(FILEDLG));
		    fild.cbSize = sizeof(FILEDLG);
		    fild.pszTitle = "Save View Matrix";
		    fild.pszOKButton = "Save";
		    fild.fl = FDS_OPEN_DIALOG | FDS_CENTER ;
		    WinFileDlg(HWND_DESKTOP, hwndClient, &fild);

		    if (strlen(fild.szFullFile) > 0)
			IGSaveCurrentMat(IGGlblViewMode, fild.szFullFile);
		    break;
		case IDM_FILE_QUIT:
		    exit(0);
		    break;

		case IDM_MOUSE_MORE:
		    IGHandleState(IG_STATE_MORE_SENSITIVE, TRUE);
		    break;
		case IDM_MOUSE_LESS:
		    IGHandleState(IG_STATE_LESS_SENSITIVE, TRUE);
		    break;

		case IDM_STATE_MORE_ISO:
		    Refresh = IGHandleState(IG_STATE_MORE_ISOLINES, TRUE);
		    break;
		case IDM_STATE_LESS_ISO:
		    Refresh = IGHandleState(IG_STATE_LESS_ISOLINES, TRUE);
		    break;
		case IDM_STATE_FINER_APPROX:
		    Refresh = IGHandleState(IG_STATE_FINER_APPROX, TRUE);
		    break;
		case IDM_STATE_COARSER_APPROX:
		    Refresh = IGHandleState(IG_STATE_COARSER_APPROX, TRUE);
		    break;
		case IDM_STATE_SHORTER_VEC:
		    Refresh = IGHandleState(IG_STATE_SHORTER_VECTORS, TRUE);
		    break;
		case IDM_STATE_LONGER_VEC:
		    Refresh = IGHandleState(IG_STATE_LONGER_VECTORS, TRUE);
		    break;
		case IDM_STATE_WIDE_LINES:
		    Refresh = IGHandleState(IG_STATE_WIDER_LINES, TRUE);
		    break;
		case IDM_STATE_THIN_LINES:
		    Refresh = IGHandleState(IG_STATE_NARROW_LINES, TRUE);
		    break;

		case IDM_TGLS_SCREEN:
		    IGHandleState(IG_STATE_SCR_OBJ_TGL, TRUE);
		    break;
		case IDM_TGLS_PERSP:
		    Refresh = IGHandleState(IG_STATE_PERS_ORTHO_TGL, TRUE);
		    break;
		case IDM_TGLS_DEPTH_CUE:
		    Refresh = IGHandleState(IG_STATE_DEPTH_CUE, TRUE);
		    break;
		case IDM_TGLS_DOUBLE_BUFFER:
		    Refresh = IGHandleState(IG_STATE_DOUBLE_BUFFER, TRUE);
		    break;
		case IDM_TGLS_DRAW_SOLID:
		    Refresh = IGHandleState(IG_STATE_DRAW_SOLID, TRUE);
		    break;
		case IDM_TGLS_BFACE_CULL:
		    Refresh = IGHandleState(IG_STATE_BACK_FACE_CULL, TRUE);
		    break;
		case IDM_TGLS_SHADING_MODES:
		    Refresh = IGHandleState(IG_STATE_SHADING_MODEL, TRUE);
		    break;
		case IDM_TGLS_INTERNAL:
		    Refresh = IGHandleState(IG_STATE_DRAW_INTERNAL, TRUE);
		    break;
		case IDM_TGLS_VRTX_NRML:
		    Refresh = IGHandleState(IG_STATE_DRAW_VNORMAL, TRUE);
		    break;
		case IDM_TGLS_POLY_NRML:
		    Refresh = IGHandleState(IG_STATE_DRAW_PNORMAL, TRUE);
		    break;
		case IDM_TGLS_CTL_MESH:
		    Refresh = IGHandleState(IG_STATE_DRAW_SRF_MESH, TRUE);
		    break;
		case IDM_TGLS_SRF_POLYS:
		    Refresh = IGHandleState(IG_STATE_DRAW_SRF_POLY, TRUE);
		    break;
		case IDM_TGLS_4_PER_FLAT:
		    Refresh = IGHandleState(IG_STATE_FOUR_PER_FLAT, TRUE);
		    break;

		case IDM_VIEW_FRONT:
		    Refresh = IGHandleState(IG_STATE_VIEW_FRONT, TRUE);
		    break;
		case IDM_VIEW_SIDE:
		    Refresh = IGHandleState(IG_STATE_VIEW_SIDE, TRUE);
		    break;
		case IDM_VIEW_TOP:
		    Refresh = IGHandleState(IG_STATE_VIEW_TOP, TRUE);
		    break;
		case IDM_VIEW_ISOMETRY:
		    Refresh = IGHandleState(IG_STATE_VIEW_ISOMETRY, TRUE);
		    break;

		case IDM_ANIM_ACTIVE:
		    WinDlgBox(HWND_DESKTOP, HWND_DESKTOP,
			      AnimWndProc, 0, IDM_ANIM, NULL);
		    break;
            }
	    if (Refresh)
		WinInvalidateRect(hwndViewFrame, NULL, FALSE);
            break;
    }
    return WinDefWindowProc(hwnd, msg, mp1, mp2);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Animation (Frame) window call back drawing function.			     *
*									     *
* PARAMETERS:                                                                *
*   hwnd:     A handle on the window.                                        *
*   msg:      Type of event to handle.                                       *
*   mp1, mp2: Parameters of event.                                           *
*                                                                            *
* RETURN VALUE:                                                              *
*   MRESULT:  Event state code.                                              *
*****************************************************************************/
static MRESULT AnimWndProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
{
    static int
	LastSliderVal = -1;
    POINTL Pt;
    RECTL rcl;
    HPS hps;
    WNDPARAMS wprm; 
    SLDCDATA sldcd;
    int i, j, Spacing;
    char Buffer[10];

    switch (msg) {
	case WM_CREATE:
	    break;
        case WM_INITDLG:
	    hwndSlider = WinWindowFromID(hwnd, ID_ANIM_SLIDER);

	    wprm.fsStatus = WPM_CTLDATA;
	    wprm.cbCtlData = sizeof(SLDCDATA);
	    wprm.pCtlData = &sldcd;

	    if (WinSendMsg(hwndSlider, WM_QUERYWINDOWPARAMS,
			   MPFROMP(&wprm), 0)) {
		GlblSliderIncrements = sldcd.usScale1Increments;
		Spacing = sldcd.usScale1Spacing;
	    }

	    for (i = 0; i <= GlblSliderIncrements; i++)
		WinSendMsg(hwndSlider, SLM_SETTICKSIZE,
			   MPFROM2SHORT(i, 4), NULL);

	    for (i = 0; i <= GlblSliderIncrements; i += 10)
	        WinSendMsg(hwndSlider, SLM_SETTICKSIZE,
			   MPFROM2SHORT(i, 8), NULL);
            WinSetPresParam(hwndSlider, PP_FONTNAMESIZE,
			    strlen(ID_ANIM_SLIDER_FONT) + 1,
			    ID_ANIM_SLIDER_FONT);

	    AnimFindAnimationTime(&IGAnimation, IGGlblDisplayList);

	    for (i = j = 0;
		 i <= GlblSliderIncrements;
		 i += GlblSliderIncrements / 3, j++) {
		RealType
		    t = (IGAnimation.StartT * (3 - j) +
		         IGAnimation.FinalT * j) / 3.0;

		sprintf(Buffer, "%4.2f", t);
		WinSendMsg(hwndSlider, SLM_SETSCALETEXT,
			   MPFROMSHORT(i), MPFROMP(Buffer));
	    }
	    break;
        case WM_PAINT:
	    hps = WinBeginPaint(hwnd, 0, NULL);

	    WinQueryWindowRect(hwnd, &rcl);
	    GpiSetColor(hps, CLR_DARKGRAY);
	    Pt.x = Pt.y = 0;
	    GpiMove(hps, &Pt);
	    Pt.x = rcl.xRight;
	    Pt.y = rcl.yTop;
	    GpiBox(hps, DRO_OUTLINEFILL, &Pt, 0, 0);

	    WinEndPaint(hps);
            break;
        case WM_COMMAND:
        case WM_CONTROL:
	    switch (COMMANDMSG(&msg) -> cmd) {
		case ID_ANIM_SLIDER:
                    i = SHORT1FROMMR(WinSendDlgItemMsg(hwnd, ID_ANIM_SLIDER,
				SLM_QUERYSLIDERINFO,
                                MPFROM2SHORT(SMA_SLIDERARMPOSITION,
					     SMA_INCREMENTVALUE),
                                0));
		    if (LastSliderVal != i) {
			RealType
			    t = i / 100.0;

			LastSliderVal = i;

			IGAnimation.RunTime =
			    (IGAnimation.FinalT * t +
			     IGAnimation.StartT * (1.0 - t));

			AnimDoSingleStep(&IGAnimation, IGGlblDisplayList);
		    }
		    break;
		case ID_ANIM_SAVE_FILE:
		    IGAnimation.SaveAnimation = !IGAnimation.SaveAnimation;
		    WinSendDlgItemMsg(hwnd, ID_ANIM_SAVE_FILE, BM_SETCHECK,
				      MPFROM2SHORT(IGAnimation.SaveAnimation,
						   0),
				      NULL);
		    break;
		case ID_ANIM_MIN_TIME:
		    HandleGetOneNumber(&IGAnimation.StartT, hwnd);
		    break;
		case ID_ANIM_MAX_TIME:
		    HandleGetOneNumber(&IGAnimation.FinalT, hwnd);
		    break;
		case ID_ANIM_TIME_STEP:
		    HandleGetOneNumber(&IGAnimation.Dt, hwnd);
		    break;
		case ID_ANIM_BEGIN:
		    if (_beginthread(OS2DrvsAnimation, NULL,
				     128 * 1024, NULL) == -1)
			WinMessageBox(HWND_DESKTOP, hwndFrame,
				      "Failed to start animation thread\n",
				      NULL, 0, MB_OK);
		    break;
		case ID_ANIM_STOP:
		    IGAnimation.StopAnim = TRUE;
		    break;
		case ID_ANIM_DISMISS:
                    WinDismissDlg(hwnd, FALSE);
		    break;
		default:
		    break;
	    }
            break;
    }
    return WinDefWindowProc(hwnd, msg, mp1, mp2);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   A secondary thread to execute an animation sequence.                     *
*                                                                            *
* PARAMETERS:                                                                *
*   Data:      Not used.                                                     *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void OS2DrvsAnimation(void *Data)
{
    AnimDoAnimation(&IGAnimation, IGGlblDisplayList);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Gets one number from the user, and make sure display is consistent.      *
*                                                                            *
* PARAMETERS:                                                                *
*   t:        Numeric data to modify.                                        *
*   hwnd:     A handle on the window.                                        *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void HandleGetOneNumber(RealType *t, HWND hwnd)
{
    int i, j;
    char Buffer[10];
    RealType
	RunTimeFrac = (IGAnimation.RunTime - IGAnimation.StartT) /
		       (IGAnimation.FinalT - IGAnimation.StartT);

    GlblAnimGetTime = *t;
    WinDlgBox(HWND_DESKTOP, HWND_DESKTOP, AnimGetTimeWndProc,
	      0, ID_ANIM_GET_TIME, NULL);
    *t = GlblAnimGetTime;

    IGAnimation.RunTime = (IGAnimation.FinalT * RunTimeFrac +
			   IGAnimation.StartT * (1.0 - RunTimeFrac));

    for (i = j = 0;
	 i <= GlblSliderIncrements;
	 i += GlblSliderIncrements / 3, j++) {
	RealType
	    t = (IGAnimation.StartT * (3 - j) +
		 IGAnimation.FinalT * j) / 3.0;

	sprintf(Buffer, "%4.2f", t);
	WinSendMsg(hwndSlider, SLM_SETSCALETEXT,
		   MPFROMSHORT(i), MPFROMP(Buffer));
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Animation (Frame) window call back drawing function.			     *
*									     *
* PARAMETERS:                                                                *
*   hwnd:     A handle on the window.                                        *
*   msg:      Type of event to handle.                                       *
*   mp1, mp2: Parameters of event.                                           *
*                                                                            *
* RETURN VALUE:                                                              *
*   MRESULT:  Event state code.                                              *
*****************************************************************************/
static MRESULT AnimGetTimeWndProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
{
    POINTL Pt;
    RECTL rcl;
    HPS hps;
    char Line[LINE_LEN];

    switch (msg) {
	case WM_CREATE:
	    break;
        case WM_PAINT:
	    hps = WinBeginPaint(hwnd, 0, NULL);

	    WinQueryWindowRect(hwnd, &rcl);
	    GpiSetColor(hps, CLR_PALEGRAY);
	    Pt.x = Pt.y = 0;
	    GpiMove(hps, &Pt);
	    Pt.x = rcl.xRight;
	    Pt.y = rcl.yTop;
	    GpiBox(hps, DRO_OUTLINEFILL, &Pt, 0, 0);

	    WinEndPaint(hps);
            break;
        case WM_COMMAND:
        case WM_CONTROL:
	    switch (COMMANDMSG(&msg) -> cmd) {
		case ID_ANIM_GET_TIME_ENT:
		    WinQueryDlgItemText(hwnd, ID_ANIM_GOT_TIME,
					LINE_LEN, Line);
		    if (sscanf(Line, "%lf", &GlblAnimGetTime) == 1)
		        WinDismissDlg(hwnd, FALSE);
		    else
		        DosBeep(300, 200);
		    break;
		case ID_ANIM_GET_TIME_CAN:
                    WinDismissDlg(hwnd, FALSE);
		    break;
		case ID_ANIM_GOT_TIME:
		    break;
		default:
		    break;
	    }
            break;
    }
    return WinDefWindowProc(hwnd, msg, mp1, mp2);
}

/*****************************************************************************
* 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.			     *
*   X, Y:		 Location of mouse event.			     *
*                                                                            *
* RETURN VALUE:                                                              *
*   IGGraphicEventType:  Type of new event.                                  *
*****************************************************************************/
static IGGraphicEventType GetGraphicEvent(RealType *ChangeFactor, int X, int Y)
{
    int i;
    IGGraphicEventType
	RetVal = IG_EVENT_NONE;
    RealType XPos, YPos;

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

    XPos = ((RealType) X) / TransWidth;
    YPos = ((RealType) Y) / TransHeight;

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

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

    /* 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;
	    WinInvalidateRect(hwndTransFrame, NULL, TRUE);
	    IGCreateStateMenu();
	    break;
	case IG_EVENT_PERS_ORTHO_TGL:
	    IGGlblViewMode = IGGlblViewMode == IG_VIEW_PERSPECTIVE ?
					       IG_VIEW_ORTHOGRAPHIC :
					       IG_VIEW_PERSPECTIVE;
	    WinInvalidateRect(hwndTransFrame, NULL, TRUE);
	    IGCreateStateMenu();
	    break;
	case IG_EVENT_DEPTH_CUE:
	    IGGlblDepthCue = !IGGlblDepthCue;
	    WinInvalidateRect(hwndTransFrame, NULL, TRUE);
	    IGCreateStateMenu();
	    break;
	default:
	    break;
    }

    *ChangeFactor = (((RealType) X) - TransWidth2) / TransWidth2;

    return RetVal;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Redraws the view window.						     M
*                                                                            *
* PARAMETERS:                                                                M
*   None                                                                     M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IGRedrawViewWindow                                                       M
*****************************************************************************/
void IGRedrawViewWindow(void)
{
    WinInvalidateRect(hwndViewFrame, NULL, FALSE);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Redraws the view window.                                                   *
*                                                                            *
* PARAMETERS:                                                                *
*   hwnd:       A handle on the window.                                      *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void RedrawViewWindow(HWND hwnd)
{
    RECTL rcl;
    MatrixType IritView;

    IGPredefinedAnimation();

    CurrentHps = WinBeginPaint(hwnd, 0, NULL);
    WinQueryWindowRect(hwnd, &rcl);
    ViewWidth2 = rcl.xRight / 2;
    ViewHeight2 = rcl.yTop / 2;
    WinFillRect(CurrentHps, &rcl, CLR_BLUE);

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

    IPTraverseObjListHierarchy(IGGlblDisplayList, IritView, ViewObject);

    WinEndPaint(CurrentHps);
    CurrentHps = 0;
}

/*****************************************************************************
* DESCRIPTION:                                                               *
*   Call back function of the IPTraverseObjListHierarchy above.              *
*                                                                            *
* PARAMETERS:                                                                *
*   PObj:      Object to display.                                            *
*   Mat:       Viewing matrix of object.                                     *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void ViewObject(IPObjectStruct *PObj, MatrixType Mat)
{
    if (IGGlblAbortKeyPressed)
        return;

    GEN_COPY(IGGlblCrntViewMat, Mat, sizeof(MatrixType));

    IGDrawObject(PObj);
}

/*****************************************************************************
* DESCRIPTION:                                                               *
* Redraws the transformation window.					     *
*                                                                            *
* PARAMETERS:                                                                *
*   hwnd:       A handle on the window.                                      *
*                                                                            *
* RETURN VALUE:                                                              *
*   void                                                                     *
*****************************************************************************/
static void RedrawTransformationWindow(HWND hwnd)
{
    int i, cxChar, cyChar,
	SubTransWidth, SubTransHeight, SubTransPosX, SubTransPosY;
    POINTL Pt;
    RECTL rcl;
    FONTMETRICS fm;
    HPS hps = WinBeginPaint(hwnd, 0, NULL);

    GpiQueryFontMetrics(WinGetPS(hwnd), (LONG) sizeof(fm), &fm);
    cxChar = fm.lAveCharWidth;
    cyChar = fm.lMaxBaselineExt;

    /* 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";
    InteractMenu.SubWindows[10].Str =
	IGGlblDepthCue ? "Depth cue" : "No depth cue";
		
    WinQueryWindowRect(hwnd, &rcl);
    TransWidth = rcl.xRight;
    TransWidth2 = rcl.xRight / 2;
    TransHeight = rcl.yTop;

    SubTransWidth = (int) (TransWidth * INTERACT_SUB_WINDOW_WIDTH);
    SubTransHeight = (int) (TransHeight *
			    INTERACT_SUB_WINDOW_HEIGHT);
    SubTransPosX = (TransWidth - SubTransWidth) / 2;

    GpiSetColor(hps, CLR_BLACK);
    Pt.x = Pt.y = 0;
    GpiMove(hps, &Pt);
    Pt.x = TransWidth;
    Pt.y = TransHeight;
    GpiBox(hps, DRO_OUTLINEFILL, &Pt, 0, 0);

    for (i = 0; i < INTERACT_NUM_OF_SUB_WNDWS; i++) {
	GpiSetColor(hps, InteractMenu.SubWindows[i].Color);
	SubTransPosY = (int) (TransHeight *
			      InteractMenu.SubWindows[i].Y);
	
	GPI_MOVE(SubTransPosX, SubTransPosY);
	GPI_LINE(SubTransPosX + SubTransWidth, SubTransPosY);
	GPI_LINE(SubTransPosX + SubTransWidth,
		 SubTransPosY + SubTransHeight);
	GPI_LINE(SubTransPosX, SubTransPosY + SubTransHeight);
	GPI_LINE(SubTransPosX, SubTransPosY);
	if (InteractMenu.SubWindows[i].TextInside) {
	    GPI_CHAR_STR_AT(InteractMenu.SubWindows[i].Str,
			    TransWidth / 2,
			    SubTransPosY + SubTransHeight / 2);
	}
	else {
	    GPI_CHAR_STR_AT(InteractMenu.SubWindows[i].Str,
			    (TransWidth - SubTransWidth) / 3,
			    SubTransPosY + SubTransHeight / 2);
	    GPI_MOVE(SubTransPosX + SubTransWidth / 2, SubTransPosY);
	    GPI_LINE(SubTransPosX + SubTransWidth / 2,
		     SubTransPosY + SubTransHeight);
	}
    }

    for (i = 0; i < INTERACT_NUM_OF_STRINGS; i++) {
	GpiSetColor(hps, InteractMenu.Strings[i].Color);
	GPI_CHAR_STR_AT(InteractMenu.Strings[i].Str,
			(int) (InteractMenu.Strings[i].X * TransWidth),
			(int) (InteractMenu.Strings[i].Y * TransHeight));
    }

    WinEndPaint(hps);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Low level 2D drawing routine. Coordinates are normalized to -1 to 1 by     M
* this time.                                                                 M
*                                                                            *
* PARAMETERS:                                                                M
*   X, Y:    Coordinates of 2D location to move to.                          M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IGMoveTo2D                                                               M
*****************************************************************************/
void IGMoveTo2D(RealType X, RealType Y)
{
    POINTL Pt;

    Pt.x = OS2_MAP_X_COORD(X);
    Pt.y = OS2_MAP_Y_COORD(Y);
    GpiMove(CurrentHps, &Pt);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Low level 2D drawing routine. Coordinates are normalized to -1 to 1 by     M
* this time.                                                                 M
*                                                                            *
* PARAMETERS:                                                                M
*   X, Y:    Coordinates of 2D location to draw to.                          M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IGLineTo2D                                                               M
*****************************************************************************/
void IGLineTo2D(RealType X, RealType Y)
{
    POINTL Pt;

    Pt.x = OS2_MAP_X_COORD(X);
    Pt.y = OS2_MAP_Y_COORD(Y);
    GpiLine(CurrentHps, &Pt);
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Sets the intensity of a color (high or low).				     M
*                                                                            *
* PARAMETERS:                                                                M
*   High:     TRUE for high, FALSE for low.                                  M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IGSetColorIntensity                                                      M
*****************************************************************************/
void IGSetColorIntensity(int High)
{
    if (!CurrentHps)
	return;
    GpiSetColor(CurrentHps,
		High ? CrntColorHighIntensity : CrntColorLowIntensity);

    IGGlblIntensityHighState = High;
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Sets the color of an object according to its color/rgb attributes.	     M
*   If object has an RGB attribute it will be used. Otherwise, if the object M
* has a COLOR attribute it will use. Otherwise, WHITE will be used.	     M
*                                                                            *
* PARAMETERS:                                                                M
*   PObj:      To set the drawing color to its color.                        M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IGSetColorObj                                                            M
*****************************************************************************/
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);
    }
}

/*****************************************************************************
* DESCRIPTION:                                                               M
* Sets the line width to draw the given object, in pixels.		     M
*                                                                            *
* PARAMETERS:                                                                M
*   Width:    In pixels of lines to draw with.                               M
*                                                                            *
* RETURN VALUE:                                                              M
*   void                                                                     M
*                                                                            *
* KEYWORDS:                                                                  M
*   IGSetWidthObj                                                            M
*****************************************************************************/
void IGSetWidthObj(int Width)
{
    GpiSetLineWidthGeom(CurrentHps, Width);
}

/*****************************************************************************
* 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)
{
    if (color > IG_MAX_COLOR)
	color = IG_IRIT_WHITE;

    CrntColorHighIntensity = ColorsHighIntensity[color];
    CrntColorLowIntensity = ColorsLowIntensity[color];

    if (CurrentHps) {
	GpiSetColor(CurrentHps, CrntColorHighIntensity);
}

    IGGlblIntensityHighState = TRUE;
}

/*****************************************************************************
* 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])
{
    if (!CurrentHps)
	return;

    CrntColorHighIntensity =
	GpiQueryColorIndex(CurrentHps, 0L,
			   RGB_COLOR(Color[0], Color[1], Color[2]));
    CrntColorLowIntensity =
	GpiQueryColorIndex(CurrentHps, 0L,
			   RGB_COLOR(Color[0] / 2, Color[1] / 2, Color[2] / 2));

    GpiSetColor(CurrentHps, CrntColorHighIntensity);

    IGGlblIntensityHighState = TRUE;
}

/*****************************************************************************
* 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:
	case IG_STATE_PERS_ORTHO_TGL:
	case IG_STATE_DEPTH_CUE:
	    WinInvalidateRect(hwndTransFrame, NULL, TRUE);
	default:
	    UpdateView = IGDefaultStateHandler(State, Refresh);
	    break;
    }

    IGCreateStateMenu();

    return UpdateView;
}

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

/*****************************************************************************
* DESCRIPTION:                                                               M
*   Should we stop this animation?					     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 (Anim ->StopAnim)
	fprintf(stderr, "\nAnimation was interrupted by the user.\n");

    return Anim -> StopAnim;
}
