/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: windraw2.cpp,v 1.19.10.1 2004/07/09 01:59:19 hubbe Exp $
 * 
 * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved.
 * 
 * The contents of this file, and the files included with this file,
 * are subject to the current version of the RealNetworks Public
 * Source License (the "RPSL") available at
 * http://www.helixcommunity.org/content/rpsl unless you have licensed
 * the file under the current version of the RealNetworks Community
 * Source License (the "RCSL") available at
 * http://www.helixcommunity.org/content/rcsl, in which case the RCSL
 * will apply. You may also obtain the license terms directly from
 * RealNetworks.  You may not use this file except in compliance with
 * the RPSL or, if you have a valid RCSL with RealNetworks applicable
 * to this file, the RCSL.  Please see the applicable RPSL or RCSL for
 * the rights, obligations and limitations governing use of the
 * contents of the file.
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL") in which case the provisions of the GPL are applicable
 * instead of those above. If you wish to allow use of your version of
 * this file only under the terms of the GPL, and not to allow others
 * to use your version of this file under the terms of either the RPSL
 * or RCSL, indicate your decision by deleting the provisions above
 * and replace them with the notice and other provisions required by
 * the GPL. If you do not delete the provisions above, a recipient may
 * use your version of this file under the terms of any one of the
 * RPSL, the RCSL or the GPL.
 * 
 * This file is part of the Helix DNA Technology. RealNetworks is the
 * developer of the Original Code and owns the copyrights in the
 * portions it created.
 * 
 * This file, and the files included with this file, is distributed
 * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS
 * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
 * ENJOYMENT OR NON-INFRINGEMENT.
 * 
 * Technology Compatibility Kit Test Suite(s) Location:
 *    http://www.helixcommunity.org/content/tck
 * 
 * Contributor(s):
 * 
 * ***** END LICENSE BLOCK ***** */

/* for MS VC++ 6.0, pntypes needs to be included before windows include files */
#include "hxtypes.h"
#include "hxcom.h" 
#include "hxwintyp.h"
#include "hxvsurf.h"
#include "hxassert.h"
#include "hxthread.h"
#include "hxtick.h"
#include "dllpath.h"
#include "hxerror.h"
#include "hxstrutl.h"

/* Windows include files: */
#include <windows.h>
#include "ddraw.h"

#define COMPILE_MULTIMON_STUBS
#include <multimon.h>

/* standard libraries: */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <math.h>            

#include "hxcom.h"
#include "hxheap.h"
#ifdef _DEBUG
#undef HX_THIS_FILE		
static const char HX_THIS_FILE[] = __FILE__;
#endif


/* our stuff:*/
#include "hxcolor.h"
#include "colormap.h"
#include "windraw2.h"
#include "ddpdb.h"         /* DirectDraw profiles database */

/* DirectDraw profile: */
static LPDDDEVICEPROFILE lpddprofDirectDrawProfile = NULL;

//#define _CHECK_PERFORMANCE
//#define _REMOVE_WINDRAW_CLIPPER 

// Exception handler mutex
static CRITICAL_SECTION g_ehandlerMutex;
static INT32 g_ehandlerMutexCnt = 0;
static INT32 g_ehandlerSilent = 1;
static INT32 g_ehandlerCount = 0;

#include "hxmap.h"

typedef void (HXEXPORT_PTR INITCOLORCONVERTERFN)();
typedef int (HXEXPORT_PTR SETRGB8PALETTEFN) (int,UINT32*,int*);

INITCOLORCONVERTERFN pfInitColorConverter = 0;
SETRGB8PALETTEFN pfSetRGB8Palette = 0;
HMODULE z_hHxColor = 0;

struct CEnumDisplaySetting
{
    
    CEnumDisplaySetting();
    ~CEnumDisplaySetting();
    
    CHXMapStringToOb m_Settings;
    
    void GetSettings(CHXSimpleList* pList);
};


CEnumDisplaySetting::CEnumDisplaySetting()
{
    DEVMODE modeDesc;
    DWORD   index = 0;
    char    buffer[64]; /* Flawfinder: ignore */

    while(EnumDisplaySettings(0, index, &modeDesc))
    {
	sprintf(buffer, "%dx%dx%d", modeDesc.dmPelsWidth, modeDesc.dmPelsHeight, modeDesc.dmBitsPerPel); /* Flawfinder: ignore */
	
	void* ptemp;
	
	if (!m_Settings.Lookup(buffer, ptemp))
	{
	    CModesDesc* pDesc = new CModesDesc;
	    pDesc->m_nWidth	= modeDesc.dmPelsWidth;
	    pDesc->m_nHeight    = modeDesc.dmPelsHeight;
	    pDesc->m_nBitCount  = modeDesc.dmBitsPerPel;
	    m_Settings.SetAt(buffer, pDesc);
	}
	index++;
    }
}

CEnumDisplaySetting::~CEnumDisplaySetting()
{
    CHXMapStringToOb::Iterator ndx = m_Settings.Begin();
    for(; ndx != m_Settings.End(); ++ndx)
    {
	CModesDesc* pDesc = (CModesDesc*) (*ndx);
	delete pDesc;
    }
    
    m_Settings.RemoveAll();
}

void CEnumDisplaySetting::GetSettings(CHXSimpleList* pList)
{
    CHXMapStringToOb::Iterator i;
    i = m_Settings.Begin();

    for(; i!= m_Settings.End(); ++i)
    {
	CModesDesc* pOldDesc	= (CModesDesc*) *i;
	
	CModesDesc* pNewDesc	= new CModesDesc;
	pNewDesc->m_nWidth	= pOldDesc->m_nWidth;
	pNewDesc->m_nHeight	= pOldDesc->m_nHeight;
	pNewDesc->m_nBitCount	= pOldDesc->m_nBitCount;
	pList->AddTail(pNewDesc);
    }
}

CEnumDisplaySetting zm_DisplaySettings;


/***************************************
*
* Routines for using DirectDraw on a multimonitor system
*
*/

/* DDRAW.H might not include these: */
#ifndef DDENUM_ATTACHEDSECONDARYDEVICES
#define DDENUM_ATTACHEDSECONDARYDEVICES     0x00000001L
#endif

#if DIRECTDRAW_VERSION <= 0x0500
typedef BOOL (FAR PASCAL * LPDDENUMCALLBACKEXA) (GUID FAR *, LPSTR, LPSTR, LPVOID, HANDLE);
typedef HRESULT (WINAPI * LPDIRECTDRAWENUMERATEEXA) ( LPDDENUMCALLBACKEXA lpCallback, LPVOID lpContext, DWORD dwFlags);
#define HMONITOR HANDLE
#endif

typedef HRESULT (*PDRAWCREATE) (IID *,LPDIRECTDRAW *,LPUNKNOWN);
typedef HRESULT (*PDRAWENUM) (LPDDENUMCALLBACKA, LPVOID);

/*
* FindDeviceCallback functions:
*/
typedef struct
{
    LPSTR   lpszDevice;
    GUID*   lpGUID;
    GUID    GUID;
    BOOL    fFound;
    
} FINDDEVICEDATA;


static BOOL CALLBACK FindDeviceCallback (GUID* lpGUID, LPSTR lpszName,
					 LPSTR lpszDevice, LPVOID lParam)
{
    FINDDEVICEDATA *p = (FINDDEVICEDATA*) lParam;
    
    if (lstrcmpiA (p->lpszDevice, lpszDevice) == 0) {
	
        if (lpGUID) {
            p->GUID = *lpGUID;
            p->lpGUID = &p->GUID;
        } else
            p->lpGUID = NULL;
	
        p->fFound = TRUE;
	return FALSE;
    }
    return TRUE;
}

static BOOL CALLBACK FindDeviceCallbackEx (GUID* lpGUID, LPSTR lpszName,
					   LPSTR lpszDevice, LPVOID lParam, HMONITOR hMonitor)
{
    FINDDEVICEDATA *p = (FINDDEVICEDATA*) lParam;
    
    if (lstrcmpiA (p->lpszDevice, lpszDevice) == 0) {
	
        if (lpGUID) {
            p->GUID = *lpGUID;
            p->lpGUID = &p->GUID;
        } else
            p->lpGUID = NULL;
	
	p->fFound = TRUE;
	return FALSE;
    }
    return TRUE;
}

HRESULT WINAPI EnumSurfacesCallback(LPDIRECTDRAWSURFACE lpDDSurface,
                                    LPDDSURFACEDESC lpDDSurfaceDesc,
                                    LPVOID pSurface)
{
    LPDIRECTDRAWSURFACE *lpSurf = (LPDIRECTDRAWSURFACE*)pSurface;

    if (lpDDSurface)
    {
        *lpSurf = lpDDSurface;
        lpDDSurface->Release();
    }

    return DDENUMRET_OK;
}

/*
* DirectDrawCreateFromDevice
*
* create a DirectDraw object for a particular device
*/
static IDirectDraw * DirectDrawCreateFromDevice (LPSTR lpszDevice,
						 PDRAWCREATE DirectDrawCreateP, PDRAWENUM DirectDrawEnumerateP)
{
    IDirectDraw *pdd = NULL;
    
    /* check if device name is specified: */
    if (lpszDevice != NULL) {
	
        /* try to find device with a given name: */
        FINDDEVICEDATA find;
        find.lpszDevice = lpszDevice;
        find.fFound = FALSE;
        DirectDrawEnumerateP (FindDeviceCallback, (LPVOID)&find);
	
        /* check if found: */
        if (find.fFound) {
	    
	/* In 4bpp mode the following DDraw call causes a
	* message box to be popped up by DDraw (!?!).
	* It's DDraw's fault, but we don't like it.
	    * So we make sure it doesn't happen. */
            UINT ErrorMode = SetErrorMode (SEM_FAILCRITICALERRORS);
            DirectDrawCreateP (find.lpGUID, &pdd, NULL);
            SetErrorMode (ErrorMode);
        }
    } else
        DirectDrawCreateP (NULL, &pdd, NULL);
    
    /* return whatever we have found: */
    return pdd;
}

/*
* DirectDrawCreateFromDeviceEx
*
* create a DirectDraw object for a particular device
*/
static IDirectDraw * DirectDrawCreateFromDeviceEx (LPSTR lpszDevice, PDRAWCREATE DirectDrawCreateP, LPDIRECTDRAWENUMERATEEXA DirectDrawEnumerateExP)
{
    IDirectDraw *pdd = NULL;
    
    /* check if device name is specified: */
    if (lpszDevice != NULL) {
	
        /* try to find device with a given name: */
        FINDDEVICEDATA find;
        find.lpszDevice = lpszDevice;
        find.fFound   = FALSE;
        DirectDrawEnumerateExP (FindDeviceCallbackEx, (LPVOID)&find, DDENUM_ATTACHEDSECONDARYDEVICES);
	
        /* check if found: */
        if (find.fFound) {
	    
	/* In 4bpp mode the following DDraw call causes a
	* message box to be popped up by DDraw (!?!).
	* It's DDraw's fault, but we don't like it.
	    * So we make sure it doesn't happen. */
            UINT ErrorMode = SetErrorMode (SEM_FAILCRITICALERRORS);
            DirectDrawCreateP (find.lpGUID, &pdd, NULL);
            SetErrorMode (ErrorMode);
        }
    } else
        DirectDrawCreateP (NULL, &pdd, NULL);
    
    /* return whatever we have found: */
    return pdd;
}

/***************************************
*
* Routines for dynamic loading and unloading of the DirectDraw library.
*
*/

typedef DWORD (WINAPI *PGETFILEVERSIONINFOSIZE) (LPTSTR,LPDWORD);
typedef BOOL (WINAPI *PGETFILEVERSIONINFO) (LPTSTR,DWORD,DWORD,LPVOID);
typedef BOOL (WINAPI *PVERQUERYVALUE) (LPVOID,LPTSTR,LPVOID,PUINT);

/* forward prototypes: */
HRESULT LockSurface(LPWINDRAW lpwd, LPDIRECTDRAWSURFACE lpSurface, DDSURFACEDESC *pddsd, BOOL bBlock=TRUE);
HRESULT IsFlipDone(LPWINDRAW lpwd, LPWINDRAWSURFACE lpwds, ENUMSURFACE* lpSurfEnum, BOOL bBlock=TRUE);
HRESULT IsFrontBuffer(LPWINDRAW lpwd, LPWINDRAWSURFACE lpwds, ENUMSURFACE* lpSurfEnum, BOOL bBlock=TRUE);
HRESULT RestoreSurfaces(LPWINDRAW lpwd, LPWINDRAWSURFACE lpSurface);
HRESULT LoadDirectDraw (LPSTR lpszDevice, LPDIRECTDRAW *ppDirectDraw, HINSTANCE *phDirectDraw);
void    ReleaseDirectDraw (LPDIRECTDRAW *ppDirectDraw, LPDIRECTDRAW2 *ppDirectDraw2, HINSTANCE *phDirectDraw);
void    DumpDDInfo(LPWINDRAW lpwd, LPDIRECTDRAWSURFACE lpSurface, char *pFunction);

/*
* We can't be sure that DirectDraw is always available
* so we can't statically link to the library.
* Therefore we load the library, get the function entry
* point addresses and call them to create the driver objects.
* We return S_OK if we manage to load DirectDraw correctly
* otherwise we return E_NOINTERFACE
* We initialise a DirectDraw instance by explicitely loading
* the library and calling GetProcAddress on the DirectDrawCreate
* entry point that it exports
*
* On a multi monitor system, we can get the DirectDraw object
* for any monitor (device) with the optional lpszDevice parameter
*/
static HRESULT LoadDirectDraw (LPSTR lpszDevice,
			       LPDIRECTDRAW *ppDirectDraw, HINSTANCE *phDirectDraw)
{
    UINT ErrorMode;
    PDRAWCREATE pDrawCreate;
    PDRAWENUM pDrawEnum;
    LPDIRECTDRAWENUMERATEEXA pDrawEnumEx;
    HRESULT hr = NOERROR;
    
    /* 
    * Check the display mode if it is less than 8 bits do not call into direct draw.
    */
    
    HDC hdc = GetDC(0);
    int bpp = GetDeviceCaps(hdc, BITSPIXEL);
    ReleaseDC(0, hdc);
    
    if (bpp < 8)
    {
	*ppDirectDraw = 0;
	return E_NOINTERFACE; /* whatever */
    }
    
    /* is DirectDraw already loaded? */
    if (*ppDirectDraw) {
        /* ... */
        return NOERROR;
    }
    
    /* make sure the library is available: */
    ErrorMode = SetErrorMode (SEM_NOOPENFILEERRORBOX);
    *phDirectDraw = LoadLibrary (TEXT ("DDRAW.DLL"));
    SetErrorMode (ErrorMode);
    if (*phDirectDraw == NULL) {
        /* ... */
        return E_NOINTERFACE;
    }
    
    /* get the DLL address for the creator function: */
    pDrawCreate = (PDRAWCREATE) GetProcAddress (*phDirectDraw, "DirectDrawCreate");
    /* force ANSI, we assume it: */
    pDrawEnum = (PDRAWENUM) GetProcAddress (*phDirectDraw, "DirectDrawEnumerateA");
    pDrawEnumEx = (LPDIRECTDRAWENUMERATEEXA)GetProcAddress (*phDirectDraw, "DirectDrawEnumerateExA");
    
    /* we don't need DirectDrawEnumerateEx, that's just for multimon stuff: */
    if (pDrawCreate == NULL || pDrawEnum == NULL) {
        /* ... */
        ReleaseDirectDraw (ppDirectDraw, NULL, phDirectDraw);
        return E_NOINTERFACE;
    }
    
    /* create a DirectDraw display provider for this device,
    * using the fancy multimon-aware version, if it exists: */
    if (pDrawEnumEx)
        *ppDirectDraw = DirectDrawCreateFromDeviceEx (lpszDevice, pDrawCreate, pDrawEnumEx);
    else
        *ppDirectDraw = DirectDrawCreateFromDevice (lpszDevice, pDrawCreate, pDrawEnum);
    if (*ppDirectDraw == NULL) 
    {
        ReleaseDirectDraw (ppDirectDraw, NULL, phDirectDraw);
        return E_NOINTERFACE;
    }
    
    return NOERROR;
}

/*
* Called to release any DirectDraw provider we previously loaded.
* We may be called at any time especially when something goes
* horribly wrong and when we need to clean up before returning
* so we can't guarantee that all state variables are consistent
* so free only those really allocated. This should only be called
* once all the reference counts have been released.
*/
void ReleaseDirectDraw (LPDIRECTDRAW *ppDirectDraw, LPDIRECTDRAW2 *ppDirectDraw2,  HINSTANCE *phDirectDraw)
{
    /* release any DirectDraw provider interface: */
    if (ppDirectDraw && *ppDirectDraw) {
        
        IDirectDraw_Release (*ppDirectDraw);
        *ppDirectDraw = NULL;
    }
    
    if (ppDirectDraw2 && *ppDirectDraw2) 
    {
        (*ppDirectDraw2)->Release();
        *ppDirectDraw2 = NULL;
    }
    
    /* decrement module load count: */
    if (*phDirectDraw) {
        FreeLibrary (*phDirectDraw);
        *phDirectDraw = NULL;
    }
}

#if 0
/*
* Are we running on Direct Draw version 1?  We need to find out as
* we rely on specific bug fixes in DirectDraw 2 for fullscreen playback. To
* find out, we simply see if it supports IDirectDraw2.  Only version 2 and
* higher support this.
*/
BOOL IsDirectDrawVersion1 (LPDIRECTDRAW pDirectDraw)
{
    IDirectDraw2 *p;
    HRESULT hr;
    
    /* check if DirectDraw is available: */
    if (pDirectDraw == NULL)
        return FALSE;
    
    /* query and release interface: */
    p = NULL;
    hr = IDirectDraw_QueryInterface(pDirectDraw, IID_IDirectDraw2, (void *)&p);
    if (p)
        IDirectDraw2_Release (p);
    
    /* returns TRUE, if !IDrectDraw2; FALSE otherwise */
    return hr != NOERROR;
}
#endif

/***************************************
*
* Routines for using GDI on a multimonitor system
*
*/

/*
* Get display DC.
* Use:
*  HDC GetDisplayDC (LPSTR lpszDevice)
* Input:
*  lpszDevice - device name to use (in a multimonitor system)
* Returns:
*  NULL, if error; !NULL - display device DC.
*/
static HDC GetDisplayDC (LPSTR lpszDevice)
{
    HDC hdcDisplay;
    
    /* get a DC on the right monitor - it's ugly, but this is the way
    * you have to do it */
    if (lpszDevice == NULL || lstrcmpiA (lpszDevice, "DISPLAY") == 0)
        hdcDisplay = CreateDCA ("DISPLAY", NULL, NULL, NULL);
    else
        hdcDisplay = CreateDCA (NULL, lpszDevice, NULL, NULL);
    
    return hdcDisplay;
}

/*
* Get pixel format for a given device context.
* Use:
*  HRESULT GetDeviceFormat (HDC hDC, LPBMI lpbmi);
* Input:
*  hDC - device context to query
*  lpbmi - pointer to a bitmapinfo structure to initialize
* Returns:
*  NOERROR, if success, E_FAIL otherwise.
*/
static HRESULT GetDeviceFormat (HDC hDC, LPBMI lpbmi)
{
    HBITMAP hBM;
    
    /* initialize the bitmapinfo structure: */
    memset (lpbmi, 0, sizeof (BMI));
    lpbmi->bmiHeader.biSize = sizeof (BITMAPINFOHEADER);
    lpbmi->bmiHeader.biBitCount = 0;
    
    /* retrieve display format parameters: */
    hBM = CreateCompatibleBitmap (hDC,1,1);
    GetDIBits (hDC,hBM,0,1,NULL, (BITMAPINFO *)& (lpbmi->bmiHeader),DIB_RGB_COLORS);
    /* this call will get the color table or the proper bitfields */
    GetDIBits (hDC,hBM,0,1,NULL, (BITMAPINFO *)& (lpbmi->bmiHeader),DIB_RGB_COLORS);
    DeleteObject (hBM);
    
    /* check the validity of the format & exit: */
    return CheckBitmap ((BITMAPINFO *)& (lpbmi->bmiHeader))? NOERROR: E_FAIL;
}

/*
* Retrieves display pixel format. This is normally called
* when we receive WM_DEVMODECHANGED device change messages.
*
* The optional szDevice parameter tells us which monitor we are interested
* in for a multi monitor system.
*
* The calling program mush use global syncronization objects to prevent
* re-entering this code.
*/
static HRESULT GetDisplayFormat (LPBMI lpbmi, LPSTR lpszDevice)
{
    HDC hdcDisplay;
    HRESULT hr;
    
    /* get a DC of a given monitor: */
    if ((hdcDisplay = GetDisplayDC (lpszDevice)) == NULL)
        return E_FAIL;
    
    /* retrieve display format parameters: */
    hr = GetDeviceFormat (hdcDisplay, lpbmi);
    
    /* delete hdcDisplay & exit: */
    DeleteDC (hdcDisplay);
    return hr;
}

/*
* Generates the "standard" WinDraw palette.
* Since we will have to deal with multiple datatypes, each having
* its own combination of colors, what we really need is some "uniform"
* palette, we could use to render all these objects with minimum loss
* of quality.
* This function generates so-called 6x6x6-color palette that contains
* all possible combinations of 6 uniformly-displaced values for each
* color component (R, G, and B). To avoid interference with Windows
* system palette (first and last 10 entries in the palette), we will
* insert our colors in 10..225 range, and mark them as non-collapsable
* in hope to boost the performance (ideally, this should prevent GDI
* from compressing the palette, and eliminate extra mapping).
*/
static HRESULT CreateDefaultPalette (LPPALETTEENTRY lppe,
				     int *pnLoColor, int *pnHiColor, LPSTR lpszDevice)
{
    HDC hdc;
    HRESULT hr = E_FAIL;
    unsigned int i, r, g, b;
    
    /* get a DC of a given monitor: */
    if ((hdc = GetDisplayDC (lpszDevice)) != NULL) {
	
        /* apparently some displays have odd numbers of system colors: */
        if (GetDeviceCaps (hdc, NUMRESERVED) == 20) {
	    
            /* retrieve system colors: */
            memset (lppe, 0, 256 * sizeof (RGBQUAD));
            GetSystemPaletteEntries (hdc, 0, 10, lppe);
            GetSystemPaletteEntries (hdc, 246, 10, lppe + 246);
	    
            /* generate 6x6x6 (~2.58 bits/channel) palette: */
            i = 10;
            for (b = 0; b <= 0xFF; b += 0x33)
		for (g = 0; g <= 0xFF; g += 0x33)
		    for (r = 0; r <= 0xFF; r += 0x33) {
			/* create a palette entry: */
			lppe [i].peGreen = g;
			lppe [i].peBlue  = b;
			lppe [i].peRed   = r;
			lppe [i].peFlags = PC_NOCOLLAPSE; /* !!! */
			i ++;
		    }
		    
		    /* store the range of our colors: */
		    *pnLoColor = 10;
		    *pnHiColor = i;
		    
		    hr = NOERROR;
        }
        DeleteDC (hdc);
    }
    return hr;
}

char*   
CollectDDInfo(LPWINDRAW lpwd, LPDIRECTDRAWSURFACE lpSurface, char *pFunction, char* pSeparator)
{
    char*       pszDDInfo = NULL;
    CHXString   pDDInfo;
    char        szTemp[MAX_DISPLAY_NAME];    /* Flawfinder: ignore */
    char        szTmp[MAX_DISPLAY_NAME];     /* Flawfinder: ignore */

    // write date and time of log
    SYSTEMTIME time;

    memset(&time, 0, sizeof(time));
    GetSystemTime(&time);

    GetDateFormat(LOCALE_SYSTEM_DEFAULT, 0, &time, "ddd',' MMM dd yyyy, ", szTmp, sizeof(szTmp));
    SafeSprintf(szTemp, MAX_DISPLAY_NAME, "\nLog Entry: %s", szTmp);
    pDDInfo = szTemp;
    
    GetTimeFormat(LOCALE_SYSTEM_DEFAULT, 0, &time, "hh:mm:ss tt", szTmp, sizeof(szTmp));
    SafeSprintf(szTemp, MAX_DISPLAY_NAME, "%s%s", szTmp, pSeparator);
    pDDInfo += szTemp;
    
    // write systime members
    SafeSprintf(szTemp, MAX_DISPLAY_NAME, "Systime: %d %d %d %d %d %d %d %d%s",
                time.wYear, time.wMonth, time.wDayOfWeek, time.wDay,
                time.wHour, time.wMinute, time.wSecond, time.wMilliseconds, pSeparator);
    pDDInfo += szTemp;
    
    // write function that caused fault
    SafeSprintf(szTemp, MAX_DISPLAY_NAME, "Function: %s%s", pFunction, pSeparator);
    pDDInfo += szTemp;

    // write OS version
    OSVERSIONINFO ver;
    memset(&ver, 0, sizeof(ver));
    ver.dwOSVersionInfoSize = sizeof(ver);

    GetVersionEx(&ver);

    SafeSprintf(szTemp, MAX_DISPLAY_NAME, 
                "OS MajorVersion: %ld MinorVersion: %ld BuildNum: %ld PlatformId: %ld Build: %s%s",
                ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber, ver.dwPlatformId, ver.szCSDVersion, pSeparator);
    pDDInfo += szTemp;

    // write processor info
    SYSTEM_INFO sysinfo;
    memset(&sysinfo, 0, sizeof(sysinfo));
    GetSystemInfo(&sysinfo);

    SafeSprintf(szTemp, MAX_DISPLAY_NAME, "Processor Type: %ld Level: %ld Revision: %ld Count: %ld%s",
                 sysinfo.dwProcessorType, 
                 sysinfo.wProcessorLevel, 
                 sysinfo.wProcessorRevision, 
                 sysinfo.dwNumberOfProcessors,
                 pSeparator);
    pDDInfo += szTemp;

    // write DD version
    TCHAR szVersion[256] = __TEXT("\0"); /* Flawfinder: ignore */
    HKEY hKey = 0;
    
    RegOpenKeyEx(HKEY_LOCAL_MACHINE, 
        __TEXT("Software\\Microsoft\\DirectX"),
        0, KEY_READ, &hKey);

    if (hKey)
    {
        DWORD dwType = REG_SZ;
        DWORD dwSize = sizeof(szVersion);
        RegQueryValueEx(hKey, __TEXT("Version"), NULL, &dwType, (BYTE*)szVersion, &dwSize);

        RegCloseKey(hKey);
    }

    SafeSprintf(szTemp, MAX_DISPLAY_NAME, "DirectDraw Version %s%s", szVersion, pSeparator);
    pDDInfo += szTemp;

    if (lpSurface)
    {
        // Get DD4* from the surface
        void* pVoid = NULL;
        IDirectDrawSurface2* lpSurface2 = NULL;
            
#if DIRECTDRAW_VERSION > 0x0500
        IDirectDraw4* pDD4 = NULL;
        IDirectDraw* pDD = NULL;
#endif //DIRECTDRAW_VERSION > 0x0500

        DDDEVICEIDENTIFIER ddID;
        memset(&ddID, 0, sizeof(ddID));

        lpSurface->QueryInterface(IID_IDirectDrawSurface2, (void**)&lpSurface2);
        
        if (lpSurface2)
        {
            lpSurface2->GetDDInterface(&pVoid);
            lpSurface2->Release();
        }

#if DIRECTDRAW_VERSION > 0x0500            
        if (pVoid)
            ((IUnknown*)pVoid)->QueryInterface(IID_IDirectDraw, (void**)&pDD);

        if (pDD)
        {
            pDD->QueryInterface(IID_IDirectDraw4, (void **)&pDD4);
            pDD->Release();
        }
        
        // Query for the driver descripton
        if (pDD4)
        {
            pDD4->GetDeviceIdentifier(&ddID, DDGDI_GETHOSTIDENTIFIER);
            pDD4->Release();
        }
        // DD4 not supportted (probably NT4)...use more drastic measures
        else
#endif //DIRECTDRAW_VERSION > 0x0500
        {
            // Check if we are runnning NT
            OSVERSIONINFO osVersion;
            memset(&osVersion,0, sizeof(OSVERSIONINFO));
            osVersion.dwOSVersionInfoSize  = sizeof(OSVERSIONINFO);

            if (GetVersionEx(&osVersion) && osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT)
            {
                char szChipType[MAXCHIPTYPE]; /* Flawfinder: ignore */
                
                // Try to get WinNT device information
                GetWinNTDeviceID (&ddID, szChipType);
            }
        }
        
        // write DD driver
        unsigned short data4 =  ddID.guidDeviceIdentifier.Data4[0]<<8  |
                                ddID.guidDeviceIdentifier.Data4[1];
        unsigned long  data5 =  ddID.guidDeviceIdentifier.Data4[2]<<24 | 
                                ddID.guidDeviceIdentifier.Data4[3]<<16 |
                                ddID.guidDeviceIdentifier.Data4[4]<<8  |
                                ddID.guidDeviceIdentifier.Data4[5];
        unsigned short data6 =  ddID.guidDeviceIdentifier.Data4[6]<<8  |
                                ddID.guidDeviceIdentifier.Data4[7];

        SafeSprintf(szTemp, MAX_DISPLAY_NAME,
                    "DirectDraw Driver: %s VendorID: %ld GUID {""%lx-%x-%x-%x-%lx%x""}%s",
                    ddID.szDescription, 
                    ddID.dwVendorId,
                    ddID.guidDeviceIdentifier.Data1,
                    ddID.guidDeviceIdentifier.Data2,
                    ddID.guidDeviceIdentifier.Data3,
                    data4,
                    data5,
                    data6,
                    pSeparator);
        pDDInfo += szTemp;
        
        // write display properties
        DDSURFACEDESC ddsd;
        memset(&ddsd, 0, sizeof(ddsd));
        ddsd.dwSize = sizeof(ddsd);
        lpSurface->GetSurfaceDesc(&ddsd);

        char fourCC[5] = {(char)(ddsd.ddpfPixelFormat.dwFourCC & 0x000000FF), /* Flawfinder: ignore */
                         ((char)(ddsd.ddpfPixelFormat.dwFourCC & 0x0000FF00)>>8),
                         ((char)(ddsd.ddpfPixelFormat.dwFourCC & 0x00FF0000)>>16),
                          (char)(ddsd.ddpfPixelFormat.dwFourCC>>24),
                          '\0'};

        SafeSprintf(szTemp, MAX_DISPLAY_NAME, 
                    "DisplayMode: %s %s BackBuffers: %ld Width: %ld Height: %ld%s",
                    (ddsd.ddsCaps.dwCaps & DDSCAPS_OVERLAY) ? "Overlay" : "Offscreen",
                    fourCC,
                    (ddsd.dwFlags & DDSD_BACKBUFFERCOUNT) ? ddsd.dwBackBufferCount : 0,
                    ddsd.dwWidth,
                    ddsd.dwHeight,
                    pSeparator);
        pDDInfo += szTemp;
    }
            
    BMI bmi;
    memset(&bmi, 0, sizeof(bmi));

    GetDisplayFormat(&bmi, NULL);

    // write monitor properties
    SafeSprintf(szTemp, MAX_DISPLAY_NAME,
                "Screen Width %ld Height %ld BPP: %ld%s",
                 GetSystemMetrics(SM_CXSCREEN),
                 GetSystemMetrics(SM_CYSCREEN),
                 bmi.bmiHeader.biBitCount,
                 pSeparator);
    pDDInfo += szTemp;

    pszDDInfo = new char[pDDInfo.GetLength()+1];
    if (pszDDInfo)
    {
        strncpy(pszDDInfo, pDDInfo, pDDInfo.GetLength()+1);
    }
    
    return pszDDInfo;
}

void DumpDDInfo(LPWINDRAW lpwd, LPDIRECTDRAWSURFACE lpSurface, char *pFunction)
{
    // A DirectDraw exception occured...bad bad bad
    //HX_ASSERT(0);

    // Serialize access to ddinfo.txt
    if (g_ehandlerMutexCnt)
        EnterCriticalSection(&g_ehandlerMutex);

    // Write a file in the plugins dir
    char    szPath[256]; /* Flawfinder: ignore */

    char    szFilePerm[] = "r+";
    int     nEntries = 1;
    char    szTmp[MAX_DISPLAY_NAME];     /* Flawfinder: ignore */
    char*   pszDDInfo = NULL;
    const char* pPath = NULL;
    pPath = GetDLLAccessPath()->GetPath(DLLTYPE_PLUGIN);
    SafeStrCpy(szPath, pPath, 256);
    SafeStrCat(szPath, "ddinfo.txt", 256);

    FILE *fp = fopen(szPath, "r"); /* Flawfinder: ignore */
    if (fp)
    {
        const int nMaxChars = 2048;
        int nMaxEntries = 50;

        // check log entry count
        fscanf(fp, "%s %d", szTmp, &nEntries);

        // Limit log file entries to nMaxEntries
        if (++nEntries > nMaxEntries)
        {
            char *pTime = NULL;
            SYSTEMTIME time;
            double dTime = 0;
            double dOldTime = 3000*8760;    // 3000 A.D. in hours
            int nOldestOffset = 0;
            int nLoc = 0;

            char pBuffer[nMaxChars]; /* Flawfinder: ignore */
            memset(&time, 0, sizeof(time));
            
            // read first <cr>
            fgets(pBuffer, nMaxChars, fp);

            // Find the oldest entry if the file
            while (!feof(fp))
            {
                nLoc = ftell(fp);
                fgets(pBuffer, nMaxChars, fp);
                
                // Find start of date
                pTime = strstr(pBuffer, "Systime: "); 
                if (pTime)
                {
                    // Read systime values for this log entry
                    pTime += 9;
                    sscanf(pTime, "%d %d %d %d %d %d %d %d",
                           &time.wYear, &time.wMonth, &time.wDayOfWeek,
                           &time.wDay, &time.wHour, &time.wMinute,
                           &time.wSecond, &time.wMilliseconds);

                    // Change date to hours for comparison
                    dTime = time.wYear*8760 + 
                            time.wMonth*720 + 
                            time.wDay*24 +
                            time.wHour +
                            time.wSecond/60.0 +
                            time.wMilliseconds/60000.0;
                    
                    // Compare with the current oldest entry
                    if (dTime < dOldTime)
                    {
                        dOldTime = dTime;
                        nOldestOffset = nLoc;
                    }
                }
            }

            // Remove oldest log entry: move contents of ddinfo to a temp file,
            // delete ddinfo.txt, ren. temp file to ddinfo.txt.
            pPath = GetDLLAccessPath()->GetPath(DLLTYPE_PLUGIN);
            SafeStrCpy(szTmp, pPath, 256);
            SafeStrCat(szTmp, "ddtemp.txt", 256);

            FILE *fpTemp = fopen(szTmp, "w"); /* Flawfinder: ignore */
            if (fpTemp)
            {
                fseek(fp, 0, SEEK_SET);
                while (!feof(fp))
                {
                    nLoc = ftell(fp);
                    if (fgets(pBuffer, nMaxChars, fp))
                    {
                        // Skip oldest entry
                        if (nLoc != nOldestOffset)
                            fputs(pBuffer, fpTemp);
                    }
                }
            }

            fclose(fp);

            if (fpTemp)
            {
                fclose(fpTemp);
                DeleteFile(szPath);

                rename(szTmp, szPath);
            }

            nEntries = nMaxEntries;
        }
        else
            fclose(fp);
    }
    else
    {
        szFilePerm[0] = 'w';
        szFilePerm[1] = '\0';
    }

    fp = fopen(szPath, szFilePerm); /* Flawfinder: ignore */
    if (fp)
    {
        fseek(fp, 0, SEEK_SET);
        fprintf(fp, "%s%d\t ", "Entries ", nEntries);
        fseek(fp, -1, SEEK_END);

        pszDDInfo = CollectDDInfo(lpwd, lpSurface, pFunction, "\t");
        if (pszDDInfo)
        {
            fprintf(fp, "%s", pszDDInfo);
        }
        HX_VECTOR_DELETE(pszDDInfo);

        fclose(fp);
    }

    if (g_ehandlerCount < g_ehandlerSilent)
    {
        // Need to throw error
        if (lpwd && lpwd->pErrMsg)
        {
            lpwd->pErrMsg->Report(HXLOG_INFO, HXR_WINDRAW_EXCEPTION, 0,
                                  szPath, NULL);
        }
    }

    if (lpwd && lpwd->pErrMsg)
        InterlockedIncrement(&g_ehandlerCount);

    if (g_ehandlerMutexCnt)
        LeaveCriticalSection(&g_ehandlerMutex);
}

/*
* Load system and/or bitmap palette.
*/
static int nSystemColors = 0;   /* !0 when system palette is loaded */
static	PALETTEENTRY	SystemPalette [256];
static	RGBQUAD		SystemPaletteRGB[256];
static int SystemPaletteIndices [256];

static int LoadPalette (LPWINDRAW lpwd)
{
    HDC hDC;
    int i, n = 0;
    
    /* get system palette first: */
    if ((hDC = GetDC (NULL)) != NULL) 
    {
        n = GetSystemPaletteEntries (hDC, 0, 256, SystemPalette);
        ReleaseDC (NULL,hDC);
    }
    
    /* check if system palette is loaded: */
    /* generate palette index: */
    for (i=0; i<n; i++)
    {
	SystemPaletteIndices[i]		= i;
	SystemPaletteRGB[i].rgbBlue	= SystemPalette[i].peBlue;
	SystemPaletteRGB[i].rgbGreen	= SystemPalette[i].peGreen;
	SystemPaletteRGB[i].rgbRed	= SystemPalette[i].peRed;
	SystemPaletteRGB[i].rgbReserved = 0;
    }
    
    /* update color conversion tables: */
    if (pfSetRGB8Palette)
    {
	(*pfSetRGB8Palette)(n, (unsigned long *)SystemPalette, SystemPaletteIndices);
    }
    
    /* indicate that palette is set: */
    nSystemColors = n;
    
    /* return # of colors loaded: */
    return n;
}

HRESULT CreateDirectDrawPalette (LPWINDRAW pwd, LPDIRECTDRAWSURFACE pSurface)
{
    HRESULT ddrval = E_FAIL;
    
    if (!pSurface || !pwd)
    {
        return ddrval;
    }
    
    try
    {

    /* load system palette if necessary: */
    if (nSystemColors) {

        /* create DirectDraw palette object: */
        if ((ddrval = IDirectDraw_CreatePalette (pwd->dd.lpDD2, DDPCAPS_8BIT, SystemPalette, &(pwd->dd.lpDDPal), NULL)) == DD_OK) 
        {
            /* check if we can attach palette to a primary surface: */
            if ((ddrval = IDirectDrawSurface_SetPalette (pSurface, pwd->dd.lpDDPal)) != DD_OK) 
            {
                /* release it and return error code: */
                IDirectDrawPalette_Release (pwd->dd.lpDDPal);
            }
        }
    }

    }
    catch (...)
    {
        DumpDDInfo(pwd, pSurface, "CreateDirectDrawPalette");
        ddrval = E_FAIL;
    }

    return ddrval;
}

/*
* Instructs color converters to use new destination palette:
*/
static void SetColorConverterPalette (PALETTEENTRY *pPalette, int l, int h)
{
    int i, palIdx [256];
    /* Instruct the color conversion library to use set of our
    * "standard" colors ( [l,h] range) from the logical palette:  */
    for (i=l; i<h; i++) palIdx [i] = i;
    
    if (pfSetRGB8Palette)
    {
	(*pfSetRGB8Palette)(h-l, (ULONG32*) (pPalette + l), palIdx + l);
    }
}

/*
* Checks if system palette contains our colors.
*/
static BOOL HasPaletteChanged (LPWINDRAW lpwd)
{
    PALETTEENTRY lppe [256], *lppe2;
    register int i;
    
    /* clear memory: */
    memset(lppe, 0, sizeof(lppe));
    
    /* retrive system palette: */
    if ((lpwd->fMode & WINDRAW_DIRECTDRAW) && lpwd->dd.lpDDPal != NULL &&
        /* use DirectDraw: */
        IDirectDrawPalette_GetEntries (lpwd->dd.lpDDPal, 0, lpwd->loColor,
	lpwd->hiColor - lpwd->loColor, lppe + lpwd->loColor) == DD_OK) {
        ; /* we're all done here */
    } else {
        /* use GDI: */
        HDC hdc = GetDisplayDC (lpwd->lpszDisplayDevice);
        GetSystemPaletteEntries (hdc, lpwd->loColor, lpwd->hiColor, lppe);
        DeleteDC (hdc);
    }
    
    /* check if our colors are still there: */
    lppe2 = lpwd->lgplDefaultPalette.palPalEntry;
    for (i=lpwd->loColor; i<lpwd->hiColor; i++)
        if (lppe [i].peGreen != lppe2 [i].peGreen ||
            lppe [i].peBlue  != lppe2 [i].peBlue  ||
            lppe [i].peRed   != lppe2 [i].peRed) {
            break;
        }
	return i == lpwd->hiColor;
}


/*
* Handles Windows palette change messages.
* Use:
*  LRESULT WinDraw2_OnPaletteChange (LPWINDRAW lpwd, HWND hwnd, UINT Message);
* Input:
*  lpwd - pointer to a WINDRAW structure we work with
*  hwnd - with WM_PALETTECHANGED: identifies a window that CAUSED the
*         palette to change; window that received a message otherwise
*  Message - either WM_QUERYNEWPALETTE or WM_PALETTECHANGED
* Returns:
*  TRUE, if we do realise our palette; FALSE, otherwise.
* Notes:
*  If our window is foreground application then we should get first
*  choice of colors in the system palette entries.
*  We get best performance when our logical palette includes the
*  standard VGA colors (at the beginning and end) otherwise GDI may have
*  to map from our palette to the device palette while drawing.
*/
LRESULT WinDraw2_OnPaletteChange (LPWINDRAW lpwd, HWND hwnd, UINT Message)
{
    if (lpwd && (lpwd->fMode & WINDRAW_OPENED) && hwnd == lpwd->hWnd && 
       (Message == WM_QUERYNEWPALETTE || Message == WM_PALETTECHANGED))
    {
        if (LoadPalette(lpwd))
        {
            /* check if we are using DirectDraw: */
            if (lpwd->fMode & WINDRAW_DIRECTDRAW) 
            {
                if (FAILED(CreateDirectDrawPalette(lpwd, lpwd->dd.lpDDSPrimary)))
                    return 0;
            }

            if (lpwd->m_pSurfaceList)
            {
                for(CHXSimpleList::Iterator i = lpwd->m_pSurfaceList->Begin(); i!= lpwd->m_pSurfaceList->End(); ++i)
                {
                    LPWINDRAWSURFACE lpwds = (LPWINDRAWSURFACE) *i;
                    if (lpwds)
                    {
                        if (lpwds->fMode & WINDRAWSURFACE_DIRECTDRAW)
                        {
                            CreateDirectDrawPalette(lpwd, lpwds->dd.lpDDSurface);
                            CreateDirectDrawPalette(lpwd, lpwds->dd.lpDDBackBuffer);
                        }
                        else
                        {
                            HBITMAP hOldBitmap;
                            for(int i = 0; i<(int)lpwds->dwBackBufferCount; i++)
                            {
                                if (lpwds->gdi.lpGDIBackBuffer[i])
                                {
                                    hOldBitmap = (HBITMAP) SelectObject (lpwd->gdi.hMemoryDC, lpwds->gdi.lpGDIBackBuffer[i]->hBitMap);
                                    SetDIBColorTable(lpwd->gdi.hMemoryDC, 0, nSystemColors, SystemPaletteRGB);
                                    SelectObject(lpwd->gdi.hMemoryDC, hOldBitmap);
                                }
                            }
                        }
                    }
                }
                return (LRESULT) 1;
            }
        }
    }
    return 0;
}

/*
* When the window size changes we adjust our WINDRAW variables that
* contain the dimensions of the client rectangle for our window so
* that we come to render an image we will know whether to stretch.
*/
LRESULT WinDraw2_OnSize (LPWINDRAW lpwd, DWORD dwWidth, DWORD dwHeight)
{
    /* check parameters: */
    if (lpwd == NULL)
        return (LRESULT) 0;
    
    /* update client area size: */
    lpwd->dwWindowWidth = dwWidth;
    lpwd->dwWindowHeight = dwHeight;
    
    /* ... */
    
    return (LRESULT) 1;
}

/*
* This function handles the WM_CLOSE message:
*/
LRESULT WinDraw2_OnClose (LPWINDRAW lpwd)
{
    /* check parameters: */
    if (lpwd == NULL || lpwd->hWnd == NULL)
        return (LRESULT) 0;
    
    /* hide window before destroying it: */
    ShowWindow (lpwd->hWnd, SW_HIDE);
    
    return TRUE;
}

HRESULT CALLBACK 
DisplayModeEnumeratorCallback(LPDDSURFACEDESC pddsd, LPVOID Context)
{
    CHXSimpleList* pList = (CHXSimpleList*) Context;
    
    CModesDesc* pDesc = new CModesDesc;
    
    pDesc->m_nWidth	= pddsd->dwWidth;
    pDesc->m_nHeight	= pddsd->dwHeight;
    pDesc->m_nBitCount	= pddsd->ddpfPixelFormat.dwRGBBitCount;
    pDesc->m_fourCC	= pddsd->ddpfPixelFormat.dwFourCC;
    
    pList->AddTail(pDesc);
    
    return DDENUMRET_OK;
} 

/*
* Initialize WinDraw engine.
* Use:
*  HRESULT WinDraw2_Open (LPWINDRAW lpwd, HWND hWnd, DWORD fMode,
*      LPSTR lpszDisplay, LPBMI lpbiDisplayFormat);
* Input:
*  lpwd - pointer to a WINDRAW structure to initialize
*  hWnd - a window handle to use
*  fMode - WinDraw mode to set (e.g. WINDRAW_FULLSCREEN)
*  lpszDisplay - monitor to use (NULL or zero string = use all monitors)
*  lpbiDisplayFormat - preferred display mode in full screen
* Returns:
*  NOERROR if OK, or the last relevant DirectDraw error code, or E_FAIL.
*/
HRESULT WinDraw2_Open (LPWINDRAW lpwd, HWND hWnd, DWORD fMode,
		       LPSTR lpszDisplay, LPBMI lpbiDisplayFormat)
{
    /* check parameters: */
    if (lpwd == NULL || hWnd == NULL || (lpwd->fMode & WINDRAW_OPENED))
        return E_INVALIDARG;
    
    HRESULT hrFail = E_FAIL;

    if (!g_ehandlerMutexCnt)
    {
        InitializeCriticalSection(&g_ehandlerMutex);
    }
    InterlockedIncrement(&g_ehandlerMutexCnt);
    
    z_hHxColor = LoadLibrary("colorcvt.dll");
    if (!z_hHxColor)
         z_hHxColor = LoadLibrary("hxltcolor.dll");

    if (z_hHxColor)
    {
	pfInitColorConverter = (INITCOLORCONVERTERFN) GetProcAddress(z_hHxColor,"InitColorConverter");
	pfSetRGB8Palette = (SETRGB8PALETTEFN) GetProcAddress(z_hHxColor,"SetRGB8Palette");
    }
    
    /* initialize WinDraw structure: */
    memset (lpwd, 0, sizeof (WINDRAW));
    lpwd->hWnd = hWnd;
    lpwd->m_pModesList	    = new CHXSimpleList;
    lpwd->m_pSurfaceList    = new CHXSimpleList;
    lpwd->nSchedulerResolution = 0;
    
    InitializeCriticalSection(&lpwd->csPrimary);
	
    /* check if want to use DirectDraw: */
    if (fMode & WINDRAW_DIRECTDRAW) 
    {
        lpwd->fMode |= WINDRAW_DIRECTDRAW;
	
        /* initialize DirectDraw objects: */
	
	LPDIRECTDRAW pDirectDraw = NULL;
	
        try
        {

        if (LoadDirectDraw (lpszDisplay, &pDirectDraw, & (lpwd->dd.hDD)) != NOERROR)
            goto dd_fail;

        }
        catch (...)
        {
            DumpDDInfo(lpwd, NULL, "WinDraw2_Open");
            goto dd_fail;
        }
	
        pDirectDraw->QueryInterface(IID_IDirectDraw2, (void**)&lpwd->dd.lpDD2);
	pDirectDraw->Release();
	pDirectDraw = 0;
	
	/* 
	* DirectX 1.0 installed (e.g. boxes with the original (Aug 95) release of Win95).
	* don't support that!
	*/
	if (!lpwd->dd.lpDD2)
	{
	    goto dd_fail;
	}
	
        /* initialize our color conversion engine: */
	if (pfInitColorConverter)
	    (*pfInitColorConverter)();
	
        /* get display format: */
        lpwd->lpszDisplayDevice = lpszDisplay;
        lpwd->cidDisplayColor = CID_UNKNOWN;
        if (GetDisplayFormat (&lpwd->bmiDisplayFormat, lpwd->lpszDisplayDevice) != NOERROR ||
            (lpwd->cidDisplayColor = GetBitmapColor ((LPBITMAPINFO)&lpwd->bmiDisplayFormat)) == CID_UNKNOWN) 
	{
	    
            /* this is very bad... */
            #ifdef _DEBUG
            OutputDebugString ("WinDraw: cannot obtain display pixel format.\n");
            #endif

            goto dd_fail;
        }
	
	/* find out what FourCC codes are supported */
	
	lpwd->dd.lpDD2->GetFourCCCodes(&(lpwd->numCodes), lpwd->lpCodes);
	
	if (lpwd->numCodes)
	{
	    lpwd->lpCodes	= (LPDWORD) malloc (lpwd->numCodes*4);
	    lpwd->lpCID	= (LPDWORD) malloc (lpwd->numCodes*4);
	    
	    lpwd->dd.lpDD2->GetFourCCCodes(&(lpwd->numCodes), lpwd->lpCodes);
	    
	    for(unsigned int i = 0; i<lpwd->numCodes; i++)
	    {
		lpwd->lpCID[i] = MapFourCCtoCID(lpwd->lpCodes[i]);
	    }
	}
	
	/* Get the device caps */
	memset(&lpwd->dd.m_caps, 0, sizeof(DDCAPS));
	lpwd->dd.m_caps.dwSize = sizeof(DDCAPS_DX3);
	lpwd->dd.lpDD2->GetCaps(&(lpwd->dd.m_caps), 0);

	/*
	*  If we have neither overlay nor streatching then we will use DibEngine (good call? time will tell)
	*/
	
        if (! ( (lpwd->dd.m_caps.dwCaps & DDCAPS_OVERLAY) || (lpwd->dd.m_caps.dwCaps &  DDCAPS_BLTSTRETCH) ))
	{
	    goto dd_fail;
	}
	
	/* set normal cooperative level  */
        if (DD_OK != lpwd->dd.lpDD2->SetCooperativeLevel(lpwd->hWnd, DDSCL_NORMAL)) 
	{
	    goto dd_fail;
        }
	
        try
        {

        if (DD_OK == WindrawSurface_CreatePrimarySurface(lpwd))
	{
            /* DirectDraw profile/mode masks: */
            DWORD dwOverlayMask, dwBltMask;
            DDPIXELFORMAT ddpf;
	    /* color formats: */
	    int cidOut = CID_UNKNOWN;
	    
            /* get pixel format of the primary surface: */
            memset (&ddpf, 0, sizeof (DDPIXELFORMAT));
            ddpf.dwSize = sizeof (DDPIXELFORMAT);
            ddpf.dwFlags = DDPF_RGB | DDPF_FOURCC;
            if (lpwd->dd.lpDDSPrimary->GetPixelFormat(&ddpf) != DD_OK)
                goto dd_fail;
	    
            /* convert it to our color ID: */
            cidOut = GetDirectDrawColor (&ddpf);
            if (cidOut < CID_RGB32 || cidOut > CID_RGB8)
                goto dd_fail;
	    
            /* check if DirectDraw profile needs to be loaded: */
            if (!lpddprofDirectDrawProfile || DDPDB_IsNewProfile ()) {
		
                /* get DirectDraw device profile: */
		lpwd->dd.lpDD2->QueryInterface(IID_IDirectDraw, (void**)&pDirectDraw);
		HX_ASSERT(pDirectDraw);
		lpddprofDirectDrawProfile = DDPDB_GetDeviceProfile(pDirectDraw);
		pDirectDraw->Release();
		pDirectDraw = 0;
            }
	    
            /* get mode masks: */
            dwOverlayMask = lpddprofDirectDrawProfile->fOverlays [cidOut-CID_RGB32];
            dwBltMask = lpddprofDirectDrawProfile->fBlts [cidOut-CID_RGB32];
	    
	    //            if (dwOverlayMask == 0 || dwBltMask == 0)
	    /* We currently disable DirectDraw if bltMask is 0.
	    * Need to add support for selecting DirectDraw features based on
	    * the info in the database.
	    */
            if (dwOverlayMask == 0)
                goto dd_fail;
	    
	    /* Enum the display modes  */
	    zm_DisplaySettings.GetSettings(lpwd->m_pModesList);
	    
	    /*
	    *  For SOME reason EnumDisplayModes is causing a number of cards to go CRAZY.
	    *	So we now we call a function which calls enum display modes early and caches 
	    *	the results. For some reason this does not cause things to go INSANE. Who knows why.
	    */
	    
	    //	    lpwd->dd.lpDD2->EnumDisplayModes(0, NULL, lpwd->m_pModesList, DisplayModeEnumeratorCallback); 	    
	}
	else
	{
            goto dd_fail;
	}
	
        }
        catch(...)
        {
            DumpDDInfo(lpwd, lpwd->dd.lpDDSPrimary, "WinDraw2_Open");
            goto dd_fail;
        }
        
        goto success;
	
dd_fail:
	
        /* release DirectDraw library, if loaded: */
        if (lpwd->dd.lpDD2 != NULL && lpwd->dd.hDD != NULL)
            ReleaseDirectDraw(&(lpwd->dd.lpDD), &(lpwd->dd.lpDD2), & (lpwd->dd.hDD));
	
        /* exit with error: */
        return hrFail;
	
    } else { /* use GDI: */

        /* grab DCs: */
	
	if ((lpwd->gdi.hDC = GetDC (lpwd->hWnd)) == NULL ||
	    (lpwd->gdi.hMemoryDC = CreateCompatibleDC (lpwd->gdi.hDC)) == NULL ||
	    (lpwd->gdi.hMemoryDC2 = CreateCompatibleDC (lpwd->gdi.hDC)) == NULL)
            goto gdi_fail;
	
        /* set default StretchBlt () mode: */
	SetStretchBltMode (lpwd->gdi.hDC, COLORONCOLOR);
        SetStretchBltMode (lpwd->gdi.hMemoryDC,COLORONCOLOR);
        SetStretchBltMode (lpwd->gdi.hMemoryDC2,COLORONCOLOR);
	
        /* get display format: */
        lpwd->lpszDisplayDevice = lpszDisplay;
        lpwd->cidDisplayColor = CID_UNKNOWN;
        if (GetDisplayFormat (&lpwd->bmiDisplayFormat, lpwd->lpszDisplayDevice) != NOERROR ||
            (lpwd->cidDisplayColor = GetBitmapColor ((LPBITMAPINFO)&lpwd->bmiDisplayFormat)) == CID_UNKNOWN) {
	    
            /* this is very bad... */
            #ifdef _DEBUG
            OutputDebugString ("WinDraw: cannot obtain display pixel format.\n");
            #endif

            goto gdi_fail;
        }
	
        /* initialize our color conversion engine: */
	if (pfInitColorConverter)
	    (*pfInitColorConverter)();
	
        goto success;
	
        /* something really bad has happened... */
gdi_fail:
	
        /* delete palette: */
        if (lpwd->gdi.hPalette) {
            DeleteObject (lpwd->gdi.hPalette);
	    lpwd->gdi.hPalette = NULL;
	}
	
	
	
	/* release DCs, if taken: */   
	if (lpwd->gdi.hDC != NULL) 
	{   
	    ReleaseDC (lpwd->hWnd, lpwd->gdi.hDC);   
	    lpwd->gdi.hDC = NULL;   
	} 
        
	if (lpwd->gdi.hMemoryDC != NULL) {
            DeleteDC (lpwd->gdi.hMemoryDC);
	    lpwd->gdi.hMemoryDC = NULL;
	}
        if (lpwd->gdi.hMemoryDC2 != NULL) {
            DeleteDC (lpwd->gdi.hMemoryDC2);
	    lpwd->gdi.hMemoryDC2 = NULL;
	}
	
        /* exit with error: */
        return E_FAIL;
    }
    
success:
    
    /* activate WinDraw & exit: */
    lpwd->fMode |= WINDRAW_OPENED;
    WinDraw2_OnPaletteChange(lpwd, lpwd->hWnd, WM_PALETTECHANGED);

    return NOERROR;
}

void DeleteDisplayModes(LPWINDRAW lpwd)
{
    CHXSimpleList::Iterator i;
    
    if (!lpwd->m_pModesList || !lpwd->m_pModesList->GetCount())
        return;

    for(i=lpwd->m_pModesList->Begin(); i!= lpwd->m_pModesList->End(); ++i)
    {
	CModesDesc* pDesc = (CModesDesc*) *i;
	HX_DELETE(pDesc);
    }
    lpwd->m_pModesList->RemoveAll();
}

/*
* Close WinDraw library.
* Use:
*  HRESULT WinDrawClose (LPWINDRAW lpwd);
* Input:
*  lpwd - pointer to a WINDRAW engine to deactivate
* Returns:
*  NOERROR if OK, or E_FAIL.
*/
HRESULT WinDraw2_Close (LPWINDRAW lpwd)
{
    /* check parameters: */
    if (lpwd == NULL || !(lpwd->fMode & WINDRAW_OPENED))
        //	return E_INVALIDARG;
        goto cleanup;

    if (z_hHxColor)
    {
        FreeLibrary(z_hHxColor);
        z_hHxColor = 0;

        pfInitColorConverter = 0;
        pfSetRGB8Palette = 0;
    }

    /* check mode: */
    if (lpwd->fMode & WINDRAW_DIRECTDRAW)
    {
        free(lpwd->lpCodes);
        free(lpwd->lpCID);

       /* 
        * Release the Primary Surface
        */

        if (lpwd->dd.lpDDSPrimary)
        {
            int rel = lpwd->dd.lpDDSPrimary->Release();
            lpwd->dd.lpDDSPrimary = NULL;
        }

        /* release DirectDraw library, if loaded: */
        if (lpwd->dd.lpDD2 != NULL && lpwd->dd.hDD != NULL)
        ReleaseDirectDraw (&(lpwd->dd.lpDD), &(lpwd->dd.lpDD2), &(lpwd->dd.hDD));


    }
    else
    { /* GDI: */
        /* delete palette: */
        if (lpwd->gdi.hPalette)
        {
            DeleteObject (lpwd->gdi.hPalette);
            lpwd->gdi.hPalette = NULL;
        }

        /* release DCs, if taken: */
        if (lpwd->gdi.hDC != NULL) 
        {   
            ReleaseDC (lpwd->hWnd, lpwd->gdi.hDC);   
            lpwd->gdi.hDC = NULL;   
        } 
        if (lpwd->gdi.hMemoryDC != NULL) 
        {
            DeleteDC (lpwd->gdi.hMemoryDC);
            lpwd->gdi.hMemoryDC = NULL;
        }
        if (lpwd->gdi.hMemoryDC2 != NULL) 
        {
            DeleteDC (lpwd->gdi.hMemoryDC2);
            lpwd->gdi.hMemoryDC2 = NULL;
        }
    }

    cleanup:
    /* deactivate WinDraw & exit: */
    lpwd->fMode = 0;

    /* remove display mode data */
    DeleteDisplayModes(lpwd);
    HX_DELETE(lpwd->m_pModesList);

    HX_DELETE(lpwd->m_pSurfaceList);

    DeleteCriticalSection(&lpwd->csPrimary);

    InterlockedDecrement(&g_ehandlerMutexCnt);
    if (!g_ehandlerMutexCnt)
    {
        DeleteCriticalSection(&g_ehandlerMutex);
    }
    

    
    return NOERROR;
}

/*
* Get display/primary surface format.
* Use:
*  HRESULT WinDraw2_GetDisplayFormat (LPWINDRAW lpwd, LPBMI lpbiDisplayFormat);
* Input:
*  lpwd - pointer to a WINDRAW structure to initialize
*  lpbiDisplayFormat - a structure to contain display format
* Returns:
*  NOERROR if OK, or the last relevant DirectDraw error code, or E_FAIL.
*/
HRESULT WinDraw2_GetDisplayFormat (LPWINDRAW lpwd, LPBMI lpbiDisplayFormat)
{
    int sz;
    
    /* check parameters: */
    if (lpwd == NULL || !(lpwd->fMode & WINDRAW_OPENED) ||
        lpbiDisplayFormat == NULL)
        return E_INVALIDARG;
    
    /* check the size of BITMAPINFO structure to copy: */
    sz = lpbiDisplayFormat->bmiHeader.biSize;
    if (sz < (int)sizeof(BITMAPINFOHEADER) || sz > (int)lpwd->bmiDisplayFormat.bmiHeader.biSize)
        sz = lpwd->bmiDisplayFormat.bmiHeader.biSize;
    
    /* copy bitmap info: */
    
    /* Since it seems that our convention was to set biSize to the sizeof 
    * BITMAPINFOHEADER instead of the actual size of the structure (which
    * causes us to lose the color table of bit fields) we will use the size
    * of the structure.
    */
    // memcpy(lpbiDisplayFormat, &(lpwd->bmiDisplayFormat), sz);
    GetDisplayFormat(&lpwd->bmiDisplayFormat, lpwd->lpszDisplayDevice);
    
    memcpy(lpbiDisplayFormat, &(lpwd->bmiDisplayFormat), sizeof(BMI) ); /* Flawfinder: ignore */
    
    return NOERROR;
}

/*
* GDISURFACE functions:
*/
#define hMEMORY ((HANDLE) 0xFFFFFFFF)   /* says to open as memory file  */

/*
* This function allocates a shared memory block for use by the source filter
* generating DIBs for us to render. The memory block is created in shared
* memory so that GDI doesn't have to copy the memory when we do a BitBlt
*/
static LPGDISURFACE GDISurface_Alloc (LPBITMAPINFO pbmi)
{
    LPGDISURFACE lpGDISurface;  /* pointer to a new GDISURFACE  */
    HANDLE      hMapping;       /* handle to mapped object      */
    HBITMAP     hBitmap;        /* DIB section bitmap handle    */
    BYTE        *pBase;         /* pointer to the actual image  */
    
    /* allocate a new GDISURFACE structure to use: */
    lpGDISurface = (LPGDISURFACE)malloc (sizeof (GDISURFACE));
    if (lpGDISurface != NULL) {
	
        /* create a file mapping object and map into our address space: */
        hMapping = CreateFileMapping (hMEMORY, NULL, PAGE_READWRITE, (DWORD) 0, pbmi->bmiHeader.biSizeImage, NULL);
        if (hMapping != NULL) {
	    
            /* create a DIB section using given image format */
            hBitmap = CreateDIBSection ((HDC) NULL, pbmi, DIB_RGB_COLORS, (VOID **) &pBase, hMapping, (DWORD) 0);
            if (hBitmap != NULL && pBase != NULL) {
		
                /* initialise the GDISURFACE structure: */
                lpGDISurface->hBitMap = hBitmap;
                lpGDISurface->hMapping = hMapping;
                lpGDISurface->lpBase = pBase;
                lpGDISurface->lpAlphaSurface = NULL;
                lpGDISurface->hEmpty = CreateEvent(NULL, TRUE, TRUE, NULL);
                
                /* lpGDISurface->PaletteVersion = PALETTE_VERSION; */
                GetObject (hBitmap, sizeof (DIBSECTION), (VOID *)& (lpGDISurface->DibSection));
		
                /* success: */
                return lpGDISurface;
            }
            /* close file mapping... */
            CloseHandle (hMapping);
        }
        /* free buffer: */
        free (lpGDISurface);
    }
    return NULL;
}

/*
* Releases previously allocated GDI buffer.
* Main application should consider calling GdiFlush ();
* before releasing these buffers.
*/
static void GDISurface_Free (LPGDISURFACE lpGDISurface)
{
    /* check pointer to a buffer structure to use: */
    if (lpGDISurface != NULL) {
        /* close dibsection: */
        DeleteObject (lpGDISurface->hBitMap);
        /* close file mapping: */
        CloseHandle (lpGDISurface->hMapping);
        /* close alpha channel */
        free(lpGDISurface->lpAlphaSurface);
        /* close event */
        CloseHandle(lpGDISurface->hEmpty);
        /* free buffer: */
        free (lpGDISurface);
    }
}

static BOOL GDISurface_AlphaBltIndirect(LPWINDRAW lpwd, LPGDISURFACE lpGDISurfaceDest,
					LPGDISURFACE lpGDISurfaceSrc, LPRECT lpDestRect, LPRECT lpSrcRect)
{
#if 0     
    INT32 destX, destY;
    INT32 destStride, srcStride, alphaStride;
    UINT32 *srcBuf, *destBuf;
    UCHAR  *alphaBuf;
    UCHAR alpha, invalpha;
    UCHAR *pSrc, *pDest;
    
    int destCID = GetBitmapColor((LPBITMAPINFO) &lpGDISurfaceDest->DibSection.dsBmih);
    int srcCID  = GetBitmapColor((LPBITMAPINFO) &lpGDISurfaceSrc->DibSection.dsBmih);
    
    assert(destCID == srcCID); // not supported
    
    switch (destCID)
    {
    case CID_RGB565:
    case CID_RGB555:
	destStride  = (lpGDISurfaceDest->DibSection.dsBmih.biWidth - (lpDestRect->right - lpDestRect->left)) * 2;
	srcStride   = (lpGDISurfaceSrc->DibSection.dsBmih.biWidth - (lpSrcRect->right - lpSrcRect->left)) * 2;
	srcBuf	    = (UINT32*)(lpGDISurfaceSrc->lpBase + ( (lpSrcRect->right - lpSrcRect->left) * lpSrcRect->top 
	    + lpSrcRect->left ) * 2);
	destBuf	    = (UINT32*)(lpGDISurfaceDest->lpBase + ( (lpDestRect->right - lpDestRect->left) * lpDestRect->top 
	    + lpDestRect->left) *2);
	break;
    case CID_RGB32:
	destStride  = (lpGDISurfaceDest->DibSection.dsBmih.biWidth - (lpDestRect->right - lpDestRect->left)) * 4;
	srcStride   = (lpGDISurfaceSrc->DibSection.dsBmih.biWidth - (lpSrcRect->right - lpSrcRect->left)) * 4;
	srcBuf	    = (UINT32*)(lpGDISurfaceSrc->lpBase + ( (lpSrcRect->right - lpSrcRect->left) * lpSrcRect->top 
	    + lpSrcRect->left ) *4);
	destBuf	    = (UINT32*)(lpGDISurfaceDest->lpBase + ( (lpDestRect->right - lpDestRect->left) * lpDestRect->top 
	    + lpDestRect->left ) *4);
	break;
    case CID_RGB24:
	break;
    default:
	assert(CID_NOTSUPPORTED);
    }
    
    alphaBuf	= lpGDISurfaceSrc->lpAlphaSurface;
    alphaStride = lpGDISurfaceDest->DibSection.dsBmih.biWidth - (lpSrcRect->right - lpSrcRect->left);
    
    switch (destCID)
    {
    case CID_RGB32:
	{
	    for (destY = lpDestRect->top; destY < lpDestRect->bottom; destY++ )
	    {
		for (destX = lpDestRect->left;destX < lpDestRect->right;destX++, srcBuf++, destBuf++, alphaBuf++)
		{
		    alpha = *alphaBuf>128 ? *alphaBuf + 1 : *alphaBuf;
		    invalpha = 256 - alpha;
		    *destBuf = 
			((((*srcBuf	& 0x00ff0000) * alpha)	    >> 8 ) & 0x00ff0000) +
			((((*destBuf    & 0x00ff0000) * invalpha)   >> 8 ) & 0x00ff0000) +
			((((*srcBuf	& 0x0000ff00) * alpha)	    >> 8 ) & 0x0000ff00) +
			((((*destBuf    & 0x0000ff00) * invalpha)   >> 8 ) & 0x0000ff00) +
			((((*srcBuf	& 0x000000ff) * alpha)	    >> 8 ) & 0x000000ff) +
			((((*destBuf    & 0x000000ff) * invalpha)   >> 8 ) & 0x000000ff);
		}						  
		destBuf	    += destStride;
		srcBuf	    += srcStride;
		alphaBuf    += alphaStride;
	    }
	    break;
	}
    case CID_RGB24:
	{
	    pSrc    = lpGDISurfaceSrc->lpBase;
	    pDest   = lpGDISurfaceDest->lpBase;
	    pDest   += (lpGDISurfaceDest->DibSection.dsBmih.biWidth * (lpGDISurfaceDest->DibSection.dsBmih.biHeight - lpDestRect->top) + lpDestRect->left)* 3;
	    pSrc    += (lpGDISurfaceSrc->DibSection.dsBmih.biWidth * (lpGDISurfaceSrc->DibSection.dsBmih.biHeight - lpSrcRect->top) + lpSrcRect->left)* 3;
	    
	    for (destY = lpDestRect->top; destY < lpDestRect->bottom; destY++ )
	    {
		for (destX = lpDestRect->left;destX < lpDestRect->right;destX++, alphaBuf++)
		{   
		    //		    alpha	= *alphaBuf;
		    alpha	= 128;
		    invalpha	= 256 - alpha;
		    
		    *pDest = (*pSrc * alpha) >> 8 + (*pDest * invalpha) >> 8;
		    pDest++; pSrc++;
		    *pDest = (*pSrc * alpha) >> 8 + (*pDest * invalpha) >> 8;
		    pDest++; pSrc++;
		    *pDest = (*pSrc * alpha) >> 8 + (*pDest * invalpha) >> 8;
		    pDest++; pSrc++;
		}
		pDest -= (lpGDISurfaceDest->DibSection.dsBmih.biWidth + (lpDestRect->right - lpDestRect->left))*3;
		pSrc -= (lpGDISurfaceSrc->DibSection.dsBmih.biWidth + (lpSrcRect->right - lpSrcRect->left))*3;
		alphaBuf +=alphaStride;
	    }
	    break;
	}
	
    case CID_RGB565:
    case CID_RGB555:
	{
	/*
	* The following bugs are plain (and all stem from the same problem)
	*
	*	1. If the dest/src rects are not both even.
	*  2. If the width of the blt is not even
	* 
	    */
	    for (destY = lpDestRect->top; destY < lpDestRect->bottom; destY++ )
	    {
		for (destX = lpDestRect->left;destX < lpDestRect->right;destX+=2, srcBuf++, destBuf++, alphaBuf++)
		{
		    alpha	= *alphaBuf>128 ? *alphaBuf + 1 : *alphaBuf;
		    invalpha	= 256 - alpha;
		    *destBuf = 
			((((*srcBuf	& 0xf8000000) * alpha)	    >> 8 ) & 0xf8000000) +
			((((*destBuf    & 0xf8000000) * invalpha)   >> 8 ) & 0xf8000000) +
			((((*srcBuf	& 0x07e00000) * alpha)	    >> 8 ) & 0x07e00000) +
			((((*destBuf    & 0x07e00000) * invalpha)   >> 8 ) & 0x07e00000) +
			((((*srcBuf	& 0x001f0000) * alpha)	    >> 8 ) & 0x001f0000) +
			((((*destBuf    & 0x001f0000) * invalpha)   >> 8 ) & 0x001f0000) +
			((((*srcBuf	& 0x0000f800) * alpha)	    >> 8 ) & 0x0000f800) +
			((((*destBuf    & 0x0000f800) * invalpha)   >> 8 ) & 0x0000f800) +
			((((*srcBuf	& 0x000007e0) * alpha)	    >> 8 ) & 0x000007e0) +
			((((*destBuf    & 0x000007e0) * invalpha)   >> 8 ) & 0x000007e0) +
			((((*srcBuf	& 0x0000001f) * alpha)	    >> 8 ) & 0x0000001f) +
			((((*destBuf    & 0x0000001f) * invalpha)   >> 8 ) & 0x0000001f);
		}
		destBuf	    += destStride;
		srcBuf	    += srcStride;
		alphaBuf    += alphaStride;
	    }
	    break;
	}
    }
#endif
    return NOERROR;
}

/*
* Blits image data into the window.
* Use:
*  BOOL DDSurface_BltIndirect(LPWINDRAW lpwd, LPGDISURFACE lpGDISurface,
*       LPRECT lpSourceRect, LPRECT lpTargetRect)
* Input:
*  lpwd - pointer to the WINDRAW structure
*  lpDDSurfaceSrc - a structure containing DIBSECTION of image to blit
*  lpDDSurfaceDst - a 
*  lpSrcRect - a source image region to blit
*  lpDstRect - a target window region to fill with image
* Returns:
*  TRUE, if success; FALSE, otherwise.
*/
static BOOL DDSurface_BltIndirect(  
				  LPWINDRAW lpwd, 
				  LPDIRECTDRAWSURFACE lpDDSurfaceDst,
				  LPDIRECTDRAWSURFACE lpDDSurfaceSrc, 
				  LPRECT lpDstRect, 
				  LPRECT lpSrcRect
				  )
{
    /* 
    * this will currently only work if the two color formats are 
    * the same.
    */
    
    /* set update mode: */
    DWORD dwFlags = DDBLT_WAIT | DDBLT_ROP;
    
    /* set effects: */
    DDBLTFX ddBltFx;
    ZeroMemory(&ddBltFx, sizeof(DDBLTFX));
    ddBltFx.dwSize  = sizeof (DDBLTFX);
    ddBltFx.dwROP   = SRCCOPY;
    
    /* try to blit data from an offscreen surface: */
    HRESULT retVal;

    try
    {
    
    retVal = lpDDSurfaceDst->Blt(lpDstRect, lpDDSurfaceSrc, lpSrcRect, dwFlags, &ddBltFx);

    }
    catch(...)
    {
        char szTmp[256]; /* Flawfinder: ignore */
        sprintf(szTmp, "DDSurface_BltIndirect srcRect %ld %ld %ld %ld dstRc %ld %ld %ld %ld", /* Flawfinder: ignore */
                       lpSrcRect->left, lpSrcRect->top, lpSrcRect->right, lpSrcRect->bottom,
                       lpDstRect->left, lpDstRect->top, lpDstRect->right, lpDstRect->bottom);

        DumpDDInfo(lpwd, lpDDSurfaceDst, "DDSurface_BltIndirect");
        retVal = HXR_FAIL;
    }

    return retVal;
}

/*
* Blits image data into the window.
* Use:
*  BOOL GDISurface_BltIndirect(LPWINDRAW lpwd, LPGDISURFACE lpGDISurface,
*       LPRECT lpSourceRect, LPRECT lpTargetRect)
* Input:
*  lpwd - pointer to the WINDRAW structure
*  lpGDISurface - a structure containing DIBSECTION of image to blit
*  lpSourceRect - a source image region to blit
*  lpTargetRect - a target window region to fill with image
* Returns:
*  TRUE, if success; FALSE, otherwise.
*/
static BOOL GDISurface_BltIndirect(LPWINDRAW lpwd, LPGDISURFACE lpGDISurfaceDest,
				   LPGDISURFACE lpGDISurfaceSrc, LPRECT lpSourceRect, LPRECT lpTargetRect)
{
    
    BOOL bResult;                   /* return value             */
    /* get sizes of source/destination rectangles: */
    LONG lTargetWidth  = lpTargetRect->right  - lpTargetRect->left;
    LONG lTargetHeight = lpTargetRect->bottom - lpTargetRect->top;
    LONG lSourceWidth  = lpSourceRect->right  - lpSourceRect->left;
    LONG lSourceHeight = lpSourceRect->bottom - lpSourceRect->top;
    
    /* select bitmap to blit: */
    HBITMAP hOldBitmap	= (HBITMAP) SelectObject (lpwd->gdi.hMemoryDC, lpGDISurfaceDest->hBitMap);
    HBITMAP hOldBitmap2 = (HBITMAP) SelectObject (lpwd->gdi.hMemoryDC2, lpGDISurfaceSrc->hBitMap);
    
    if (lpGDISurfaceSrc->lpAlphaSurface)
    {
	return GDISurface_AlphaBltIndirect(lpwd, lpGDISurfaceDest, lpGDISurfaceSrc, lpTargetRect, lpSourceRect);
    }
    
    /* is the window the same size as the video: */
    if (lTargetWidth == lSourceWidth && lTargetHeight == lSourceHeight) 
    {
        /* put the image straight into the destination: */
        bResult = BitBlt (
            lpwd->gdi.hMemoryDC,    /* target device HDC        */
            lpTargetRect->left,     /* x sink position          */
            lpTargetRect->top,      /* y sink position          */
            lTargetWidth,           /* destination width        */
            lTargetHeight,          /* destination height       */
            lpwd->gdi.hMemoryDC2,   /* source device context    */
            lpSourceRect->left,     /* x source position        */
            lpSourceRect->top,      /* y source position        */
            SRCCOPY);               /* simple copy              */
	
    } 
    else 
    {
        /* stretch the image when copying to the destination:	*/
        bResult = StretchBlt (
            lpwd->gdi.hMemoryDC,    /* target device HDC        */
            lpTargetRect->left,     /* x sink position          */
            lpTargetRect->top,      /* y sink position          */
            lTargetWidth,           /* destination width        */
            lTargetHeight,          /* destination height       */
            lpwd->gdi.hMemoryDC2,   /* source device HDC        */
            lpSourceRect->left,     /* x source position        */
            lpSourceRect->top,      /* y source position        */
            lSourceWidth,           /* source width             */
            lSourceHeight,          /* source height            */
            SRCCOPY);               /* simple copy              */
    }
    /* put the old bitmap back into the device context so we don't leak */
    SelectObject (lpwd->gdi.hMemoryDC, hOldBitmap);
    SelectObject (lpwd->gdi.hMemoryDC2, hOldBitmap2);
    
    /* pass the result of Bit/StretchBlt: */
    return bResult;
}

/*
* Blits image data into the window.
* Use:
*  BOOL DDSurface_Blt(LPWINDRAW lpwd, LPDIRECTDRAWSURFACE lpDDSurface,
*       LPRECT lpSourceRect, LPRECT lpTargetRect)
* Input:
*  lpwd - pointer to the WINDRAW structure
*  lpDDSurface	 - a structure containing DIBSECTION of image to blit
*  lpSourceRect - a source image region to blit
*  lpTargetRect - a target window region to fill with image
* Returns:
*  TRUE, if success; FALSE, otherwise.
*/

BOOL DDSurface_Blt(LPWINDRAW lpwd, LPDIRECTDRAWSURFACE lpDDSurface,
		   LPRECT lpSourceRect, LPRECT lpTargetRect)
{
    /* 
    * this will currently only work if the two color formats are 
    * the same.
    */
    /* set update mode: */
    DWORD dwFlags = DDBLT_WAIT;
    
    /* set effects: */
    DDBLTFX ddBltFx;
    ZeroMemory(&ddBltFx, sizeof(DDBLTFX));
    ddBltFx.dwSize  = sizeof (DDBLTFX);
    ddBltFx.dwDDFX  = DDBLTFX_NOTEARING;
    //ddBltFx.dwROP   = SRCCOPY;
    
    /* try to blit data from an offscreen surface: */
    
    RECT rect, destRect;
    GetWindowRect(lpwd->hWnd, &rect);
    destRect.left   = lpTargetRect->left    + rect.left;
    destRect.right  = lpTargetRect->right   + rect.left;
    destRect.top    = lpTargetRect->top	    + rect.top;
    destRect.bottom = lpTargetRect->bottom  + rect.top;
    
    /* handle monitor spanning blts */
    RECT srcRect = *lpSourceRect;
    double ratio;
    
    UINT32 uHorzRes = xGetSystemMetrics(SM_CXVIRTUALSCREEN);
    UINT32 uVertRes = xGetSystemMetrics(SM_CYVIRTUALSCREEN);
    
    if (destRect.left < 0)
    {
        ratio = ((double)abs(destRect.left)) / (double)(destRect.right - destRect.left);
        destRect.left = 0;
        srcRect.left = (int) (((double) (lpSourceRect->right-lpSourceRect->left)) * ratio);
    }

    if (destRect.right> uHorzRes)
    {
        ratio = ((double)(destRect.right - uHorzRes)) / (double)(destRect.right - destRect.left);
        destRect.right = uHorzRes;
        srcRect.right = (int) (((double) (lpSourceRect->right-lpSourceRect->left)) * (1.0 - ratio));
    }

    if (destRect.top < 0)
    {
        ratio = ((double)abs(destRect.top)) / (double)(destRect.bottom - destRect.top);
        destRect.top = 0;
        srcRect.top = (int) (((double) (lpSourceRect->bottom-lpSourceRect->top)) * ratio);
    }

    if (destRect.bottom > uVertRes)
    {
        ratio = ((double)(destRect.bottom - uVertRes)) / (double)(destRect.bottom - destRect.top);
        destRect.bottom = uVertRes;
        srcRect.bottom = (int) (((double) (lpSourceRect->bottom-lpSourceRect->top)) * (1.0 - ratio));
    }

#ifdef _CHECK_PERFORMANCE
    static LARGE_INTEGER QueryPerformanceCounterResult = {0,0};
    static LARGE_INTEGER QueryPerformanceFrequencyResult = {0,0};
    
    QueryPerformanceFrequency(&QueryPerformanceFrequencyResult);
    
    double frequency = ((double)QueryPerformanceFrequencyResult.LowPart + 4294967296.0*QueryPerformanceFrequencyResult.HighPart);
    QueryPerformanceCounter(&QueryPerformanceCounterResult);
    
    double startTime = ((double)QueryPerformanceCounterResult.LowPart + 4294967296.0*QueryPerformanceCounterResult.HighPart)/frequency;
#endif
    
#ifdef _CHECK_PERFORMANCE
    FILE* f1 = fopen("c:\\status.txt", "a+"); /* Flawfinder: ignore */
    fprintf(f1, "DDSurface_Blt: (%d, %d, %d, %d) -> (%d, %d, %d, %d)\n", lpSourceRect->left, lpSourceRect->top, lpSourceRect->right, lpSourceRect->bottom , lpTargetRect->left, lpTargetRect->top, lpTargetRect->right, lpTargetRect->bottom);
    fclose(f1);
#endif
    
    HRESULT retVal = E_FAIL;
    
    EnterCriticalSection(&lpwd->csPrimary);

    if (!lpwd->dd.lpDDSPrimary)
    {
        WindrawSurface_CreatePrimarySurface(lpwd);
    }
    
    if (lpwd->dd.lpDDSPrimary)
    {
        try
        {

        retVal = lpwd->dd.lpDDSPrimary->Blt(&destRect, lpDDSurface, &srcRect, dwFlags, &ddBltFx);

        if (retVal != DD_OK)
        {
            lpDDSurface->Restore();
            lpwd->dd.lpDDSPrimary->Restore();
            retVal = lpwd->dd.lpDDSPrimary->Blt(&destRect, lpDDSurface, &srcRect, dwFlags, &ddBltFx);
        }

        }
        catch (...)
        {
            char szTmp[256]; /* Flawfinder: ignore */
            sprintf(szTmp, "DDSurface_Blt srcRect %ld %ld %ld %ld dstRc %ld %ld %ld %ld", /* Flawfinder: ignore */
                       lpSourceRect->left, lpSourceRect->top, lpSourceRect->right, lpSourceRect->bottom,
                       lpTargetRect->left, lpTargetRect->top, lpTargetRect->right, lpTargetRect->bottom);
            
            DumpDDInfo(lpwd, lpDDSurface, szTmp);
            retVal = HXR_FAIL;
        }
    }

    LeaveCriticalSection(&lpwd->csPrimary);
    
#ifdef _CHECK_PERFORMANCE
    QueryPerformanceCounter(&QueryPerformanceCounterResult);
    double endTime = ((double)QueryPerformanceCounterResult.LowPart + 4294967296.0*QueryPerformanceCounterResult.HighPart)/frequency;
    
    static UINT32 z_nANumTimes = 0;
    static double z_fATotalTime;
    static double z_fAAverageTime;
    
    z_nANumTimes++;
    z_fATotalTime += endTime - startTime;
    z_fAAverageTime = z_fATotalTime / (double) z_nANumTimes;
    
    if (! (z_nANumTimes % 25))
    {
	FILE* f1 = ::fopen("c:\\performance.txt", "a+"); /* Flawfinder: ignore */
	::fprintf(f1, "WINDRAW2 - ACTUAL BLT TIME: %d blts. Total CPU time: %f, CPU/Blt: %f -- Blt/s Second Max: %f\n", z_nANumTimes, z_fATotalTime, z_fAAverageTime, 1.0/z_fAAverageTime);
	fclose(f1);
    }
#endif
    
    return retVal;
}


/*
* Blits image data into the window.
* Use:
*  BOOL GDISurface_Blt (LPWINDRAW lpwd, LPGDISURFACE lpGDISurface,
*       LPRECT lpSourceRect, LPRECT lpTargetRect)
* Input:
*  lpwd - pointer to the WINDRAW structure
*  lpGDISurface - a structure containing DIBSECTION of image to blit
*  lpSourceRect - a source image region to blit
*  lpTargetRect - a target window region to fill with image
* Returns:
*  TRUE, if success; FALSE, otherwise.
*/
static BOOL GDISurface_Blt (LPWINDRAW lpwd, LPGDISURFACE lpGDISurface,
			    LPRECT lpSourceRect, LPRECT lpTargetRect)
{
    BOOL bResult;                   /* return value             */
    HBITMAP hOldBitmap;             /* store the old bitmap     */
    
    /* get sizes of source/destination rectangles: */
    LONG lTargetWidth  = lpTargetRect->right  - lpTargetRect->left;
    LONG lTargetHeight = lpTargetRect->bottom - lpTargetRect->top;
    LONG lSourceWidth  = lpSourceRect->right  - lpSourceRect->left;
    LONG lSourceHeight = lpSourceRect->bottom - lpSourceRect->top;
    
    /* select bitmap to blit: */
    hOldBitmap = (HBITMAP) SelectObject (lpwd->gdi.hMemoryDC, lpGDISurface->hBitMap);
    
    if (lpGDISurface->DibSection.dsBmih.biBitCount <= 8)
    {
	SetDIBColorTable(lpwd->gdi.hMemoryDC, 0, nSystemColors, SystemPaletteRGB);
    }

    /* is the window the same size as the video: */
    if (lTargetWidth == lSourceWidth && lTargetHeight == lSourceHeight) {
	
	/* put the image straight into the window: */
        bResult = BitBlt (
            lpwd->gdi.hDC,	    /* target device HDC        */
            lpTargetRect->left,     /* x sink position          */
            lpTargetRect->top,      /* y sink position          */
            lTargetWidth,           /* destination width        */
            lTargetHeight,          /* destination height       */
            lpwd->gdi.hMemoryDC,    /* source device context    */
            lpSourceRect->left,     /* x source position        */
            lpSourceRect->top,      /* y source position        */
            SRCCOPY);               /* simple copy              */
	
    } else {
        /* stretch the image when copying to the window: */
        bResult = StretchBlt (
            lpwd->gdi.hDC,	    /* target device HDC        */
            lpTargetRect->left,     /* x sink position          */
            lpTargetRect->top,      /* y sink position          */
            lTargetWidth,           /* destination width        */
            lTargetHeight,          /* destination height       */
            lpwd->gdi.hMemoryDC,    /* source device HDC        */
            lpSourceRect->left,     /* x source position        */
            lpSourceRect->top,      /* y source position        */
            lSourceWidth,           /* source width             */
            lSourceHeight,          /* source height            */
            SRCCOPY);               /* simple copy              */
    }
    /* put the old bitmap back into the device context so we don't leak */
    SelectObject (lpwd->gdi.hMemoryDC, hOldBitmap);
    /* pass the result of Bit/StretchBlt: */
    return bResult;
}

/*
* Creates a new DD surface to be used for image data.
* Use:
*   HRESULT WinDraw2_CreateDDSurface(LPWINDRAW lpwd, LPWINDRAWSURFACE lpwds,
*    LPBMI lpImageFormat, DWORD fSurfaceType, int nBackBuffers, DWORD dwCKey);
* Input:
*  lpwd - pointer to a base WINDRAW structure
*  lpwds - pointer to a WINDRAWSURFACE structure to initialize
*  lpImageFormat - pointer to BITMAPINFO structure describing image format
*  fSurfaceType - combination of desired properties for a surface
*  nBackBuffers - number of flippable backbuffers to allocate for this surface
*  dwCKey - a color key to use (if transparent surface)
* Returns:
*  NOERROR if OK, or the last relevant DirectDraw error code, or E_FAIL.
*/
HRESULT WinDraw2_CreateDDSurface (LPWINDRAW lpwd, LPWINDRAWSURFACE lpwds,
				  LPBMI lpImageFormat, DWORD fSurfaceType, int nBackBuffers, DWORD dwCKey)
{
   /*
    *  Are we in windraw Mode?
    */
    
    if (!(lpwd->fMode & WINDRAW_DIRECTDRAW))
        return E_INVALIDARG;
    
    /* Is the color format supported by direct draw? */
    
    unsigned int surfaceCID = GetBitmapColor((LPBITMAPINFO) lpImageFormat);
    
    int directDrawSupported = 0;
    
    for(unsigned int i = 0; i<lpwd->numCodes; i++)
    {
	if (surfaceCID == lpwd->lpCID[i])
	{
	    directDrawSupported = 1;
	    break;
	}
    }
    
    /*
    if (!IsGDI(lpwds->cidSurfaceColor) && !directDrawSupported)
    return E_FAIL;
    */
    
    /* Create the surface  */
    DDSURFACEDESC ddsd;
    
    ZeroMemory(&ddsd, sizeof(ddsd));
    ddsd.dwSize		= sizeof (DDSURFACEDESC);
    ddsd.dwFlags	= DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH; 
    ddsd.dwWidth	= lpImageFormat->bmiHeader.biWidth;
    ddsd.dwHeight	= lpImageFormat->bmiHeader.biHeight;
    
    if (IsYUV(surfaceCID))
    {
	ddsd.dwFlags |= DDSD_PIXELFORMAT;
	ddsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
	SetDirectDrawColor(&ddsd.ddpfPixelFormat, surfaceCID);
    }

    /*
    *  Which type of surface was requested?
    */
    if (fSurfaceType & WINDRAWSURFACE_OVERLAY)
    {
	ddsd.ddsCaps.dwCaps = DDSCAPS_OVERLAY; 
	
	if (nBackBuffers)
	{
	    ddsd.dwFlags	    |= DDSD_BACKBUFFERCOUNT;
	    ddsd.dwBackBufferCount  = nBackBuffers;
	    ddsd.ddsCaps.dwCaps	    = DDSCAPS_OVERLAY | DDSCAPS_FLIP | DDSCAPS_COMPLEX; 
	}
    }
    else
    {
	ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
    }
    
    /*
    * Which type of memory do you want?
    */
    if (fSurfaceType & WINDRAWSURFACE_VIDEOMEMORY)
    {
	ddsd.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY;
    }
    else if (fSurfaceType & WINDRAWSURFACE_NONLOCALVIDMEM)
    {
	ddsd.ddsCaps.dwCaps |= DDSCAPS_NONLOCALVIDMEM;
    }
    // do not specify memory type for WINDRAWSURFACE_DEFAULTMEMORY
    else if (!(fSurfaceType & WINDRAWSURFACE_DEFAULTMEMORY)) 
    {
	ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
    }

    int res;

    try
    {

    res = lpwd->dd.lpDD2->CreateSurface(&ddsd, &lpwds->dd.lpDDSurface, NULL);

    }
    catch (...)
    {
        char szTmp[256]; /* Flawfinder: ignore */
        sprintf(szTmp, "WinDraw2_CreateDDSurface w %ld h %ld  cid %ld bb %ld caps %lx", /* Flawfinder: ignore */
                        ddsd.dwWidth, ddsd.dwHeight, surfaceCID, nBackBuffers, ddsd.ddsCaps.dwCaps);
    
        DumpDDInfo(lpwd, lpwds->dd.lpDDSurface, szTmp);
        res = HXR_FAIL;
    }


    if (DD_OK != res)
    {
	lpwds->dd.lpDDSurface = NULL;
	return E_FAIL;
    }
    
    lpwds->dwBackBufferCount = nBackBuffers;
    lpwds->fMode = WINDRAWSURFACE_OPENED | fSurfaceType;
    lpwds->cidSurfaceColor = surfaceCID;
    memcpy(&lpwds->bmiSurfaceFormat, lpImageFormat, sizeof(BMI)); /* Flawfinder: ignore */
    
    /* check to see if we have an attached surface */
    lpwds->dd.lpDDBackBuffer = NULL; /* just in case !! */
    if (nBackBuffers)
    {
        DDSCAPS ddscaps;
        ddscaps.dwCaps = DDSCAPS_BACKBUFFER;

        // Build flipping chain - set surfaces
        lpwds->dd.lpChain = new ENUMSURFACE[nBackBuffers+1];

        lpwds->dwFrontBuffer = 0;
        lpwds->dwNextBuffer = 1;

        lpwds->hOverlayIndexMutex = CreateMutex(NULL, FALSE, NULL);
        lpwds->hOverlayMutex = CreateMutex(NULL, FALSE, NULL);
        lpwds->dd.hAbort = CreateEvent(NULL, TRUE, FALSE, NULL);

        lpwds->dd.lpChain[0].lpSurface = lpwds->dd.lpDDSurface;

        lpwds->dd.lpDDSurface->GetAttachedSurface(&ddscaps, &lpwds->dd.lpDDBackBuffer);
        lpwds->dd.lpDDBackBuffer->Release();


        if (nBackBuffers > 1)
        {
            for (int i=1; i<nBackBuffers+1; i++)
            {
                lpwds->dd.lpChain[i-1].lpSurface->EnumAttachedSurfaces((void*)&lpwds->dd.lpChain[i].lpSurface, EnumSurfacesCallback);
            }
        }
        else
        {
            lpwds->dd.lpChain[1].lpSurface = lpwds->dd.lpDDBackBuffer;
        }

        // Build flipping chain - set hw mem locations
        for (int i=0; i<nBackBuffers+1; i++)
        {
            memset(&ddsd, 0, sizeof(ddsd));
            ddsd.dwSize = sizeof(ddsd);
            lpwds->dd.lpChain[i].hEmpty = CreateEvent(NULL, TRUE, TRUE, NULL);
            lpwds->dd.lpChain[i].pHwMemBuffer = NULL;
            lpwds->dd.lpChain[i].dTimeAvailable = 0.0;
        }
    }
    
    lpwd->m_pSurfaceList->AddTail(lpwds);

    WinDraw2_OnPaletteChange(lpwd, lpwd->hWnd, WM_PALETTECHANGED);
    
    // Store the default cc values
    memset(&lpwds->dd.ddcc, 0, sizeof(lpwds->dd.ddcc));
    lpwds->dd.ddcc.dwSize = sizeof(lpwds->dd.ddcc);
    
    IDirectDrawColorControl *pcc = NULL;
    res = NOERROR;

    try
    {

    lpwds->dd.lpDDSurface->QueryInterface(IID_IDirectDrawColorControl, (void**)&pcc);
    if (pcc)
    {
        pcc->GetColorControls(&lpwds->dd.ddcc);
        pcc->Release();
    }

    }
    catch (...)
    {
        char szTmp[256]; /* Flawfinder: ignore */
        sprintf(szTmp, "WinDraw2_CreateDDSurface w %ld h %ld  cid %ld bb %ld caps %lx", /* Flawfinder: ignore */
                        ddsd.dwWidth, ddsd.dwHeight, surfaceCID, nBackBuffers, ddsd.ddsCaps.dwCaps);
        
        DumpDDInfo(lpwd, lpwds->dd.lpDDSurface, szTmp);
        
        HX_RELEASE(pcc);
        memset(&lpwds->dd.ddcc, 0, sizeof(lpwds->dd.ddcc));
    }

    return res;
}

HRESULT WinDraw2_SetColorKey (LPWINDRAW lpwd, DWORD dwLowColor, DWORD dwHighColor)
{
    HRESULT hr = E_FAIL;

    if (lpwd == NULL)
    {
        HX_ASSERT(0);
        return E_INVALIDARG;
    }

    if (lpwd->fMode & WINDRAW_DIRECTDRAW)
    {
        DDCOLORKEY ddColorKey;

        /* convert color key values: */
        ddColorKey.dwColorSpaceLowValue  = dwLowColor;
        ddColorKey.dwColorSpaceHighValue = dwHighColor;

        EnterCriticalSection(&lpwd->csPrimary);
        /* set color key: */
        if (lpwd->dd.lpDDSPrimary)
        {
	    try
            {
            hr = lpwd->dd.lpDDSPrimary->SetColorKey(DDCKEY_DESTOVERLAY, &ddColorKey);
            }
            catch(...)
            {
                DumpDDInfo(lpwd, lpwd->dd.lpDDSPrimary, "WinDraw2_SetColorKey");
                hr = HXR_FAIL;
            }
        }

        LeaveCriticalSection(&lpwd->csPrimary);

    }
    return hr;
}

HRESULT WinDraw2_SetOverlayPosition(LPWINDRAW lpwd, LPWINDRAWSURFACE lpwds, UINT32 x, UINT32 y)
{
    HRESULT hr = E_FAIL;

    if (lpwd == NULL)
    {
        HX_ASSERT(0);
        return E_INVALIDARG;
    }
    
    if (lpwds->fMode & WINDRAWSURFACE_OPENED && lpwds->fMode & WINDRAWSURFACE_OVERLAY)
    {
        try
        {

        hr = lpwds->dd.lpDDSurface->SetOverlayPosition(x,y);

        }
        catch(...)
        {
            DumpDDInfo(lpwd, lpwds->dd.lpDDSurface, "WinDraw2_SetOverlayPosition");
            hr = HXR_FAIL;
        }

    }
    return hr;
}

HRESULT WinDraw2_UpdateOverlay(LPWINDRAW lpwd, LPWINDRAWSURFACE lpwds, RECT* pDestRect, RECT* pSrcRect, DWORD flags)
{
//      {
//          char szBuff[256];
//          sprintf( szBuff,"UpdateOverlay src (%d, %d)-(%d, %d)  dest(%d, %d)-(%d, %d) flags: %p\n",
//                   pSrcRect->left,
//                   pSrcRect->top,
//                   pSrcRect->right,
//                   pSrcRect->bottom,
//                   pDestRect->left,
//                   pDestRect->top,
//                   pDestRect->right,
//                   pDestRect->bottom, 
//  		 flags
//                   );
//          _DumpString(szBuff);
//      } 

    if ((lpwd == NULL) | (lpwds == NULL))
    {
        HX_ASSERT(0);
        return E_INVALIDARG;
    }
    
    if (lpwds->fMode & WINDRAWSURFACE_OPENED && lpwds->fMode & WINDRAWSURFACE_OVERLAY)
    {
        EnterCriticalSection(&lpwd->csPrimary);
        HRESULT res;

        try
        {

        if (lpwds->dd.lpDDSurface)
        {
            res = lpwds->dd.lpDDSurface->UpdateOverlay(pSrcRect, lpwd->dd.lpDDSPrimary, pDestRect, flags, NULL);
            if (DDERR_SURFACELOST == res)
            {
                res = RestoreSurfaces(lpwd, lpwds);

                if (res == DD_OK)
                {
                    lpwds->dd.lpDDSurface->UpdateOverlay(pSrcRect, lpwd->dd.lpDDSPrimary, pDestRect, flags, NULL);

                    // Let caller know we lost our surfaces
                    res = DDERR_SURFACELOST;
                }
            }
        }

        }
        catch(...)
        {
            char szTmp[256]; /* Flawfinder: ignore */
            sprintf(szTmp, "WinDraw2_UpdateOverlay srcRect %ld %ld %ld %ld dstRc %ld %ld %ld %ld", /* Flawfinder: ignore */
                           pSrcRect->left, pSrcRect->top, pSrcRect->right, pSrcRect->bottom,
                           pDestRect->left, pDestRect->top, pDestRect->right, pDestRect->bottom);
            
            DumpDDInfo(lpwd, lpwds->dd.lpDDSurface, szTmp);
            res = HXR_FAIL;
        }

        LeaveCriticalSection(&lpwd->csPrimary);

        return res;
    }
    return E_FAIL;
}

HRESULT WinDraw2_GetOverlayPosition(LPWINDRAW lpwd, LPWINDRAWSURFACE lpwds, POINT* pLocation)
{
    HRESULT hr = E_FAIL;

    if ((lpwd == NULL) | (lpwds == NULL))
    {
        HX_ASSERT(0);
        return E_INVALIDARG;
    }
    
    if (lpwds->fMode & WINDRAWSURFACE_OPENED && lpwds->fMode & WINDRAWSURFACE_OVERLAY)
    {
	try
        {

        hr = lpwds->dd.lpDDSurface->GetOverlayPosition((LONG*) &pLocation->x, (LONG*) &pLocation->y);

        }
        catch(...)
        {
            DumpDDInfo(lpwd, lpwds->dd.lpDDSurface, "WinDraw2_GetOverlayPosition");
            hr = HXR_FAIL;
        }
    }
    return hr;
}

BOOL WinDraw2_IsSurfaceVisible(LPWINDRAW lpwd, LPWINDRAWSURFACE lpwds)
{
    if ((lpwd == NULL) | (lpwds == NULL))
    {
        HX_ASSERT(0);
        return E_INVALIDARG;
    }
    
    if (lpwds->fMode & WINDRAWSURFACE_OPENED && 
        lpwds->fMode & WINDRAWSURFACE_OVERLAY &&
        lpwds->dd.lpDDSurface)
    {
	DDSCAPS ddcaps;
	memset(&ddcaps, 0, sizeof(DDSCAPS));
	
	try
        {
        lpwds->dd.lpDDSurface->GetCaps(&ddcaps);
        }
        catch(...)
        {
            DumpDDInfo(lpwd, lpwds->dd.lpDDSurface, "WinDraw2_IsSurfaceVisible");
            return FALSE;
        }
	
        if (DDSCAPS_VISIBLE & ddcaps.dwCaps)
	{
	    return TRUE;
	}
    }
    return FALSE;
}

BOOL WinDraw2_IsDDAvailable(LPWINDRAW lpwd)
{
    if (lpwd &&
        lpwd->fMode & WINDRAW_OPENED && 
        lpwd->fMode & WINDRAW_DIRECTDRAW &&
        lpwd->dd.lpDD2)
    {
        BOOL bRet = TRUE;
        
#if DIRECTDRAW_VERSION > 0x0500        
        IDirectDraw4 *pDD4 = NULL;
        lpwd->dd.lpDD2->QueryInterface(IID_IDirectDraw4, (void**)&pDD4);

        // First, check if another app has DD in exclusive mode
        if (pDD4)
        {
            try
            {
            HRESULT hr = pDD4->TestCooperativeLevel();
            bRet = !(hr == DDERR_EXCLUSIVEMODEALREADYSET);

            }
            catch(...)
            {
                DumpDDInfo(lpwd, lpwd->dd.lpDDSPrimary, "WinDraw2_IsDDAvailable");
                bRet = TRUE;
            }

            pDD4->Release();
        }
#endif //DIRECTDRAW_VERSION > 0x0500

        // Check if we have hardware assisted blitting
        if (bRet)
        {
            UINT32  ulCaps = 0,
                    ulTemp;
            if (HXR_OK == Windraw_GetCaps(lpwd, &ulCaps, &ulTemp, &ulTemp))
                bRet = (ulCaps & HX_OVERLAY | ulCaps & HX_BLTFOURCC);
        }

        return bRet;
    }
    
    return FALSE;
}


/*
* Creates a new GDI surface to be used for image data.
* Use:
*   HRESULT WinDraw2_CreateGDISurface(LPWINDRAW lpwd, LPWINDRAWSURFACE lpwds,
*    LPBMI lpImageFormat, DWORD fSurfaceType, int nBackBuffers, DWORD dwCKey);
* Input:
*  lpwd - pointer to a base WINDRAW structure
*  lpwds - pointer to a WINDRAWSURFACE structure to initialize
*  lpImageFormat - pointer to BITMAPINFO structure describing image format
*  fSurfaceType - combination of desired properties for a surface
*  nBackBuffers - number of flippable backbuffers to allocate for this surface
*  dwCKey - a color key to use (if transparent surface)
* Returns:
*  NOERROR if OK, or the last relevant DirectDraw error code, or E_FAIL.
*/
HRESULT WinDraw2_CreateGDISurface (LPWINDRAW lpwd, LPWINDRAWSURFACE lpwds,
				   LPBMI lpImageFormat, DWORD fSurfaceType, int nBackBuffers, DWORD dwCKey)
{
    int i;
    
    if (lpwd->fMode & WINDRAW_DIRECTDRAW)
        return E_INVALIDARG;
    
    /* check if surface of given color format can be created: */
    if (!IsGDI(lpwds->cidSurfaceColor))
        return E_FAIL;
    
    /* allocate GDI surfaces: */
    for (i=0; i<=(int)lpwds->dwBackBufferCount; i++) 
    {
        LPGDISURFACE lpgdis;
	
        /* allocate a new surface: */
        if ((lpgdis = GDISurface_Alloc ((LPBITMAPINFO)lpImageFormat)) == NULL)
            goto release_buffers;
	
        /* store pointer to a new surface */
        lpwds->gdi.lpGDIBackBuffer [i] = lpgdis;
    }
    
    /* store surface parameters & exit: */
    lpwds->fMode |= WINDRAWSURFACE_OPENED;
    lpwds->gdi.lPitch = GetBitmapPitch ((LPBITMAPINFO)lpImageFormat);
    lpwd->m_pSurfaceList->AddTail(lpwds);
    WinDraw2_OnPaletteChange(lpwd, lpwd->hWnd, WM_PALETTECHANGED);
    return NOERROR;
    
release_buffers:
    /* release GDI buffers: */
    for ( i--; i>=0; i--) 
    {
        if (lpwds->gdi.lpGDIBackBuffer [i] != NULL) 
	{
            GDISurface_Free(lpwds->gdi.lpGDIBackBuffer [i]);
            lpwds->gdi.lpGDIBackBuffer [i] = NULL;
        }
    }
    
    return E_FAIL;
}


/*
* Creates a new surface to be used for image data.
* Use:
*   HRESULT WinDraw2_CreateSurface (LPWINDRAW lpwd, LPWINDRAWSURFACE lpwds,
*    LPBMI lpImageFormat, DWORD fSurfaceType, int nBackBuffers, DWORD dwCKey);
* Input:
*  lpwd - pointer to a base WINDRAW structure
*  lpwds - pointer to a WINDRAWSURFACE structure to initialize
*  lpImageFormat - pointer to BITMAPINFO structure describing image format
*  fSurfaceType - combination of desired properties for a surface
*  nBackBuffers - number of flippable backbuffers to allocate for this surface
*  dwCKey - a color key to use (if transparent surface)
* Returns:
*  NOERROR if OK, or the last relevant DirectDraw error code, or E_FAIL.
*/
HRESULT WinDraw2_CreateSurface (LPWINDRAW lpwd, LPWINDRAWSURFACE lpwds,
				LPBMI lpImageFormat, DWORD fSurfaceType, int nBackBuffers, DWORD dwCKey)
{
    int sz;
    
    /* check parameters: */
    if (lpwd == NULL || lpwds == NULL || lpImageFormat == NULL ||
        nBackBuffers > MAX_BACKBUFFERCOUNT || lpwd->hWnd == NULL ||
        !(lpwd->fMode & WINDRAW_OPENED) || (lpwds->fMode & WINDRAWSURFACE_OPENED))
        return E_INVALIDARG;
    
    /* check image format: */
    sz = lpImageFormat->bmiHeader.biSize;
    memset (lpwds, 0, sizeof (WINDRAWSURFACE));
    if (sz < (int)sizeof(BITMAPINFOHEADER) || sz > (int)sizeof(BMI) ||
        (lpwds->cidSurfaceColor = GetBitmapColor((LPBITMAPINFO)lpImageFormat)) == CID_UNKNOWN)
        return E_INVALIDARG;
    
    /* initialize WinDrawSurface structure: */
    memcpy (&(lpwds->bmiSurfaceFormat), lpImageFormat, sz); /* Flawfinder: ignore */
    lpwds->dwBackBufferCount = nBackBuffers;
    
    /* check type of surface we will need to create: */
    if (fSurfaceType & WINDRAWSURFACE_DIRECTDRAW)
    {
	return WinDraw2_CreateDDSurface(lpwd, lpwds, lpImageFormat, fSurfaceType, nBackBuffers, dwCKey);
    }
    else
    {
	return WinDraw2_CreateGDISurface(lpwd, lpwds, lpImageFormat, fSurfaceType, nBackBuffers, dwCKey);
    }
}



/*
* Creates a new surface to be used for image data.
* Use:
*   HRESULT WinDraw2_CreateSurface (LPWINDRAW lpwd, LPWINDRAWSURFACE lpwds,
*    LPBMI lpImageFormat, DWORD fSurfaceType, int nBackBuffers, DWORD dwCKey);
* Input:
*  lpwd - pointer to a base WINDRAW structure
*  lpwds - pointer to a WINDRAWSURFACE structure to initialize
*  lpImageFormat - pointer to BITMAPINFO structure describing image format
*  fSurfaceType - combination of desired properties for a surface
*  nBackBuffers - number of flippable backbuffers to allocate for this surface
*  dwCKey - a color key to use (if transparent surface)
* Returns:
*  NOERROR if OK, or the last relevant DirectDraw error code, or E_FAIL.
*/
HRESULT Windraw_AttachSurface(LPWINDRAW lpwd, LPWINDRAWSURFACE lpwds,
			      BITMAPINFOHEADER* lpImageFormat, void* pImageData)
{
    HRESULT retVal = E_INVALIDARG;
    
    
    /* check parameters: */
    if (lpwd == NULL || lpwds == NULL || lpImageFormat == NULL ||
        lpwd->hWnd == NULL || !(lpwd->fMode & WINDRAW_OPENED) || 
	!(lpwds->fMode & WINDRAWSURFACE_OPENED))
        return retVal;
    
    /* check type of surface we will need to create: */
    if (lpwds->fMode & WINDRAWSURFACE_DIRECTDRAW)
    {
	DDSURFACEDESC ddsd;
	ZeroMemory(&ddsd, sizeof(ddsd));
	ddsd.dwSize	= sizeof(ddsd);
	ddsd.dwFlags	= DDSD_WIDTH | DDSD_HEIGHT | DDSD_PITCH | DDSD_LPSURFACE | DDSD_PIXELFORMAT;
	ddsd.dwHeight	= lpImageFormat->biHeight;
	ddsd.dwWidth	= lpImageFormat->biWidth;
	ddsd.lPitch	= GetBitmapPitch((LPBITMAPINFO)lpImageFormat);
	ddsd.lpSurface	= pImageData;
	
	SetDirectDrawColor(&ddsd.ddpfPixelFormat, GetBitmapColor((LPBITMAPINFO)lpImageFormat));
	
	IDirectDrawSurface3* pSurf3 = NULL;
	
	if (SUCCEEDED(lpwds->dd.lpDDSurface->QueryInterface(IID_IDirectDrawSurface3, (void**)&pSurf3)))
	{
	    try
            {
            retVal = pSurf3->SetSurfaceDesc(&ddsd, 0);
            }

            catch(...)
            {
                DumpDDInfo(lpwd, lpwds->dd.lpDDSurface, "Windraw_AttachSurface");
                retVal = HXR_FAIL;
            }
	    pSurf3->Release();
	}
	return retVal;
    }
    else
    {
	return retVal;
    }
}

/*
* Adds alpha data to a specified surface.
* Use:
*   HRESULT WinDraw2_AttachAlphaSurface (LPWINDRAW lpwd, LPWINDRAWSURFACE lpwds,
*    UCHAR* pAlphaData);
* Input:
*  lpwd	- pointer to a base WINDRAW structure
*  lpwds	- pointer to a WINDRAWSURFACE structure to initialize
* Returns:
*  NOERROR if OK, or the last relevant DirectDraw error code, or E_FAIL.
*/

HRESULT WinDraw2_CreateAlphaSurface(LPWINDRAW lpwd, LPWINDRAWSURFACE lpwds)
{
    /* check parameters: */
    if (lpwd == NULL || lpwds == NULL || lpwd->hWnd == NULL ||
        !(lpwd->fMode & WINDRAW_OPENED) || (!(lpwds->fMode & WINDRAWSURFACE_OPENED)))
        return E_INVALIDARG;
    
    if (lpwds->fMode & WINDRAWSURFACE_DIRECTDRAW) 
    {
        /* blit DirectDraw surfaces: */ ;
    } else 
    { /* GDI: */
	GDISURFACE* pGDISurf = lpwds->gdi.lpGDIBackBuffer [FRONTBUFFER];
        pGDISurf->lpAlphaSurface = (UCHAR*) malloc(pGDISurf->DibSection.dsBmih.biWidth * pGDISurf->DibSection.dsBmih.biHeight);
    }
    
    return NOERROR;
}

/*
* Adds alpha data to a specified surface.
* Use:
*   HRESULT WinDraw2_AttachAlphaSurface (LPWINDRAW lpwd, LPWINDRAWSURFACE lpwds,
*    UCHAR* pAlphaData);
* Input:
*  lpwd	- pointer to a base WINDRAW structure
*  lpwds	- pointer to a WINDRAWSURFACE structure to initialize
* Returns:
*  NOERROR if OK, or the last relevant DirectDraw error code, or E_FAIL.
*/

HRESULT WinDraw2_GetAlphaSurface(LPWINDRAW lpwd, LPWINDRAWSURFACE lpwds, UCHAR** pSurface)
{
    /* check parameters: */
    if (lpwd == NULL || lpwds == NULL || lpwd->hWnd == NULL ||
        !(lpwd->fMode & WINDRAW_OPENED) || (!(lpwds->fMode & WINDRAWSURFACE_OPENED)))
        return E_INVALIDARG;
    
    if (lpwds->fMode & WINDRAWSURFACE_DIRECTDRAW) 
    {
        /* blit DirectDraw surfaces: */ ;
    } else 
    { /* GDI: */
	GDISURFACE* pGDISurf = lpwds->gdi.lpGDIBackBuffer [FRONTBUFFER];
        *pSurface = pGDISurf->lpAlphaSurface;
    }
    
    return NOERROR;
}

/*
* Release a WinDraw surface.
* Use:
*  HRESULT WinDraw2_ReleaseSurface (LPWINDRAW lpwd, LPWINDRAWSURFACE lpwds);
* Input:
*  lpwd - pointer to a base WINDRAW structure
*  lpwds - pointer to a WINDRAWSURFACE structure to remove
* Returns:
*  NOERROR if OK, or the last relevant DirectDraw error code, or E_FAIL.
*/
HRESULT WinDraw2_ReleaseSurface (LPWINDRAW lpwd, LPWINDRAWSURFACE lpwds)
{
    /* check parameters: */
    if (lpwd == NULL || lpwds == NULL || lpwd->hWnd == NULL ||
        !(lpwd->fMode & WINDRAW_OPENED) || !(lpwds->fMode & WINDRAWSURFACE_OPENED))
        return E_INVALIDARG;
    
    /* check if DirectDraw surface: */
    if (lpwds->fMode & WINDRAWSURFACE_DIRECTDRAW) 
    {
        if (lpwds->dd.lpChain)
        {
            for (int i=0; i<(int)lpwds->dwBackBufferCount+1; i++)
            {
                // Wait until the flip has been called on this surface
                WaitForSingleObject(lpwds->dd.lpChain[i].hEmpty, INFINITE);
                CloseHandle(lpwds->dd.lpChain[i].hEmpty);
            }

            CloseHandle(lpwds->dd.hAbort);
            lpwds->dd.hAbort = 0;

            delete [] lpwds->dd.lpChain;
            lpwds->dd.lpChain = NULL;

        CloseHandle(lpwds->hOverlayIndexMutex);
        CloseHandle(lpwds->hOverlayMutex);
        lpwds->hOverlayIndexMutex = 0;
        lpwds->hOverlayMutex = 0;
        lpwds->dwFrontBuffer = 0;
        lpwds->dwNextBuffer = 0;

        }

        /* release DirectDraw objects: */ ;
	int res;
        
        try
        {
        res = lpwds->dd.lpDDSurface->Release();
        }
        catch(...)
        {
            DumpDDInfo(lpwd, lpwds->dd.lpDDSurface, "WinDraw2_ReleaseSurface");
            res = 0;
        }
	
	lpwds->dd.lpDDSurface = 0;
	lpwds->dd.lpDDBackBuffer = 0;

    } else 
    { /* GDI: */
        int i;
        /* release GDI surfaces: */
        for (i=lpwds->dwBackBufferCount; i>=0; i--) 
	{
            if (lpwds->gdi.lpGDIBackBuffer [i] != NULL) {
                GDISurface_Free(lpwds->gdi.lpGDIBackBuffer [i]);
                lpwds->gdi.lpGDIBackBuffer [i] = NULL;
            }
        }
    }
    
    /* reset mode & exit: */
    lpwds->fMode = 0;
    
    LISTPOSITION pos = lpwd->m_pSurfaceList->Find(lpwds);
    if (pos)
        lpwd->m_pSurfaceList->RemoveAt(pos);

	lpwds->dwBackBufferCount = 0;
    
    return NOERROR;
}

HRESULT WinDraw2_GetMonitorProperties (LPWINDRAW lpwd)
{
    HRESULT hr;
    DWORD dwTemp = 0;

    if (!lpwd || !lpwd->dd.lpDD2)
        return E_FAIL;

    lpwd->ulBadConsecutiveVblankCount = 0;
    lpwd->dwMaxScanLine = 0;
    lpwd->dwReportedHeight = GetSystemMetrics(SM_CYSCREEN);

    LARGE_INTEGER   llStart,
                    llTime,
                    llTemp;

    llStart.QuadPart = 0;
    llTime.QuadPart = 0;
    llTemp.QuadPart = 0;

    QueryPerformanceFrequency(&llTemp);
    LONGLONG llClockFreq = llTemp.QuadPart;

    // Try to get the max scan line but limit the time spent
    // in the loop.  Some cards always return 0 from GetScanLine.
    QueryPerformanceCounter(&llStart);

    do
    {
        hr = lpwd->dd.lpDD2->GetScanLine(&dwTemp);
        
        if (DD_OK == hr ||
            DDERR_VERTICALBLANKINPROGRESS == hr)
        {
            // The G400 occasionally reports an obscenely high value here;
            // we filter to 130% of the reported display height 
            if (dwTemp > lpwd->dwMaxScanLine 
                && dwTemp <= lpwd->dwReportedHeight*1.3)
                lpwd->dwMaxScanLine = dwTemp;
        }
        else
            break;

        QueryPerformanceCounter(&llTime);

        if ((llTime.QuadPart - llStart.QuadPart)*1000 / llClockFreq > 1000)
            break;

    } while (dwTemp >= lpwd->dwMaxScanLine);

    // GetScanLine problem
    if (!lpwd->dwMaxScanLine)
        return HXR_FAIL;

    // Set max scan line to a max of the line and screen screen height
    // we assume a minimum of one vblank line; max line is zero based
    lpwd->dwMaxScanLine = max(lpwd->dwMaxScanLine, (DWORD)GetSystemMetrics(SM_CYSCREEN));
    //HX_TRACE("Max scan line: %lu\n", lpwd->dwMaxScanLine);

    // Query the monitor's refresh rate
    lpwd->dd.lpDD2->GetMonitorFrequency(&dwTemp);
    //HX_TRACE("Reported refresh rate: %lu\n", dwTemp);

    if (dwTemp)
    {
        lpwd->dRefreshRate = dwTemp;
        lpwd->dMsPerVBlank = 1000.0 / lpwd->dRefreshRate;
    }

    // Approximate the refresh rate
    const int   LIST_SIZE = 6;
    LONGLONG    aList[LIST_SIZE],
                llAvgVBlank = 0;

    lpwd->dd.lpDD2->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN, NULL);
    QueryPerformanceCounter(&llStart);

    // Grab the times for LIST_SIZE vblanks
    for (int i=0; i<LIST_SIZE; i++)
    {
        hr = lpwd->dd.lpDD2->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN, NULL);

        if (DD_OK == hr)
        {
            QueryPerformanceCounter(&llTime);

            aList[i] = llTime.QuadPart;

            if (i>0)
            {
                llAvgVBlank += aList[i] - aList[i-1];
            }
        }
    }

    llAvgVBlank /= LIST_SIZE-1;

    LONGLONG    llModAvg = 0;
    int         nCount = 0;

    // Weed out bogus values
    for (i=0; i<LIST_SIZE; i++)
    {
        if (i>0)
        {
            if (abs((int)(aList[i] - aList[i-1] - llAvgVBlank)) <= .3 * llAvgVBlank)
            {
                ++nCount;
                llModAvg += aList[i] - aList[i-1];
            }
        }
    }

    LONGLONG    llTmp  = llStart.QuadPart,
                llTmp1 = llTime.QuadPart;

    double      dEstRate = (double)(nCount*llClockFreq) / (llModAvg);

    //HX_TRACE("Estimated refresh rate: %f\n", dEstRate);

    // Map to a common refresh rate
    const int   BASE_RATES = 5;
    double      dBestRate = -1;
    double      aRateList[BASE_RATES] = {24, 25, 29.97, 30, 85};
    double      dBestError = 100.0,
                dError;
            
    // Try 10x base rates
    for (int j=1; j<11; j++)
    {
        for (i=0; i<BASE_RATES; i++)
        {
            dError = dEstRate - aRateList[i]*j;
            if (fabs(dError) < dBestError)
            {
                dBestError = fabs(dError);
                dBestRate = aRateList[i]*j;
            }
        }
    }

    //HX_TRACE("Best refresh rate match: %f\n", dBestRate);

    // If we are within 1.5% if a known rate, use it.  This will not handle 
    // cases where the estimate is *closer* to reality than the reported rate 
    // due to integer truncation or rounding in GetMonitorFrequency().  
    // We do, however, handle 29.97 below.
    if (dBestRate > -1 && 
        dBestError / dBestRate <= .015)
        dEstRate = dBestRate;

    // Compare estimate with DD value and use estimate
    // if the the two are not close.
    if (fabs(dEstRate - lpwd->dRefreshRate) > 5
        || dEstRate == 29.97)       // Special case 29.97; Windows reports 
                                    // integer frequencies.
    {
        lpwd->dRefreshRate = dEstRate;
        lpwd->dMsPerVBlank = 1000.0 / lpwd->dRefreshRate;
    }

    //HX_TRACE("Final refresh rate chosen: %f\n", lpwd->dRefreshRate);

    return NOERROR;
}

/*
* Get WinDrawSurface capabilities.
* Use:
*  HRESULT WinDrawSurface_GetCaps (LPWINDRAW lpwd, LPWINDRAWSURFACE lpwds,
*      DWORD *lpdwCaps);
* Input:
*  lpwd - pointer to a WINDRAW structure to query
*  lpwds - pointer to a WINDRAWSURFACE structure to query
*  lpdwCaps - a variable to contain WinDrawSurface capabilities
* Returns:
*  NOERROR if OK, or the last relevant DirectDraw error code, or E_FAIL.
*/
HRESULT WinDrawSurface_GetCaps (LPWINDRAW lpwd, LPWINDRAWSURFACE lpwds,
				DWORD *lpdwCaps)
{
    /* check parameters: */
    if (lpwd == NULL || lpwds == NULL || lpdwCaps == NULL || lpwd->hWnd == NULL ||
        !(lpwd->fMode & WINDRAW_OPENED) || !(lpwds->fMode & WINDRAWSURFACE_OPENED))
        return E_INVALIDARG;
    
    /* store surface caps and exit: */
    *lpdwCaps = lpwds->fMode;
    return NOERROR;
}

HRESULT Windraw_GetCaps(LPWINDRAW lpwd, UINT32* pfSurfaceCaps, UINT32* nMin, UINT32* nMax)
{
    /* check parameters: */
    if (lpwd == NULL || pfSurfaceCaps == NULL || !(lpwd->fMode & WINDRAW_OPENED) || !(lpwd->fMode & WINDRAW_DIRECTDRAW))
        return E_INVALIDARG;
    
    HRESULT hr = NOERROR;
    	
    /* Get the current device caps */
    if (lpwd->dd.lpDD2)
    {
        memset(&lpwd->dd.m_caps, 0, sizeof(DDCAPS));
        lpwd->dd.m_caps.dwSize = sizeof(DDCAPS_DX3);
        
        try
        {
        lpwd->dd.lpDD2->GetCaps(&(lpwd->dd.m_caps), 0);
        }
        catch(...)
        {
            DumpDDInfo(lpwd, lpwd->dd.lpDDSPrimary, "Windraw_GetCaps");
            hr = HXR_FAIL;

            memset(&lpwd->dd.m_caps, 0, sizeof(DDCAPS));
        }
    }
    
    *pfSurfaceCaps = 0;
    if (lpwd->dd.m_caps.dwCaps & DDCAPS_OVERLAY)
    {
	*pfSurfaceCaps |= HX_OVERLAY;  
    }
    
    if (lpwd->dd.m_caps.dwCaps & DDCAPS_OVERLAYSTRETCH)
    {
	*pfSurfaceCaps |= HX_OVERLAY_STRETCH;  
    }
    
    if (lpwd->dd.m_caps.dwCaps & DDCAPS_BLTFOURCC)
    {
	*pfSurfaceCaps |= HX_BLTFOURCC;  
    }
    
    *nMin = lpwd->dd.m_caps.dwMinOverlayStretch;
    *nMax = lpwd->dd.m_caps.dwMaxOverlayStretch;
    
    return hr;
}

/*
* Get WinDrawSurface format.
* Use:
*  HRESULT WinDrawSurface_GetFormat (LPWINDRAW lpwd, LPWINDRAWSURFACE lpwds,
*      LPBMI lpbiSurfaceFormat);
* Input:
*  lpwd - pointer to a WINDRAW structure to query
*  lpwds - pointer to a WINDRAWSURFACE structure to query
*  lpbiFormat - a structure to contain surface resolution and color format
* Returns:
*  NOERROR if OK, or the last relevant DirectDraw error code, or E_FAIL.
*/
HRESULT WinDrawSurface_GetFormat (LPWINDRAW lpwd, LPWINDRAWSURFACE lpwds,
				  LPBMI lpbiSurfaceFormat)
{
    int sz;
    
    /* check parameters: */
    if (lpwd == NULL || lpwds == NULL ||
        !(lpwd->fMode & WINDRAW_OPENED) || !(lpwds->fMode & WINDRAWSURFACE_OPENED) ||
        lpbiSurfaceFormat == NULL)
        return E_INVALIDARG;
    
    /* check the size of BITMAPINFO structure to copy: */
    sz = lpbiSurfaceFormat->bmiHeader.biSize;
    if (sz < (int)sizeof(BITMAPINFOHEADER) || sz > (int)lpwds->bmiSurfaceFormat.bmiHeader.biSize)
        sz = lpwds->bmiSurfaceFormat.bmiHeader.biSize;
    
    /* copy bitmap info: */
    memcpy(lpbiSurfaceFormat, &(lpwds->bmiSurfaceFormat), sz); /* Flawfinder: ignore */
    return NOERROR;
}

/*
* Locks pointer to a buffer.
* Use:
*  HRESULT WinDrawSurface_Lock (LPWINDRAW lpwd, LPWINDRAWSURFACE lpwds,
*      int nBufferNo, LPVOID *lpSurfPtr, LONG  *lplSurfPitch);
* Input:
*  lpwd - pointer to a base WINDRAW structure
*  lpwds - pointer to a WINDRAWSURFACE structure to lock
*  nBufferNo - # of the surface buffer to lock
*  lpSurfPtr - a variable to contain a base pointer to the surface's data
*  lplSurfPitch - a variable to contain the pitch of the surface
* Returns:
*  NOERROR if OK, or the last relevant DirectDraw error code, or E_FAIL.
* Notes:
* This functions shall be called before copying data into the surface.
* Unlock it as soon as you are done with moving data in!!!
*/
HRESULT WinDrawSurface_Lock (LPWINDRAW lpwd, LPWINDRAWSURFACE lpwds,
			     int nBufferNo, LPVOID *lpSurfPtr, LONG  *lplSurfPitch)
{

    /* check parameters: */
    if (lpwd == NULL || lpwds == NULL || nBufferNo > (int)lpwds->dwBackBufferCount ||
        lpSurfPtr == NULL || lplSurfPitch == NULL || lpwd->hWnd == NULL ||
        !(lpwd->fMode & WINDRAW_OPENED) || !(lpwds->fMode & WINDRAWSURFACE_OPENED))
        return E_INVALIDARG;
    
    /* check if DirectDraw surface: */
    if (lpwds->fMode & WINDRAWSURFACE_DIRECTDRAW) 
    {
        /* lock DirectDraw surfaces: */
        DDSURFACEDESC ddsd;
        ZeroMemory(&ddsd, sizeof(DDSURFACEDESC));
        ddsd.dwSize = sizeof(DDSURFACEDESC);
        ddsd.dwFlags= DDSD_PITCH;
	
	LPDIRECTDRAWSURFACE lpDDSurface = NULL;
	if (lpwds->dd.lpDDBackBuffer)
	{
            lpDDSurface = lpwds->dd.lpDDBackBuffer;

            if (!lpDDSurface)
                return E_INVALIDARG;

            // For backbuffer locking, we need to keep our locks consistent
            // with videosurface2 locks since we are possibly here after a switch.
            if (DD_OK == LockSurface(lpwd, lpDDSurface, &ddsd, TRUE))
            {
                *lplSurfPitch = ddsd.lPitch;
                lpwds->dd.lpSurfaceData = *lpSurfPtr = (void*) ddsd.lpSurface;

                return NOERROR;
            }
            if (DDERR_WRONGMODE == lpDDSurface->Restore())
            {
                return DDERR_WRONGMODE;
            }

            if (DD_OK == LockSurface(lpwd, lpDDSurface, &ddsd, TRUE))
            {
                /*
                *  Consider checking DDSCAPS_VISIBLE if this is not set (and it is not on NT)
                *  then something is SERIOUSLY MESSED UP!
                */

                *lplSurfPitch = ddsd.lPitch;
                lpwds->dd.lpSurfaceData = *lpSurfPtr = (void*) ddsd.lpSurface;

                return NOERROR;
            }
        }
	else
	{
            lpDDSurface = lpwds->dd.lpDDSurface;

            if (!lpDDSurface)
                return E_INVALIDARG;

            HRESULT hr;
            try
            {
                hr = lpDDSurface->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL);
            }
            catch(...)
            {
                DumpDDInfo(lpwd, lpwds->dd.lpDDSurface, "WinDrawSurface_Lock");
                hr = HXR_FAIL;

                *lplSurfPitch = 0;
                lpwds->dd.lpSurfaceData = NULL;

                return hr;
            }

            if (DD_OK == hr)
            {
                /*
                *  Consider checking DDSCAPS_VISIBLE if this is not set (and it is not on NT)
                *  then something is SERIOUSLY MESSED UP!
                */

                *lplSurfPitch = ddsd.lPitch;
                lpwds->dd.lpSurfaceData = *lpSurfPtr = (void*) ddsd.lpSurface;

                return NOERROR;
            }

            if (DDERR_WRONGMODE == lpDDSurface->Restore())
            {
                return DDERR_WRONGMODE;
            }

            try
            {
            hr = lpDDSurface->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL);
            }
            catch(...)
            {
                DumpDDInfo(lpwd, lpwds->dd.lpDDSurface, "WinDrawSurface_Lock");
                hr = HXR_FAIL;

                *lplSurfPitch = 0;
                lpwds->dd.lpSurfaceData = NULL;

                return hr;
            }

            if (DD_OK == hr)
            {
                /*
                *  Consider checking DDSCAPS_VISIBLE if this is not set (and it is not on NT)
                *  then something is SERIOUSLY MESSED UP!
                */

                *lplSurfPitch = ddsd.lPitch;
                lpwds->dd.lpSurfaceData = *lpSurfPtr = (void*) ddsd.lpSurface;

                return NOERROR;
            }
        }
	
    } else { /* GDI: */
	
        /* check requested GDI surface: */
        if (lpwds->gdi.lpGDIBackBuffer [nBufferNo] != NULL) {
	    
            /* retrieve parameters and exit: */
            *lpSurfPtr = lpwds->gdi.lpGDIBackBuffer [nBufferNo]->lpBase;
            *lplSurfPitch = lpwds->gdi.lPitch;
            return NOERROR;
        }
    }
    return E_FAIL;
}

/*
* Unlocks the buffer.
* Use:
*  HRESULT WinDrawSurface_Lock (LPWINDRAW lpwd, LPWINDRAWSURFACE lpwds,
*      int nBufferNo, LPVOID *lpSurfPtr, LONG  *lplSurfPitch);
* Input:
*  lpwd - pointer to a base WINDRAW structure
*  lpwds - pointer to a WINDRAWSURFACE structure to unlock
*  nBufferNo - # of the surface buffer to unlock
* Returns:
*  NOERROR if OK, or the last relevant DirectDraw error code, or E_FAIL.
*/
HRESULT WinDrawSurface_Unlock (LPWINDRAW lpwd, LPWINDRAWSURFACE lpwds,
			       int nBufferNo)
{
    /* check parameters: */
    if (lpwd == NULL || lpwds == NULL ||
        nBufferNo > (int)lpwds->dwBackBufferCount || lpwd->hWnd == NULL ||
        !(lpwd->fMode & WINDRAW_OPENED) || !(lpwds->fMode & WINDRAWSURFACE_OPENED))
        return E_INVALIDARG;
    
    /* check if DirectDraw surface: */
    if (lpwds->fMode & WINDRAWSURFACE_DIRECTDRAW) 
    {
        /* unlock DirectDraw surfaces: */ ;
	
	LPDIRECTDRAWSURFACE lpDDSurface = NULL;
	if (lpwds->dd.lpDDBackBuffer)
	{
	    lpDDSurface = lpwds->dd.lpDDBackBuffer;
	}
	else
	{
	    lpDDSurface = lpwds->dd.lpDDSurface;
	}
	
	try
        {

        if (DD_OK == lpDDSurface->Unlock(lpwds->dd.lpSurfaceData))
	{
	    lpwds->dd.lpSurfaceData = NULL;
	    if (lpwds->dd.lpDDBackBuffer)
	    {
		HRESULT res = lpwds->dd.lpDDSurface->Flip(NULL, 0);
	    }
	    return NOERROR;
	}

        }
        catch(...)
        {
            DumpDDInfo(lpwd, lpwds->dd.lpDDSurface, "WinDrawSurface_Unlock");
            return HXR_FAIL;
        }
    } 
    else 
    { /* GDI: */
        /* check requested GDI surface: */
        if (lpwds->gdi.lpGDIBackBuffer [nBufferNo] != NULL) {
	    
            /* unlock GDI surface & exit: */
            return NOERROR;
        }
    }
    return E_FAIL;
}

/*
* Gives a DC to the surface.
* Use:
*  HRESULT WinDrawSurface_GetDC(LPWINDRAW lpwd, LPWINDRAWSURFACE lpwds,
*      HDC* pDC);
* Input:
*  lpwd    - pointer to a base WINDRAW structure
*  lpwds   - pointer to a WINDRAWSURFACE structure to unlock
*  HDC*    - pointer to a DC which will be created.
* Returns:
*  NOERROR if OK, or the last relevant DirectDraw error code, or E_FAIL.
*/

HRESULT WinDrawSurface_GetDC(LPWINDRAW lpwd, LPWINDRAWSURFACE lpwds,
			     HDC* pDC)
{
    /* check parameters: */
    if (lpwd == NULL || lpwds == NULL || lpwd->hWnd == NULL ||
        !(lpwd->fMode & WINDRAW_OPENED) || !(lpwds->fMode & WINDRAWSURFACE_OPENED))
        return E_INVALIDARG;
    
    /* check if DirectDraw surface: */
    if (lpwds->fMode & WINDRAWSURFACE_DIRECTDRAW) 
    {
        /* Get DC from Direct Draw: */ ;
	HRESULT hr;
        try
        {
        hr = lpwds->dd.lpDDSurface->GetDC(pDC);
        }
        catch(...)
        {
            DumpDDInfo(lpwd, lpwds->dd.lpDDSurface, "WinDrawSurface_GetDC");
            hr = HXR_FAIL;
        }

        return hr;
    } 
    else 
    { /* GDI: */
	lpwd->gdi.hOldBitmap = (HBITMAP) SelectObject(lpwd->gdi.hMemoryDC, lpwds->gdi.lpGDIBackBuffer[FRONTBUFFER]->hBitMap);
	*pDC = lpwd->gdi.hMemoryDC;
	return NOERROR;
    }
}

/*
* Releases a DC obtained by WinDrawSurface_GetDC
* Use:
*  HRESULT WinDrawSurface_ReleaseDC(LPWINDRAW lpwd, LPWINDRAWSURFACE lpwds,
*      HDC pDC);
* Input:
*  lpwd    - pointer to a base WINDRAW structure
*  lpwds   - pointer to a WINDRAWSURFACE structure to unlock
*  HDC     - pointer to a DC which is to be released.
* Returns:
*  NOERROR if OK, or the last relevant DirectDraw error code, or E_FAIL.
*/

HRESULT WinDrawSurface_ReleaseDC(LPWINDRAW lpwd, LPWINDRAWSURFACE lpwds,
				 HDC hDC)
{
    /* check parameters: */
    if (lpwd == NULL || lpwds == NULL || lpwd->hWnd == NULL ||
        !(lpwd->fMode & WINDRAW_OPENED) || !(lpwds->fMode & WINDRAWSURFACE_OPENED))
        return E_INVALIDARG;
    
    /* check if DirectDraw surface: */
    if (lpwds->fMode & WINDRAWSURFACE_DIRECTDRAW) 
    {
        /* Release DC through Direct Draw: */ ;
	HRESULT hr;
        try
        {
        hr = lpwds->dd.lpDDSurface->ReleaseDC(hDC);
        }
        catch(...)
        {
            DumpDDInfo(lpwd, lpwds->dd.lpDDSurface, "WinDrawSurface_ReleaseDC");
            hr = HXR_FAIL;
        }

        return hr;
    } 
    else 
    { /* GDI: */
	assert(lpwd->gdi.hMemoryDC == hDC);
	SelectObject (lpwd->gdi.hMemoryDC, lpwd->gdi.hOldBitmap);
	return NOERROR;
    }
}



/*
* Blits the surface data to the screen (primary surface).
* Use:
*  HRESULT WinDrawSurface_Blt (LPWINDRAW lpwd, LPWINDRAWSURFACE lpwds,
*      LPRECT lpDestRect, LPRECT lpSrcRect);
* Input:
*  lpwd - pointer to a base WINDRAW structure
*  lpwds - pointer to a WINDRAWSURFACE structure containing data to blit
*  lpDestRect - rectangle on the primary surface to be blitted to
*  lpSrcRect - rectangle on the source surface to be blitted from
* Returns:
*  NOERROR if OK, or the last relevant DirectDraw error code, or E_FAIL.
*/
HRESULT WinDrawSurface_Blt (LPWINDRAW lpwd, LPWINDRAWSURFACE lpwds,
			    LPRECT lpDestRect, LPRECT lpSrcRect, int nGdiBufferNo)
{
    /* check parameters: */
    if (lpwd == NULL || lpwds == NULL ||
        lpDestRect == NULL || lpSrcRect == NULL || lpwd->hWnd == NULL ||
        !(lpwd->fMode & WINDRAW_OPENED) || !(lpwds->fMode & WINDRAWSURFACE_OPENED))
        return E_INVALIDARG;
    
    
    /* check if DirectDraw surface: */
    if (lpwds->fMode & WINDRAWSURFACE_DIRECTDRAW) 
    {
        /* blit DirectDraw surfaces: */ ;
	if (lpwds->dd.lpDDSurface)
	{
        return DDSurface_Blt(lpwd, lpwds->dd.lpDDSurface, lpSrcRect, lpDestRect);
	}
    
    } else 
    { /* GDI: */
        /* check GDI front surface: */
        if (lpwds->gdi.lpGDIBackBuffer [nGdiBufferNo] != NULL) {
            /* blit GDI surface: */
            if (GDISurface_Blt (lpwd, lpwds->gdi.lpGDIBackBuffer [nGdiBufferNo], lpSrcRect, lpDestRect))
                return NOERROR;
        }
    }
    return E_FAIL;
}


/*
* Blits one secondary surface to another.
* Use:
*  HRESULT WinDrawSurface_BltIndirect (LPWINDRAW lpwd,
*      LPWINDRAWSURFACE lpwdsDestSurf, LPWINDRAWSURFACE lpwdsSrcSurf,
*      LPRECT lpDestRect, LPRECT lpSrcRect);
* Input:
*  lpwd - pointer to a base WINDRAW structure
*  lpwdsDestSurf - pointer to a destination WINDRAW structure
*  lpwdsSrcSurf - pointer to a source WINDRAW structure
*  lpDestRect - rectangle on the primary surface to be blitted to
*  lpSrcRect - rectangle on the source surface to be blitted from
* Returns:
*  NOERROR if OK, or the last relevant DirectDraw error code, or E_FAIL.
*/
HRESULT WinDrawSurface_BltIndirect (LPWINDRAW lpwd,
				    LPWINDRAWSURFACE lpwdsDestSurf, LPWINDRAWSURFACE lpwdsSrcSurf,
				    LPRECT lpDestRect, LPRECT lpSrcRect,int nGdiBufferNo)
{
    /* check parameters: */
    if (lpwd == NULL || lpwdsDestSurf == NULL || lpwdsSrcSurf == NULL ||
        lpDestRect == NULL || lpSrcRect == NULL || lpwd->hWnd == NULL ||
        !(lpwd->fMode & WINDRAW_OPENED) || !(lpwdsSrcSurf->fMode & WINDRAWSURFACE_OPENED)
	|| !(lpwdsDestSurf->fMode & WINDRAWSURFACE_OPENED))
        return E_INVALIDARG;
    
    /* check if DirectDraw surface: */
    if (lpwdsSrcSurf->fMode & WINDRAWSURFACE_DIRECTDRAW) 
    {
        /* blit DirectDraw surfaces: */ ;
	if (NOERROR == DDSurface_BltIndirect(lpwd, lpwdsDestSurf->dd.lpDDSurface, lpwdsSrcSurf->dd.lpDDSurface, lpDestRect, lpSrcRect))
	{
	    return NOERROR;
	}
	
    } else 
    { /* GDI: */
        if (GDISurface_BltIndirect(lpwd, lpwdsDestSurf->gdi.lpGDIBackBuffer [nGdiBufferNo], 
	    lpwdsSrcSurf->gdi.lpGDIBackBuffer [nGdiBufferNo], lpSrcRect, lpDestRect))
            return NOERROR;
    }
    return E_FAIL;
}

/*
* Get Description of all modes availiable on the system
* Use:
*  HRESULT WinDraw2_GetModes(LPWINDRAW lpwd, CModesDesc* pModesDesc, UINT32* nNumModes);
* Input:
*  lpwd - pointer to a WINDRAW engine to work with 
*  pModesDesc - pointer to a block of memory into which the mode description data will 
*		 be filled.
*  nNumModes  - the number of elements which the pModesDesc can contain. If this is too small
*		 then proper value will be placed within it.
* Returns:
*  NOERROR if OK, or E_FAIL.
*/

HRESULT WinDraw2_GetModes(LPWINDRAW lpwd, CModesDesc* pModesDesc, UINT32* nNumModes)
{
/* 
* For testing we will allow the following:
* if we are in GDI mode we will create a temp
* DirectDrawObject. And we will query it for the data.
    */
    if (!(lpwd->fMode & WINDRAW_DIRECTDRAW) && !lpwd->m_pModesList->GetCount())
    {
	WINDRAW tempWindraw;
	memset(&tempWindraw, 0, sizeof(WINDRAW));
	WinDraw2_Open(&tempWindraw, (HWND) lpwd->hWnd, WINDRAW_DIRECTDRAW, NULL, NULL);
	/* Enum the display modes  */
	DeleteDisplayModes(lpwd);
	if (tempWindraw.dd.lpDD2)
	{
	/*
	*  For SOME reason EnumDisplayModes is causing a number of cards to go CRAZY.
	*	So we now we call a function which calls enum display modes early and caches 
	*	the results. For some reason this does not cause things to go INSANE. Who knows why.
	    */
	    zm_DisplaySettings.GetSettings(lpwd->m_pModesList);
	    //	    tempWindraw.dd.lpDD2->EnumDisplayModes(0, NULL, lpwd->m_pModesList, DisplayModeEnumeratorCallback); 
	}
	else
	{
	    // create a node based on the current display setting
	    // use GDI to get the current mode that we are in.
	    HDC	    hDC		    = GetDC(NULL);
	    int	    width	    = GetDeviceCaps(hDC, HORZRES);
	    int	    height	    = GetDeviceCaps(hDC, VERTRES);
	    int	    bitCount	    = GetDeviceCaps(hDC, BITSPIXEL) * GetDeviceCaps(hDC, PLANES);
	    CModesDesc* pDesc = new CModesDesc;
	    pDesc->m_nWidth	= width;
	    pDesc->m_nHeight	= height;
	    pDesc->m_nBitCount	= bitCount;
	    pDesc->m_fourCC	= 0;
	    lpwd->m_pModesList->AddTail(pDesc);
	    ReleaseDC(NULL, hDC);
	}
	WinDraw2_Close(&tempWindraw);
    }
    
    if (*nNumModes != (UINT32)lpwd->m_pModesList->GetCount())
    {
	*nNumModes = lpwd->m_pModesList->GetCount();
	return NOERROR;
    }
    
    CHXSimpleList::Iterator i;
    for(i=lpwd->m_pModesList->Begin(); i!= lpwd->m_pModesList->End(); ++i)
    {
	CModesDesc* pDesc = (CModesDesc*) *i;
	
	pModesDesc->m_nWidth	= pDesc->m_nWidth;
	pModesDesc->m_nHeight	= pDesc->m_nHeight;
	pModesDesc->m_nBitCount	= pDesc->m_nBitCount;
	pModesDesc->m_fourCC	= pDesc->m_fourCC;
	pModesDesc++;
    }
    return NOERROR;
}

/*
* Sets Windraw to DD exclusive and sets the display mode to what is specified (if availiable)
* Use:
*  HRESULT WinDraw2_SetResolution(LPWINDRAW lpwd, UINT32 width, UINT32 height, UINT32 depth);
* Input:
*  lpwd    - pointer to a WINDRAW engine to work with 
*  width   - target width
*  height  - target height
*  depth   - target pixel depth
*
* Returns:
*  NOERROR if OK, or E_FAIL.
*/

HRESULT WinDraw2_GetResolution(LPWINDRAW lpwd, UINT32* width, UINT32* height, UINT32* depth)
{
    if (! (lpwd->fMode & WINDRAW_DIRECTDRAW) || !width || !height || !depth)
    {
        return E_FAIL;
    }

    DDSURFACEDESC  ddDesc;
    memset(&ddDesc,0,sizeof(ddDesc));
    ddDesc.dwSize = sizeof(ddDesc);
    
    HRESULT hr;
    try
    {
    hr = lpwd->dd.lpDD2->GetDisplayMode(&ddDesc);
    }
    catch(...)
    {
        DumpDDInfo(lpwd, lpwd->dd.lpDDSPrimary, "WinDraw2_GetResolution");
        return HXR_FAIL;
    }

    if (DD_OK != hr)
        return E_FAIL;

    *width = ddDesc.dwWidth;
    *height = ddDesc.dwHeight;
    *depth = ddDesc.ddpfPixelFormat.dwRGBBitCount;

    return NO_ERROR;
}

HRESULT WinDraw2_SetResolution(LPWINDRAW lpwd, UINT32 width, UINT32 height, UINT32 depth, HWND hwnd)
{
    if (! (lpwd->fMode & WINDRAW_DIRECTDRAW) )
    {
	return E_FAIL;
    }
    
    HRESULT hr = E_FAIL;

    EnterCriticalSection(&lpwd->csPrimary);

    try
    {
    hr = lpwd->dd.lpDD2->SetCooperativeLevel(hwnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);

    if (NOERROR == hr)
    {
        if (lpwd->dd.lpDDSPrimary)
        {
            int ref = lpwd->dd.lpDDSPrimary->Release();

            if (!ref)
                lpwd->dd.lpDDSPrimary = 0;
        }

        // we're not going to change the bit depth so we need to overide the depth variable
        UINT32 tmp,tmpDepth;
        if (NO_ERROR == WinDraw2_GetResolution(lpwd, &tmp, &tmp, &tmpDepth))
            depth = tmpDepth;

        lpwd->dd.lpDD2->SetDisplayMode(width, height, depth, NULL, NULL);
        WindrawSurface_CreatePrimarySurface(lpwd);
    }

    }
    catch(...)
    {
        DumpDDInfo(lpwd, lpwd->dd.lpDDSPrimary, "WinDraw2_SetResolution");
        hr = HXR_FAIL;
    }

    LeaveCriticalSection(&lpwd->csPrimary);
    
    return hr;
}

HRESULT WinDraw2_RestoreResolution(LPWINDRAW lpwd)
{
    if (! (lpwd->fMode & WINDRAW_DIRECTDRAW) )
    {
	return E_FAIL;
    }
    
    HRESULT res;

    EnterCriticalSection(&lpwd->csPrimary);

    try
    {

    lpwd->dd.lpDD2->SetCooperativeLevel(lpwd->hWnd, DDSCL_NORMAL);
    
    if (lpwd->dd.lpDDSPrimary)
    {
        int ref = lpwd->dd.lpDDSPrimary->Release();
        if (!ref)
            lpwd->dd.lpDDSPrimary = 0;
    }
    
    lpwd->dd.lpDD2->RestoreDisplayMode();
    
    res = WindrawSurface_CreatePrimarySurface(lpwd);

    }
    catch(...)
    {
        DumpDDInfo(lpwd, lpwd->dd.lpDDSPrimary, "WinDraw2_RestoreResolution");
        res = HXR_FAIL;
    }
    
    LeaveCriticalSection(&lpwd->csPrimary);

    return res;
}

HRESULT WinDraw2_RestorePrimarySurface(LPWINDRAW lpwd)
{
    HRESULT hr = DD_OK;
    if (lpwd && lpwd->fMode & WINDRAW_DIRECTDRAW)
    {
        EnterCriticalSection(&lpwd->csPrimary);

        if (lpwd->dd.lpDDSPrimary)
        {
        int ref = lpwd->dd.lpDDSPrimary->Release();
        if (!ref)
	        lpwd->dd.lpDDSPrimary = 0;
        }
    
        hr =  WindrawSurface_CreatePrimarySurface(lpwd);
        LeaveCriticalSection(&lpwd->csPrimary);
    }

    return hr;
}


/*
* Updates the window that the clipper should use
* Use:
*  HRESULT WinDrawSurface_Clipper(LPWINDRAW lpwd, HWND hwnd);
* Input:
*  lpwd - pointer to a base WINDRAW structure
*  hwnd - the window that the clipper should be set to
* Returns:
*  NOERROR if OK, or the last relevant DirectDraw error code, or E_FAIL.
*/

HRESULT WinDrawSurface_SetClipper(LPWINDRAW lpwd, HWND hwnd)
{
    HRESULT hr = NO_ERROR;

#ifndef _REMOVE_WINDRAW_CLIPPER 
    if (! (lpwd->fMode & WINDRAW_DIRECTDRAW) || ! (lpwd->fMode & WINDRAW_OPENED)  )
    {
        return E_FAIL;
    }
    
    IDirectDrawClipper*	lpClipper;
    
    try
    {
    hr = lpwd->dd.lpDDSPrimary->GetClipper(&lpClipper);
    }
    catch(...)
    {
        DumpDDInfo(lpwd, lpwd->dd.lpDDSPrimary, "WinDraw2_SetClipper");
        hr = HXR_FAIL;
    }

    if (DD_OK == hr)
    {
        lpClipper->SetHWnd(0, hwnd);
        lpwd->m_hCurrentClipWindow = hwnd;
        return NO_ERROR;
    }
#endif
    
    return hr;
}

HRESULT WinDrawSurface_GetClipperWindow(LPWINDRAW lpwd, HWND* pHwnd)
{
#ifndef _REMOVE_WINDRAW_CLIPPER 
    if (! (lpwd->fMode & WINDRAW_DIRECTDRAW)  || ! (lpwd->fMode & WINDRAW_OPENED) )
    {
	*pHwnd = 0;
	return E_FAIL;
    }
    
    *pHwnd = lpwd->m_hCurrentClipWindow;
#endif
    return HXR_OK;
}

HRESULT WinDrawSurface_GetColorControls(LPWINDRAW lpwd, LPWINDRAWSURFACE lpwds,
        float &fBright, float &fContrast, float &fSaturation,
        float &fHue, float &fSharpness)
{
    HRESULT hr = HXR_FAIL;

    if ((lpwd == NULL) | (lpwds == NULL))
    {
        HX_ASSERT(0);
        return E_INVALIDARG;
    }

    IDirectDrawColorControl *pcc = NULL;
    lpwds->dd.lpDDSurface->QueryInterface(IID_IDirectDrawColorControl, (void**)&pcc);
    if (pcc)
    {
        DDCOLORCONTROL ddcc;
        memset(&ddcc, 0, sizeof(ddcc));
        ddcc.dwSize = sizeof(ddcc);
        
        try
        {

        pcc->GetColorControls(&ddcc);
        HX_RELEASE(pcc);

        // Map dd values to rma values
        fBright = (float)((ddcc.lBrightness - 5000.0) / 5000.0);

        fContrast = (float)((ddcc.lContrast - 10000.0) / 10000.0);

        fHue = (float)(ddcc.lHue/180.0);
         
        fSaturation = (float)((ddcc.lSaturation - 10000.0) / 10000.0);

        fSharpness = (float)((ddcc.lSharpness - 2.5) / 2.5);

        hr = HXR_OK;

        }
        catch(...)
        {
            DumpDDInfo(lpwd, lpwds->dd.lpDDSurface, "WinDrawSurface_GetColorControls");
            hr = HXR_FAIL;
            
            HX_RELEASE(pcc);
        }
    }

    return hr;
}

HRESULT WinDrawSurface_SetColorControls(LPWINDRAW lpwd, LPWINDRAWSURFACE lpwds,
        float fBright, float fContrast, float fSaturation,
        float fHue, float fSharpness)
{
    HRESULT hr = HXR_OK;
    
    if ((lpwd == NULL) | (lpwds == NULL))
    {
        HX_ASSERT(0);
        return E_INVALIDARG;
    }

    // Map dd values to rma values (where zero is default)
    IDirectDrawColorControl *pcc = NULL;
    lpwds->dd.lpDDSurface->QueryInterface(IID_IDirectDrawColorControl, (void**)&pcc);
    if (pcc)
    {
        DDCOLORCONTROL ddcc;
        memset(&ddcc, 0, sizeof(ddcc));
        ddcc.dwSize = sizeof(ddcc);

        DDCOLORCONTROL ddccCurrent;
        memset(&ddccCurrent, 0, sizeof(ddccCurrent));
        ddccCurrent.dwSize = sizeof(ddccCurrent);

        try
        {

        hr = pcc->GetColorControls(&ddccCurrent);
        ddcc = ddccCurrent;
        
        // (0-10000, 750 is dd default)
        if (fBright == 0.0)
            ddcc.lBrightness = lpwds->dd.ddcc.lBrightness;
        else
        {
            // Mapping [-1,1] to [0,10000] with a default of 750
            if (fBright > 0)
                ddcc.lBrightness = (LONG)(fBright * 9250.0 + 750.0);
            else
                ddcc.lBrightness = (LONG)((fBright + 1.0) * 750.0);
        }

        // (0-20000, 10000 is dd default - limit to 2k-18k for compatabilty)
        if (fContrast == 0.0)
            ddcc.lContrast = lpwds->dd.ddcc.lContrast;
        else
            ddcc.lContrast = (LONG)(fContrast * 8000.0 + 10000.0);
        
        // (-180-180, 0 is dd default)
        if (fHue == 0.0)
            ddcc.lHue = lpwds->dd.ddcc.lHue;
        else
            ddcc.lHue = (LONG)(fHue*180.0);

        // (0-20000, 10000 is dd default limit to 4k-16k for compatabilty)
        if (fSaturation == 0.0)
            ddcc.lSaturation = lpwds->dd.ddcc.lSaturation;
        else
            ddcc.lSaturation = (LONG)(fSaturation * 6000.0 + 10000.0);

        // (0-10, 5 is dd default and -1 is rma def)
        if (fSharpness == 0.0)
            ddcc.lSharpness = lpwds->dd.ddcc.lSharpness;
        else
            ddcc.lSharpness = (LONG)(fSharpness * 5 + 5);

        if (ddccCurrent.lBrightness != ddcc.lBrightness ||
            ddccCurrent.lContrast   != ddcc.lContrast ||
            ddccCurrent.lHue        != ddcc.lHue ||
            ddccCurrent.lSaturation != ddcc.lSaturation ||
            ddccCurrent.lSharpness  != ddcc.lSharpness)
        {
            hr = pcc->SetColorControls(&ddcc);
        }

        }
        catch(...)
        {
            DumpDDInfo(lpwd, lpwds->dd.lpDDSurface, "WinDrawSurface_SetColorControls");
            hr = HXR_FAIL;
        }

        pcc->Release();
    }


    return hr;
}



HRESULT WindrawSurface_CreatePrimarySurface(LPWINDRAW lpwd)
{
    if (lpwd == NULL)
    {
        HX_ASSERT(0);
        return E_INVALIDARG;
    }

    EnterCriticalSection(&lpwd->csPrimary);

    if (lpwd->dd.lpDDSPrimary)
    {
        lpwd->dd.lpDDSPrimary->Release();
        lpwd->dd.lpDDSPrimary = NULL;
    }

    /* Create the primary surface  */
    DDSURFACEDESC ddsd;
    ZeroMemory(&ddsd, sizeof(ddsd));
    ddsd.dwSize = sizeof(ddsd);
    ddsd.dwFlags = DDSD_CAPS;
    ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
    
    int res;
    IDirectDrawClipper*	lpClipper = NULL;
    
    try
    {
    res = lpwd->dd.lpDD2->CreateSurface (&ddsd, &lpwd->dd.lpDDSPrimary, NULL);

#ifndef _REMOVE_WINDRAW_CLIPPER 
    if (res == DD_OK)
    {
	if (DD_OK == (res = lpwd->dd.lpDD2->CreateClipper(0, &(lpClipper), NULL))) 
	{
            lpClipper->SetHWnd(0, lpwd->hWnd);
	    lpwd->m_hCurrentClipWindow = lpwd->hWnd;
	    lpwd->dd.lpDDSPrimary->SetClipper(lpClipper);
	    lpClipper->Release(); /* ok the surface has the ref on it now */
	    lpClipper = NULL;
	}
    }    
#endif

    
    }
    catch(...)
    {
        DumpDDInfo(lpwd, lpwd->dd.lpDDSPrimary, "WindrawSurface_CreatePrimarySurface");
        res = HXR_FAIL;

        HX_RELEASE(lpClipper);
        HX_RELEASE(lpwd->dd.lpDDSPrimary);
    }

    LeaveCriticalSection(&lpwd->csPrimary);
    return res;
}


int Windraw_ColorMatch(LPWINDRAW lpwd, COLORREF crColor)
{
    HDC hdc;
    DDSURFACEDESC ddsd;
    int retVal;
    HRESULT res;
    
    if ( lpwd == NULL ||
        (!(lpwd->fMode & WINDRAW_OPENED)) || 
        (!(lpwd->fMode & WINDRAW_DIRECTDRAW)))
    {
	return 0x0000000;
    }
    
    memset(&ddsd, 0, sizeof(DDSURFACEDESC));
    ddsd.dwSize = sizeof(DDSURFACEDESC);
    
    // get primary surface CID.
    BMI bmi;
    memset(&bmi, 0, sizeof(BMI));
    WinDraw2_GetDisplayFormat(lpwd, &bmi);
    bmi.bmiHeader.biHeight = 2;
    bmi.bmiHeader.biWidth = 2;
    
    WINDRAWSURFACE tempSurf;
    memset(&tempSurf, 0, sizeof(WINDRAWSURFACE));
    
    try
    {

    // now attempt to create the surface
    res = WinDraw2_CreateSurface(lpwd, &tempSurf, &bmi, WINDRAWSURFACE_DIRECTDRAW, 0, 0);
    res = WinDrawSurface_GetDC(lpwd, &tempSurf, &hdc);
    COLORREF ref = SetPixel (hdc, 0, 0, crColor);
    res = WinDrawSurface_ReleaseDC(lpwd, &tempSurf, hdc);
    
    res = tempSurf.dd.lpDDSurface->Lock(NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL);
    retVal = *(PDWORD)ddsd.lpSurface;
    res = tempSurf.dd.lpDDSurface->Unlock(ddsd.lpSurface);
    
    if (ddsd.ddpfPixelFormat.dwRGBBitCount < 32)
        retVal &= (1L << ddsd.ddpfPixelFormat.dwRGBBitCount) - 1;
    res = WinDraw2_ReleaseSurface(lpwd, &tempSurf);
    
    }
    catch(...)
    {
        DumpDDInfo(lpwd, lpwd->dd.lpDDSPrimary, "Windraw_ColorMatch");
        retVal = HXR_FAIL;

        if (tempSurf.dd.lpDDSurface)
            WinDraw2_ReleaseSurface(lpwd, &tempSurf);
    }
    
    return retVal; 
}

int countBits(int input)
{
    int count = 0;
    
    while(input)
    {
	input &= input-1;
	count++;
    }
    return count;
}

int Windraw_GetLowestColor(LPWINDRAW lpwd)
{
    DDSURFACEDESC ddsd;
    int retVal;
    HRESULT res;
    
    if ( lpwd == NULL || 
        (!(lpwd->fMode & WINDRAW_OPENED)) || 
        (!(lpwd->fMode & WINDRAW_DIRECTDRAW)))
    {
	return 0x0000000;
    }
    
    memset(&ddsd, 0, sizeof(DDSURFACEDESC));
    ddsd.dwSize = sizeof(DDSURFACEDESC);
    
    // get primary surface CID.
    BMI bmi;
    memset(&bmi, 0, sizeof(BMI));
    WinDraw2_GetDisplayFormat(lpwd, &bmi);
    bmi.bmiHeader.biHeight = 2;
    bmi.bmiHeader.biWidth = 2;
    
    WINDRAWSURFACE tempSurf;
    memset(&tempSurf, 0, sizeof(WINDRAWSURFACE));
    
    try
    {

    // now attempt to create the surface
    res = WinDraw2_CreateSurface(lpwd, &tempSurf, &bmi, WINDRAWSURFACE_DIRECTDRAW, 0, 0);
    res = tempSurf.dd.lpDDSurface->Lock(NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL);
    res = tempSurf.dd.lpDDSurface->Unlock(ddsd.lpSurface);
    
    int redMask = ddsd.ddpfPixelFormat.dwRBitMask;
    int greenMask = ddsd.ddpfPixelFormat.dwGBitMask;
    int blueMask = ddsd.ddpfPixelFormat.dwBBitMask;
    
    retVal	= 1 << (9 - countBits(redMask));
    retVal	|= (1 << (9 - countBits(greenMask))) << 8;
    retVal	|= (1 << (9 - countBits(blueMask))) << 16;
    
    res = WinDraw2_ReleaseSurface(lpwd, &tempSurf);
    
    }
    catch(...)
    {
        DumpDDInfo(lpwd, lpwd->dd.lpDDSPrimary, "Windraw_GetLowestColor");
        retVal = HXR_FAIL;

        if (tempSurf.dd.lpDDSurface)
            WinDraw2_ReleaseSurface(lpwd, &tempSurf);
    }
    
    return retVal; 
}

HRESULT WinDraw2_GetScanLine(LPWINDRAW lpwd, DWORD *pdwScanLine)
{
    if ((lpwd == NULL) |
        (lpwd->dd.lpDD2 == NULL) | 
        (lpwd->dd.lpDDSPrimary == NULL))
    {
        *pdwScanLine = 0;
        return E_INVALIDARG;
    }
    
    HRESULT hr = NOERROR;
    try
    {

    hr = lpwd->dd.lpDD2->GetScanLine(pdwScanLine);

    if (DD_OK == hr ||
        DDERR_VERTICALBLANKINPROGRESS == hr)
    {
        // The G400 occasionally reports an obscenely high value here;
        // we filter to 130% of the reported display height
        if (*pdwScanLine > lpwd->dwReportedHeight*1.3)
        {
            *pdwScanLine = 0;
            return hr;
        }

        if (*pdwScanLine > lpwd->dwMaxScanLine) 
            lpwd->dwMaxScanLine = *pdwScanLine;
    }
    else
        *pdwScanLine = 0;

    }
    catch(...)
    {
        DumpDDInfo(lpwd, lpwd->dd.lpDDSPrimary, "WinDraw2_GetScanLine");
        hr = HXR_FAIL;

        *pdwScanLine = 0;
    }

    return hr;
}

HRESULT LockSurface(LPWINDRAW lpwd, LPDIRECTDRAWSURFACE lpSurface, DDSURFACEDESC *pddsd, BOOL bBlock)
{
    DWORD	dwFlags = DDLOCK_WRITEONLY | DDLOCK_NOSYSLOCK;
    HRESULT hr;

    if (bBlock)
        dwFlags |= DDLOCK_WAIT;

    for (int i=0;; i++)
    {
        memset(pddsd, 0, sizeof(*pddsd));
        pddsd->dwSize = sizeof(*pddsd);

        try
        {

        hr = lpSurface->Lock(NULL, pddsd, dwFlags, NULL);

        }
        catch(...)
        {
            char szTmp[256]; /* Flawfinder: ignore */
            sprintf(szTmp, "LockSurface flags %lx", dwFlags); /* Flawfinder: ignore */
            
            DumpDDInfo(lpwd, lpSurface, szTmp);
            hr = HXR_FAIL;
        }

        if (hr == DDERR_INVALIDPARAMS)
        {		
            if (i)
                break;

            dwFlags = 0;
            dwFlags |= DDLOCK_WAIT;
        }
        else
            break;
    }

    return hr;
}

HRESULT RestoreSurfaces(LPWINDRAW lpwd, LPWINDRAWSURFACE lpwds)
{
    HRESULT hr;

    EnterCriticalSection(&lpwd->csPrimary);

    try
    {

    // Restore the surfaces if we lost them
    hr = lpwd->dd.lpDDSPrimary->Restore();

    if (hr == DD_OK)
        hr = lpwds->dd.lpDDSurface->Restore();

    }
    catch(...)
    {
        DumpDDInfo(lpwd, lpwds->dd.lpDDSurface, "RestoreSurfaces");
        hr = HXR_FAIL;
    }

    LeaveCriticalSection(&lpwd->csPrimary);

    return hr;
}

#if defined (HELIX_FEATURE_VS2)
/*
 * WinDraw2_GetColorspacePriorities
 *
 * Request preferred color spaces from the DD driver
 */

HRESULT	WinDraw2_GetColorspacePriorities(int cidIn, int *pCPList, int &nSize)
{
    LPDIRECTDRAW        lpTemp = 0;
    LPDIRECTDRAW2       lpDD = 0;
    HINSTANCE           hDirectDraw = 0;
    HRESULT             ddrval = 0;
    DWORD               pFourcc[128];
    DWORD               dwNumCodes = 0;
    int                 j=0;
    unsigned int        yv12 = -1,
                        yuy2 = -1,
                        uyvy = -1;

    try
    {
    
    LoadDirectDraw(NULL, &lpTemp, &hDirectDraw);
	
    if (lpTemp)
    {
        lpTemp->QueryInterface(IID_IDirectDraw2, (void**)&lpDD);
        lpTemp->Release();
    }

    if(lpDD)
    {
        memset(pFourcc, 0, sizeof(pFourcc));

        // Get the number of supported 4CC codes
        if (DD_OK != lpDD->GetFourCCCodes(&dwNumCodes, 0))
        {
            ReleaseDirectDraw(NULL, &lpDD, &hDirectDraw);
            return E_FAIL;
        }

        // Get the list of supported 4CC codes
        ddrval = lpDD->GetFourCCCodes(&dwNumCodes, pFourcc);

        if (ddrval != DD_OK || !dwNumCodes)
        {
            ReleaseDirectDraw(NULL, &lpDD, &hDirectDraw);
            return E_FAIL;
        }

        memset(pCPList, 0, nSize * sizeof(*pCPList));

        // If I420 is not the source format, make it the first priority in hopes
        // of avoiding an unnecessary color conversion when writing to video memory.
        if (IsYUV(cidIn) && CID_I420 != cidIn)
        {
            pCPList[j++] = cidIn;
        }
        
        if(dwNumCodes && pFourcc && DD_OK == ddrval)
        {
            for(int i=0; i < (int)dwNumCodes && j < nSize; i++)
            {
                switch(MapFourCCtoCID(pFourcc[i]))
                {
                    case CID_I420: pCPList[j++] = CID_I420; break;
                    case CID_YV12: pCPList[j++] = CID_YV12; yv12 = j-1; break;
                    case CID_UYVY: pCPList[j++] = CID_UYVY; uyvy = j-1; break;
                    case CID_YUY2: pCPList[j++] = CID_YUY2; yuy2 = j-1; break;
                }
            }

            nSize = j;

            BOOL bTrySort = FALSE;
            DDDEVICEIDENTIFIER ddID;

            // Do some hard-coded ovverrides.  We want yv12 over packed 
            // formats, so, if yv12 is in the list put it at the head
            // EXCEPT for nvidia and Matrox G200, which have poor yv12 support.

#if DIRECTDRAW_VERSION > 0x0500
            IDirectDraw4* lpDD4 = NULL;
            lpDD->QueryInterface(IID_IDirectDraw4, (void **) &lpDD4);
    
            if (lpDD4)
            {
                DDDEVICEIDENTIFIER  dddi;
                memset(&dddi, 0, sizeof(dddi));

                lpDD4->GetDeviceIdentifier(&dddi, DDGDI_GETHOSTIDENTIFIER);
                lpDD4->Release();

                SafeStrCpy(ddID.szDescription, dddi.szDescription, MAX_DDDEVICEID_STRING);
                SafeStrCpy(ddID.szDriver, dddi.szDriver, MAX_DDDEVICEID_STRING);
                ddID.dwVendorId = dddi.dwVendorId;
                ddID.dwDeviceId = dddi.dwDeviceId;

                bTrySort = TRUE;
            }
            // DD4 not supportted (probably NT4)...use more drastic measures
            else
#endif //DIRECTDRAW_VERSION > 0x0500
            {
                // Check if we are runnning NT
                OSVERSIONINFO osVersion;
                memset(&osVersion,0, sizeof(OSVERSIONINFO));
                osVersion.dwOSVersionInfoSize  = sizeof(OSVERSIONINFO);

                if (GetVersionEx(&osVersion) && osVersion.dwPlatformId == VER_PLATFORM_WIN32_NT)
                {
                    char szChipType[MAXCHIPTYPE]; /* Flawfinder: ignore */
                    
                    // Try to get WinNT device information
                    if (GetWinNTDeviceID (&ddID, szChipType))
                    {
                        bTrySort = TRUE;
                    }
                }
            }

            if (bTrySort &&
                yv12 != -1 &&
                
                // TNT yv12 chroma does not line up
                ddID.dwVendorId != 0x10DE &&

                // NeoMagic yv12 chroma probs when overlay is cropped
                ddID.dwVendorId != 0x10C8 &&

                // Riva128 yv12 has software conversion and
                // locks always return same ptr.
                strcmpi(ddID.szDescription, "NV3") &&
                ddID.dwVendorId != 0x12D2 &&

                // G200 uses sofware converstion to yv12
                !strstr(ddID.szDescription, "Matrox Millennium G200"))
            {
                if (yv12 > uyvy)
                {
                    pCPList[uyvy] = CID_YV12;
                    pCPList[yv12] = CID_UYVY;

                    yv12 = uyvy;
                }

                if (yv12 > yuy2)
                {
                    pCPList[yuy2] = CID_YV12;
                    pCPList[yv12] = CID_YUY2;
                }
            }
            
            // Filter bad Radeon drivers
            if (ddID.dwVendorId == 0x1002 &&
                ddID.szDriver &&
                pCPList[0] == CID_DVPF)
            {
                // The only difference is in the dll's file version
                char szTmp[256] = "\0";
                SafeStrCpy(szTmp, getenv("windir"), sizeof(szTmp));
                int nLen = strlen(szTmp);
                
                if (szTmp[nLen-1] != '\\')
                {
                    szTmp[nLen] = '\\';
                    szTmp[++nLen] = '\0';
                }

                // write OS version
                OSVERSIONINFO ver;
                memset(&ver, 0, sizeof(ver));
                ver.dwOSVersionInfoSize = sizeof(ver);

                GetVersionEx(&ver);
                
                if (VER_PLATFORM_WIN32_NT == ver.dwPlatformId)
                {
                    SafeStrCat(szTmp, "system32\\", sizeof(szTmp));
                }
                else
                {
                    SafeStrCat(szTmp, "system\\", sizeof(szTmp));
                }
                 
                SafeStrCat(szTmp, ddID.szDriver, sizeof(szTmp));
                
                // Query the file version of this dll
                DWORD dwHandle = 0;
                DWORD dwSize = GetFileVersionInfoSize(szTmp, &dwHandle);
                if (dwSize)
                { 
                    BYTE* pBuf = new BYTE[dwSize];
                    if (pBuf)
                    {
                        if (GetFileVersionInfo(szTmp, NULL, dwSize, pBuf))
                        {
                            UINT nLen = 0;
                            char szQuery[64];
                            char* pFileVersion = NULL;
                            
                            struct LANGANDCODEPAGE
                            {
                                WORD wLanguage;
                                WORD wCodePage;
                            } *lpTranslate;

                            // Read the list of languages and code pages.
                            VerQueryValue(pBuf, 
                                          TEXT("\\VarFileInfo\\Translation"),
                                          (LPVOID*)&lpTranslate,
                                          &nLen);

                            // Read the file description for each language and code page.
                            for(UINT32 i=0; i < (nLen/sizeof(struct LANGANDCODEPAGE)); i++ )
                            {
                                // Use first english entry
                                if (lpTranslate[i].wLanguage == 0x409)
                                {
                                    SafeSprintf(szQuery, sizeof(szQuery),
                                                TEXT("\\StringFileInfo\\%04x%04x\\FileVersion"),
                                                lpTranslate[i].wLanguage,
                                                lpTranslate[i].wCodePage);
                                    
                                    if (VerQueryValue(pBuf, szQuery, (void**)&pFileVersion, &nLen))
                                        break;
                                }    
                            }

                            // catalyst drvier 3.0 and 3.1 do not work w/ R1 on winNT
                            if (!strcmp(pFileVersion, "6.14.01.6255") ||
                                !strcmp(pFileVersion, "6.14.01.6292") ||
                                
                            // catalyst drvier 3.2 on 9700's do not work w/ R1 on Win9x/NT    
                                (ddID.dwDeviceId == 0x4e44 &&
                                  (!strcmp(pFileVersion, "6.14.01.6307") || 
                                   !strcmp(pFileVersion, "4.14.01.3699"))))
                            {
                                pCPList[0] = pCPList[1];
                            }
                        }
                        
                        delete [] pBuf; 
                    }
                }
            }
        }

        ReleaseDirectDraw(NULL, &lpDD, &hDirectDraw);
    }
    else
        return E_FAIL;
			
    }

    catch(...)
    {
        DumpDDInfo(NULL, NULL, "WinDraw2_GetColorspacePriorities");

        if (lpDD)
            ReleaseDirectDraw(NULL, &lpDD, &hDirectDraw);

        return HXR_FAIL;;
    }

    return NOERROR;
}

HRESULT WinDraw_CanLockSurface (LPWINDRAW lpwd, WINDRAWSURFACE *lpwds, UCHAR* &pBuffer, int& nIndex, BOOL bBlock)
{
    HRESULT hr;

    if ((lpwd == NULL) | (lpwds == NULL)) 
    {
        HX_ASSERT(0);
        return E_INVALIDARG;
    }

    // Get the next buffer in the flipping chain
    if (-1 == nIndex)
    {
        hr = WaitForSingleObject(lpwds->hOverlayIndexMutex, 0);

        // Do not block if flags say so
        if (WAIT_TIMEOUT == hr)
        {
            if (bBlock)
            {
                HANDLE  aWaitObjs[2] = {lpwds->hOverlayIndexMutex, lpwds->dd.hAbort};
                hr = WaitForMultipleObjects(2, aWaitObjs, FALSE, INFINITE);

                if (WAIT_OBJECT_0+1 == hr)
                    return DDERR_WASSTILLDRAWING;
            }
            else
                return DDERR_WASSTILLDRAWING;
        }
        
        nIndex = lpwds->dwNextBuffer;
        ReleaseMutex(lpwds->hOverlayIndexMutex);
    }

    int nNextIndex = nIndex + 1;
    if (nNextIndex == (int)(lpwds->dwBackBufferCount+1))
        nNextIndex = 0;
    
    // Wait until the buffer ahead of this one has flipped so that we have
    // a valid wait-until-time
    if (!bBlock)
    {
        hr = WaitForSingleObject(lpwds->dd.lpChain[nNextIndex].hEmpty, 0);

        if (WAIT_OBJECT_0 != hr)
            return DDERR_WASSTILLDRAWING;
    }
    else
    {
        HANDLE  aWaitObjects[2] = {lpwds->dd.lpChain[nNextIndex].hEmpty, lpwds->dd.hAbort};
        hr = WaitForMultipleObjects(2, aWaitObjects, FALSE, INFINITE);
    }
    
    if (hr != WAIT_OBJECT_0)
        return HXR_FAIL;

    double dTimeAvailable = lpwds->dd.lpChain[nIndex].dTimeAvailable;

    // Check the time of the surface
    double dTime = GetMSTickDouble32();

    if (dTimeAvailable > dTime)
    {
        //HX_TRACE("Waiting for buffer, %lu", (UINT32) (dTimeAvailable - dTime));

        if (bBlock)
        {
            if (lpwd->nSchedulerResolution)
                timeBeginPeriod(lpwd->nSchedulerResolution);
            
            DWORD dwWait = (DWORD)(dTimeAvailable - dTime + 1);
            HX_ASSERT(dwWait < 20000);

            WaitForSingleObject(lpwds->dd.hAbort, dwWait);

            if (lpwd->nSchedulerResolution)
                timeEndPeriod(lpwd->nSchedulerResolution);
        }
        else
            return DDERR_WASSTILLDRAWING;
    }

    hr = IsFlipDone(lpwd, lpwds, &lpwds->dd.lpChain[nIndex], bBlock);

    if (DD_OK != hr)
    {
        // For breakpoints?
        return hr;
    }

    return hr;
}

HRESULT WinDraw_GetLockedSurface (LPWINDRAW lpwd, WINDRAWSURFACE *lpwds, UCHAR* &pBuffer, int nIndex, BOOL bBlock)
{
    DDSURFACEDESC ddsd;
    HRESULT hr = HXR_FAIL;
    HRESULT hRet;
    BOOL    bLoop = TRUE;

    if ((lpwd == NULL) | 
        (lpwds == NULL) | 
        (lpwds->dd.lpChain == NULL))
    {
        HX_ASSERT(0);
        return E_INVALIDARG;
    }

    #ifdef _DEBUG
    LARGE_INTEGER   lFreq,
                    lTime;
    LONGLONG        llTime;

    QueryPerformanceFrequency(&lFreq);
    #endif

    memset(&ddsd, 0, sizeof(ddsd));
    ddsd.dwSize = sizeof(ddsd);

    // Get the next buffer in the flipping chain
    if (-1 == nIndex)
    {
        hr = WaitForSingleObject(lpwds->hOverlayIndexMutex, 0);

        // Do not block if flags say so
        if (WAIT_TIMEOUT == hr)
        {
            if (bBlock)
            {
                HANDLE  aWaitObjs[2] = {lpwds->hOverlayIndexMutex, lpwds->dd.hAbort};
                hr = WaitForMultipleObjects(2, aWaitObjs, FALSE, INFINITE);

                if (WAIT_OBJECT_0+1 == hr)
                    return DDERR_WASSTILLDRAWING;
            }
            else
                return DDERR_WASSTILLDRAWING;
        }
        
        nIndex = lpwds->dwNextBuffer;
        ReleaseMutex(lpwds->hOverlayIndexMutex);
    }
    
    int nNextIndex = nIndex + 1;
    if (nNextIndex == (int)(lpwds->dwBackBufferCount+1))
        nNextIndex = 0;

    HANDLE  aWaitObjects[2] = {lpwds->dd.lpChain[nNextIndex].hEmpty, lpwds->dd.hAbort};

    int nCount = 0;

    // Try and lock the surface while handling various dd errors
    while (bLoop)
    {
        hRet = WaitForSingleObject(lpwds->hOverlayMutex, 0);
        if (hRet == WAIT_TIMEOUT)
        {
            if (!bBlock)
                return DDERR_WASSTILLDRAWING;
            else
                WaitForSingleObject(lpwds->hOverlayMutex, INFINITE);
        }

        hr = LockSurface(lpwd, lpwds->dd.lpChain[nIndex].lpSurface, &ddsd, FALSE);
    
        if (DD_OK != hr)
            ReleaseMutex(lpwds->hOverlayMutex);

        // Could not lock the surface right now so wait a bit and try again
        if (DDERR_WASSTILLDRAWING == hr)
        {
            #ifdef _DEBUG
            QueryPerformanceCounter(&lTime);
            llTime = lTime.QuadPart;
            #endif
            
            if (!bBlock)
                break;

            if (WaitForSingleObject(lpwds->dd.hAbort, 5) != WAIT_TIMEOUT)
                break;
        }
        else if (DDERR_SURFACELOST == hr)
        {
            hr = RestoreSurfaces(lpwd, lpwds);

            if (++nCount>1)
                hr = HXR_FAIL;

            // Could not restore, so get out
            if (hr != DD_OK)
                bLoop = FALSE;
        }
        // If this surface is locked by another thread...unlock it.
        // As far as my debugging went, could not find another thread
        // locking these surfaces, but so says the driver.
        else if (DDERR_SURFACEBUSY == hr)
        {
            hr = lpwds->dd.lpChain[nIndex].lpSurface->Unlock(NULL);
        }
        
        // The lock worked
        else if (DD_OK == hr)
        {
            bLoop = FALSE;

            // Store hw memory address and pitch
            lpwds->dd.lpChain[nIndex].pHwMemBuffer = (BYTE*)ddsd.lpSurface;
            lpwds->dd.lPitch = ddsd.lPitch;

            pBuffer = (UCHAR*)ddsd.lpSurface;
        }
        // Some other error
        else
        {
            bLoop = FALSE;
        }
    }

    return hr;
}

HRESULT WinDraw_ReleaseLockedSurface (LPWINDRAW lpwd, WINDRAWSURFACE *lpwds, BYTE *pHwBuf, BOOL bDiscard)
{
    HRESULT hr = HXR_OK;
    BOOL    bLoop = TRUE;
    int     nCount = 0;

    if ((lpwd == NULL) | 
        (lpwds == NULL) | 
        (lpwds->dd.lpChain == NULL))
    {
        HX_ASSERT(0);
        return E_INVALIDARG;
    }

    WaitForSingleObject(lpwds->hOverlayIndexMutex, INFINITE);

    for (int i=0; i<(int)lpwds->dwBackBufferCount+1; i++)
    {
        if (pHwBuf == lpwds->dd.lpChain[i].pHwMemBuffer)
        {
            while (bLoop)
            {
                lpwds->dd.lpChain[i].pHwMemBuffer = NULL;
                try
                {
                hr = lpwds->dd.lpChain[i].lpSurface->Unlock(NULL);
                }
                catch(...)
                {
                    DumpDDInfo(lpwd, lpwds->dd.lpDDSurface, "WinDraw_ReleaseLockedSurface");
                    hr = HXR_FAIL;
                }
                
                ReleaseMutex(lpwds->hOverlayMutex);

                if (bDiscard)
                    SetEvent(lpwds->dd.lpChain[i].hEmpty);

                if (DDERR_SURFACELOST == hr)
                {
                    hr = RestoreSurfaces(lpwd, lpwds);

                    if (++nCount>1)
                        hr = HXR_FAIL;
                    
                    // Could not restore, so get out
                    if (hr != DD_OK)
                    {
                        bLoop = FALSE;

                        #ifdef _DEBUG
                        //OutputDebugString("WinDraw_ReleaseLockedSurface::Unlock Failed\n");
                        #endif
                    }
                }
                else
                    bLoop = FALSE;
            }

            break;
        }
    }
    
    ReleaseMutex(lpwds->hOverlayIndexMutex);

    return hr;
}

HRESULT WinDraw_AdvanceSurface (LPWINDRAW lpwd, WINDRAWSURFACE *lpwds, int &nIndex)
{
    HRESULT hr = HXR_OK;

    if ((lpwd == NULL) | (lpwds == NULL)) 
    {
        HX_ASSERT(0);
        return E_INVALIDARG;
    }

    WaitForSingleObject(lpwds->hOverlayIndexMutex, INFINITE);

    try
    {

    // Maintain front and next buffer indexes for default case
    if (-1 == nIndex)
    {
        nIndex = lpwds->dwNextBuffer;

        if (!lpwds->dd.lpChain[nIndex].lpSurface->IsLost())
        {
            if (++lpwds->dwNextBuffer == lpwds->dwBackBufferCount+1)
                lpwds->dwNextBuffer = 0;    
        }
        else
            hr = HXR_FAIL;
    }
    else
    {
        if (lpwds->dd.lpChain[nIndex].lpSurface->IsLost())
            hr = HXR_FAIL;
    }

    }
    catch(...)
    {
        DumpDDInfo(lpwd, lpwds->dd.lpDDSurface, "WinDraw_AdvanceSurface");
        hr = HXR_FAIL;
    }
    
    // Mark this surface memory as full
    if (hr == HXR_OK)
        ResetEvent(lpwds->dd.lpChain[nIndex].hEmpty);

    ReleaseMutex(lpwds->hOverlayIndexMutex);

    return hr;

}

HRESULT WinDraw_ResetSurfaceIndexes (LPWINDRAW lpwd, WINDRAWSURFACE *lpwds)
{
    HRESULT hr;
    DDSURFACEDESC ddsd;

    if ((lpwd == NULL) | (lpwds == NULL))
    {
        HX_ASSERT(0);
        return E_INVALIDARG;
    }

    HX_ASSERT( lpwds->dwBackBufferCount );
    
    // Reset the hw locations of the flipping chain
    for (UINT32 i=0; i<lpwds->dwBackBufferCount+1; i++)
    {
        memset(&ddsd, 0, sizeof(ddsd));
        ddsd.dwSize = sizeof(ddsd);

        hr = LockSurface(lpwd, lpwds->dd.lpChain[i].lpSurface, &ddsd, FALSE);

        if (DD_OK != hr)
            hr = LockSurface(lpwd, lpwds->dd.lpChain[i].lpSurface, &ddsd, TRUE);

        HX_ASSERT(DD_OK == hr);

        try
        {
        lpwds->dd.lpChain[i].lpSurface->Unlock(NULL);
        }
        catch(...)
        {
            DumpDDInfo(lpwd, lpwds->dd.lpDDSurface, "WinDraw_ResetSurfaceIndexes");
            // nothing to do
        }

        lpwds->dd.lpChain[i].pHwMemBuffer = (BYTE*)ddsd.lpSurface;
    }
    
    // Set our next buffer to lock to the one after the front buffer
    memset(&ddsd, 0, sizeof(ddsd));
    ddsd.dwSize = sizeof(ddsd);

    WaitForSingleObject(lpwds->hOverlayMutex, INFINITE);

    hr = LockSurface(lpwd, lpwds->dd.lpDDSurface, &ddsd, FALSE);

    if (DD_OK != hr)
        hr = LockSurface(lpwd, lpwds->dd.lpDDSurface, &ddsd, TRUE);

    HX_ASSERT(DD_OK == hr);

    if (DD_OK == hr)
    {
        try
        {
        lpwds->dd.lpDDSurface->Unlock(NULL);
        }
        catch(...)
        {
            DumpDDInfo(lpwd, lpwds->dd.lpDDSurface, "WinDraw_ResetSurfaceIndexes");
            // nothing to do
        }

        for (UINT32 i=0; i<lpwds->dwBackBufferCount+1; i++)
        {
            if ((BYTE*)ddsd.lpSurface == lpwds->dd.lpChain[i].pHwMemBuffer)
            {
                lpwds->dwNextBuffer = i+1;
                
                if (lpwds->dwNextBuffer == lpwds->dwBackBufferCount+1)
                    lpwds->dwNextBuffer = 0;

                lpwds->dd.lpDDBackBuffer = lpwds->dd.lpChain[lpwds->dwNextBuffer].lpSurface;
		break;
            }
        }

        HX_ASSERT(lpwds->dd.lpDDBackBuffer);
    }

    ReleaseMutex(lpwds->hOverlayMutex);

    return HXR_OK;
}

HRESULT WinDraw_DisplaySurface (LPWINDRAW lpwd, WINDRAWSURFACE *lpwds, DWORD dwFlags, int nIndex, BOOL bAbort)
{
    HRESULT hr = HXR_OK,
            hWaitRet;
    BOOL    bLoop = TRUE;
    int     nCount = 0;
    HANDLE  aWaitObjs[2] = {lpwds->dd.hAbort, lpwds->hOverlayMutex};

    if ((lpwd == NULL) | (lpwds == NULL))
    {
        HX_ASSERT(0);
        return E_INVALIDARG;
    }
    
    // Flip the appropriate backbuffer and keep track of the
    // surface pointer location in the flipping chain.
    if (1)//-1 == nIndex)
    {
        while (bLoop)
        {
            try
            {

            if (bAbort)
                hr = DDERR_WASSTILLDRAWING;
            else
            {
                hWaitRet = WaitForMultipleObjects(2, aWaitObjs, FALSE, INFINITE);
                hr = lpwds->dd.lpDDSurface->Flip(NULL, dwFlags);

                if (WAIT_OBJECT_0+1 == hWaitRet &&
                    DDERR_SURFACEBUSY != hr)
                    ReleaseMutex(lpwds->hOverlayMutex);
            }

            if (DDERR_SURFACELOST == hr)
            {
                hr = RestoreSurfaces(lpwd, lpwds);

                if (++nCount>1)
                    hr = HXR_FAIL;

                if (hr != DD_OK)
                {
                    SetEvent(lpwds->dd.lpChain[nIndex].hEmpty);
                    bLoop = FALSE;
                }
            }
            else if (DDERR_SURFACEBUSY == hr)
            {
                lpwds->dd.lpChain[nIndex].lpSurface->Unlock(NULL);

                hr = lpwds->dd.lpDDSurface->Flip(NULL, dwFlags);
                ReleaseMutex(lpwds->hOverlayMutex);

                bLoop = FALSE;
            }
            else
                bLoop = FALSE;

            }
            catch(...)
            {
                DumpDDInfo(lpwd, lpwds->dd.lpDDSurface, "WinDraw_DisplaySurface");
                hr = HXR_FAIL;

                bLoop = FALSE;
            }
        }
        
        if (DDERR_WASSTILLDRAWING != hr &&
            DDERR_SURFACEBUSY != hr &&
            DDERR_INVALIDPARAMS != hr)
        {
            WaitForSingleObject(lpwds->hOverlayIndexMutex, INFINITE);

            LPDIRECTDRAWSURFACE lpTemp = lpwds->dd.lpChain[lpwds->dwBackBufferCount].lpSurface;

            for (int i=lpwds->dwBackBufferCount; i>0; i--)
            {
                lpwds->dd.lpChain[i].lpSurface = lpwds->dd.lpChain[i-1].lpSurface;
            }

            lpwds->dd.lpChain[i].lpSurface = lpTemp;

            if (++lpwds->dwFrontBuffer == lpwds->dwBackBufferCount+1)
                lpwds->dwFrontBuffer = 0;
            
            ReleaseMutex(lpwds->hOverlayIndexMutex);

            SetEvent(lpwds->dd.lpChain[nIndex].hEmpty);
        }
        else if (bAbort)
        {
            WaitForSingleObject(lpwds->hOverlayIndexMutex, INFINITE);

            if ((INT32)(--lpwds->dwNextBuffer) == -1)
                lpwds->dwNextBuffer = lpwds->dwBackBufferCount;    
            
            ReleaseMutex(lpwds->hOverlayIndexMutex);
            
            SetEvent(lpwds->dd.lpChain[nIndex].hEmpty);

            hr = HXR_OK;
        }
    }
    else
    {
        while (bLoop)
        {
            try
            {

            if (bAbort)
                hr = DDERR_WASSTILLDRAWING;
            else
            {    
                hWaitRet = WaitForMultipleObjects(2, aWaitObjs, FALSE, INFINITE);
                hr = lpwds->dd.lpDDSurface->Flip(lpwds->dd.lpChain[nIndex].lpSurface, dwFlags);

                if (WAIT_OBJECT_0+1 == hWaitRet)
                    ReleaseMutex(lpwds->hOverlayMutex);
            }

            if (DDERR_SURFACELOST == hr)
            {
                hr = RestoreSurfaces(lpwd, lpwds);

                if (++nCount>1)
                    hr = HXR_FAIL;
                
                if (hr != DD_OK)
                {
                    SetEvent(lpwds->dd.lpChain[nIndex].hEmpty);
                    bLoop = FALSE;
                }
            }
            else if (DDERR_SURFACEBUSY == hr)
            {
                lpwds->dd.lpChain[nIndex].lpSurface->Unlock(NULL);

                hr = lpwds->dd.lpDDSurface->Flip(lpwds->dd.lpChain[nIndex].lpSurface, dwFlags);
                bLoop = FALSE;
            }
            else
                bLoop = FALSE;

            }
            catch(...)
            {
                DumpDDInfo(lpwd, lpwds->dd.lpDDSurface, "WinDraw_DisplaySurface");
                hr = HXR_FAIL;

                bLoop = FALSE;
            }
        }
        
        // Let caller handle these
        if (DDERR_WASSTILLDRAWING != hr &&
            DDERR_SURFACEBUSY != hr &&
            DDERR_INVALIDPARAMS != hr)
        {
            WaitForSingleObject(lpwds->hOverlayIndexMutex, INFINITE);
           
            // Flip a specific backbuffer - it swaps places
            // with the front buffer.
            LPDIRECTDRAWSURFACE lpTemp = lpwds->dd.lpChain[lpwds->dwFrontBuffer].lpSurface;

            lpwds->dd.lpChain[lpwds->dwFrontBuffer].lpSurface = lpwds->dd.lpChain[nIndex].lpSurface;
            lpwds->dd.lpChain[nIndex].lpSurface = lpTemp;
            lpwds->dwFrontBuffer = nIndex;
            
            ReleaseMutex(lpwds->hOverlayIndexMutex);

            SetEvent(lpwds->dd.lpChain[nIndex].hEmpty);
        }
        else if (bAbort)
        {
            WaitForSingleObject(lpwds->hOverlayIndexMutex, INFINITE);

            if ((INT32)(--lpwds->dwNextBuffer) == -1)
                lpwds->dwNextBuffer = lpwds->dwBackBufferCount;    
            
            ReleaseMutex(lpwds->hOverlayIndexMutex);
           
            SetEvent(lpwds->dd.lpChain[nIndex].hEmpty);

            hr = HXR_OK;
        }
    }

    return hr;
}

HRESULT WinDraw_CacelPendingDisplay (LPWINDRAW lpwd, WINDRAWSURFACE *lpwds)
{
    if (lpwds == NULL) 
    {
        HX_ASSERT(0);
        return E_INVALIDARG;
    }
    
    PulseEvent(lpwds->dd.hAbort);
    return HXR_OK;
}

HRESULT IsFlipDone(LPWINDRAW lpwd, LPWINDRAWSURFACE lpwds, ENUMSURFACE* lpSurfEnum, BOOL bBlock)
{
    HRESULT hr;
    int     nCount = 0,
	    nOtherError = 0;
    
    WaitForSingleObject(lpwds->hOverlayIndexMutex, INFINITE);

    try
    {

    while (1)
    {
        hr = lpSurfEnum->lpSurface->GetFlipStatus(DDGFS_ISFLIPDONE);

        if (DDERR_SURFACELOST == hr)
        {
            hr = RestoreSurfaces(lpwd, lpwds);

            if (++nCount>1)
                hr = HXR_FAIL;
            
            // Could not restore, so get out
            if (hr != DD_OK)
                break;
        }
        else if (DD_OK == hr)
        {
            hr = lpwds->dd.lpDDBackBuffer->GetFlipStatus(DDGFS_ISFLIPDONE);

            if (DD_OK != hr)
            {
                #ifdef _DEBUG
                //OutputDebugString("Backbuffer still drawing\n");
                #endif

                //HX_TRACE("Spinning %lu\n");
                continue;    // Spin?
            }
            
            break;
        }

        else
        {
            #ifdef _DEBUG
            //OutputDebugString("Could not lock...previous flip still going\n");
            #endif

            hr = DDERR_WASSTILLDRAWING;

            if (!bBlock)
                break;

            // Some other error...wait a bit and give up after 2 vblanks
            if (++nOtherError > 2)
            {
                hr = HXR_FAIL;
                break;
            }
            
            DWORD dwScan = 0;
            WinDraw2_GetScanLine(lpwd, &dwScan);

            // Sleep till next vblank
            long lSleep = (long)((1.0 - (double)dwScan/(lpwd->dwMaxScanLine+1)) * lpwd->dMsPerVBlank + 1);

            //HX_TRACE("IsFlipDone; sleeping for %lu\n", lSleep);
            if (lpwd->nSchedulerResolution)
                timeBeginPeriod(lpwd->nSchedulerResolution);
            
            HRESULT hrWait = WaitForSingleObject(lpwds->dd.hAbort, lSleep);

            if (lpwd->nSchedulerResolution)
                timeEndPeriod(lpwd->nSchedulerResolution);

             if (hrWait!= WAIT_TIMEOUT)
                break;
        }
    }

    }
    catch(...)
    {
        DumpDDInfo(lpwd, lpwds->dd.lpDDSurface, "IsFlipDone");
        hr = HXR_FAIL;
    }
    
    ReleaseMutex(lpwds->hOverlayIndexMutex);
    return hr;
}

#if 0

// This is how we handle the fact that some dd drivers don't properly
// handle GetFlipStatus and Lock for async flipping.  So, before
// we lock a surface, make sure it is not the front buffer - this causes
// tearing with an out of order display flavor.

HRESULT IsFrontBuffer(LPWINDRAW lpwd, LPWINDRAWSURFACE lpwds, ENUMSURFACE* lpSurfEnum, BOOL bBlock)
{
    DDSURFACEDESC ddsd;
    HRESULT hr;
    int     nCount = 0,
	    nOtherError = 0;

    try
    {

    while (1)
    {
        memset(&ddsd, 0, sizeof(ddsd));
        ddsd.dwSize = sizeof(ddsd);

        WaitForSingleObject(lpwds->hOverlayMutex, INFINITE);

        hr = LockSurface(lpwd, lpwds->dd.lpDDSurface, &ddsd, FALSE);

        if (hr != DD_OK)
            ReleaseMutex(lpwds->hOverlayMutex);

        if (DD_OK == hr)
        {
            lpwds->dd.lpDDSurface->Unlock(NULL);

            ReleaseMutex(lpwds->hOverlayMutex);

            if ((BYTE*)ddsd.lpSurface == lpSurfEnum->pHwMemBuffer)
            {
                #ifdef _DEBUG
                //if ((BYTE*)ddsd.lpSurface == lpwds->dd.lpChain[nIndex].pHwMemBuffer)
                //    OutputDebugString("Whoops...front buffer mem\n");
                //else
                //    OutputDebugString("Whoops...front buffer index\n");
                #endif

                // This should not happen, but you know how that goes.
                // If we are trying to lock the front buffer and all other buffers
                // are empty, go ahead and allow it.  Otherwise, we get stuck here.
                int nFullBuffers = 0;
                for (int i=0; i<lpwds->dwBackBufferCount+1; i++)
                {
                    if (WaitForSingleObject(lpwds->dd.lpChain[i].hEmpty, 0) != WAIT_OBJECT_0)
                        ++nFullBuffers;
                }

                if (!nFullBuffers)
                    break;

                if (!bBlock)
                {
                    hr = HXR_FAIL;
                    break;
                }
        
                if (WaitForSingleObject(lpwds->dd.hAbort, 5) != WAIT_TIMEOUT)
                {
                    hr = HXR_FAIL;
                    break;
                }
            }
            else
                break;
        }
        else if (DDERR_SURFACELOST == hr)
        {
            hr = RestoreSurfaces(lpwd, lpwds);

            if (++nCount>1)
                hr = HXR_FAIL;
            
            // Could not restore, so get out
            if (hr != DD_OK)
                break;
        }
        else if (DDERR_WASSTILLDRAWING == hr ||
	         DDERR_SURFACEBUSY == hr)
        {
            #ifdef _DEBUG
            //OutputDebugString("Could not lock...front buffer\n");
            #endif

            if (!bBlock)
                break;

            if (WaitForSingleObject(lpwds->dd.hAbort, 5) != WAIT_TIMEOUT)
                break;
        }
        else
        {
            #ifdef _DEBUG
            //OutputDebugString("Other error...front buffer\n");
            #endif
            
	    if (!bBlock)
	        break;

	    // Some other error...wait a bit and give up after 20 ms
	    if (++nOtherError > 4)
	    {
		hr = HXR_FAIL;
		break;
	    }

            if (WaitForSingleObject(lpwds->dd.hAbort, 5) != WAIT_TIMEOUT)
                break;
        }
    }

    }
    catch(...)
    {
        DumpDDInfo(lpwd, lpwds->dd.lpDDSurface, "IsFrontBuffer");
        hr = HXR_FAIL;
    }

    return hr;
}
#endif //0

#endif //HELIX_FEATURE_VS2
