/*
 * Copyright (C) 1997-2009, R3vis Corporation.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA, or visit http://www.gnu.org/copyleft/lgpl.html.
 *
 * Original Contributor:
 *   Wes Bethel, R3vis Corporation, Marin County, California
 *   http://www.r3vis.com/
 * Additional Contributor(s):
 *
 * The OpenRM project is located at http://openrm.sourceforge.net/.
 */
/*
 * $Id: rmwin.c,v 1.12 2005/09/12 04:03:51 wes Exp $
 * Version: $Name: v180-alpha-02 $
 * $Revision: 1.12 $
 * $Log: rmwin.c,v $
 * Revision 1.12  2005/09/12 04:03:51  wes
 * Minor documentation updates.
 *
 * Revision 1.11  2005/06/09 00:45:29  wes
 * More compiler warning fixes turned up by Windows build.
 *
 * Revision 1.10  2005/02/19 16:40:20  wes
 * Distro sync and consolidation.
 * Repairs to fix memory leak associated with repeated calls to rmPipeNew,
 * rmPipeMakeCurrent, rmPipeClose.
 *
 * Revision 1.9  2005/01/23 17:00:22  wes
 * Copyright updated to 2005.
 *
 * Revision 1.8  2004/01/16 16:49:50  wes
 * Updated copyright line for 2004.
 *
 * Revision 1.7  2003/10/03 19:19:07  wes
 * Migrate away from platform-specific interfaces to the OpenGL context,
 * and use a single interface: rmPipeSet/GetContext.
 *
 * Revision 1.6  2003/07/23 13:32:28  wes
 * Win32: problems with offscreen rendering appeared with new context
 * initialization code sequence (1.5.0). Minor repairs needed to fix the bug.
 *
 * Revision 1.5  2003/03/16 21:56:16  wes
 * Documentation updates.
 *
 * Revision 1.4  2003/02/17 17:36:02  wes
 * Mods to fix Win32 compile problems.
 *
 * Revision 1.3  2003/02/02 02:07:16  wes
 * Updated copyright to 2003.
 *
 * Revision 1.2  2003/02/01 17:56:15  wes
 * Win32 code work to reflect new RMpipe initialization sequence.
 *
 * Revision 1.1.1.1  2003/01/28 02:15:23  wes
 * Manual rebuild of rm150 repository.
 *
 * Revision 1.11  2003/01/16 22:21:17  wes
 * Updated all source files to reflect new organization of header files:
 * all header files formerly located in include/rmaux, include/rmi, include/rmv
 * are now located in include/rm.
 *
 * Revision 1.10  2002/12/31 00:55:22  wes
 *
 * Various enhancements to support Chromium - achitecture-specific sections
 * of RMpipe were cleaned up, etc.
 *
 * Revision 1.9  2002/04/30 19:37:09  wes
 * Updated copyright dates.
 *
 * Revision 1.8  2001/10/15 00:14:28  wes
 * New routine: rmPipeSetOffscreenWindow() added to ensure consistency
 * between Win32 and X11 APIs.
 *
 * Revision 1.7  2001/03/31 17:12:39  wes
 * v1.4.0-alpha-2 checkin.
 *
 * Revision 1.6  2000/12/03 22:33:55  wes
 * Mods for thread-safety.
 *
 * Revision 1.5  2000/08/23 23:33:14  wes
 * Moved private_rmInitQuadrics to rmPipeSetWindow. Context-specific
 * display lists are built when a window is assigned to a pipe, and
 * there's no already-initialized OpenGL rendering context.
 *
 * Revision 1.4  2000/05/14 23:37:11  wes
 * Added control via RMpipe attribute to how OpenGL matrix stack
 * is initialized or used during rendering.
 *
 * Revision 1.3  2000/04/20 16:29:47  wes
 * Documentation additions/enhancements, some code rearragement.
 *
 * Revision 1.2  2000/03/02 23:46:05  wes
 * rmPipeSetWindow (win32), fix for hRC not assigned.
 *
 * Revision 1.1.1.1  2000/02/28 21:29:40  wes
 * OpenRM 1.2 Checkin
 *
 * Revision 1.1.1.1  2000/02/28 17:18:48  wes
 * Initial entry - pre-RM120 release, source base for OpenRM 1.2.
 *
 */

#include <rm/rm.h>
#include "rmprivat.h"



#ifdef RM_WIN

static RMpipe *win_current_pipe=NULL;

/* temp until we get uniform event handling working */
static float q1[4],q2[4];
static float x1,yy1,x2,y2;
static float xscale_delta,yscale_delta;

static RMnode *save_obj=NULL;
static RMmatrix save_matrix;

static float pixeltovp(int ipixel, int idim)
{
    float t;
    t = (float)(ipixel - (idim>>1)) / (float)(idim >> 1);
    return(t);
}

RMenum
rmPipeSwapBuffersWin32(const RMpipe *p)
{
   glFlush();
   glFinish();
   SwapBuffers(p->hdc);
   return RM_CHILL;
}

HWND
rmPipeGetWindow(RMpipe *p)
{
   return(p->hwnd);
}

/*
 * ----------------------------------------------------
 * @Name rmPipeSetContext (Win32)
 @pstart
 RMenum rmPipeSetContext (RMpipe *toUse,
		          HGLRC theContext)
 @pend

 @astart
 RMpipe *toUse - a handle to an RMpipe object (input, but not const).

 HGLRC theContext - a valid to a Windows OpenGL context (created by
 wglCreateContext, or obtained by some other nefarious means).

 @aend

 @dstart

 Returns the current Windows OpenGL associated with an RMpipe object upon
 success.  A return value of NULL is returned if there is a problem.

 Note that the OpenGL returned by this routine is not necessarily
 "active" unless this call is made after the RMpipe is "made current"
 by using rmPipeMakeCurrent.

 @dend
 * ----------------------------------------------------
 */
HGLRC
rmPipeGetContext(const RMpipe *p)
{
    if (RM_ASSERT(p, "rmPipeGetContext [Win32 version] error: the input RMpipe is NULL") == RM_WHACKED)
	return NULL;
    return p->hRC;
}

/*
 * ----------------------------------------------------
 * @Name rmPipeSetContext (Win32)
 @pstart
 RMenum rmPipeSetContext (RMpipe *toUse,
		          HGLRC theContext)
 @pend

 @astart
 RMpipe *toUse - a handle to an RMpipe object (input, but not const).

 HGLRC theContext - a valid to a Windows OpenGL context (created by
 wglCreateContext, or obtained by some other nefarious means).

 @aend

 @dstart

 Use this routine to assign a Windows OpenGL rendering context to an
 RMpipe. Inside this routine, the input context "theContext" is copied
 into a field internal to the RMpipe object. This routine would be used,
 for example, to obtain an OpenGL context from your application (e.g., some
 FLTK infrastructure) and to tell OpenRM to use it for subsequent rendering.

 Since this routine only makes a copy of the context, you need to "make
 it current" with a call to rmPipeMakeCurrent *after* you call
 rmPipeSetContext.

 This routine will return RM_WHACKED if the input RMpipe is NULL. Otherwise,
 it returns RM_CHILL. No error checking is performed on the input
 Windows OpenGL context.
 
 @dend
 * ----------------------------------------------------
 */
RMenum
rmPipeSetContext(RMpipe *p,
		 HGLRC theContext)
{
    if (RM_ASSERT(p, "rmPipeSetContext [Win32 version] error: the input RMpipe is NULL") == RM_WHACKED)
	return(RM_WHACKED);
   p->hRC = theContext;
   return RM_CHILL;
}

/*
 * ----------------------------------------------------
 * @Name rmPipeSetWindow (Win32)
 @pstart
 RMenum rmPipeSetWindow (RMpipe *toUse,
		         HWND window,
			 int windowWidth,
			 int windowHeight)
 @pend

 @astart
 RMpipe *toUse - a handle to an RMpipe object (input, but not const).

 HWND window - a valid Win32 window handle (input).

 int windowWidth, int windowHeight - integer values specifying the
    pixel width & height of the window "w". 
 @aend

 @dstart

 Use this routine to "bind" an X11 Window to an RMpipe. When this
 routine is called, the RMpipe's window attribute is set to the value
 specified by theWindow parameter, and the RMpipe's window pixel dimension
 attributes are set. 

 Note that there are no event callbacks associated with the RMpipe: when the
 window geometry changes (size, etc) it is the responsibility of the
 application to inform RM that the window geometry has changed
 (rmPipeSetWindowSize). There are separate versions of this routine
 for Win32 and X.

 To assign an offscreen rendering area to the RMpipe, use the routine
 rmPipeSetOffscreenWindow() rather than rmPipeSetWindow().

 Win32 notes: in addition to setting the window handle and size attributes,
 this routine also grabs the DC associated with the window, and stores the
 DC in a field in the RMpipe structure.

 This routine returns RM_CHILL upon success, or RM_WHACKED upon failure.
 
 @dend
 * ----------------------------------------------------
 */
RMenum
rmPipeSetWindow(RMpipe *p,
		HWND hWnd,
		int width,
		int height)
{
    if (RM_ASSERT(p, "rmPipeSetWindow [Win32 version] error: the input RMpipe is NULL") == RM_WHACKED)
	return(RM_WHACKED);
    
    if (hWnd == 0)
    {
	if ((p->hwnd != NULL) && (p->hdc != NULL))
	    ReleaseDC(p->hwnd, p->hdc);
	p->hdc = 0;
	p->hwnd = 0;
    }
    else
    {
	p->hwnd = hWnd;

	if (p->offscreen != RM_TRUE)
	    p->hdc = GetDC(hWnd);
    }

    if (p->offscreen != RM_TRUE)
	p->hdc = GetDC(hWnd);
    rmPipeSetWindowSize(p,width,height);
    return RM_CHILL;
}

/*
 * ----------------------------------------------------
 * @Name rmPipeSetOffscreenWindow (Win32)
 @pstart
 int rmPipeSetOffscreenWindow (RMpipe *toUse,
		               HWND window,
		               int windowWidth,
		               int windowHeight)
 @pend

 @astart
 RMpipe *toUse - a handle to an RMpipe object (input, but not const).

 HWND window - a valid Win32 window handle (input).

 int windowWidth, int windowHeight - integer values specifying the
    pixel width & height of the window "w". 
 @aend

 @dstart

 Use this routine to "bind" a Win32 HWND window to an RMpipe. When this
 routine is called, a number of things happen:

 1. The RMpipe's notion of the window size is set
 (rmPipeSetWindowSize).

 2. The OpenGL context contained within the RMpipe is made current.

 3. Final internal initialization within RM on the RMpipe's OpenGL context
 is performed, readying both RM and OpenGL for use.

 After this call succeeds, on X11 systems is is safe to begin making
 raw OpenGL calls. Note that things work a bit differently in Win32.
 See rmauxSetInitFunc().

 Returns RM_CHILL upon success, or RM_WHACKED upon failure.

 This routine should be used by all applications to activate an OpenGL
 context and to bind a window to the RMpipe.

 Note that there are no event callbacks associated with the RMpipe: when the
 window geometry changes (size, etc) it is the responsibility of the
 application to inform RM that the window geometry has changed
 (rmPipeSetWindowSize). There are separate versions of this routine
 for Win32 and X.

 On Win32, there is no distinction between an onscreen and offscreen
 window. The routine rmPipeSetOffscreenWindow() is present to provide
 API compatibility between X and Win32. Internally, this routine simply
 makes a call to rmPipeSetWindow().
 
 @dend
 * ----------------------------------------------------
 */
void
rmPipeSetOffscreenWindow(RMpipe *p,
			 HWND hWnd,
			 int width,
			 int height)
{
    rmPipeSetWindow(p, hWnd, width, height);
}

/* PRIVATE */
RMenum
rmwPipeCreateContext(RMpipe *p)
{
    HGLRC theContext;
    HDC deviceContext;
    RMenum rstat;
    RMenum stereoFormatBool;

    if (p->hwnd == NULL)
    {
	rmError("rmwPipeCreateContext() error: creation of an OpenGL context on windows requires that you first create a window and assign it to the RMpipe using rmPipeSetWindow. This is different from how it works on X systems where you need to create the context prior to creating the window.");
	return RM_WHACKED;
    }

    deviceContext = GetDC(p->hwnd); 

    if (deviceContext == NULL)
    {
	rmError("rmwPipeCreateContext() error: the deviceContext for the RMpipe's window is NULL. This is bad.");
	return RM_WHACKED;
    }

    /* if the format is offscreen, create a DIB */
    if (private_rmPipeIsOffscreenFormat(p))
    {
	int w, h;
	p->hdc = deviceContext = CreateCompatibleDC(deviceContext);
	rmPipeGetWindowSize(p, &w, &h);
	private_rmwSetupDIB(deviceContext, w, h, 16);
    }

    if (rmPipeGetChannelFormat(p) == RM_MBUF_STEREO_CHANNEL)
	stereoFormatBool = RM_TRUE;
    else
	stereoFormatBool = RM_FALSE;

    /* win32 stereo code not tested. 1/31/03 - wes */
    private_rmwSetupPixelFormat(deviceContext, 16,
				private_rmPipeIsOffscreenFormat(p),
				stereoFormatBool);
    /* 16 bit zbuffer? */
    
    private_rmwSetupPalette(deviceContext);

    theContext = wglCreateContext(deviceContext);
    p->hRC = theContext;
    if (theContext != NULL)
	rstat = RM_CHILL;
    else
	rstat = RM_WHACKED;
    
    return rstat;
}

/* PRIVATE */
void
private_rmwSetupDIB(HDC hDC,
		    int width,
		    int height,
		    int depth)
{
    BITMAPINFO *bmInfo;
    BITMAPINFOHEADER *bmHeader;
    UINT usage;
    VOID *base;
    int bmiSize;
    int bitsPerPixel;
    HBITMAP hBitmap, hOldBitmap;

    bmiSize = sizeof(*bmInfo);
    bitsPerPixel = GetDeviceCaps(hDC, BITSPIXEL);

    switch (bitsPerPixel) {
    case 8:
	/* bmiColors is 256 WORD palette indices */
	bmiSize += (256 * sizeof(WORD)) - sizeof(RGBQUAD);
	break;
    case 16:
	/* bmiColors is 3 WORD component masks */
	bmiSize += (3 * sizeof(DWORD)) - sizeof(RGBQUAD);
	break;
    case 24:
    case 32:
    default:
	/* bmiColors not used */
	break;
    }

    bmInfo = (BITMAPINFO *) calloc(1, bmiSize);
    bmHeader = &bmInfo->bmiHeader;

    bmHeader->biSize = sizeof(*bmHeader);
    bmHeader->biWidth = width,
    bmHeader->biHeight = height,
    bmHeader->biPlanes = 1;			/* must be 1 */
    bmHeader->biBitCount = bitsPerPixel;
    bmHeader->biXPelsPerMeter = 0;
    bmHeader->biYPelsPerMeter = 0;
    bmHeader->biClrUsed = 0;			/* all are used */
    bmHeader->biClrImportant = 0;		/* all are important */

    switch (bitsPerPixel) {
    case 8:
	bmHeader->biCompression = BI_RGB;
	bmHeader->biSizeImage = 0;
	usage = DIB_PAL_COLORS;
	/* bmiColors is 256 WORD palette indices */
	{
	    WORD *palIndex = (WORD *) &bmInfo->bmiColors[0];
	    int i;

	    for (i=0; i<256; i++) {
		palIndex[i] = i;
	    }
	}
	break;
    case 16:
	bmHeader->biCompression = BI_RGB;
	bmHeader->biSizeImage = 0;
	usage = DIB_RGB_COLORS;
	/* bmiColors is 3 WORD component masks */
	{
	    DWORD *compMask = (DWORD *) &bmInfo->bmiColors[0];

	    compMask[0] = 0xF800;
	    compMask[1] = 0x07E0;
	    compMask[2] = 0x001F;
	}
	break;
    case 24:
    case 32:
    default:
	bmHeader->biCompression = BI_RGB;
	bmHeader->biSizeImage = 0;
	usage = DIB_RGB_COLORS;
	/* bmiColors not used */
	break;
    }

    hBitmap = CreateDIBSection(hDC, bmInfo, usage, &base, NULL, 0);
    if (hBitmap == NULL) {
	(void) MessageBox(WindowFromDC(hDC),
		"Failed to create DIBSection.",
		"OpenGL application error",
		MB_ICONERROR | MB_OK);
	exit(1);
    }

    hOldBitmap = SelectObject(hDC, hBitmap);

    free(bmInfo);
}

/* PRIVATE */
void
private_rmwSetupPixelFormat(HDC hDC,
			    int depth,
			    int offscreenBool,
			    RMenum stereoFormatBool)
{
    PIXELFORMATDESCRIPTOR pfd = {
	sizeof(PIXELFORMATDESCRIPTOR),	/* size of this pfd */
	1,				/* version num */
	PFD_SUPPORT_OPENGL,		/* support OpenGL */
	0,				/* pixel type */
	0,				/* 8-bit color depth */
	0, 0, 0, 0, 0, 0,		/* color bits (ignored) */
	0,				/* no alpha buffer */
	0,				/* alpha bits (ignored) */
	0,				/* no accumulation buffer */
	0, 0, 0, 0,			/* accum bits (ignored) */
	16,				/* depth buffer */
	0,				/* no stencil buffer */
	0,				/* no auxiliary buffers */
	PFD_MAIN_PLANE,			/* main layer */
	0,				/* reserved */
	0, 0, 0,			/* no layer, visible, damage masks */
    };
    int SelectedPixelFormat;
    BOOL retVal;

    pfd.cDepthBits = depth;
    
    /* which of the following two lines of code is correct? */
    pfd.cColorBits = GetDeviceCaps(hDC, BITSPIXEL) * GetDeviceCaps(hDC, PLANES);  
/*    pfd.cColorBits = GetDeviceCaps(hDC, BITSPIXEL); */
    pfd.cDepthBits = pfd.cColorBits;
    pfd.iPixelType = PFD_TYPE_RGBA;

#if 0
    {
	char buf[128];
	sprintf(buf," bits/pixel=%d, planes=%d, cColorBits=%d \n",GetDeviceCaps(hDC,BITSPIXEL), GetDeviceCaps(hDC,PLANES), pfd.cColorBits);
	rmWarning(buf);
    }
#endif
    
    if (offscreenBool)
    {
	pfd.dwFlags |= PFD_DRAW_TO_BITMAP;
    }
    else
    {
	pfd.dwFlags |= PFD_DRAW_TO_WINDOW;
        pfd.dwFlags |= PFD_DOUBLEBUFFER;
        pfd.dwFlags |= PFD_GENERIC_ACCELERATED;
	
	if (stereoFormatBool == RM_TRUE)
	    pfd.dwFlags |= PFD_STEREO;

    }

    SelectedPixelFormat = ChoosePixelFormat(hDC, &pfd);
    if (SelectedPixelFormat == 0) {
	(void) MessageBox(WindowFromDC(hDC),
		"Failed to find acceptable pixel format.",
		"OpenGL application error",
		MB_ICONERROR | MB_OK);
	exit(1);
    }

    retVal = SetPixelFormat(hDC, SelectedPixelFormat, &pfd);
    if (retVal != TRUE) {
	(void) MessageBox(WindowFromDC(hDC),
		"Failed to set pixel format.",
		"OpenGL application error",
		MB_ICONERROR | MB_OK);
	exit(1);
    }
}

/* PRIVATE */
void
private_rmwSetupPalette(HDC hDC)
{
    PIXELFORMATDESCRIPTOR pfd;
    LOGPALETTE* pPal;
    int pixelFormat = GetPixelFormat(hDC);
    int paletteSize;
    HPALETTE hPalette;

    DescribePixelFormat(hDC, pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd);

    /*
    ** Determine if a palette is needed and if so what size.
    */
    if (pfd.dwFlags & PFD_NEED_PALETTE) {
	paletteSize = 1 << pfd.cColorBits;
    } else if (pfd.iPixelType == PFD_TYPE_COLORINDEX) {
	paletteSize = 4096;
    } else {
	return;
    }

    pPal = (LOGPALETTE*)
	malloc(sizeof(LOGPALETTE) + paletteSize * sizeof(PALETTEENTRY));
    pPal->palVersion = 0x300;
    pPal->palNumEntries = paletteSize;

    if (pfd.iPixelType == PFD_TYPE_RGBA) {
	/*
	** Fill the logical paletee with RGB color ramps
	*/
	int redMask = (1 << pfd.cRedBits) - 1;
	int greenMask = (1 << pfd.cGreenBits) - 1;
	int blueMask = (1 << pfd.cBlueBits) - 1;
	int i;

	for (i=0; i<paletteSize; ++i) {
	    pPal->palPalEntry[i].peRed =
		    (((i >> pfd.cRedShift) & redMask) * 255) / redMask;
	    pPal->palPalEntry[i].peGreen =
		    (((i >> pfd.cGreenShift) & greenMask) * 255) / greenMask;
	    pPal->palPalEntry[i].peBlue =
		    (((i >> pfd.cBlueShift) & blueMask) * 255) / blueMask;
	    pPal->palPalEntry[i].peFlags = 0;
	}
    } else {
#if 0
	/* color index mode not supported at this time. 2/8/2001 - wes */
	/*
	** Fill the logical palette with color ramps.
	**
	** Set up the logical palette so that it can be realized
	** into the system palette as an identity palette.
	**
	** 1) The default static entries should be present and at the right
	**    location.  The easiest way to do this is to grab them from
	**    the current system palette.
	**
	** 2) All non-static entries should be initialized to unique values.
	**    The easiest way to do this is to ensure that all of the non-static
	**    entries have the PC_NOCOLLAPSE flag bit set.
	*/
	int numRamps = NUM_COLORS;
	int rampSize = (paletteSize - 20) / numRamps;
	int extra = (paletteSize - 20) - (numRamps * rampSize);
	int i, r;

	/*
	** Initialize static entries by copying them from the
	** current system palette.
	*/
	GetSystemPaletteEntries(hDC, 0, paletteSize, &pPal->palPalEntry[0]);

	/*
	** Fill in non-static entries with desired colors.
	*/
	for (r=0; r<numRamps; ++r) {
	    int rampBase = r * rampSize + 10;
	    PALETTEENTRY *pe = &pPal->palPalEntry[rampBase];
	    int diffSize = (int) (rampSize * colors[r].ratio);
	    int specSize = rampSize - diffSize;

	    for (i=0; i<rampSize; ++i) {
		GLfloat *c0, *c1;
		GLint a;

		if (i < diffSize) {
		    c0 = colors[r].amb;
		    c1 = colors[r].diff;
		    a = (i * 255) / (diffSize - 1);
		} else {
		    c0 = colors[r].diff;
		    c1 = colors[r].spec;
		    a = ((i - diffSize) * 255) / (specSize - 1);
		}

		pe[i].peRed = (BYTE) (a * (c1[0] - c0[0]) + 255 * c0[0]);
		pe[i].peGreen = (BYTE) (a * (c1[1] - c0[1]) + 255 * c0[1]);
		pe[i].peBlue = (BYTE) (a * (c1[2] - c0[2]) + 255 * c0[2]);
		pe[i].peFlags = PC_NOCOLLAPSE;
	    }

	    colors[r].indexes[0] = rampBase;
	    colors[r].indexes[1] = rampBase + (diffSize-1);
	    colors[r].indexes[2] = rampBase + (rampSize-1);
	}

	/*
	** Initialize any remaining non-static entries.
	*/
	for (i=0; i<extra; ++i) {
	    int index = numRamps*rampSize+10+i;
	    PALETTEENTRY *pe = &pPal->palPalEntry[index];

	    pe->peRed = (BYTE) 0;
	    pe->peGreen = (BYTE) 0;
	    pe->peBlue = (BYTE) 0;
	    pe->peFlags = PC_NOCOLLAPSE;
	}
#endif
    }

    hPalette = CreatePalette(pPal);
    free(pPal);

    if (hPalette) {
	SelectPalette(hDC, hPalette, FALSE);
	RealizePalette(hDC);
    }
}

/* PRIVATE */
void
private_rmPipeCloseContextW32(RMpipe *toClose)
{
    if ((toClose != NULL) && (toClose->hRC != NULL))
	wglDeleteContext(toClose->hRC);
}

/* PRIVATE */
RMenum
private_rmwCheckAndDisplayLastError(const char *callerMsg)
{
    DWORD errorStatus;

    /*
     * checks for Win32 errors. If an error is detected, a message box
     * is displayed that contains (hopefully) useful information.
     * This routine returns zero if there is no error, or a non-zero value if
     * there is an error of some type.
     */

    errorStatus = GetLastError();

    if (errorStatus != 0)
    {
	LPVOID lpMsgBuf;
	FormatMessage( 
		      FORMAT_MESSAGE_ALLOCATE_BUFFER | 
		      FORMAT_MESSAGE_FROM_SYSTEM | 
		      FORMAT_MESSAGE_IGNORE_INSERTS,
		      NULL,
		      errorStatus,
		      MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
		      (LPTSTR) &lpMsgBuf,
		      0,
		      NULL 
		      );
	MessageBox( NULL, (LPCTSTR)lpMsgBuf, callerMsg, MB_ICONERROR | MB_ICONINFORMATION );
	LocalFree( lpMsgBuf );
    }
    return errorStatus;
}
#endif /* RM_WIN */
/* EOF */
