/****************************************************************************
*
*                        Mesa 3-D graphics library
*                        Direct3D Driver Interface
*
*  ========================================================================
*
*   Copyright (C) 1991-2004 SciTech Software, Inc. All rights reserved.
*
*   Permission is hereby granted, free of charge, to any person obtaining a
*   copy of this software and associated documentation files (the "Software"),
*   to deal in the Software without restriction, including without limitation
*   the rights to use, copy, modify, merge, publish, distribute, sublicense,
*   and/or sell copies of the Software, and to permit persons to whom the
*   Software is furnished to do so, subject to the following conditions:
*
*   The above copyright notice and this permission notice shall be included
*   in all copies or substantial portions of the Software.
*
*   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
*   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
*   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
*   SCITECH SOFTWARE INC BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
*   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
*   OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*   SOFTWARE.
*
*  ======================================================================
*
* Language:     ANSI C
* Environment:  Windows 9x (Win32)
*
* Description:  Context handling.
*
****************************************************************************/

#include "dglcontext.h"

// Get compile errors without this. KeithH
#include "scitech.h"	// ibool, etc.

#ifdef _USE_GLD3_WGL
#include "gld_driver.h"

extern void _gld_mesa_warning(GLcontext *, char *);
extern void _gld_mesa_fatal(GLcontext *, char *);
#endif // _USE_GLD3_WGL

// TODO: Clean out old DX6-specific code from GLD 2.x CAD driver
// if it is no longer being built as part of GLDirect. (DaveM)

// ***********************************************************************

#define GLDERR_NONE     0
#define GLDERR_MEM      1
#define GLDERR_DDRAW    2
#define GLDERR_D3D      3
#define GLDERR_BPP      4

char szResourceWarning[] =
"GLDirect does not have enough video memory resources\n"
"to support the requested OpenGL rendering context.\n\n"
"You may have to reduce the current display resolution\n"
"to obtain satisfactory OpenGL performance.\n";

char szDDrawWarning[] =
"GLDirect is unable to initialize DirectDraw for the\n"
"requested OpenGL rendering context.\n\n"
"You will have to check the DirectX control panel\n"
"for further information.\n";

char szD3DWarning[] =
"GLDirect is unable to initialize Direct3D for the\n"
"requested OpenGL rendering context.\n\n"
"You may have to change the display mode resolution\n"
"color depth or check the DirectX control panel for\n"
"further information.\n";

char szBPPWarning[] =
"GLDirect is unable to use the selected color depth for\n"
"the requested OpenGL rendering context.\n\n"
"You will have to change the display mode resolution\n"
"color depth with the Display Settings control panel.\n";

int nContextError = GLDERR_NONE;

// ***********************************************************************

#define VENDORID_ATI 0x1002

static DWORD devATIRagePro[] = {
	0x4742, // 3D RAGE PRO BGA AGP 1X/2X
	0x4744, // 3D RAGE PRO BGA AGP 1X only
	0x4749, // 3D RAGE PRO BGA PCI 33 MHz
	0x4750, // 3D RAGE PRO PQFP PCI 33 MHz
	0x4751, // 3D RAGE PRO PQFP PCI 33 MHz limited 3D
	0x4C42, // 3D RAGE LT PRO BGA-312 AGP 133 MHz
	0x4C44, // 3D RAGE LT PRO BGA-312 AGP 66 MHz
	0x4C49, // 3D RAGE LT PRO BGA-312 PCI 33 MHz
	0x4C50, // 3D RAGE LT PRO BGA-256 PCI 33 MHz
	0x4C51, // 3D RAGE LT PRO BGA-256 PCI 33 MHz limited 3D
};

static DWORD devATIRageIIplus[] = {
	0x4755, // 3D RAGE II+
	0x4756, // 3D RAGE IIC PQFP PCI
	0x4757, // 3D RAGE IIC BGA AGP
	0x475A, // 3D RAGE IIC PQFP AGP
	0x4C47, // 3D RAGE LT-G
};

// ***********************************************************************

#ifndef _USE_GLD3_WGL
extern DGL_mesaFuncs mesaFuncs;
#endif

extern DWORD dwLogging;

#ifdef GLD_THREADS
#pragma message("compiling DGLCONTEXT.C vars for multi-threaded support")
CRITICAL_SECTION CriticalSection;		// for serialized access
DWORD		dwTLSCurrentContext = 0xFFFFFFFF;	// TLS index for current context
DWORD		dwTLSPixelFormat = 0xFFFFFFFF;		// TLS index for current pixel format
#endif
HGLRC		iCurrentContext = 0;		// Index of current context (static)
BOOL		bContextReady = FALSE;		// Context state ready ?

DGL_ctx		ctxlist[DGL_MAX_CONTEXTS];	// Context list

// ***********************************************************************

static BOOL bHaveWin95 = FALSE;
static BOOL bHaveWinNT = FALSE;
static BOOL bHaveWin2K = FALSE;

/****************************************************************************
REMARKS:
Detect the installed OS type.
****************************************************************************/
static void DetectOS(void)
{
    OSVERSIONINFO VersionInformation;
    LPOSVERSIONINFO lpVersionInformation = &VersionInformation;

    VersionInformation.dwOSVersionInfoSize = sizeof(VersionInformation);

	GetVersionEx(lpVersionInformation);

    switch (VersionInformation.dwPlatformId) {
    	case VER_PLATFORM_WIN32_WINDOWS:
			bHaveWin95 = TRUE;
			bHaveWinNT = FALSE;
			bHaveWin2K = FALSE;
            break;
    	case VER_PLATFORM_WIN32_NT:
			bHaveWin95 = FALSE;
			if (VersionInformation.dwMajorVersion <= 4) {
				bHaveWinNT = TRUE;
				bHaveWin2K = FALSE;
                }
            else {
				bHaveWinNT = FALSE;
				bHaveWin2K = TRUE;
                }
			break;
		case VER_PLATFORM_WIN32s:
			bHaveWin95 = FALSE;
			bHaveWinNT = FALSE;
			bHaveWin2K = FALSE;
			break;
        }
}

// ***********************************************************************

HWND hWndEvent = NULL;					// event monitor window
HWND hWndLastActive = NULL;				// last active client window
LONG __stdcall GLD_EventWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam);

// ***********************************************************************

// Checks if the HGLRC is valid in range of context list.
BOOL dglIsValidContext(
	HGLRC a)
{
	return ((int)a > 0 && (int)a <= DGL_MAX_CONTEXTS);
}

// ***********************************************************************

// Convert a HGLRC to a pointer into the context list.
DGL_ctx* dglGetContextAddress(
	const HGLRC a)
{
	if (dglIsValidContext(a))
		return &ctxlist[(int)a-1];
	return NULL;
}

// ***********************************************************************

// Return the current HGLRC (however it may be stored for multi-threading).
HGLRC dglGetCurrentContext(void)
{
#ifdef GLD_THREADS
	HGLRC hGLRC;
	// load from thread-specific instance
	if (glb.bMultiThreaded) {
		// protect against calls from arbitrary threads
		__try {
			hGLRC = (HGLRC)TlsGetValue(dwTLSCurrentContext);
		}
		__except(EXCEPTION_EXECUTE_HANDLER) {
			hGLRC = iCurrentContext;
		}
	}
	// load from global static var
	else {
		hGLRC = iCurrentContext;
	}
	return hGLRC;
#else
	return iCurrentContext;
#endif
}

// ***********************************************************************

// Set the current HGLRC (however it may be stored for multi-threading).
void dglSetCurrentContext(HGLRC hGLRC)
{
#ifdef GLD_THREADS
	// store in thread-specific instance
	if (glb.bMultiThreaded) {
		// protect against calls from arbitrary threads
		__try {
			TlsSetValue(dwTLSCurrentContext, (LPVOID)hGLRC);
		}
		__except(EXCEPTION_EXECUTE_HANDLER) {
			iCurrentContext = hGLRC;
		}
	}
	// store in global static var
	else {
		iCurrentContext = hGLRC;
	}
#else
	iCurrentContext = hGLRC;
#endif
}

// ***********************************************************************

// Return the current HDC only for a currently active HGLRC.
HDC dglGetCurrentDC(void)
{
	HGLRC hGLRC;
	DGL_ctx* lpCtx;

	hGLRC = dglGetCurrentContext();
	if (hGLRC) {
		lpCtx = dglGetContextAddress(hGLRC);
		return lpCtx->hDC;
	}
	return 0;
}

// ***********************************************************************

void dglInitContextState()
{
	int i;
	WNDCLASS wc;

#ifdef GLD_THREADS
	// Allocate thread local storage indexes for current context and pixel format
	dwTLSCurrentContext = TlsAlloc();
	dwTLSPixelFormat = TlsAlloc();
#endif

	dglSetCurrentContext(NULL); // No current rendering context

	 // Clear all context data
	ZeroMemory(ctxlist, sizeof(ctxlist[0]) * DGL_MAX_CONTEXTS);

	for (i=0; i<DGL_MAX_CONTEXTS; i++)
		ctxlist[i].bAllocated = FALSE; // Flag context as unused

	// This section of code crashes the dll in circumstances where the app
	// creates and destroys contexts.
/*
	// Register the class for our event monitor window
	wc.style = 0;
	wc.lpfnWndProc = GLD_EventWndProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = GetModuleHandle(NULL);
	wc.hIcon = LoadIcon(GetModuleHandle(NULL), IDI_APPLICATION);
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
	wc.lpszMenuName = NULL;
	wc.lpszClassName = "GLDIRECT";
	RegisterClass(&wc);

	// Create the non-visible window to monitor all broadcast messages
	hWndEvent = CreateWindowEx(
		WS_EX_TOOLWINDOW,"GLDIRECT","GLDIRECT",WS_POPUP,
		0,0,0,0,
		NULL,NULL,GetModuleHandle(NULL),NULL);
*/

#ifdef GLD_THREADS
	// Create a critical section object for serializing access to
	// DirectDraw and DDStereo create/destroy functions in multiple threads
	if (glb.bMultiThreaded)
		InitializeCriticalSection(&CriticalSection);
#endif

	// Context state is now initialized and ready
	bContextReady = TRUE;
}

// ***********************************************************************

void dglDeleteContextState()
{
	int i;
	static BOOL bOnceIsEnough = FALSE;

	// Only call once, from either DGL_exitDriver(), or DLL_PROCESS_DETACH
	if (bOnceIsEnough)
		return;
	bOnceIsEnough = TRUE;

	for (i=0; i<DGL_MAX_CONTEXTS; i++) {
		if (ctxlist[i].bAllocated == TRUE) {
			ddlogPrintf(DDLOG_WARN, "** Context %i not deleted - cleaning up.", (i+1));
			dglDeleteContext((HGLRC)(i+1));
		}
	}

	// Context state is no longer ready
	bContextReady = FALSE;

    // If executed when DLL unloads, DDraw objects may be invalid.
    // So catch any page faults with this exception handler.
__try {

	// Release final DirectDraw interfaces
	if (glb.bDirectDrawPersistant) {
//		RELEASE(glb.lpGlobalPalette);
//		RELEASE(glb.lpDepth4);
//		RELEASE(glb.lpBack4);
//		RELEASE(glb.lpPrimary4);
//	    RELEASE(glb.lpDD4);
    }
}
__except(EXCEPTION_EXECUTE_HANDLER) {
    ddlogPrintf(DDLOG_WARN, "Exception raised in dglDeleteContextState.");
}

	// Destroy our event monitor window
	if (hWndEvent) {
		DestroyWindow(hWndEvent);
		hWndEvent = hWndLastActive = NULL;
	}

#ifdef GLD_THREADS
	// Destroy the critical section object
	if (glb.bMultiThreaded)
		DeleteCriticalSection(&CriticalSection);

	// Release thread local storage indexes for current HGLRC and pixel format
	TlsFree(dwTLSPixelFormat);
	TlsFree(dwTLSCurrentContext);
#endif
}

// ***********************************************************************

// Application Window message handler interception
static LONG __stdcall dglWndProc(
	HWND hwnd,
	UINT msg,
	WPARAM wParam,
	LPARAM lParam)
{
	DGL_ctx* 	lpCtx = NULL;
	LONG 		lpfnWndProc = 0L;
	int  		i;
	HGLRC 		hGLRC;
	RECT 		rect;
	PAINTSTRUCT	ps;
    BOOL        bQuit = FALSE;
    BOOL        bMain = FALSE;
    LONG        rc;

    // Get the window's message handler *before* it is unhooked in WM_DESTROY

    // Is this the main window?
    if (hwnd == glb.hWndActive) {
        bMain = TRUE;
        lpfnWndProc = glb.lpfnWndProc;
    }
    // Search for DGL context matching window handle
    for (i=0; i<DGL_MAX_CONTEXTS; i++) {
	    if (ctxlist[i].hWnd == hwnd) {
	        lpCtx = &ctxlist[i];
	        lpfnWndProc = lpCtx->lpfnWndProc;
		    break;
        }
    }
	// Not one of ours...
	if (!lpfnWndProc)
	    return DefWindowProc(hwnd, msg, wParam, lParam);

    // Intercept messages amd process *before* passing on to window
	switch (msg) {
#ifdef _USE_GLD3_WGL
	case WM_DISPLAYCHANGE:
		glb.bPixelformatsDirty = TRUE;
		break;
#endif
	case WM_ACTIVATEAPP:
		glb.bAppActive = (BOOL)wParam;
		ddlogPrintf(DDLOG_INFO, "Calling app has been %s", glb.bAppActive ? "activated" : "de-activated");
		break;
	case WM_ERASEBKGND:
		// Eat the GDI erase event for the GL window
        if (!lpCtx || !lpCtx->bHasBeenCurrent)
            break;
		lpCtx->bGDIEraseBkgnd = TRUE;
		return TRUE;
	case WM_PAINT:
		// Eat the invalidated update region if render scene is in progress
        if (!lpCtx || !lpCtx->bHasBeenCurrent)
            break;
		if (lpCtx->bFrameStarted) {
			if (GetUpdateRect(hwnd, &rect, FALSE)) {
				BeginPaint(hwnd, &ps);
				EndPaint(hwnd, &ps);
				ValidateRect(hwnd, &rect);
				return TRUE;
				}
			}
		break;
	}
	// Call the appropriate window message handler
	rc = CallWindowProc((WNDPROC)lpfnWndProc, hwnd, msg, wParam, lParam);

    // Intercept messages and process *after* passing on to window
	switch (msg) {
    case WM_QUIT:
	case WM_DESTROY:
        bQuit = TRUE;
		if (lpCtx && lpCtx->bAllocated) {
			ddlogPrintf(DDLOG_WARN, "WM_DESTROY detected for HWND=%X, HDC=%X, HGLRC=%d", hwnd, lpCtx->hDC, i+1);
			dglDeleteContext((HGLRC)(i+1));
		}
		break;
#if 0
	case WM_SIZE:
		// Resize surfaces to fit window but not viewport (in case app did not bother)
        if (!lpCtx || !lpCtx->bHasBeenCurrent)
            break;
		w = LOWORD(lParam);
		h = HIWORD(lParam);
		if (lpCtx->dwWidth < w || lpCtx->dwHeight < h) {
			if (!dglWglResizeBuffers(lpCtx->glCtx, TRUE))
                 dglWglResizeBuffers(lpCtx->glCtx, FALSE);
        }
		break;
#endif
    }

    // If the main window is quitting, then so should we...
    if (bMain && bQuit) {
		ddlogPrintf(DDLOG_SYSTEM, "shutting down after WM_DESTROY detected for main HWND=%X", hwnd);
        dglDeleteContextState();
        dglExitDriver();
    }

    return rc;
}

// ***********************************************************************

// Driver Window message handler
static LONG __stdcall GLD_EventWndProc(
	HWND hwnd,
	UINT msg,
	WPARAM wParam,
	LPARAM lParam)
{
	switch (msg) {
        // May be sent by splash screen dialog on exit
        case WM_ACTIVATE:
            if (LOWORD(wParam) == WA_ACTIVE && glb.hWndActive) {
                SetForegroundWindow(glb.hWndActive);
                return 0;
                }
            break;
	}
	return DefWindowProc(hwnd, msg, wParam, lParam);
}

// ***********************************************************************

// Intercepted Keyboard handler for detecting hot keys.
LRESULT CALLBACK dglKeyProc(
	int code,
	WPARAM wParam,
	LPARAM lParam)
{
	HWND hWnd, hWndFrame;
	HGLRC hGLRC = NULL;
	DGL_ctx* lpCtx = NULL;
	int cmd = 0, dx1 = 0, dx2 = 0, i;
	static ibool bAltPressed = false;
	static ibool bCtrlPressed = false;
	static ibool bShiftPressed = false;
    RECT r, rf, rc;
    POINT pt;
    ibool bForceReshape = false;

	return CallNextHookEx(hKeyHook, code, wParam, lParam);
}

// ***********************************************************************

HWND hWndMatch;

// Window handle enumeration procedure.
BOOL CALLBACK dglEnumChildProc(
    HWND hWnd,
    LPARAM lParam)
{
    RECT rect;

    // Find window handle with matching client rect.
    GetClientRect(hWnd, &rect);
    if (EqualRect(&rect, (RECT*)lParam)) {
        hWndMatch = hWnd;
        return FALSE;
        }
    // Continue with next child window.
    return TRUE;
}

// ***********************************************************************

// Find window handle with matching client rect.
HWND dglFindWindowRect(
    RECT* pRect)
{
    hWndMatch = NULL;
    EnumChildWindows(GetForegroundWindow(), dglEnumChildProc, (LPARAM)pRect);
    return hWndMatch;
}

// ***********************************************************************
#ifndef _USE_GLD3_WGL
void dglChooseDisplayMode(
	DGL_ctx *lpCtx)
{
	// Note: Choose an exact match if possible.

	int				i;
	DWORD			area;
	DWORD			bestarea;
	DDSURFACEDESC2	*lpDDSD		= NULL;	// Mode list pointer
	DDSURFACEDESC2	*lpBestDDSD = NULL;	// Pointer to best

	lpDDSD = glb.lpDisplayModes;
	for (i=0; i<glb.nDisplayModeCount; i++, lpDDSD++) {
		if ((lpDDSD->dwWidth == lpCtx->dwWidth) &&
			(lpDDSD->dwHeight == lpCtx->dwHeight))
			goto matched; // Mode has been exactly matched
		// Choose modes that are larger in both dimensions than
		// the window, but smaller in area than the current best.
		if ( (lpDDSD->dwWidth >= lpCtx->dwWidth) &&
			 (lpDDSD->dwHeight >= lpCtx->dwHeight))
		{
			if (lpBestDDSD == NULL) {
				lpBestDDSD = lpDDSD;
				bestarea = lpDDSD->dwWidth * lpDDSD->dwHeight;
				continue;
			}
			area = lpDDSD->dwWidth * lpDDSD->dwHeight;
			if (area < bestarea) {
				lpBestDDSD = lpDDSD;
				bestarea = area;
			}
		}
	}

	// Safety check
	if (lpBestDDSD == NULL) {
		ddlogMessage(DDLOG_CRITICAL, "dglChooseDisplayMode");
		return;
	}

	lpCtx->dwModeWidth = lpBestDDSD->dwWidth;
	lpCtx->dwModeHeight = lpBestDDSD->dwHeight;
matched:
	ddlogPrintf(DDLOG_INFO, "Matched (%ldx%ld) to (%ldx%ld)",
		lpCtx->dwWidth, lpCtx->dwHeight, lpCtx->dwModeWidth, lpCtx->dwModeHeight);
}
#endif // _USE_GLD3_WGL
// ***********************************************************************

static BOOL IsDevice(
	DWORD *lpDeviceIdList,
	DWORD dwDeviceId,
	int count)
{
	int i;

	for (i=0; i<count; i++)
		if (dwDeviceId == lpDeviceIdList[i])
			return TRUE;

	return FALSE;
}

// ***********************************************************************

void dglTestForBrokenCards(
	DGL_ctx *lpCtx)
{
#ifndef _GLD3
	DDDEVICEIDENTIFIER	dddi; // DX6 device identifier

	// Sanity check.
	if (lpCtx == NULL) {
		// Testing for broken cards is sensitive area, so we don't want
		// anything saying "broken cards" in the error message. ;)
		ddlogMessage(DDLOG_ERROR, "Null context passed to TFBC\n");
		return;
	}

	if (lpCtx->lpDD4 == NULL) {
		// Testing for broken cards is sensitive area, so we don't want
		// anything saying "broken cards" in the error message. ;)
		ddlogMessage(DDLOG_ERROR, "Null DD4 passed to TFBC\n");
		return;
	}

	// Microsoft really fucked up with the GetDeviceIdentifier function
	// on Windows 2000, since it locks up on stock driers on the CD. Updated
	// drivers from vendors appear to work, but we can't identify the drivers
	// without this function!!! For now we skip these tests on Windows 2000.
	if ((GetVersion() & 0x80000000UL) == 0)
		return;

	// Obtain device info
	if (FAILED(IDirectDraw4_GetDeviceIdentifier(lpCtx->lpDD4, &dddi, 0)))
		return;

	// Useful info. Log it.
	ddlogPrintf(DDLOG_INFO, "DirectDraw: VendorId=0x%x, DeviceId=0x%x", dddi.dwVendorId, dddi.dwDeviceId);

	// Vendor 1: ATI
	if (dddi.dwVendorId == VENDORID_ATI) {
		// Test A: ATI Rage PRO
		if (IsDevice(devATIRagePro, dddi.dwDeviceId, sizeof(devATIRagePro)))
			glb.bUseMipmaps = FALSE;
		// Test B: ATI Rage II+
		if (IsDevice(devATIRageIIplus, dddi.dwDeviceId, sizeof(devATIRageIIplus)))
			glb.bEmulateAlphaTest = TRUE;
	}

	// Vendor 2: Matrox
	if (dddi.dwVendorId == 0x102B) {
		// Test: Matrox G400 stencil buffer support does not work for AutoCAD
		if (dddi.dwDeviceId == 0x0525) {
			lpCtx->lpPF->pfd.cStencilBits = 0;
			if (lpCtx->lpPF->iZBufferPF != -1) {
				glb.lpZBufferPF[lpCtx->lpPF->iZBufferPF].dwStencilBitDepth = 0;
				glb.lpZBufferPF[lpCtx->lpPF->iZBufferPF].dwStencilBitMask = 0;
				glb.lpZBufferPF[lpCtx->lpPF->iZBufferPF].dwFlags &= ~DDPF_STENCILBUFFER;
			}
		}
	}
#endif // _GLD3
}

// ***********************************************************************

BOOL dglCreateContextBuffers(
	HDC a,
	DGL_ctx *lpCtx,
	BOOL bFallback)
{
	HRESULT				hResult;

	int					i;
//	HGLRC				hGLRC;
//	DGL_ctx*			lpCtx;

#ifndef _USE_GLD3_WGL
	DWORD				dwFlags;
	DDSURFACEDESC2		ddsd2;
	DDSCAPS2			ddscaps2;
	LPDIRECTDRAWCLIPPER	lpddClipper;
	D3DDEVICEDESC		D3DHWDevDesc;	// Direct3D Hardware description
	D3DDEVICEDESC		D3DHELDevDesc;	// Direct3D Hardware Emulation Layer
#endif // _USE_GLD3_WGL

	float				inv_aspect;

	GLenum				bDoubleBuffer;	// TRUE if double buffer required
	GLenum				bDepthBuffer;	// TRUE if depth buffer required

	const PIXELFORMATDESCRIPTOR	*lpPFD = &lpCtx->lpPF->pfd;

	// Vars for Mesa visual
	DWORD				dwDepthBits		= 0;
	DWORD				dwStencilBits	= 0;
	DWORD				dwAlphaBits		= 0;
	DWORD				bAlphaSW		= GL_FALSE;
	DWORD				bDouble			= GL_FALSE;

	DDSURFACEDESC2 		ddsd2DisplayMode;
	BOOL				bFullScrnWin	= FALSE;	// fullscreen-size window ?
	DDBLTFX 			ddbltfx;
	DWORD				dwMemoryType 	= (bFallback) ? DDSCAPS_SYSTEMMEMORY : glb.dwMemoryType;
	BOOL				bBogusWindow	= FALSE;	// non-drawable window ?
	DWORD               dwColorRef      = 0;        // GDI background color
	RECT				rcDst;						// GDI window rect
	POINT				pt;							// GDI window point

	// Palette used for creating default global palette
	PALETTEENTRY	ppe[256];

#ifndef _USE_GLD3_WGL
	// Vertex buffer description. Used for creation of vertex buffers
	D3DVERTEXBUFFERDESC vbufdesc;
#endif // _USE_GLD3_WGL

#define DDLOG_CRITICAL_OR_WARN	(bFallback ? DDLOG_CRITICAL : DDLOG_WARN)

	ddlogPrintf(DDLOG_SYSTEM, "dglCreateContextBuffers for HDC=%X", a);
    nContextError = GLDERR_NONE;

#ifdef GLD_THREADS
	// Serialize access to DirectDraw object creation or DDS start
	if (glb.bMultiThreaded)
		EnterCriticalSection(&CriticalSection);
#endif

	// Check for back buffer
	bDoubleBuffer = GL_TRUE; //(lpPFD->dwFlags & PFD_DOUBLEBUFFER) ? GL_TRUE : GL_FALSE;
	// Since we always do back buffering, check if we emulate front buffering
	lpCtx->EmulateSingle =
		(lpPFD->dwFlags & PFD_DOUBLEBUFFER) ? FALSE : TRUE;
#if 0	// Don't have to mimic MS OpenGL behavior for front-buffering (DaveM)
	lpCtx->EmulateSingle |=
		(lpPFD->dwFlags & PFD_SUPPORT_GDI) ? TRUE : FALSE;
#endif

	// Check for depth buffer
	bDepthBuffer = (lpPFD->cDepthBits) ? GL_TRUE : GL_FALSE;

	lpCtx->bDoubleBuffer = bDoubleBuffer;
	lpCtx->bDepthBuffer = bDepthBuffer;

	// Set the Fullscreen flag for the context.
//	lpCtx->bFullscreen = glb.bFullscreen;

	// Obtain the dimensions of the rendering window
	lpCtx->hDC = a; // Cache DC
	lpCtx->hWnd = WindowFromDC(lpCtx->hDC);
	// Check for non-window DC = memory DC ?
	if (lpCtx->hWnd == NULL) {
        // bitmap memory contexts are always single-buffered
        lpCtx->EmulateSingle = TRUE;
		bBogusWindow = TRUE;
		ddlogPrintf(DDLOG_INFO, "Non-Window Memory Device Context");
		if (GetClipBox(lpCtx->hDC, &lpCtx->rcScreenRect) == ERROR) {
			ddlogMessage(DDLOG_WARN, "GetClipBox failed in dglCreateContext\n");
			SetRect(&lpCtx->rcScreenRect, 0, 0, 0, 0);
		}
	}
	else if (!GetClientRect(lpCtx->hWnd, &lpCtx->rcScreenRect)) {
		bBogusWindow = TRUE;
		ddlogMessage(DDLOG_WARN, "GetClientRect failed in dglCreateContext\n");
		SetRect(&lpCtx->rcScreenRect, 0, 0, 0, 0);
	}
	lpCtx->dwWidth = lpCtx->rcScreenRect.right - lpCtx->rcScreenRect.left;
	lpCtx->dwHeight = lpCtx->rcScreenRect.bottom - lpCtx->rcScreenRect.top;

	ddlogPrintf(DDLOG_INFO, "Input window %X: w=%i, h=%i",
							lpCtx->hWnd, lpCtx->dwWidth, lpCtx->dwHeight);

	// What if app only zeroes one dimension instead of both? (DaveM)
	if ( (lpCtx->dwWidth == 0) || (lpCtx->dwHeight == 0) ) {
		// Make the buffer size something sensible
		lpCtx->dwWidth = 8;
		lpCtx->dwHeight = 8;
	}

	// Set defaults
	lpCtx->dwModeWidth = lpCtx->dwWidth;
	lpCtx->dwModeHeight = lpCtx->dwHeight;
/*
	// Find best display mode for fullscreen
	if (glb.bFullscreen || !glb.bPrimary) {
		dglChooseDisplayMode(lpCtx);
	}
*/
	// Misc initialisation
	lpCtx->bCanRender = FALSE; // No rendering allowed yet
	lpCtx->bSceneStarted = FALSE;
	lpCtx->bFrameStarted = FALSE;

	// Detect OS (specifically 'Windows 2000' or 'Windows XP')
	DetectOS();

	// NOTE: WinNT not supported
	ddlogPrintf(DDLOG_INFO, "OS: %s", bHaveWin95 ? "Win9x" : (bHaveWin2K ? "Win2000/XP" : "Unsupported") );

	// Test for Fullscreen
	if (bHaveWin95) { // Problems with fullscreen on Win2K/XP
		if ((GetSystemMetrics(SM_CXSCREEN) == lpCtx->dwWidth) && 
			(GetSystemMetrics(SM_CYSCREEN) == lpCtx->dwHeight))
		{
			// Workaround for some apps that crash when going fullscreen.
			//lpCtx->bFullscreen = TRUE;
		}
		
	}

#ifdef _USE_GLD3_WGL
	_gldDriver.CreateDrawable(lpCtx, glb.bDirectDrawPersistant, glb.bPersistantBuffers);
#else
	// Check if DirectDraw has already been created by original GLRC (DaveM)
	if (glb.bDirectDrawPersistant && glb.bDirectDraw) {
		lpCtx->lpDD4 = glb.lpDD4;
		IDirectDraw4_AddRef(lpCtx->lpDD4);
		goto SkipDirectDrawCreate;
	}

	// Create DirectDraw object
	if (glb.bPrimary)
		hResult = DirectDrawCreate(NULL, &lpCtx->lpDD1, NULL);
	else {
		// A non-primary device is to be used.
		// Force context to be Fullscreen, secondary adaptors can not
		// be used in a window.
		hResult = DirectDrawCreate(&glb.ddGuid, &lpCtx->lpDD1, NULL);
		lpCtx->bFullscreen = TRUE;
	}
	if (FAILED(hResult)) {
		MessageBox(NULL, "Unable to initialize DirectDraw", "GLDirect", MB_OK);
		ddlogError(DDLOG_CRITICAL_OR_WARN, "Unable to create DirectDraw interface", hResult);
        nContextError = GLDERR_DDRAW;
		goto return_with_error;
	}

	// Query for DX6 IDirectDraw4.
	hResult = IDirectDraw_QueryInterface(lpCtx->lpDD1,
										 &IID_IDirectDraw4,
										 (void**)&lpCtx->lpDD4);
	if (FAILED(hResult)) {
		MessageBox(NULL, "GLDirect requires DirectX 6.0 or above", "GLDirect", MB_OK);
		ddlogError(DDLOG_CRITICAL_OR_WARN, "Unable to create DirectDraw4 interface", hResult);
        nContextError = GLDERR_DDRAW;
		goto return_with_error;
	}

	// Cache DirectDraw interface for subsequent GLRCs
	if (glb.bDirectDrawPersistant && !glb.bDirectDraw) {
		glb.lpDD4 = lpCtx->lpDD4;
		IDirectDraw4_AddRef(glb.lpDD4);
		glb.bDirectDraw = TRUE;
	}
SkipDirectDrawCreate:

	// Now we have a DD4 interface we can check for broken cards
	dglTestForBrokenCards(lpCtx);

	// Test if primary device can use flipping instead of blitting
	ZeroMemory(&ddsd2DisplayMode, sizeof(ddsd2DisplayMode));
	ddsd2DisplayMode.dwSize = sizeof(ddsd2DisplayMode);
	hResult = IDirectDraw4_GetDisplayMode(
					lpCtx->lpDD4,
					&ddsd2DisplayMode);
	if (SUCCEEDED(hResult)) {
		if ( (lpCtx->dwWidth == ddsd2DisplayMode.dwWidth) &&
				 (lpCtx->dwHeight == ddsd2DisplayMode.dwHeight) ) {
			// We have a fullscreen-size window
			bFullScrnWin = TRUE;
			// OK to use DirectDraw fullscreen mode ?
			if (glb.bPrimary && !glb.bFullscreenBlit && !lpCtx->EmulateSingle && !glb.bDirectDrawPersistant) {
				lpCtx->bFullscreen = TRUE;
				ddlogMessage(DDLOG_INFO, "Primary upgraded to page flipping.\n");
			}
		}
		// Cache the display mode dimensions
		lpCtx->dwModeWidth = ddsd2DisplayMode.dwWidth;
		lpCtx->dwModeHeight = ddsd2DisplayMode.dwHeight;
	}

	// Clamp the effective window dimensions to primary surface.
	// We need to do this for D3D viewport dimensions even if wide
	// surfaces are supported. This also is a good idea for handling
	// whacked-out window dimensions passed for non-drawable windows
	// like Solid Edge. (DaveM)
	if (lpCtx->dwWidth > ddsd2DisplayMode.dwWidth)
		lpCtx->dwWidth = ddsd2DisplayMode.dwWidth;
	if (lpCtx->dwHeight > ddsd2DisplayMode.dwHeight)
		lpCtx->dwHeight = ddsd2DisplayMode.dwHeight;

	// Check for non-RGB desktop resolution
	if (!lpCtx->bFullscreen && ddsd2DisplayMode.ddpfPixelFormat.dwRGBBitCount <= 8) {
		ddlogPrintf(DDLOG_CRITICAL_OR_WARN, "Desktop color depth %d bpp not supported",
			ddsd2DisplayMode.ddpfPixelFormat.dwRGBBitCount);
        nContextError = GLDERR_BPP;
		goto return_with_error;
	}
#endif // _USE_GLD3_WGL

	ddlogPrintf(DDLOG_INFO, "Window: w=%i, h=%i (%s)",
							lpCtx->dwWidth,
							lpCtx->dwHeight,
							lpCtx->bFullscreen ? "fullscreen" : "windowed");

#ifndef _USE_GLD3_WGL
	// Obtain ddraw caps
    ZeroMemory(&lpCtx->ddCaps, sizeof(DDCAPS));
	lpCtx->ddCaps.dwSize = sizeof(DDCAPS);
	if (glb.bHardware) {
		// Get HAL caps
		IDirectDraw4_GetCaps(lpCtx->lpDD4, &lpCtx->ddCaps, NULL);
	} else {
		// Get HEL caps
		IDirectDraw4_GetCaps(lpCtx->lpDD4, NULL, &lpCtx->ddCaps);
	}

	// If this flag is present then we can't default to Mesa
	// SW rendering between BeginScene() and EndScene().
	if (lpCtx->ddCaps.dwCaps2 & DDCAPS2_NO2DDURING3DSCENE) {
		ddlogMessage(DDLOG_INFO,
			"Warning          : No 2D allowed during 3D scene.\n");
	}

	// Query for DX6 Direct3D3 interface
	hResult = IDirectDraw4_QueryInterface(lpCtx->lpDD4,
										  &IID_IDirect3D3,
										  (void**)&lpCtx->lpD3D3);
	if (FAILED(hResult)) {
		MessageBox(NULL, "Unable to initialize Direct3D", "GLDirect", MB_OK);
		ddlogError(DDLOG_CRITICAL_OR_WARN, "Unable to create Direct3D interface", hResult);
        nContextError = GLDERR_D3D;
		goto return_with_error;
	}

	// Context creation
	if (lpCtx->bFullscreen) {
		// FULLSCREEN

        // Disable warning popups when in fullscreen mode
        ddlogWarnOption(FALSE);

		// Have to release persistant primary surface if fullscreen mode
		if (glb.bDirectDrawPersistant && glb.bDirectDrawPrimary) {
			RELEASE(glb.lpPrimary4);
			glb.bDirectDrawPrimary = FALSE;
		}

		dwFlags = DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_ALLOWREBOOT;
		if (glb.bFastFPU)
			dwFlags |= DDSCL_FPUSETUP;	// fast FPU setup optional (DaveM)
		hResult = IDirectDraw4_SetCooperativeLevel(lpCtx->lpDD4, lpCtx->hWnd, dwFlags);
		if (FAILED(hResult)) {
			ddlogError(DDLOG_CRITICAL_OR_WARN, "Unable to set Exclusive Fullscreen mode", hResult);
			goto return_with_error;
		}

		hResult = IDirectDraw4_SetDisplayMode(lpCtx->lpDD4,
											  lpCtx->dwModeWidth,
											  lpCtx->dwModeHeight,
											  lpPFD->cColorBits,
											  0,
											  0);
		if (FAILED(hResult)) {
			ddlogError(DDLOG_CRITICAL_OR_WARN, "SetDisplayMode failed", hResult);
			goto return_with_error;
		}

		// ** The display mode has changed, so dont use MessageBox! **

		ZeroMemory(&ddsd2, sizeof(ddsd2));
		ddsd2.dwSize = sizeof(ddsd2);

		if (bDoubleBuffer) {
			// Double buffered
			// Primary surface
			ddsd2.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
			ddsd2.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE |
								   DDSCAPS_FLIP |
								   DDSCAPS_COMPLEX |
								   DDSCAPS_3DDEVICE |
								   dwMemoryType;
			ddsd2.dwBackBufferCount = 1;

			hResult = IDirectDraw4_CreateSurface(lpCtx->lpDD4, &ddsd2, &lpCtx->lpFront4, NULL);
			if (FAILED(hResult)) {
				ddlogError(DDLOG_CRITICAL_OR_WARN, "CreateSurface (primary) failed", hResult);
                nContextError = GLDERR_MEM;
				goto return_with_error;
			}

			// Render target surface
			ZeroMemory(&ddscaps2, sizeof(ddscaps2)); // Clear the entire struct.
			ddscaps2.dwCaps = DDSCAPS_BACKBUFFER;
			hResult = IDirectDrawSurface4_GetAttachedSurface(lpCtx->lpFront4, &ddscaps2, &lpCtx->lpBack4);
			if (FAILED(hResult)) {
				ddlogError(DDLOG_CRITICAL_OR_WARN, "GetAttachedSurface failed", hResult);
                nContextError = GLDERR_MEM;
				goto return_with_error;
			}
		} else {
			// Single buffered
			// Primary surface
			ddsd2.dwFlags = DDSD_CAPS;
			ddsd2.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE |
								   //DDSCAPS_3DDEVICE |
								   dwMemoryType;

			hResult = IDirectDraw4_CreateSurface(lpCtx->lpDD4, &ddsd2, &lpCtx->lpFront4, NULL);
			if (FAILED(hResult)) {
				ddlogError(DDLOG_CRITICAL_OR_WARN, "CreateSurface (primary) failed", hResult);
                nContextError = GLDERR_MEM;
				goto return_with_error;
			}

			lpCtx->lpBack4 = NULL;
		}
	} else {
		// WINDOWED

        // OK to enable warning popups in windowed mode
        ddlogWarnOption(glb.bMessageBoxWarnings);

		dwFlags = DDSCL_NORMAL;
		if (glb.bMultiThreaded)
			dwFlags |= DDSCL_MULTITHREADED;
		if (glb.bFastFPU)
			dwFlags |= DDSCL_FPUSETUP;	// fast FPU setup optional (DaveM)
		hResult = IDirectDraw4_SetCooperativeLevel(lpCtx->lpDD4,
												  lpCtx->hWnd,
												  dwFlags);
		if (FAILED(hResult)) {
			ddlogError(DDLOG_CRITICAL_OR_WARN, "Unable to set Normal coop level", hResult);
			goto return_with_error;
		}
		// Has Primary surface already been created for original GLRC ?
		// Note this can only be applicable for windowed modes
		if (glb.bDirectDrawPersistant && glb.bDirectDrawPrimary) {
			lpCtx->lpFront4 = glb.lpPrimary4;
			IDirectDrawSurface4_AddRef(lpCtx->lpFront4);
			// Update the window on the default clipper
			IDirectDrawSurface4_GetClipper(lpCtx->lpFront4, &lpddClipper);
			IDirectDrawClipper_SetHWnd(lpddClipper, 0, lpCtx->hWnd);
			IDirectDrawClipper_Release(lpddClipper);
			goto SkipPrimaryCreate;
		}

		// Primary surface
		ZeroMemory(&ddsd2, sizeof(ddsd2));
		ddsd2.dwSize = sizeof(ddsd2);
		ddsd2.dwFlags = DDSD_CAPS;
		ddsd2.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
		hResult = IDirectDraw4_CreateSurface(lpCtx->lpDD4, &ddsd2, &lpCtx->lpFront4, NULL);
		if (FAILED(hResult)) {
			ddlogError(DDLOG_CRITICAL_OR_WARN, "CreateSurface (primary) failed", hResult);
            nContextError = GLDERR_MEM;
			goto return_with_error;
		}

		// Cache Primary surface for subsequent GLRCs
		// Note this can only be applicable to subsequent windowed modes
		if (glb.bDirectDrawPersistant && !glb.bDirectDrawPrimary) {
			glb.lpPrimary4 = lpCtx->lpFront4;
			IDirectDrawSurface4_AddRef(glb.lpPrimary4);
			glb.bDirectDrawPrimary = TRUE;
		}

		// Clipper object
		hResult = DirectDrawCreateClipper(0, &lpddClipper, NULL);
		if (FAILED(hResult)) {
			ddlogError(DDLOG_CRITICAL_OR_WARN, "CreateClipper failed", hResult);
			goto return_with_error;
		}
		hResult = IDirectDrawClipper_SetHWnd(lpddClipper, 0, lpCtx->hWnd);
		if (FAILED(hResult)) {
			RELEASE(lpddClipper);
			ddlogError(DDLOG_CRITICAL_OR_WARN, "SetHWnd failed", hResult);
			goto return_with_error;
		}
		hResult = IDirectDrawSurface4_SetClipper(lpCtx->lpFront4, lpddClipper);
		RELEASE(lpddClipper); // We have finished with it.
		if (FAILED(hResult)) {
			ddlogError(DDLOG_CRITICAL_OR_WARN, "SetClipper failed", hResult);
			goto return_with_error;
		}
SkipPrimaryCreate:

		if (bDoubleBuffer) {
			// Render target surface
			ZeroMemory(&ddsd2, sizeof(ddsd2));
			ddsd2.dwSize = sizeof(ddsd2);
			ddsd2.dwFlags        = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
			ddsd2.dwWidth        = lpCtx->dwWidth;
			ddsd2.dwHeight       = lpCtx->dwHeight;
			ddsd2.ddsCaps.dwCaps = DDSCAPS_3DDEVICE |
								   DDSCAPS_OFFSCREENPLAIN |
								   dwMemoryType;

			// Reserve the entire desktop size for persistant buffers option
			if (glb.bDirectDrawPersistant && glb.bPersistantBuffers) {
				ddsd2.dwWidth = ddsd2DisplayMode.dwWidth;
				ddsd2.dwHeight = ddsd2DisplayMode.dwHeight;
			}
			// Re-use original back buffer if persistant buffers exist
			if (glb.bDirectDrawPersistant && glb.bPersistantBuffers && glb.lpBack4)
				hResult = IDirectDrawSurface4_AddRef(lpCtx->lpBack4 = glb.lpBack4);
			else
				hResult = IDirectDraw4_CreateSurface(lpCtx->lpDD4, &ddsd2, &lpCtx->lpBack4, NULL);
			if (FAILED(hResult)) {
				ddlogError(DDLOG_CRITICAL_OR_WARN, "Create Backbuffer failed", hResult);
                nContextError = GLDERR_MEM;
				goto return_with_error;
			}
			if (glb.bDirectDrawPersistant && glb.bPersistantBuffers && !glb.lpBack4)
				IDirectDrawSurface4_AddRef(glb.lpBack4 = lpCtx->lpBack4);
		} else {
			lpCtx->lpBack4 = NULL;
		}
	}

	//
	// Now create the Z-buffer
	//
	lpCtx->bStencil = FALSE; // Default to no stencil buffer
	if (bDepthBuffer && (lpCtx->lpPF->iZBufferPF != -1)) {
		// Get z-buffer dimensions from the render target
		// Setup the surface desc for the z-buffer.
		ZeroMemory(&ddsd2, sizeof(ddsd2));
		ddsd2.dwSize = sizeof(ddsd2);
		ddsd2.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT;
		ddsd2.ddsCaps.dwCaps = DDSCAPS_ZBUFFER | dwMemoryType;
		ddsd2.dwWidth = lpCtx->dwWidth;
		ddsd2.dwHeight = lpCtx->dwHeight;
		memcpy(&ddsd2.ddpfPixelFormat,
			&glb.lpZBufferPF[lpCtx->lpPF->iZBufferPF],
			sizeof(DDPIXELFORMAT) );

		// Reserve the entire desktop size for persistant buffers option
		if (glb.bDirectDrawPersistant && glb.bPersistantBuffers) {
			ddsd2.dwWidth = ddsd2DisplayMode.dwWidth;
			ddsd2.dwHeight = ddsd2DisplayMode.dwHeight;
		}

		// Create a z-buffer
		if (glb.bDirectDrawPersistant && glb.bPersistantBuffers && glb.lpDepth4)
			hResult = IDirectDrawSurface4_AddRef(lpCtx->lpDepth4 = glb.lpDepth4);
		else
			hResult = IDirectDraw4_CreateSurface(lpCtx->lpDD4, &ddsd2, &lpCtx->lpDepth4, NULL);
		if (FAILED(hResult)) {
			ddlogError(DDLOG_CRITICAL_OR_WARN, "CreateSurface (ZBuffer) failed", hResult);
            nContextError = GLDERR_MEM;
			goto return_with_error;
		}
		if (glb.bDirectDrawPersistant && glb.bPersistantBuffers && !glb.lpDepth4)
			IDirectDrawSurface4_AddRef(glb.lpDepth4 = lpCtx->lpDepth4);
		else if (glb.bDirectDrawPersistant && glb.bPersistantBuffers && glb.lpDepth4 && glb.lpBack4)
			IDirectDrawSurface4_DeleteAttachedSurface(glb.lpBack4, 0, glb.lpDepth4);

		// Attach Zbuffer to render target
		TRY(IDirectDrawSurface4_AddAttachedSurface(
			bDoubleBuffer ? lpCtx->lpBack4 : lpCtx->lpFront4,
			lpCtx->lpDepth4),
			"dglCreateContext: Attach Zbuffer");
		if (glb.lpZBufferPF[lpCtx->lpPF->iZBufferPF].dwFlags & DDPF_STENCILBUFFER) {
			lpCtx->bStencil = TRUE;
			ddlogMessage(DDLOG_INFO, "Depth buffer has stencil\n");
		}
	}

	// Clear all back buffers and Z-buffers in case of memory recycling.
	ZeroMemory(&ddbltfx, sizeof(ddbltfx));
	ddbltfx.dwSize = sizeof(ddbltfx);
	IDirectDrawSurface4_Blt(lpCtx->lpBack4, NULL, NULL, NULL,
		DDBLT_COLORFILL | DDBLT_WAIT, &ddbltfx);
	if (lpCtx->lpDepth4)
		IDirectDrawSurface4_Blt(lpCtx->lpDepth4, NULL, NULL, NULL,
			DDBLT_COLORFILL | DDBLT_WAIT, &ddbltfx);

	// Now that we have a Z-buffer we can create the 3D device
	hResult = IDirect3D3_CreateDevice(lpCtx->lpD3D3,
									  &glb.d3dGuid,
									  bDoubleBuffer ? lpCtx->lpBack4 : lpCtx->lpFront4,
									  &lpCtx->lpDev3,
									  NULL);
	if (FAILED(hResult)) {
		ddlogError(DDLOG_CRITICAL_OR_WARN, "Unable to create Direct3D device", hResult);
        nContextError = GLDERR_D3D;
		goto return_with_error;
	}

	// We must do this as soon as the device is created
	dglInitStateCaches(lpCtx);

	// Obtain the D3D Device Description
	D3DHWDevDesc.dwSize = D3DHELDevDesc.dwSize = sizeof(D3DDEVICEDESC);
	TRY(IDirect3DDevice3_GetCaps(lpCtx->lpDev3,
								 &D3DHWDevDesc,
								 &D3DHELDevDesc),
								 "dglCreateContext: GetCaps failed");

	// Choose the relevant description and cache it in the context.
	// We will use this description later for caps checking
	memcpy(	&lpCtx->D3DDevDesc,
			glb.bHardware ? &D3DHWDevDesc : &D3DHELDevDesc,
			sizeof(D3DDEVICEDESC));

	// Now we can examine the texture formats
	if (!dglBuildTextureFormatList(lpCtx->lpDev3)) {
		ddlogMessage(DDLOG_CRITICAL_OR_WARN, "dglBuildTextureFormatList failed\n");
		goto return_with_error;
	}

	// Get the pixel format of the back buffer
	lpCtx->ddpfRender.dwSize = sizeof(lpCtx->ddpfRender);
	if (bDoubleBuffer)
		hResult = IDirectDrawSurface4_GetPixelFormat(
					lpCtx->lpBack4,
					&lpCtx->ddpfRender);
	else
		hResult = IDirectDrawSurface4_GetPixelFormat(
					lpCtx->lpFront4,
					&lpCtx->ddpfRender);

	if (FAILED(hResult)) {
		ddlogError(DDLOG_CRITICAL_OR_WARN, "GetPixelFormat failed", hResult);
		goto return_with_error;
	}
	// Find a pixel packing function suitable for this surface
	pxClassifyPixelFormat(&lpCtx->ddpfRender,
						  &lpCtx->fnPackFunc,
						  &lpCtx->fnUnpackFunc,
						  &lpCtx->fnPackSpanFunc);

	// Viewport
	hResult = IDirect3D3_CreateViewport(lpCtx->lpD3D3, &lpCtx->lpViewport3, NULL);
	if (FAILED(hResult)) {
		ddlogError(DDLOG_CRITICAL_OR_WARN, "CreateViewport failed", hResult);
		goto return_with_error;
	}

	hResult = IDirect3DDevice3_AddViewport(lpCtx->lpDev3, lpCtx->lpViewport3);
	if (FAILED(hResult)) {
		ddlogError(DDLOG_CRITICAL_OR_WARN, "AddViewport failed", hResult);
		goto return_with_error;
	}

	// Initialise the viewport
	// Note screen coordinates are used for viewport clipping since D3D
	// transform operations are not used in the GLD CAD driver. (DaveM)
	inv_aspect = (float)lpCtx->dwHeight/(float)lpCtx->dwWidth;

	lpCtx->d3dViewport.dwSize = sizeof(lpCtx->d3dViewport);
	lpCtx->d3dViewport.dwX = 0;
	lpCtx->d3dViewport.dwY = 0;
	lpCtx->d3dViewport.dwWidth = lpCtx->dwWidth;
	lpCtx->d3dViewport.dwHeight = lpCtx->dwHeight;
	lpCtx->d3dViewport.dvClipX = 0; // -1.0f;
	lpCtx->d3dViewport.dvClipY = 0; // inv_aspect;
	lpCtx->d3dViewport.dvClipWidth = lpCtx->dwWidth; // 2.0f;
	lpCtx->d3dViewport.dvClipHeight = lpCtx->dwHeight; // 2.0f * inv_aspect;
	lpCtx->d3dViewport.dvMinZ = 0.0f;
	lpCtx->d3dViewport.dvMaxZ = 1.0f;
	TRY(IDirect3DViewport3_SetViewport2(lpCtx->lpViewport3, &lpCtx->d3dViewport), "dglCreateContext: SetViewport2");

	hResult = IDirect3DDevice3_SetCurrentViewport(lpCtx->lpDev3, lpCtx->lpViewport3);
	if (FAILED(hResult)) {
		ddlogError(DDLOG_CRITICAL_OR_WARN, "SetCurrentViewport failed", hResult);
		goto return_with_error;
	}

	lpCtx->dwBPP = lpPFD->cColorBits;
	lpCtx->iZBufferPF = lpCtx->lpPF->iZBufferPF;

	// Set last texture to NULL
	for (i=0; i<MAX_TEXTURE_UNITS; i++) {
		lpCtx->ColorOp[i] = D3DTOP_DISABLE;
		lpCtx->AlphaOp[i] = D3DTOP_DISABLE;
		lpCtx->tObj[i] = NULL;
	}

	// Default to perspective correct texture mapping
	dglSetRenderState(lpCtx, D3DRENDERSTATE_TEXTUREPERSPECTIVE, TRUE, "TexturePersp");

	// Set the default culling mode
	lpCtx->cullmode = D3DCULL_NONE;
	dglSetRenderState(lpCtx, D3DRENDERSTATE_CULLMODE, D3DCULL_NONE, "CullMode");

	// Disable specular
	dglSetRenderState(lpCtx, D3DRENDERSTATE_SPECULARENABLE, FALSE, "SpecularEnable");
	// Disable subpixel correction
//	dglSetRenderState(lpCtx, D3DRENDERSTATE_SUBPIXEL, FALSE, "SubpixelEnable");
	// Disable dithering
	dglSetRenderState(lpCtx, D3DRENDERSTATE_DITHERENABLE, FALSE, "DitherEnable");

	// Initialise the primitive caches
//	lpCtx->dwNextLineVert	= 0;
//	lpCtx->dwNextTriVert	= 0;

	// Init the global texture palette
	lpCtx->lpGlobalPalette = NULL;

	// Init the HW/SW usage counters
//	lpCtx->dwHWUsageCount = lpCtx->dwSWUsageCount = 0L;

	//
	// Create two D3D vertex buffers.
	// One will hold the pre-transformed data with the other one
	// being used to hold the post-transformed & clipped verts.
	//
#if 0  // never used (DaveM)
	vbufdesc.dwSize = sizeof(D3DVERTEXBUFFERDESC);
	vbufdesc.dwCaps = D3DVBCAPS_WRITEONLY;
	if (glb.bHardware == FALSE)
		vbufdesc.dwCaps = D3DVBCAPS_SYSTEMMEMORY;
	vbufdesc.dwNumVertices = 32768; // For the time being

	// Source vertex buffer
	vbufdesc.dwFVF = DGL_LVERTEX;
	hResult = IDirect3D3_CreateVertexBuffer(lpCtx->lpD3D3, &vbufdesc, &lpCtx->m_vbuf, 0, NULL);
	if (FAILED(hResult)) {
		ddlogError(DDLOG_CRITICAL_OR_WARN, "CreateVertexBuffer(src) failed", hResult);
		goto return_with_error;
	}

	// Destination vertex buffer
	vbufdesc.dwFVF = (glb.bMultitexture == FALSE) ? D3DFVF_TLVERTEX : (D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX2);
	hResult = IDirect3D3_CreateVertexBuffer(lpCtx->lpD3D3, &vbufdesc, &lpCtx->m_pvbuf, 0, NULL);
	if(FAILED(hResult)) {
		ddlogError(DDLOG_CRITICAL_OR_WARN, "CreateVertexBuffer(dst) failed", hResult);
		goto return_with_error;
	}
#endif

#endif _USE_GLD3_WGL

	//
	//	Now create the Mesa context
	//

	// Create the Mesa visual
	if (lpPFD->cDepthBits)
		dwDepthBits = 16;
	if (lpPFD->cStencilBits)
		dwStencilBits = 8;
	if (lpPFD->cAlphaBits) {
		dwAlphaBits = 8;
		bAlphaSW = GL_TRUE;
	}
	if (lpPFD->dwFlags & PFD_DOUBLEBUFFER)
		bDouble = GL_TRUE;
//	lpCtx->EmulateSingle =
//		(lpPFD->dwFlags & PFD_DOUBLEBUFFER) ? FALSE : TRUE;

#ifdef _USE_GLD3_WGL
	lpCtx->glVis = _mesa_create_visual(
		GL_TRUE,		// RGB mode
		bDouble,    /* double buffer */
		GL_FALSE,			// stereo
		lpPFD->cRedBits,
		lpPFD->cGreenBits,
		lpPFD->cBlueBits,
		dwAlphaBits,
		0,				// index bits
		dwDepthBits,
		dwStencilBits,
		lpPFD->cAccumRedBits,	// accum bits
		lpPFD->cAccumGreenBits,	// accum bits
		lpPFD->cAccumBlueBits,	// accum bits
		lpPFD->cAccumAlphaBits,	// accum alpha bits
		1				// num samples
		);
#else // _USE_GLD3_WGL
	lpCtx->glVis = (*mesaFuncs.gl_create_visual)(
		GL_TRUE,			// RGB mode
		bAlphaSW,			// Is an alpha buffer required?
		bDouble,			// Is an double-buffering required?
		GL_FALSE,			// stereo
		dwDepthBits,		// depth_size
		dwStencilBits,		// stencil_size
		lpPFD->cAccumBits,	// accum_size
		0,					// colour-index bits
		lpPFD->cRedBits,	// Red bit count
		lpPFD->cGreenBits,	// Green bit count
		lpPFD->cBlueBits,	// Blue bit count
		dwAlphaBits			// Alpha bit count
		);
#endif // _USE_GLD3_WGL

	if (lpCtx->glVis == NULL) {
		ddlogMessage(DDLOG_CRITICAL_OR_WARN, "gl_create_visual failed\n");
		goto return_with_error;
	}

#ifdef _USE_GLD3_WGL
	lpCtx->glCtx = _mesa_create_context(lpCtx->glVis, NULL, (void *)lpCtx, GL_TRUE);
#else
	// Create the Mesa context
	lpCtx->glCtx = (*mesaFuncs.gl_create_context)(
					lpCtx->glVis,	// Mesa visual
					NULL,			// share list context
					(void *)lpCtx,	// Pointer to our driver context
					GL_TRUE			// Direct context flag
				   );
#endif // _USE_GLD3_WGL

	if (lpCtx->glCtx == NULL) {
		ddlogMessage(DDLOG_CRITICAL_OR_WARN, "gl_create_context failed\n");
		goto return_with_error;
	}

	// Create the Mesa framebuffer
#ifdef _USE_GLD3_WGL
	lpCtx->glBuffer = _mesa_create_framebuffer(
		lpCtx->glVis,
		lpCtx->glVis->depthBits > 0,
		lpCtx->glVis->stencilBits > 0,
		lpCtx->glVis->accumRedBits > 0,
		GL_FALSE //swalpha
		);
#else
	lpCtx->glBuffer = (*mesaFuncs.gl_create_framebuffer)(lpCtx->glVis);
#endif // _USE_GLD3_WGL

	if (lpCtx->glBuffer == NULL) {
		ddlogMessage(DDLOG_CRITICAL_OR_WARN, "gl_create_framebuffer failed\n");
		goto return_with_error;
	}

#ifdef _USE_GLD3_WGL
	// Init Mesa internals
	_swrast_CreateContext( lpCtx->glCtx );
	_ac_CreateContext( lpCtx->glCtx );
	_tnl_CreateContext( lpCtx->glCtx );
	_swsetup_CreateContext( lpCtx->glCtx );

	_gldDriver.InitialiseMesa(lpCtx);
	
	lpCtx->glCtx->imports.warning	= _gld_mesa_warning;
	lpCtx->glCtx->imports.fatal		= _gld_mesa_fatal;

#else
	// Tell Mesa how many texture stages we have
	glb.wMaxSimultaneousTextures = lpCtx->D3DDevDesc.wMaxSimultaneousTextures;
	// Only use as many Units as the spec requires
	if (glb.wMaxSimultaneousTextures > MAX_TEXTURE_UNITS)
		glb.wMaxSimultaneousTextures = MAX_TEXTURE_UNITS;
	lpCtx->glCtx->Const.MaxTextureUnits = glb.wMaxSimultaneousTextures;
	ddlogPrintf(DDLOG_INFO, "Texture stages   : %d", glb.wMaxSimultaneousTextures);

	// Set the max texture size.
	// NOTE: clamped to a max of 1024 for extra performance!
	lpCtx->dwMaxTextureSize = (lpCtx->D3DDevDesc.dwMaxTextureWidth <= 1024) ? lpCtx->D3DDevDesc.dwMaxTextureWidth : 1024;

// Texture resize takes place elsewhere. KH
// NOTE: This was added to workaround an issue with the Intel app.
#if 0
	lpCtx->glCtx->Const.MaxTextureSize = lpCtx->dwMaxTextureSize;
#else
	lpCtx->glCtx->Const.MaxTextureSize = 1024;
#endif

	// Setup the Display Driver pointers
	dglSetupDDPointers(lpCtx->glCtx);

	// Initialise all the Direct3D renderstates
	dglInitStateD3D(lpCtx->glCtx);

#if 0
	// Signal a reload of texture state on next glBegin
	lpCtx->m_texHandleValid = FALSE;
	lpCtx->m_mtex = FALSE;
	lpCtx->m_texturing = FALSE;
#else
	// Set default texture unit state
//	dglSetTexture(lpCtx, 0, NULL);
//	dglSetTexture(lpCtx, 1, NULL);
#endif

	//
	// Set the global texture palette to default values.
	//

	// Clear the entire palette
	ZeroMemory(ppe, sizeof(PALETTEENTRY) * 256);

	// Fill the palette with a default colour.
	// A garish colour is used to catch bugs. Here Magenta is used.
	for (i=0; i < 256; i++) {
		ppe[i].peRed	= 255;
		ppe[i].peGreen	= 0;
		ppe[i].peBlue	= 255;
	}

	RELEASE(lpCtx->lpGlobalPalette);

	if (glb.bDirectDrawPersistant && glb.bPersistantBuffers && glb.lpGlobalPalette)
		hResult = IDirectDrawPalette_AddRef(lpCtx->lpGlobalPalette = glb.lpGlobalPalette);
	else
		hResult = IDirectDraw4_CreatePalette(
				lpCtx->lpDD4,
				DDPCAPS_INITIALIZE | DDPCAPS_8BIT | DDPCAPS_ALLOW256,
				ppe,
				&(lpCtx->lpGlobalPalette),
				NULL);
	if (FAILED(hResult)) {
		ddlogError(DDLOG_ERROR, "Default CreatePalette failed\n", hResult);
		lpCtx->lpGlobalPalette = NULL;
		goto return_with_error;
	}
	if (glb.bDirectDrawPersistant && glb.bPersistantBuffers && !glb.lpGlobalPalette)
		IDirectDrawPalette_AddRef(glb.lpGlobalPalette = lpCtx->lpGlobalPalette);

#endif // _USE_GLD3_WGL

	// ** If we have made it to here then we can enable rendering **
	lpCtx->bCanRender = TRUE;

//	ddlogMessage(DDLOG_SYSTEM, "dglCreateContextBuffers succeded\n");

#ifdef GLD_THREADS
	// Release serialized access
	if (glb.bMultiThreaded)
		LeaveCriticalSection(&CriticalSection);
#endif

	return TRUE;

return_with_error:
	// Clean up before returning.
	// This is critical for secondary devices.

	lpCtx->bCanRender = FALSE;

#ifdef _USE_GLD3_WGL
	// Destroy the Mesa context
	if (lpCtx->glBuffer)
		_mesa_destroy_framebuffer(lpCtx->glBuffer);
	if (lpCtx->glCtx)
		_mesa_destroy_context(lpCtx->glCtx);
	if (lpCtx->glVis)
		_mesa_destroy_visual(lpCtx->glVis);

	// Destroy driver data
	_gldDriver.DestroyDrawable(lpCtx);
#else
	// Destroy the Mesa context
	if (lpCtx->glBuffer)
		(*mesaFuncs.gl_destroy_framebuffer)(lpCtx->glBuffer);
	if (lpCtx->glCtx)
		(*mesaFuncs.gl_destroy_context)(lpCtx->glCtx);
	if (lpCtx->glVis)
		(*mesaFuncs.gl_destroy_visual)(lpCtx->glVis);

	RELEASE(lpCtx->m_pvbuf); // Release D3D vertex buffer
	RELEASE(lpCtx->m_vbuf); // Release D3D vertex buffer

	if (lpCtx->lpViewport3) {
		if (lpCtx->lpDev3) IDirect3DDevice3_DeleteViewport(lpCtx->lpDev3, lpCtx->lpViewport3);
		RELEASE(lpCtx->lpViewport3);
		lpCtx->lpViewport3 = NULL;
	}

	RELEASE(lpCtx->lpDev3);
	if (lpCtx->lpDepth4) {
		if (lpCtx->lpBack4)
			IDirectDrawSurface4_DeleteAttachedSurface(lpCtx->lpBack4, 0L, lpCtx->lpDepth4);
		else
			IDirectDrawSurface4_DeleteAttachedSurface(lpCtx->lpFront4, 0L, lpCtx->lpDepth4);
		RELEASE(lpCtx->lpDepth4);
		lpCtx->lpDepth4 = NULL;
	}
	RELEASE(lpCtx->lpBack4);
	RELEASE(lpCtx->lpFront4);
	else
	if (lpCtx->bFullscreen) {
		IDirectDraw4_RestoreDisplayMode(lpCtx->lpDD4);
		IDirectDraw4_SetCooperativeLevel(lpCtx->lpDD4, NULL, DDSCL_NORMAL);
	}
	RELEASE(lpCtx->lpD3D3);
	RELEASE(lpCtx->lpDD4);
	RELEASE(lpCtx->lpDD1);
#endif // _USE_GLD3_WGL

	lpCtx->bAllocated = FALSE;

#ifdef GLD_THREADS
	// Release serialized access
	if (glb.bMultiThreaded)
		LeaveCriticalSection(&CriticalSection);
#endif

	return FALSE;

#undef DDLOG_CRITICAL_OR_WARN
}

// ***********************************************************************

HGLRC dglCreateContext(
	HDC a,
	const DGL_pixelFormat *lpPF)
{
	int i;
	HGLRC				hGLRC;
	DGL_ctx*			lpCtx;
	static BOOL			bWarnOnce = TRUE;
	DWORD				dwThreadId = GetCurrentThreadId();
    char                szMsg[256];
    HWND                hWnd;
    LONG                lpfnWndProc;

	// Validate license
	if (!dglValidate())
		return NULL;

	// Is context state ready ?
	if (!bContextReady)
		return NULL;

	ddlogPrintf(DDLOG_SYSTEM, "dglCreateContext for HDC=%X, ThreadId=%X", a, dwThreadId);

	// Find next free context.
	// Also ensure that only one Fullscreen context is created at any one time.
	hGLRC = 0; // Default to Not Found
	for (i=0; i<DGL_MAX_CONTEXTS; i++) {
		if (ctxlist[i].bAllocated) {
			if (/*glb.bFullscreen && */ctxlist[i].bFullscreen)
				break;
		} else {
			hGLRC = (HGLRC)(i+1);
			break;
		}
	}

	// Bail if no GLRC was found
	if (!hGLRC)
		return NULL;

	// Set the context pointer
	lpCtx = dglGetContextAddress(hGLRC);
	// Make sure that context is zeroed before we do anything.
	// MFC and C++ apps call wglCreateContext() and wglDeleteContext() multiple times,
	// even though only one context is ever used by the app, so keep it clean. (DaveM)
	ZeroMemory(lpCtx, sizeof(DGL_ctx));
	lpCtx->bAllocated = TRUE;
	// Flag that buffers need creating on next wglMakeCurrent call.
	lpCtx->bHasBeenCurrent = FALSE;
	lpCtx->lpPF = (DGL_pixelFormat *)lpPF;	// cache pixel format
	lpCtx->bCanRender = FALSE;

	// Create all the internal resources here, not in dglMakeCurrent().
	// We do a re-size check in dglMakeCurrent in case of re-allocations. (DaveM)
	// We now try context allocations twice, first with video memory,
	// then again with system memory. This is similar to technique
	// used for dglWglResizeBuffers(). (DaveM)
	if (lpCtx->bHasBeenCurrent == FALSE) {
		if (!dglCreateContextBuffers(a, lpCtx, FALSE)) {
			if (glb.bMessageBoxWarnings && bWarnOnce && dwLogging) {
				bWarnOnce = FALSE;
                switch (nContextError) {
                   case GLDERR_DDRAW: strcpy(szMsg, szDDrawWarning); break;
                   case GLDERR_D3D: strcpy(szMsg, szD3DWarning); break;
                   case GLDERR_MEM: strcpy(szMsg, szResourceWarning); break;
                   case GLDERR_BPP: strcpy(szMsg, szBPPWarning); break;
                   default: strcpy(szMsg, "");
                }
                if (strlen(szMsg))
                    MessageBox(NULL, szMsg, "GLDirect", MB_OK | MB_ICONWARNING);
			}
            // Only need to try again if memory error
            if (nContextError == GLDERR_MEM) {
			    ddlogPrintf(DDLOG_WARN, "dglCreateContext failed 1st time with video memory");
            }
            else {
			    ddlogPrintf(DDLOG_ERROR, "dglCreateContext failed");
                return NULL;
            }
		}
	}

	// Now that we have a hWnd, we can intercept the WindowProc.
    hWnd = lpCtx->hWnd;
    if (hWnd) {
		// Only hook individual window handler once if not hooked before.
		lpfnWndProc = GetWindowLong(hWnd, GWL_WNDPROC);
		if (lpfnWndProc != (LONG)dglWndProc) {
			lpCtx->lpfnWndProc = lpfnWndProc;
			SetWindowLong(hWnd, GWL_WNDPROC, (LONG)dglWndProc);
			}
        // Find the parent window of the app too.
        if (glb.hWndActive == NULL) {
            while (hWnd != NULL) {
                glb.hWndActive = hWnd;
                hWnd = GetParent(hWnd);
            }
            // Hook the parent window too.
            lpfnWndProc = GetWindowLong(glb.hWndActive, GWL_WNDPROC);
            if (glb.hWndActive == lpCtx->hWnd)
                glb.lpfnWndProc = lpCtx->lpfnWndProc;
            else if (lpfnWndProc != (LONG)dglWndProc)
                glb.lpfnWndProc = lpfnWndProc;
            if (glb.lpfnWndProc)
                SetWindowLong(glb.hWndActive, GWL_WNDPROC, (LONG)dglWndProc);
        }
    }

	ddlogPrintf(DDLOG_SYSTEM, "dglCreateContext succeeded for HGLRC=%d", (int)hGLRC);

	return hGLRC;
}

// ***********************************************************************
// Make a DirectGL context current
// Used by wgl functions and dgl functions
BOOL dglMakeCurrent(
	HDC a,
	HGLRC b)
{
	int context;
	DGL_ctx* lpCtx;
	HWND hWnd;
	BOOL bNeedResize = FALSE;
	BOOL bWindowChanged, bContextChanged;
	LPDIRECTDRAWCLIPPER	lpddClipper;
	DWORD dwThreadId = GetCurrentThreadId();
	LONG lpfnWndProc;

	// Validate license
	if (!dglValidate())
		return FALSE;

	// Is context state ready ?
	if (!bContextReady)
		return FALSE;

	context = (int)b; // This is as a result of STRICT!
	ddlogPrintf(DDLOG_SYSTEM, "dglMakeCurrent: HDC=%X, HGLRC=%d, ThreadId=%X", a, context, dwThreadId);

	// If the HGLRC is NULL then make no context current;
	// Ditto if the HDC is NULL either. (DaveM)
	if (context == 0 || a == 0) {
		// Corresponding Mesa operation
#ifdef _USE_GLD3_WGL
		_mesa_make_current(NULL, NULL);
#else
		(*mesaFuncs.gl_make_current)(NULL, NULL);
#endif
		dglSetCurrentContext(0);
		return TRUE;
	}

	// Make sure the HGLRC is in range
	if ((context > DGL_MAX_CONTEXTS) || (context < 0)) {
		ddlogMessage(DDLOG_ERROR, "dglMakeCurrent: HGLRC out of range\n");
		return FALSE;
	}

	// Find address of context and make sure that it has been allocated
	lpCtx = dglGetContextAddress(b);
	if (!lpCtx->bAllocated) {
		ddlogMessage(DDLOG_ERROR, "dglMakeCurrent: Context not allocated\n");
//		return FALSE;
		return TRUE; // HACK: Shuts up "WebLab Viewer Pro". KeithH
	}

#ifdef GLD_THREADS
	// Serialize access to DirectDraw or DDS operations
	if (glb.bMultiThreaded)
		EnterCriticalSection(&CriticalSection);
#endif

	// Check if window has changed
	hWnd = (a != lpCtx->hDC) ? WindowFromDC(a) : lpCtx->hWnd;
	bWindowChanged = (hWnd != lpCtx->hWnd) ? TRUE : FALSE;
	bContextChanged = (b != dglGetCurrentContext()) ? TRUE : FALSE;

	// If the window has changed, make sure the clipper is updated. (DaveM)
	if (glb.bDirectDrawPersistant && !lpCtx->bFullscreen && (bWindowChanged || bContextChanged)) {
		lpCtx->hWnd = hWnd;
#ifndef _USE_GLD3_WGL
		IDirectDrawSurface4_GetClipper(lpCtx->lpFront4, &lpddClipper);
		IDirectDrawClipper_SetHWnd(lpddClipper, 0, lpCtx->hWnd);
		IDirectDrawClipper_Release(lpddClipper);
#endif // _USE_GLD3_WGL
	}

	// Make sure hDC and hWnd is current. (DaveM)
	// Obtain the dimensions of the rendering window
	lpCtx->hDC = a; // Cache DC
	lpCtx->hWnd = hWnd;
	hWndLastActive = hWnd;

	// Check for non-window DC = memory DC ?
	if (hWnd == NULL) {
		if (GetClipBox(a, &lpCtx->rcScreenRect) == ERROR) {
			ddlogMessage(DDLOG_WARN, "GetClipBox failed in dglMakeCurrent\n");
			SetRect(&lpCtx->rcScreenRect, 0, 0, 0, 0);
		}
	}
	else if (!GetClientRect(lpCtx->hWnd, &lpCtx->rcScreenRect)) {
		ddlogMessage(DDLOG_WARN, "GetClientRect failed in dglMakeCurrent\n");
		SetRect(&lpCtx->rcScreenRect, 0, 0, 0, 0);
	}
	// Check if buffers need to be re-sized;
	// If so, wait until Mesa GL stuff is setup before re-sizing;
	if (lpCtx->dwWidth != lpCtx->rcScreenRect.right - lpCtx->rcScreenRect.left ||
		lpCtx->dwHeight != lpCtx->rcScreenRect.bottom - lpCtx->rcScreenRect.top)
		bNeedResize = TRUE;

	// Now we can update our globals
	dglSetCurrentContext(b);

	// Corresponding Mesa operation
#ifdef _USE_GLD3_WGL
	_mesa_make_current(lpCtx->glCtx, lpCtx->glBuffer);
	lpCtx->glCtx->Driver.UpdateState(lpCtx->glCtx, _NEW_ALL);
	if (bNeedResize) {
		// Resize buffers (Note Mesa GL needs to be setup beforehand);
		// Resize Mesa internal buffer too via glViewport() command,
		// which subsequently calls dglWglResizeBuffers() too.
		lpCtx->glCtx->Driver.Viewport(lpCtx->glCtx, 0, 0, lpCtx->dwWidth, lpCtx->dwHeight);
		lpCtx->bHasBeenCurrent = TRUE;
	}
#else
	(*mesaFuncs.gl_make_current)(lpCtx->glCtx, lpCtx->glBuffer);

	dglSetupDDPointers(lpCtx->glCtx);

	// Insure DirectDraw surfaces fit current window DC
	if (bNeedResize) {
		// Resize buffers (Note Mesa GL needs to be setup beforehand);
		// Resize Mesa internal buffer too via glViewport() command,
		// which subsequently calls dglWglResizeBuffers() too.
		(*mesaFuncs.gl_Viewport)(lpCtx->glCtx, 0, 0, lpCtx->dwWidth, lpCtx->dwHeight);
		lpCtx->bHasBeenCurrent = TRUE;
	}
#endif // _USE_GLD3_WGL
	ddlogPrintf(DDLOG_SYSTEM, "dglMakeCurrent: width = %d, height = %d", lpCtx->dwWidth, lpCtx->dwHeight);

	// We have to clear D3D back buffer and render state if emulated front buffering
	// for different window (but not context) like in Solid Edge.
	if (glb.bDirectDrawPersistant && glb.bPersistantBuffers
		&& (bWindowChanged /* || bContextChanged */) && lpCtx->EmulateSingle) {
#ifdef _USE_GLD3_WGL
//		IDirect3DDevice8_EndScene(lpCtx->pDev);
//		lpCtx->bSceneStarted = FALSE;
		lpCtx->glCtx->Driver.Clear(lpCtx->glCtx, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT,
			GL_TRUE, 0, 0, lpCtx->dwWidth, lpCtx->dwHeight);
#else
		IDirect3DDevice3_EndScene(lpCtx->lpDev3);
		lpCtx->bSceneStarted = FALSE;
		dglClearD3D(lpCtx->glCtx, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT,
			GL_TRUE, 0, 0, lpCtx->dwWidth, lpCtx->dwHeight);
#endif // _USE_GLD3_WGL
	}

	// The first time we call MakeCurrent we set the initial viewport size
	if (lpCtx->bHasBeenCurrent == FALSE)
#ifdef _USE_GLD3_WGL
		lpCtx->glCtx->Driver.Viewport(lpCtx->glCtx, 0, 0, lpCtx->dwWidth, lpCtx->dwHeight);
#else
		(*mesaFuncs.gl_Viewport)(lpCtx->glCtx, 0, 0, lpCtx->dwWidth, lpCtx->dwHeight);
#endif // _USE_GLD3_WGL
	lpCtx->bHasBeenCurrent = TRUE;

#ifdef GLD_THREADS
	// Release serialized access
	if (glb.bMultiThreaded)
		LeaveCriticalSection(&CriticalSection);
#endif

	return TRUE;
}

// ***********************************************************************

BOOL dglDeleteContext(
	HGLRC a)
{
	DGL_ctx* lpCtx;
	DWORD dwThreadId = GetCurrentThreadId();
    char argstr[256];

#if 0	// We have enough trouble throwing exceptions as it is... (DaveM)
	// Validate license
	if (!dglValidate())
		return FALSE;
#endif

	// Is context state ready ?
	if (!bContextReady)
		return FALSE;

	ddlogPrintf(DDLOG_SYSTEM, "dglDeleteContext: Deleting context HGLRC=%d, ThreadId=%X", (int)a, dwThreadId);

	// Make sure the HGLRC is in range
	if (((int) a> DGL_MAX_CONTEXTS) || ((int)a < 0)) {
		ddlogMessage(DDLOG_ERROR, "dglDeleteCurrent: HGLRC out of range\n");
		return FALSE;
	}

	// Make sure context is valid
	lpCtx = dglGetContextAddress(a);
	if (!lpCtx->bAllocated) {
		ddlogPrintf(DDLOG_WARN, "Tried to delete unallocated context HGLRC=%d", (int)a);
//		return FALSE;
		return TRUE; // HACK: Shuts up "WebLab Viewer Pro". KeithH
	}

	// Make sure context is de-activated
	if (a == dglGetCurrentContext()) {
		ddlogPrintf(DDLOG_WARN, "dglDeleteContext: context HGLRC=%d still active", (int)a);
		dglMakeCurrent(NULL, NULL);
	}

#ifdef GLD_THREADS
	// Serialize access to DirectDraw or DDS operations
	if (glb.bMultiThreaded)
		EnterCriticalSection(&CriticalSection);
#endif

	// We are about to destroy all Direct3D objects.
	// Therefore we must disable rendering
	lpCtx->bCanRender = FALSE;

	// This exception handler was installed to catch some
	// particularly nasty apps. Console apps that call exit()
	// fall into this catagory (i.e. Win32 Glut).

    // VC cannot successfully implement multiple exception handlers
    // if more than one exception occurs. Therefore reverting back to
    // single exception handler as Keith originally had it. (DaveM)

#define WARN_MESSAGE(p) strcpy(argstr, (#p));
#define SAFE_RELEASE(p) WARN_MESSAGE(p); RELEASE(p);

__try {
#ifdef _USE_GLD3_WGL
    WARN_MESSAGE(gl_destroy_framebuffer);
	if (lpCtx->glBuffer)
		_mesa_destroy_framebuffer(lpCtx->glBuffer);
    WARN_MESSAGE(gl_destroy_context);
	if (lpCtx->glCtx)
		_mesa_destroy_context(lpCtx->glCtx);
    WARN_MESSAGE(gl_destroy_visual);
	if (lpCtx->glVis)
		_mesa_destroy_visual(lpCtx->glVis);

	_gldDriver.DestroyDrawable(lpCtx);
#else
	// Destroy the Mesa context
    WARN_MESSAGE(gl_destroy_framebuffer);
	if (lpCtx->glBuffer)
		(*mesaFuncs.gl_destroy_framebuffer)(lpCtx->glBuffer);
    WARN_MESSAGE(gl_destroy_context);
	if (lpCtx->glCtx)
		(*mesaFuncs.gl_destroy_context)(lpCtx->glCtx);
    WARN_MESSAGE(gl_destroy_visual);
	if (lpCtx->glVis)
		(*mesaFuncs.gl_destroy_visual)(lpCtx->glVis);

	SAFE_RELEASE(lpCtx->m_pvbuf); // release D3D vertex buffer
	SAFE_RELEASE(lpCtx->m_vbuf); // release D3D vertex buffer

	// Delete the global palette
	SAFE_RELEASE(lpCtx->lpGlobalPalette);

	// Clean up.
	if (lpCtx->lpViewport3) {
		if (lpCtx->lpDev3) IDirect3DDevice3_DeleteViewport(lpCtx->lpDev3, lpCtx->lpViewport3);
		SAFE_RELEASE(lpCtx->lpViewport3);
		lpCtx->lpViewport3 = NULL;
	}

	SAFE_RELEASE(lpCtx->lpDev3);
	if (lpCtx->lpDepth4) {
		if (lpCtx->lpBack4)
			IDirectDrawSurface4_DeleteAttachedSurface(lpCtx->lpBack4, 0L, lpCtx->lpDepth4);
		else
			IDirectDrawSurface4_DeleteAttachedSurface(lpCtx->lpFront4, 0L, lpCtx->lpDepth4);
		SAFE_RELEASE(lpCtx->lpDepth4);
		lpCtx->lpDepth4 = NULL;
	}
	SAFE_RELEASE(lpCtx->lpBack4);
	SAFE_RELEASE(lpCtx->lpFront4);
	if (lpCtx->bFullscreen) {
		IDirectDraw4_RestoreDisplayMode(lpCtx->lpDD4);
		IDirectDraw4_SetCooperativeLevel(lpCtx->lpDD4, NULL, DDSCL_NORMAL);
	}
	SAFE_RELEASE(lpCtx->lpD3D3);
	SAFE_RELEASE(lpCtx->lpDD4);
	SAFE_RELEASE(lpCtx->lpDD1);
#endif // _ULSE_GLD3_WGL

}
__except(EXCEPTION_EXECUTE_HANDLER) {
    ddlogPrintf(DDLOG_WARN, "Exception raised in dglDeleteContext: %s", argstr);
}

	// Restore the window message handler because this context may be used
	// again by another window with a *different* message handler. (DaveM)
	if (lpCtx->lpfnWndProc) {
		SetWindowLong(lpCtx->hWnd, GWL_WNDPROC, (LONG)lpCtx->lpfnWndProc);
		lpCtx->lpfnWndProc = (LONG)NULL;
		}

	lpCtx->bAllocated = FALSE; // This context is now free for use

#ifdef GLD_THREADS
	// Release serialized access
	if (glb.bMultiThreaded)
		LeaveCriticalSection(&CriticalSection);
#endif

	return TRUE;
}

// ***********************************************************************

BOOL dglSwapBuffers(
	HDC hDC)
{
	RECT		rSrcRect;	// Source rectangle
	RECT		rDstRect;	// Destination rectangle
	POINT		pt;
	HRESULT		hResult;

	DDBLTFX		bltFX;
	DWORD		dwBlitFlags;
	DDBLTFX		*lpBltFX;

//	DWORD		dwThreadId = GetCurrentThreadId();
	HGLRC		hGLRC = dglGetCurrentContext();
	DGL_ctx		*lpCtx = dglGetContextAddress(hGLRC);
	HWND		hWnd;

	HDC 		hDCAux;		// for memory DC
	int 		x,y,w,h;	// for memory DC BitBlt

#if 0	// Perhaps not a good idea. Called too often. KH
	// Validate license
	if (!dglValidate())
		return FALSE;
#endif

	if (!lpCtx) {
		return TRUE; //FALSE; // No current context
	}

	if (!lpCtx->bCanRender) {
		// Don't return false else some apps will bail.
		return TRUE;
	}

	hWnd = lpCtx->hWnd;
	if (hDC != lpCtx->hDC) {
		ddlogPrintf(DDLOG_WARN, "dglSwapBuffers: HDC=%X does not match HDC=%X for HGLRC=%d", hDC, lpCtx->hDC, hGLRC);
		hWnd = WindowFromDC(hDC);
	}

#ifndef _USE_GLD3_WGL
	// Ensure that the surfaces exist before we tell
	// the device to render to them.
	IDirectDraw4_RestoreAllSurfaces(lpCtx->lpDD4);

	// Make sure that the vertex caches have been emptied
//	dglStateChange(lpCtx);

	// Some OpenGL programs don't issue a glFinish - check for it here.
	if (lpCtx->bSceneStarted) {
		IDirect3DDevice3_EndScene(lpCtx->lpDev3);
		lpCtx->bSceneStarted = FALSE;
	}
#endif

#if 0
	// If the calling app is not active then we don't need to Blit/Flip.
	// We can therefore simply return TRUE.
	if (!glb.bAppActive)
		return TRUE;
	// Addendum: This is WRONG! We should bail if the app is *minimized*,
	//           not merely if the app is just plain 'not active'.
	//           KeithH, 27/May/2000.
#endif

	// Check for non-window DC = memory DC ?
	if (hWnd == NULL) {
		if (GetClipBox(hDC, &rSrcRect) == ERROR)
			return TRUE;
		// Use GDI BitBlt instead from compatible DirectDraw DC
		x = rSrcRect.left;
		y = rSrcRect.top;
		w = rSrcRect.right - rSrcRect.left;
		h = rSrcRect.bottom - rSrcRect.top;

		// Ack. DX8 does not have a GetDC() function...
                // TODO: Defer to DX7 or DX9 drivers... (DaveM)
		return TRUE;
	}

	// Bail if window client region is not drawable, like in Solid Edge
	if (!IsWindow(hWnd) /* || !IsWindowVisible(hWnd) */ || !GetClientRect(hWnd, &rSrcRect))
		return TRUE;

#ifdef GLD_THREADS
	// Serialize access to DirectDraw or DDS operations
	if (glb.bMultiThreaded)
		EnterCriticalSection(&CriticalSection);
#endif

#ifdef _USE_GLD3_WGL
	// Notify Mesa of impending swap, so Mesa can flush internal buffers.
	_mesa_notifySwapBuffers(lpCtx->glCtx);
	// Now perform driver buffer swap
	_gldDriver.SwapBuffers(lpCtx, hDC, hWnd);
#else
	if (lpCtx->bFullscreen) {
		// Sync with retrace if required
		if (glb.bWaitForRetrace) {
			IDirectDraw4_WaitForVerticalBlank(
				lpCtx->lpDD4,
				DDWAITVB_BLOCKBEGIN,
				0);
		}

		// Perform the fullscreen flip
		TRY(IDirectDrawSurface4_Flip(
			lpCtx->lpFront4,
			NULL,
			DDFLIP_WAIT),
			"dglSwapBuffers: Flip");
	} else {
		// Calculate current window position and size
		pt.x = pt.y = 0;
		ClientToScreen(hWnd, &pt);
		GetClientRect(hWnd, &rDstRect);
		if (rDstRect.right > lpCtx->dwModeWidth)
			rDstRect.right = lpCtx->dwModeWidth;
		if (rDstRect.bottom > lpCtx->dwModeHeight)
			rDstRect.bottom = lpCtx->dwModeHeight;
		OffsetRect(&rDstRect, pt.x, pt.y);
		rSrcRect.left = rSrcRect.top = 0;
		rSrcRect.right = lpCtx->dwWidth;
		rSrcRect.bottom = lpCtx->dwHeight;
		if (rSrcRect.right > lpCtx->dwModeWidth)
			rSrcRect.right = lpCtx->dwModeWidth;
		if (rSrcRect.bottom > lpCtx->dwModeHeight)
			rSrcRect.bottom = lpCtx->dwModeHeight;

		if (glb.bWaitForRetrace) {
			// Sync the blit to the vertical retrace
			ZeroMemory(&bltFX, sizeof(bltFX));
			bltFX.dwSize = sizeof(bltFX);
			bltFX.dwDDFX = DDBLTFX_NOTEARING;
			dwBlitFlags = DDBLT_WAIT | DDBLT_DDFX;
			lpBltFX = &bltFX;
		} else {
			dwBlitFlags = DDBLT_WAIT;
			lpBltFX = NULL;
		}

		// Perform the actual blit
		TRY(IDirectDrawSurface4_Blt(
			lpCtx->lpFront4,
			&rDstRect,
			lpCtx->lpBack4, // Blit source
			&rSrcRect,
			dwBlitFlags,
			lpBltFX),
			"dglSwapBuffers: Blt");
	}
#endif // _USE_GLD3_WGL

#ifdef GLD_THREADS
	// Release serialized access
	if (glb.bMultiThreaded)
		LeaveCriticalSection(&CriticalSection);
#endif

    // TODO: Re-instate rendering bitmap snapshot feature??? (DaveM)

	// Render frame is completed
	ValidateRect(hWnd, NULL);
	lpCtx->bFrameStarted = FALSE;

	return TRUE;
}

// ***********************************************************************
