/****************************************************************************
*
*                        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:  Win32 DllMain functions.
*
****************************************************************************/

// INITGUID must only be defined once.
// Don't put it in a shared header file!
// GLD3 uses dxguid.lib, so INITGUID must *not* be used!
#ifndef _USE_GLD3_WGL
#define INITGUID
#endif // _USE_GLD3_WGL

#include "dllmain.h"

#include "snap/graphics.h"
#include "drvlib/os/os.h"

#ifdef _USE_GLD3_WGL
typedef void (APIENTRY *LPDGLSPLASHSCREEN)(int, int, char*);
#include "gld_driver.h"
#endif

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

BOOL bInitialized = FALSE;              // callback driver initialized?
BOOL bExited = FALSE;                   // callback driver exited this instance?
HINSTANCE hInstanceDll = NULL;          // DLL instance handle

static BOOL bDriverValidated = FALSE;	// prior validation status
static BOOL	bSplashScreen = TRUE;	    // Splash Screen ?
static BOOL bValidINIFound = FALSE;     // Have we found a valid INI file?

HHOOK 	hKeyHook = NULL;				// global keyboard handler hook

// Multi-threaded support needs to be reflected in Mesa code. (DaveM)
int _gld_bMultiThreaded = FALSE;

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

DWORD dwLogging = 0; 					// Logging flag
DWORD dwDebugLevel = 0;                 // Log debug level

char szLogPath[_MAX_PATH] = {"\0"};		// Log file path
char szSNAPPath[_MAX_PATH] = {"\0"};	// SNAP driver path

#ifndef _USE_GLD3_WGL
DGL_wglFuncs wglFuncs = {
	sizeof(DGL_wglFuncs),
	DGL_ChoosePixelFormat,
	DGL_CopyContext,
	DGL_CreateContext,
	DGL_CreateLayerContext,
	DGL_DeleteContext,
	DGL_DescribeLayerPlane,
	DGL_DescribePixelFormat,
	DGL_GetCurrentContext,
	DGL_GetCurrentDC,
	DGL_GetDefaultProcAddress,
	DGL_GetLayerPaletteEntries,
	DGL_GetPixelFormat,
	DGL_GetProcAddress,
	DGL_MakeCurrent,
	DGL_RealizeLayerPalette,
	DGL_SetLayerPaletteEntries,
	DGL_SetPixelFormat,
	DGL_ShareLists,
	DGL_SwapBuffers,
	DGL_SwapLayerBuffers,
	DGL_UseFontBitmapsA,
	DGL_UseFontBitmapsW,
	DGL_UseFontOutlinesA,
	DGL_UseFontOutlinesW,
};

DGL_mesaFuncs mesaFuncs = {
	sizeof(DGL_mesaFuncs),
};
#endif // _USE_GLD3_WGL

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

typedef struct {
	DWORD	dwDriver;			// 0=SciTech SW, 1=Direct3D SW, 2=Direct3D HW
	BOOL	bMipmapping;		// 0=off, 1=on
	BOOL	bMultitexture;		// 0=off, 1=on
	BOOL	bWaitForRetrace;	// 0=off, 1=on
	BOOL	bFullscreenBlit;	// 0=off, 1=on
	BOOL	bFastFPU;			// 0=off, 1=on
	BOOL	bDirectDrawPersistant;// 0=off, 1=on
	BOOL	bPersistantBuffers; // 0=off, 1=on
	DWORD	dwLogging;			// 0=off, 1=normal, 2=crash-proof
	DWORD	dwLoggingSeverity;	// 0=all, 1=warnings+errors, 2=errors only
	BOOL	bMessageBoxWarnings;// 0=off, 1=on
	BOOL	bMultiThreaded;		// 0=off, 1=on
	BOOL	bAppCustomizations;	// 0=off, 1=on
	BOOL	bHotKeySupport;		// 0=off, 1=on
	BOOL	bSplashScreen;		// 0=off, 1=on

#ifdef _USE_GLD3_WGL
	//
	// New for GLDirect 3.0
	//
	DWORD	dwAdapter;			// DX8 adpater ordinal
	DWORD	dwTnL;				// Transform & Lighting type
	DWORD	dwMultisample;		// DX8 multisample type
#endif // _USE_GLD3_WGL
} INI_settings;

static INI_settings ini;

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

BOOL APIENTRY DGL_initDriver(
#ifdef _USE_GLD3_WGL
	void)
{
#else
	DGL_wglFuncs *lpWglFuncs,
	DGL_mesaFuncs *lpMesaFuncs)
{
	// Check for valid pointers
	if ((lpWglFuncs == NULL) || (lpMesaFuncs == NULL))
		return FALSE;

	// Check for valid structs
	if (lpWglFuncs->dwSize != sizeof(DGL_wglFuncs)) {
		return FALSE;
	}

	// Check for valid structs
	if (lpMesaFuncs->dwSize != sizeof(DGL_mesaFuncs)) {
		return FALSE;
	}

	// Copy the Mesa functions
	memcpy(&mesaFuncs, lpMesaFuncs, sizeof(DGL_mesaFuncs));

	// Pass back the wgl functions
	memcpy(lpWglFuncs, &wglFuncs, sizeof(DGL_wglFuncs));
#endif // _USE_GLD3_WGL

    // Finally initialize the callback driver
    if (!dglInitDriver())
        return FALSE;

	return TRUE;
};

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

BOOL ReadINIFile(
	HINSTANCE hInstance)
{
	char		szModuleFilename[MAX_PATH];
	char		szSystemDirectory[MAX_PATH];
	const char	szSectionName[] = "Config";
	char		szINIFile[MAX_PATH];
	int			pos;

	// Now using the DLL module handle. KeithH, 24/May/2000.
	// Addendum: GetModuleFileName(NULL, ...    returns process filename,
	//           GetModuleFileName(hModule, ... returns DLL filename,

	// Get the dll path and filename.
	GetModuleFileName(hInstance, &szModuleFilename[0], MAX_PATH); // NULL for current process
	// Get the System directory.
	GetSystemDirectory(&szSystemDirectory[0], MAX_PATH);

	// Test to see if DLL is in system directory.
	if (strnicmp(szModuleFilename, szSystemDirectory, strlen(szSystemDirectory))==0) {
		// DLL *is* in system directory.
		// Return FALSE to indicate that registry keys should be read.
		return FALSE;
	}

	// Compose filename of INI file
	strcpy(szINIFile, szModuleFilename);
	pos = strlen(szINIFile);
	while (szINIFile[pos] != '\\') {
		pos--;
	}
	szINIFile[pos+1] = '\0';
    // Use run-time DLL path for log file too
    strcpy(szLogPath, szINIFile);
    szLogPath[pos] = '\0';
    // Complete full INI file path
	strcat(szINIFile, "gldirect.ini");

	// Read settings from private INI file.
	// Note that defaults are contained in the calls.
	ini.dwDriver = GetPrivateProfileInt(szSectionName, "dwDriver", 2, szINIFile);
	ini.bMipmapping = GetPrivateProfileInt(szSectionName, "bMipmapping", 1, szINIFile);
	ini.bMultitexture = GetPrivateProfileInt(szSectionName, "bMultitexture", 1, szINIFile);
	ini.bWaitForRetrace = GetPrivateProfileInt(szSectionName, "bWaitForRetrace", 0, szINIFile);
	ini.bFullscreenBlit = GetPrivateProfileInt(szSectionName, "bFullscreenBlit", 0, szINIFile);
	ini.bFastFPU = GetPrivateProfileInt(szSectionName, "bFastFPU", 1, szINIFile);
	ini.bDirectDrawPersistant = GetPrivateProfileInt(szSectionName, "bPersistantDisplay", 0, szINIFile);
	ini.bPersistantBuffers = GetPrivateProfileInt(szSectionName, "bPersistantResources", 0, szINIFile);
	ini.dwLogging = GetPrivateProfileInt(szSectionName, "dwLogging", 0, szINIFile);
	ini.dwLoggingSeverity = GetPrivateProfileInt(szSectionName, "dwLoggingSeverity", 0, szINIFile);
	ini.bMessageBoxWarnings = GetPrivateProfileInt(szSectionName, "bMessageBoxWarnings", 0, szINIFile);
	ini.bMultiThreaded = GetPrivateProfileInt(szSectionName, "bMultiThreaded", 0, szINIFile);
	ini.bAppCustomizations = GetPrivateProfileInt(szSectionName, "bAppCustomizations", 1, szINIFile);
	ini.bHotKeySupport = GetPrivateProfileInt(szSectionName, "bHotKeySupport", 0, szINIFile);
	ini.bSplashScreen = GetPrivateProfileInt(szSectionName, "bSplashScreen", 1, szINIFile);

#ifdef _USE_GLD3_WGL
	// New for GLDirect 3.x
	ini.dwAdapter		= GetPrivateProfileInt(szSectionName, "dwAdapter", 0, szINIFile);
	// dwTnL now defaults to zero (chooses TnL at runtime). KeithH
	ini.dwTnL			= GetPrivateProfileInt(szSectionName, "dwTnL", 0, szINIFile);
	ini.dwMultisample	= GetPrivateProfileInt(szSectionName, "dwMultisample", 0, szINIFile);
#endif

	return TRUE;
}

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

BOOL dllReadRegistry(
	HINSTANCE hInstance)
{
	// Read settings from INI file, if available
    bValidINIFound = FALSE;
	if (ReadINIFile(hInstance)) {
		const char *szRendering[3] = {
			"SciTech Software Renderer",
			"Direct3D MMX Software Renderer",
			"Direct3D Hardware Renderer"
		};
		// Set globals
		glb.bPrimary = 1;
		glb.bHardware = (ini.dwDriver == 2) ? 1 : 0;
#ifndef _USE_GLD3_WGL
		memset(&glb.ddGuid, 0, sizeof(glb.ddGuid));
		glb.d3dGuid = (ini.dwDriver == 2) ? IID_IDirect3DHALDevice : IID_IDirect3DRGBDevice;
#endif // _USE_GLD3_WGL
		strcpy(glb.szDDName, "Primary");
		strcpy(glb.szD3DName, szRendering[ini.dwDriver]);
		glb.dwRendering = ini.dwDriver;
		glb.bUseMipmaps = ini.bMipmapping;
		glb.bMultitexture = ini.bMultitexture;
		glb.bWaitForRetrace = ini.bWaitForRetrace;
		glb.bFullscreenBlit = ini.bFullscreenBlit;
		glb.bFastFPU = ini.bFastFPU;
		glb.bDirectDrawPersistant = ini.bDirectDrawPersistant;
		glb.bPersistantBuffers = ini.bPersistantBuffers;
		dwLogging = ini.dwLogging;
		dwDebugLevel = ini.dwLoggingSeverity;
		glb.bMessageBoxWarnings = ini.bMessageBoxWarnings;
		glb.bMultiThreaded = ini.bMultiThreaded;
		glb.bAppCustomizations = ini.bAppCustomizations;
        glb.bHotKeySupport = ini.bHotKeySupport;
		bSplashScreen = ini.bSplashScreen;
#ifdef _USE_GLD3_WGL
		// New for GLDirect 3.x
		glb.dwAdapter		= ini.dwAdapter;
		glb.dwDriver		= ini.dwDriver;
		glb.dwTnL			= ini.dwTnL;
		glb.dwMultisample	= ini.dwMultisample;
#endif
        bValidINIFound = TRUE;
		return TRUE;
	}
	// Read settings from registry
	else {
	HKEY	hReg;
	DWORD	cbValSize;
	DWORD	dwType = REG_SZ; // Registry data type for strings
	BOOL	bRegistryError;
	BOOL	bSuccess;

#define REG_READ_DWORD(a, b)							\
	cbValSize = sizeof(b);								\
	if (ERROR_SUCCESS != RegQueryValueEx( hReg, (a),	\
		NULL, NULL, (LPBYTE)&(b), &cbValSize ))			\
		bRegistryError = TRUE;

#define REG_READ_DEVICEID(a, b)									\
	cbValSize = MAX_DDDEVICEID_STRING;							\
	if(ERROR_SUCCESS != RegQueryValueEx(hReg, (a), 0, &dwType,	\
					(LPBYTE)&(b), &cbValSize))					\
		bRegistryError = TRUE;

#define REG_READ_STRING(a, b)									\
	cbValSize = sizeof((b));									\
	if(ERROR_SUCCESS != RegQueryValueEx(hReg, (a), 0, &dwType,	\
					(LPBYTE)&(b), &cbValSize))					\
		bRegistryError = TRUE;

	// Read settings from the registry.

	// Open the registry key for the current user if it exists.
	bSuccess = (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER,
									  DIRECTGL_REG_SETTINGS_KEY,
									  0,
									  KEY_READ,
									  &hReg));
    // Otherwise open the registry key for the local machine.
    if (!bSuccess)
        bSuccess = (ERROR_SUCCESS == RegOpenKeyEx(DIRECTGL_REG_KEY_ROOT,
									  DIRECTGL_REG_SETTINGS_KEY,
									  0,
									  KEY_READ,
									  &hReg));
    if (!bSuccess)
        return FALSE;

	bRegistryError = FALSE;

	REG_READ_DWORD(DIRECTGL_REG_SETTING_PRIMARY, glb.bPrimary);
	REG_READ_DWORD(DIRECTGL_REG_SETTING_D3D_HW, glb.bHardware);
#ifndef _USE_GLD3_WGL
	REG_READ_DWORD(DIRECTGL_REG_SETTING_DD_GUID, glb.ddGuid);
	REG_READ_DWORD(DIRECTGL_REG_SETTING_D3D_GUID, glb.d3dGuid);
#endif // _USE_GLD3_WGL
	REG_READ_DWORD(DIRECTGL_REG_SETTING_LOGGING, dwLogging);
	REG_READ_DWORD(DIRECTGL_REG_SETTING_DEBUGLEVEL, dwDebugLevel);
	REG_READ_DWORD(DIRECTGL_REG_SETTING_RENDERING, glb.dwRendering);
	REG_READ_DWORD(DIRECTGL_REG_SETTING_MULTITEXTURE, glb.bMultitexture);
	REG_READ_DWORD(DIRECTGL_REG_SETTING_WAITFORRETRACE, glb.bWaitForRetrace);
	REG_READ_DWORD(DIRECTGL_REG_SETTING_FULLSCREENBLIT, glb.bFullscreenBlit);
	REG_READ_DWORD(DIRECTGL_REG_SETTING_USEMIPMAPS, glb.bUseMipmaps);

	REG_READ_DEVICEID(DIRECTGL_REG_SETTING_DD_NAME, glb.szDDName);
	REG_READ_DEVICEID(DIRECTGL_REG_SETTING_D3D_NAME, glb.szD3DName);

	REG_READ_DWORD(DIRECTGL_REG_SETTING_MSGBOXWARNINGS, glb.bMessageBoxWarnings);
	REG_READ_DWORD(DIRECTGL_REG_SETTING_PERSISTDISPLAY, glb.bDirectDrawPersistant);
	REG_READ_DWORD(DIRECTGL_REG_SETTING_PERSISTBUFFERS, glb.bPersistantBuffers);
	REG_READ_DWORD(DIRECTGL_REG_SETTING_FASTFPU, glb.bFastFPU);
	REG_READ_DWORD(DIRECTGL_REG_SETTING_HOTKEYS, glb.bHotKeySupport);
	REG_READ_DWORD(DIRECTGL_REG_SETTING_MULTITHREAD, glb.bMultiThreaded);
	REG_READ_DWORD(DIRECTGL_REG_SETTING_APPCUSTOM, glb.bAppCustomizations);
    REG_READ_DWORD(DIRECTGL_REG_SETTING_SPLASHSCREEN, bSplashScreen);

#ifdef _USE_GLD3_WGL
	// New for GLDirect 3.x
	glb.dwDriver = glb.dwRendering;
	REG_READ_DWORD(DIRECTGL_REG_SETTING_ADAPTER, glb.dwAdapter);
	REG_READ_DWORD(DIRECTGL_REG_SETTING_TNL, glb.dwTnL);
	REG_READ_DWORD(DIRECTGL_REG_SETTING_MULTISAMPLE, glb.dwMultisample);
#endif

	RegCloseKey(hReg);

	// Open the global registry key for GLDirect
	bSuccess = (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE,
									  DIRECTGL_REG_SETTINGS_KEY,
									  0,
									  KEY_READ,
									  &hReg));
    if (bSuccess) {
	    // Read the installation path for GLDirect
	    REG_READ_STRING("InstallLocation",szLogPath);
	    RegCloseKey(hReg);
        }

	if (bRegistryError || !bSuccess)
		return FALSE;
	else
		return TRUE;

#undef REG_READ_DWORD
#undef REG_READ_DEVICEID
#undef REG_READ_STRING
	}
}

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

BOOL dllWriteRegistry(
	void )
{
	HKEY 	hReg;
	DWORD 	dwCreateDisposition, cbValSize;
	BOOL 	bRegistryError = FALSE;

#define REG_WRITE_DWORD(a, b)							\
	cbValSize = sizeof(b);								\
	if (ERROR_SUCCESS != RegSetValueEx( hReg, (a),		\
		0, REG_DWORD, (LPBYTE)&(b), cbValSize ))		\
		bRegistryError = TRUE;

	if (ERROR_SUCCESS == RegCreateKeyEx( DIRECTGL_REG_KEY_ROOT, DIRECTGL_REG_SETTINGS_KEY,
										0, NULL, 0, KEY_WRITE, NULL, &hReg,
										&dwCreateDisposition )) {
		RegFlushKey(hReg); // Make sure keys are written to disk
		RegCloseKey(hReg);
		hReg = NULL;
		}

	if (bRegistryError)
		return FALSE;
	else
		return TRUE;

#undef REG_WRITE_DWORD
}

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

void dglInitHotKeys(HINSTANCE hInstance)
{
	// Hot-Key support at all?
	if (!glb.bHotKeySupport)
		return;

	// Install global keyboard interceptor
	hKeyHook = SetWindowsHookEx(WH_KEYBOARD, dglKeyProc, hInstance, 0);
}

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

void dglExitHotKeys(void)
{
	// Hot-Key support at all?
	if (!glb.bHotKeySupport)
		return;

	// Remove global keyboard interceptor
	if (hKeyHook)
		UnhookWindowsHookEx(hKeyHook);
	hKeyHook = NULL;
}

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

// Note: This app-customization step must be performed in both the main
// OpenGL32 driver and the callback driver DLLs for multithreading option.
void dglSetAppCustomizations(void)
{
	char		szModuleFileName[MAX_PATH];
	int			iSize = MAX_PATH;

	// Get the currently loaded EXE filename.
	GetModuleFileName(NULL, &szModuleFileName[0], MAX_PATH); // NULL for current process
	strupr(szModuleFileName);
	iSize = strlen(szModuleFileName);

	// Check for specific EXEs and adjust global settings accordingly

	// NOTE: In GLD3.x "bDirectDrawPersistant" corresponds to IDirect3D8 and
	//       "bPersistantBuffers" corresponds to IDirect3DDevice8. KeithH

	// Case 1: 3DStudio must be multi-threaded
	// Added: Discreet GMAX (3DStudio MAX 4 for gamers. KeithH)
	if (strstr(szModuleFileName, "3DSMAX.EXE")
		|| strstr(szModuleFileName, "3DSVIZ.EXE")
		|| strstr(szModuleFileName, "GMAX.EXE")) {
		glb.bMultiThreaded = TRUE;
		glb.bDirectDrawPersistant = FALSE;
		glb.bPersistantBuffers = FALSE;
		return;
	}

	// Case 2: Solid Edge must use pre-allocated resources for all GLRCs
	if (strstr(szModuleFileName, "PART.EXE")
		|| strstr(szModuleFileName, "ASSEMBL.EXE")
		|| strstr(szModuleFileName, "DRAFT.EXE")
		|| strstr(szModuleFileName, "SMARTVW.EXE")
		|| strstr(szModuleFileName, "SMETAL.EXE")) {
		glb.bMultiThreaded = FALSE;
		glb.bDirectDrawPersistant = TRUE;
		glb.bPersistantBuffers = FALSE;
		return;
	}

	// Case 3: Sudden Depth creates and destroys GLRCs on paint commands
	if (strstr(szModuleFileName, "SUDDEPTH.EXE")
		|| strstr(szModuleFileName, "SUDDEMO.EXE")) {
		glb.bMultiThreaded = FALSE;
		glb.bDirectDrawPersistant = TRUE;
		glb.bPersistantBuffers = TRUE;
		glb.bFullscreenBlit = TRUE;
		return;
	}

	// Case 4: StereoGraphics test apps create and destroy GLRCs on paint commands
	if (strstr(szModuleFileName, "REDBLUE.EXE")
		|| strstr(szModuleFileName, "DIAGNOSE.EXE")) {
		glb.bMultiThreaded = FALSE;
		glb.bDirectDrawPersistant = TRUE;
		glb.bPersistantBuffers = TRUE;
		return;
	}

	// Case 5: Pipes screen savers share multiple GLRCs for same window
	if (strstr(szModuleFileName, "PIPES.SCR")
		|| (strstr(szModuleFileName, "PIPES") && strstr(szModuleFileName, ".SCR"))) {
		glb.bMultiThreaded = FALSE;
		glb.bDirectDrawPersistant = TRUE;
		glb.bPersistantBuffers = TRUE;
		return;
	}

	// Case 6: AutoVue uses sub-viewport ops which are temporarily broken in stereo window
	if (strstr(szModuleFileName, "AVWIN.EXE")) {
		glb.bMultiThreaded = FALSE;
		glb.bDirectDrawPersistant = TRUE;
		glb.bPersistantBuffers = TRUE;
		return;
	}
	// Case 7: Quake3 is waiting for DDraw objects to be released at exit
	if (strstr(szModuleFileName, "QUAKE")) {
		glb.bMultiThreaded = FALSE;
		glb.bDirectDrawPersistant = FALSE;
		glb.bPersistantBuffers = FALSE;
        glb.bFullscreenBlit = FALSE;
		return;
	}
	// Case 8: Reflection GLX server is unable to switch contexts at run-time
	if (strstr(szModuleFileName, "RX.EXE")) {
		glb.bMultiThreaded = FALSE;
        glb.bMessageBoxWarnings = FALSE;
		return;
	}
	// Case 9: Original AutoCAD 2000 must share DDraw objects across GLRCs
	if (strstr(szModuleFileName, "ACAD.EXE")) {
		glb.bFastFPU = FALSE;
        if (GetModuleHandle("wopengl6.hdi") != NULL) {
		glb.bMultiThreaded = FALSE;
		glb.bDirectDrawPersistant = TRUE;
		glb.bPersistantBuffers = FALSE;
		}
		return;
	}
}

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

BOOL dglInitDriver(void)
{
	UCHAR szExeName[MAX_PATH];
	const char *szRendering[] = {
		"Mesa Software",
		"Direct3D RGB SW",
		"Direct3D HW",
	};
    static ibool bWarnOnce = false;

    // Already initialized?
    if (bInitialized)
        return TRUE;

    // Moved from DllMain DLL_PROCESS_ATTACH:

		// (Re-)Init defaults
		dglInitGlobals();

		// Read registry or INI file settings
		if (!dllReadRegistry(hInstanceDll)) {
            if (!bWarnOnce)
			    MessageBox( NULL, "GLDirect has not been configured.\n\n"
							  "Please run the configuration program\n"
                              "before using GLDirect with applications.\n",
							  "GLDirect", MB_OK | MB_ICONWARNING);
            bWarnOnce = true;
            return FALSE;
		}

#ifdef _USE_GLD3_WGL
		// Must do this as early as possible.
		// Need to read regkeys/ini-file first though.
		gldInitDriverPointers(glb.dwDriver);

		// Create private driver globals
		_gldDriver.CreatePrivateGlobals();
#endif
		// Overide settings with application customizations
		if (glb.bAppCustomizations)
			dglSetAppCustomizations();

//#ifndef _USE_GLD3_WGL
		// Set the global memory type to either sysmem or vidmem
		glb.dwMemoryType = glb.bHardware ? DDSCAPS_VIDEOMEMORY : DDSCAPS_SYSTEMMEMORY;
//#endif

		// Multi-threaded support overides persistant display support
		if (glb.bMultiThreaded)
			glb.bDirectDrawPersistant = glb.bPersistantBuffers = FALSE;

        // Multi-threaded support needs to be reflected in Mesa code. (DaveM)
        _gld_bMultiThreaded = glb.bMultiThreaded;

		// Start logging
        ddlogPathOption(szLogPath);
		ddlogWarnOption(glb.bMessageBoxWarnings);
		ddlogOpen((DDLOG_loggingMethodType)dwLogging,
				  (DDLOG_severityType)dwDebugLevel);

		// Obtain the name of the calling app
		ddlogMessage(DDLOG_SYSTEM, "Driver           : SciTech GLDirect 4.0\n");
		GetModuleFileName(NULL, szExeName, sizeof(szExeName));
		ddlogPrintf(DDLOG_SYSTEM, "Executable       : %s", szExeName);

		ddlogPrintf(DDLOG_SYSTEM, "DirectDraw device: %s", glb.szDDName);
		ddlogPrintf(DDLOG_SYSTEM, "Direct3D driver  : %s", glb.szD3DName);

		ddlogPrintf(DDLOG_SYSTEM, "Rendering type   : %s", szRendering[glb.dwRendering]);

		ddlogPrintf(DDLOG_SYSTEM, "Multithreaded    : %s", glb.bMultiThreaded ? "Enabled" : "Disabled");
		ddlogPrintf(DDLOG_SYSTEM, "Display resources: %s", glb.bDirectDrawPersistant ? "Persistant" : "Instanced");
		ddlogPrintf(DDLOG_SYSTEM, "Buffer resources : %s", glb.bPersistantBuffers ? "Persistant" : "Instanced");

		dglInitContextState();
		dglBuildPixelFormatList();
		//dglBuildTextureFormatList();

    // D3D callback driver is now successfully initialized
    bInitialized = TRUE;
    // D3D callback driver is now ready to be exited
    bExited = FALSE;

    return TRUE;
}

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

void dglExitDriver(void)
{

	// Only need to clean up once per instance:
	// May be called implicitly from DLL_PROCESS_DETACH,
	// or explicitly from DGL_exitDriver().
	if (bExited)
		return;
	bExited = TRUE;

    // DDraw objects may be invalid when DLL unloads.
__try {

	// Clean-up sequence (moved from DLL_PROCESS_DETACH)
#ifndef _USE_GLD3_WGL
	dglReleaseTextureFormatList();
#endif
	dglReleasePixelFormatList();
	dglDeleteContextState();

#ifdef _USE_GLD3_WGL
	_gldDriver.DestroyPrivateGlobals();
#endif

}
__except(EXCEPTION_EXECUTE_HANDLER) {
	    ddlogPrintf(DDLOG_WARN, "Exception raised in dglExitDriver.");
}

	// Close the log file
	ddlogClose();
}

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

int WINAPI DllMain(
	HINSTANCE hInstance,
	DWORD fdwReason,
	PVOID pvReserved)
{
	switch (fdwReason) {
	case DLL_PROCESS_ATTACH:
        // Cache DLL instance handle
        hInstanceDll = hInstance;

        // Flag that callback driver has yet to be initialized
        bInitialized = bExited = FALSE;

#ifndef _USE_GLD3_WGL
        // Init internal Mesa function pointers
		memset(&mesaFuncs, 0, sizeof(DGL_mesaFuncs));
#endif // _USE_GLD3_WGL

		// Init defaults
		dglInitGlobals();

        // Defer rest of DLL initialization to 1st WGL function call
		break;

	case DLL_PROCESS_DETACH:
		// Call exit clean-up sequence
		dglExitDriver();
		break;
	}

	return TRUE;
}

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

void APIENTRY DGL_exitDriver(void)
{
	// Call exit clean-up sequence
	dglExitDriver();
}

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

void APIENTRY DGL_reinitDriver(void)
{
	// Force init sequence again
    bInitialized = bExited = FALSE;
	dglInitDriver();
}

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

int WINAPI DllInitialize(
	HINSTANCE hInstance,
	DWORD fdwReason,
	PVOID pvReserved)
{
	// Some Watcom compiled executables require this.
	return DllMain(hInstance, fdwReason, pvReserved);
}

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

void DGL_LoadSplashScreen(int piReg, char* pszUser)
{
	HINSTANCE			hSplashDll = NULL;
	LPDGLSPLASHSCREEN 	dglSplashScreen = NULL;
	static BOOL 		bOnce = FALSE;
    static int          iReg = 0;
    static char         szUser[255] = {"\0"};

    // Display splash screen at all?
    if (!bSplashScreen)
        return;

	// Only display splash screen once
	if (bOnce)
		return;
	bOnce = TRUE;

    // Make local copy of string for passing to DLL
    if (pszUser)
        strcpy(szUser, pszUser);
    iReg = piReg;

	// Load Splash Screen DLL
	// (If it fails to load for any reason, we don't care...)
	hSplashDll = LoadLibrary("gldsplash.dll");
	if (hSplashDll) {
		// Execute the Splash Screen function
		dglSplashScreen = (LPDGLSPLASHSCREEN)GetProcAddress(hSplashDll, "GLDSplashScreen");
		if (dglSplashScreen)
			(*dglSplashScreen)(1, iReg, szUser);
		// Don't unload the DLL since splash screen dialog is modeless now
		}
}

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

BOOL dglValidate()
{
	char *szCaption = "SciTech GLDirect Driver";
	UINT uType = MB_OK | MB_ICONEXCLAMATION;

#ifdef _USE_GLD3_WGL
	// (Re)build pixelformat list
	if (glb.bPixelformatsDirty)
		_gldDriver.BuildPixelformatList();
#endif

	// Check to see if we have already validated
	if (bDriverValidated && bInitialized)
		return TRUE;

    // Since all (most) the WGL functions must be validated at this point,
    // this also insure that the callback driver is completely initialized.
    if (!bInitialized)
        if (!dglInitDriver()) {
			MessageBox(NULL,
				"The GLDirect driver could not initialize.\n\n"
				"Please run the configuration program to\n"
				"properly configure the driver, or else\n"
                "re-run the installation program.", szCaption, uType);
			_exit(1); // Bail
        }

    return TRUE;
}

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

