/******************************Module*Header*******************************\
* Module Name: wcreate.c
*
* wgl Context creation routines
*
* Created: 08-27-1996
* Author: Drew Bliss [drewb]
*
* Copyright (c) 1996 Microsoft Corporation
\**************************************************************************/

#include "precomp.h"
#pragma hdrstop

#include <ddrawpr.h>

#include <glscreen.h>
#include <glgenwin.h>

#include <gencx.h>

#include "metasup.h"
#include "wgldef.h"

// List of loaded GL drivers for the process.
// A driver is loaded only once per process.  Once it is loaded,
// it will not be freed until the process quits.

static PGLDRIVER pGLDriverList = (PGLDRIVER) NULL;

/******************************Public*Routine******************************\
* iAllocLRC
*
* Allocates a LRC and a handle.  Initializes the LDC to have the default
* attributes.  Returns the handle index.  On error returns INVALID_INDEX.
*
* History:
*  Tue Oct 26 10:25:26 1993     -by-    Hock San Lee    [hockl]
* Wrote it.
\**************************************************************************/

static LRC lrcDefault =
{
    0,                    // dhrc
    0,                    // hrc
    0,                    // iPixelFormat
    LRC_IDENTIFIER,       // ident
    INVALID_THREAD_ID,    // tidCurrent
    NULL,                 // pGLDriver
    GLWID_ERROR, NULL, NULL, NULL, // gwidCurrent
    GLWID_ERROR, NULL, NULL, NULL, // gwidCreate
#ifdef GL_METAFILE
    0,                    // uiGlsCaptureContext
    0,                    // uiGlsPlaybackContext
    FALSE,                // fCapturing
    0, 0, 0, 0, 0,        // Metafile scaling constants
    0, 0, 0, 0.0f, 0.0f,
#endif

    NULL,  // GLubyte *pszExtensions

#ifdef GL_METAFILE
    {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // XFORM xformMeta
    NULL,                                 // LPRECTL prclGlsBounds
#endif

    NULL, 0,                    // DDraw texture formats
};

static ULONG iAllocLRC(int iPixelFormat)
{
    ULONG  irc = INVALID_INDEX;
    PLRC   plrc;

// Allocate a local RC.

    plrc = (PLRC) ALLOC(sizeof(LRC));
    if (plrc == (PLRC) NULL)
    {
        DBGERROR("Alloc failed\n");
        return(irc);
    }

// Initialize the local RC.

    *plrc = lrcDefault;
    plrc->iPixelFormat = iPixelFormat;

// Allocate a local handle.

    irc = iAllocHandle(LO_RC, 0, (PVOID) plrc);
    if (irc == INVALID_INDEX)
    {
        vFreeLRC(plrc);
        return(irc);
    }
    return(irc);
}

/******************************Public*Routine******************************\
* vFreeLRC
*
* Free a local side RC.
*
* History:
*  Tue Oct 26 10:25:26 1993     -by-    Hock San Lee    [hockl]
* Copied from gdi client.
\**************************************************************************/

VOID vFreeLRC(PLRC plrc)
{
// The driver will not be unloaded here.  It is loaded for the process forever.
// Some assertions.

    ASSERTOPENGL(plrc->ident == LRC_IDENTIFIER,
                 "vFreeLRC: Bad plrc\n");
    ASSERTOPENGL(plrc->dhrc == (DHGLRC) 0,
                 "vFreeLRC: Driver RC is not freed!\n");
    ASSERTOPENGL(plrc->tidCurrent == INVALID_THREAD_ID,
                 "vFreeLRC: RC is current!\n");
    ASSERTOPENGL(plrc->gwidCurrent.iType == GLWID_ERROR,
                 "vFreeLRC: Current surface is not NULL!\n");
#ifdef GL_METAFILE
    ASSERTOPENGL(plrc->uiGlsCaptureContext == 0,
                 "vFreeLRC: GLS capture context not freed");
    ASSERTOPENGL(plrc->uiGlsPlaybackContext == 0,
                 "vFreeLRC: GLS playback context not freed");
    ASSERTOPENGL(plrc->fCapturing == FALSE,
                 "vFreeLRC: GLS still capturing");
#endif

// Smash the identifier.

    plrc->ident = 0;

// Free the memory.

    if (plrc->pszExtensions)
        FREE(plrc->pszExtensions);

    if (plrc->pddsdTexFormats != NULL)
    {
        FREE(plrc->pddsdTexFormats);
    }

    FREE(plrc);
}

/******************************Public*Routine******************************\
* vCleanupAllLRC
*
* Process cleanup -- make sure all HGLRCs are deleted.  This is done by
* scanning the local handle table for all currently allocated objects
* of type LO_RC and deleting them.
*
* Called *ONLY* during DLL process detach.
*
* History:
*  24-Jul-1995 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

VOID vCleanupAllLRC()
{
    UINT ii;

    if ( pLocalTable )
    {
        ENTERCRITICALSECTION(&semLocal);

        // Scan handle table for handles of type LO_RC.  Make sure to always
        // read the commit value since we need to periodically release the
        // semaphore.

        for (ii = 0; ii < *((volatile ULONG *)&cLheCommitted); ii++)
        {
            if ( pLocalTable[ii].iType == LO_RC )
            {
                if ( !wglDeleteContext((HGLRC) ULongToPtr(LHANDLE(ii))) )
                {
                    WARNING1("bCleanupAllLRC: failed to remove hrc = 0x%lx\n",
                             LHANDLE(ii));
                }
            }
        }

        LEAVECRITICALSECTION(&semLocal);
    }
}

/******************************Public*Routine******************************\
*
* GetDrvRegInfo
*
* Looks up driver registry information by name.
* An old-style ICD registry entry has a REG_SZ value under the given name.
* A new-style ICD registry entry has a key of the given name with
* various values.
*
* This routine checks first for a key and then will optionally
* try the value.  If a key is not found then extended driver information
* is filled out with the defaults.
*
* History:
*  Tue Apr 01 17:33:12 1997     -by-    Drew Bliss [drewb]
*   Created
*
\**************************************************************************/

typedef struct _GLDRVINFO
{
    DWORD dwFlags;
    TCHAR tszDllName[MAX_GLDRIVER_NAME+1];
    DWORD dwVersion;
    DWORD dwDriverVersion;
} GLDRVINFO;

#ifdef _WIN95_
#define STR_OPENGL_DRIVER_LIST (PCSTR)"Software\\Microsoft\\Windows\\CurrentVersion\\OpenGLDrivers"
#else
#define STR_OPENGL_DRIVER_LIST (PCWSTR)L"Software\\Microsoft\\Windows NT\\CurrentVersion\\OpenGLDrivers"
#endif

BOOL GetDrvRegInfo(PTCHAR ptszName, GLDRVINFO *pgdi)
{
    HKEY hkDriverList = NULL;
    HKEY hkDriverInfo;
    DWORD dwDataType;
    DWORD cjSize;
    BOOL bRet;

    bRet = FALSE;

    // Open the registry key for the list of OpenGL drivers.
    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, STR_OPENGL_DRIVER_LIST,
                     0, KEY_READ, &hkDriverList) != ERROR_SUCCESS)
    {
        WARNING("RegOpenKeyEx failed\n");
        return bRet;
    }

    // Force a terminator on the DLL name so that we can check for
    // valid DLL name data.
    pgdi->tszDllName[MAX_GLDRIVER_NAME] = 0;

    cjSize = sizeof(TCHAR) * MAX_GLDRIVER_NAME;

    // Attempt to open a key under the driver's name.
    if (RegOpenKeyEx(hkDriverList, ptszName, 0, KEY_READ,
                     &hkDriverInfo) == ERROR_SUCCESS)
    {
        // New-style driver entry.  Fetch information from values.

        bRet = TRUE;

        if (RegQueryValueEx(hkDriverInfo, __TEXT("DLL"), NULL, &dwDataType,
                            (LPBYTE)pgdi->tszDllName,
                            &cjSize) != ERROR_SUCCESS ||
            dwDataType != REG_SZ)
        {
            WARNING("Invalid DLL value in ICD key\n");
            bRet = FALSE;
        }

        cjSize = sizeof(DWORD);

        if (bRet &&
            (RegQueryValueEx(hkDriverInfo, __TEXT("Flags"), NULL, &dwDataType,
                             (LPBYTE)&pgdi->dwFlags,
                             &cjSize) != ERROR_SUCCESS ||
             dwDataType != REG_DWORD))
        {
            WARNING("Invalid Flags value in ICD key\n");
            bRet = FALSE;
        }

        if (bRet &&
            (RegQueryValueEx(hkDriverInfo, __TEXT("Version"), NULL,
                             &dwDataType, (LPBYTE)&pgdi->dwVersion,
                             &cjSize) != ERROR_SUCCESS ||
             dwDataType != REG_DWORD))
        {
            WARNING("Invalid Version value in ICD key\n");
            bRet = FALSE;
        }

        if (bRet &&
            (RegQueryValueEx(hkDriverInfo, __TEXT("DriverVersion"), NULL,
                             &dwDataType, (LPBYTE)&pgdi->dwDriverVersion,
                             &cjSize) != ERROR_SUCCESS ||
             dwDataType != REG_DWORD))
        {
            WARNING("Invalid DriverVersion value in ICD key\n");
            bRet = FALSE;
        }

        // Mark as having full information.
        pgdi->dwFlags |= GLDRIVER_FULL_REGISTRY;

        RegCloseKey(hkDriverInfo);
    }
    else
    {
        // Attempt to fetch value under driver's name.

        if (RegQueryValueEx(hkDriverList, ptszName, NULL, &dwDataType,
                            (LPBYTE)pgdi->tszDllName,
                            &cjSize) != ERROR_SUCCESS ||
            dwDataType != REG_SZ)
        {
            WARNING1("RegQueryValueEx failed, %d\n", GetLastError());
        }
        else
        {
            // We found old-style information which only provides the
            // DLL name.  Fill in the rest with defaults.
            //
            // Version and DriverVersion are not set here under the
            // assumption that the display driver set them in the
            // OPENGL_GETINFO escape since the old-style path requires
            // the escape to occur before getting here.

            pgdi->dwFlags = 0;

            bRet = TRUE;
        }
    }

    RegCloseKey(hkDriverList);

    // Validate the driver name.  It must have some characters and
    // it must be terminated.
    if (bRet &&
        (pgdi->tszDllName[0] == 0 ||
         pgdi->tszDllName[MAX_GLDRIVER_NAME] != 0))
    {
        WARNING("Invalid DLL name information for ICD\n");
        bRet = FALSE;
    }

#ifdef _WIN95_
    // Force client-side buffer calls for Win95.
    pgdi->dwFlags |= GLDRIVER_CLIENT_BUFFER_CALLS;
#endif

    return bRet;
}

/******************************Public*Routine******************************\
* bGetDriverInfo
*
* The HDC is used to determine the display driver name.  This name in turn
* is used as a subkey to search the registry for a corresponding OpenGL
* driver name.
*
* The OpenGL driver name is returned in the buffer pointed to by pwszDriver.
* If the name is not found or does not fit in the buffer, an error is
* returned.
*
* Returns:
*   TRUE if sucessful.
*   FALSE if the driver name does not fit in the buffer or if an error occurs.
*
* History:
*  16-Jan-1994 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

BOOL bGetDriverInfo(HDC hdc, GLDRVINFO *pgdi)
{
    GLDRVNAME    dn;
    GLDRVNAMERET dnRet;

// Get display driver name.

    dn.oglget.ulSubEsc = OPENGL_GETINFO_DRVNAME;
    if ( ExtEscape(hdc, OPENGL_GETINFO, sizeof(GLDRVNAME), (LPCSTR) &dn,
                      sizeof(GLDRVNAMERET), (LPSTR) &dnRet) <= 0 )
    {
        WARNING("ExtEscape(OPENGL_GETINFO, "
                "OPENGL_GETINFO_DRVNAME) failed\n");
        return FALSE;
    }

    pgdi->dwVersion = dnRet.ulVersion;
    pgdi->dwDriverVersion = dnRet.ulDriverVersion;

    if (GetDrvRegInfo((PTCHAR)dnRet.awch, pgdi))
    {
        // Verify that the client-side driver version information
        // matches the information returned from the display driver.
        // Is this too restrictive?  Old scheme used
        // DrvValidateVersion to allow the client-side DLL to validate
        // the display driver's version however it felt like.
        // In the new scheme DrvValidateVersion is mostly useless because
        // of the below code.
        return pgdi->dwVersion == dnRet.ulVersion &&
            pgdi->dwDriverVersion == dnRet.ulDriverVersion;
    }
    else
    {
        return FALSE;
    }
}

/*****************************Private*Routine******************************\
*
* wglCbSetCurrentValue
*
* Sets a thread-local value for a client-side driver
*
* History:
*  Wed Dec 21 15:10:40 1994     -by-    Drew Bliss [drewb]
*   Created
*
\**************************************************************************/

void APIENTRY wglCbSetCurrentValue(VOID *pv)
{
    GLTEB_SET_CLTDRIVERSLOT(pv);
}

/*****************************Private*Routine******************************\
*
* wglCbGetCurrentValue
*
* Gets a thread-local value for a client-side driver
*
* History:
*  Wed Dec 21 15:11:32 1994     -by-    Drew Bliss [drewb]
*   Created
*
\**************************************************************************/

PVOID APIENTRY wglCbGetCurrentValue(void)
{
    return GLTEB_CLTDRIVERSLOT();
}

/******************************Public*Routine******************************\
*
* wglCbGetDhglrc
*
* Translates an HGLRC to a DHGLRC for a client-side driver
*
* History:
*  Mon Jan 16 17:03:38 1995     -by-    Drew Bliss [drewb]
*   Created
*
\**************************************************************************/

DHGLRC APIENTRY wglCbGetDhglrc(HGLRC hrc)
{
    PLRC plrc;
    ULONG irc;
    PLHE plheRC;

    irc = MASKINDEX(hrc);
    plheRC = pLocalTable + irc;
    if ((irc >= cLheCommitted) ||
        (!MATCHUNIQ(plheRC, hrc)) ||
        ((plheRC->iType != LO_RC))
       )
    {
        DBGLEVEL1(LEVEL_ERROR, "wglCbGetDhglrc: invalid hrc 0x%lx\n", hrc);
        SetLastError(ERROR_INVALID_HANDLE);
        return 0;
    }

    plrc = (PLRC)plheRC->pv;
    ASSERTOPENGL(plrc->ident == LRC_IDENTIFIER,
                 "wglCbGetDhglrc: Bad plrc\n");

    return plrc->dhrc;
}

/******************************Public*Routine******************************\
*
* wglCbGetDdHandle
*
* Callback to allow ICDs to extract kernel-mode handles for DDraw surfaces
*
* History:
*  Tue Feb 25 17:14:29 1997     -by-    Drew Bliss [drewb]
*   Created
*
\**************************************************************************/

HANDLE APIENTRY wglCbGetDdHandle(LPDIRECTDRAWSURFACE pdds)
{
    return (HANDLE)(((LPDDRAWI_DDRAWSURFACE_INT)pdds)->lpLcl)->hDDSurface;
}

// wgl's default callback procedures
#define CALLBACK_PROC_COUNT 4

static PROC __wglCallbackProcs[CALLBACK_PROC_COUNT] =
{
    (PROC)wglCbSetCurrentValue,
    (PROC)wglCbGetCurrentValue,
    (PROC)wglCbGetDhglrc,
    (PROC)wglCbGetDdHandle
};

static char *pszDriverEntryPoints[] =
{
    "DrvCreateContext",
    "DrvDeleteContext",
    "DrvSetContext",
    "DrvReleaseContext",
    "DrvCopyContext",
    "DrvCreateLayerContext",
    "DrvShareLists",
    "DrvGetProcAddress",
    "DrvDescribeLayerPlane",
    "DrvSetLayerPaletteEntries",
    "DrvGetLayerPaletteEntries",
    "DrvRealizeLayerPalette",
    "DrvSwapLayerBuffers",
    "DrvCreateDirectDrawContext",
    "DrvEnumTextureFormats",
    "DrvBindDirectDrawTexture",
    "DrvSwapMultipleBuffers",
    "DrvDescribePixelFormat",
    "DrvSetPixelFormat",
    "DrvSwapBuffers"
};
#define DRIVER_ENTRY_POINTS (sizeof(pszDriverEntryPoints)/sizeof(char *))

/******************************Public*Routine******************************\
* pgldrvLoadInstalledDriver
*
* Loads the opengl driver for the given device.  Once the driver is loaded,
* it will not be freed until the process goes away!  It is loaded only once
* for each process that references it.
*
* Returns the GLDRIVER structure if the driver is loaded.
* Returns NULL if no driver is found or an error occurs.
*
* History:
*  Tue Oct 26 10:25:26 1993     -by-    Hock San Lee    [hockl]
* Rewrote it.
\**************************************************************************/

PGLDRIVER APIENTRY pgldrvLoadInstalledDriver(HDC hdc)
{
    GLDRVINFO gdi;
    PGLDRIVER pGLDriverNext;
    PGLDRIVER pGLDriver = (PGLDRIVER) NULL;     // needed by clean up
    PGLDRIVER pGLDriverRet = (PGLDRIVER) NULL;  // return value, assume error
    PFN_DRVVALIDATEVERSION pfnDrvValidateVersion = (PFN_DRVVALIDATEVERSION) NULL;
    PFN_DRVSETCALLBACKPROCS pfnDrvSetCallbackProcs;
    DWORD        dwEscape;
    int          i;
    PROC        *pproc;
    GLGENwindow *pwnd;
    GLWINDOWID   gwid;

    DBGENTRY("pgldrvLoadInstalledDriver\n");

// Try to grab the cached pgldrv from the GLGENwindow if it exists.
// This only works for DCs that have a window with a device pixel format.

    WindowIdFromHdc(hdc, &gwid);
    pwnd = pwndGetFromID(&gwid);
    if (pwnd)
    {
        ULONG ulFlags;

        ulFlags = pwnd->ulFlags;
        pGLDriverRet = (PGLDRIVER) pwnd->pvDriver;

        pwndRelease(pwnd);

        if ( ulFlags & GLGENWIN_DRIVERSET )
        {
            return pGLDriverRet;
        }
    }

// Do a quick check and see if this driver even understands OpenGL

    dwEscape = OPENGL_GETINFO;
    if (ExtEscape(hdc, QUERYESCSUPPORT, sizeof(dwEscape), (LPCSTR)&dwEscape,
                  0, NULL) <= 0)
    {
        // Don't output a message since this code path is traversed often
        // for the pixel format routines.

#ifdef CHECK_DEFAULT_ICD
        // The display driver doesn't support a specific ICD.  Check
        // for a default ICD.  It must have full registry information.
        if (!GetDrvRegInfo(__TEXT("Default"), &gdi) ||
            (gdi.dwFlags & GLDRIVER_FULL_REGISTRY) == 0)
        {
            return NULL;
        }
#else
        return NULL;
#endif
    }

// Determine driver info from hdc

    else if ( !bGetDriverInfo(hdc, &gdi) )
    {
        WARNING("bGetDriverInfo failed\n");
        return NULL;
    }

// Load the driver only once per process.

    ENTERCRITICALSECTION(&semLocal);

// Look for the OpenGL driver in the previously loaded driver list.

    for (pGLDriverNext = pGLDriverList;
         pGLDriverNext != (PGLDRIVER) NULL;
         pGLDriverNext = pGLDriverNext->pGLDriver)
    {
        PTCHAR ptszDllName1 = pGLDriverNext->tszDllName;
        PTCHAR ptszDllName2 = gdi.tszDllName;

        while (*ptszDllName1 == *ptszDllName2)
        {
// If we find one, return that driver.

            if (*ptszDllName1 == 0)
            {
                DBGINFO("pgldrvLoadInstalledDriver: "
                        "return previously loaded driver\n");
                pGLDriverRet = pGLDriverNext;       // found one
                goto pgldrvLoadInstalledDriver_crit_exit;
            }

            ptszDllName1++;
            ptszDllName2++;
        }
    }

// Load the driver for the first time.
// Allocate the driver data.

    pGLDriver = (PGLDRIVER) ALLOC(sizeof(GLDRIVER));
    if (pGLDriver == (PGLDRIVER) NULL)
    {
        WARNING("Alloc failed\n");
        goto pgldrvLoadInstalledDriver_crit_exit;   // error
    }

// Load the driver.

    pGLDriver->hModule = LoadLibrary(gdi.tszDllName);
    if (pGLDriver->hModule == (HINSTANCE) NULL)
    {
        WARNING("pgldrvLoadInstalledDriver: LoadLibrary failed\n");
        goto pgldrvLoadInstalledDriver_crit_exit;   // error
    }

// Copy the driver info.

    memcpy
    (
        pGLDriver->tszDllName,
        gdi.tszDllName,
        (MAX_GLDRIVER_NAME + 1) * sizeof(TCHAR)
    );
    pGLDriver->dwFlags = gdi.dwFlags;

// Get the proc addresses.
// DrvGetProcAddress is optional.  It must be provided if a driver supports
// extensions.

    pfnDrvValidateVersion = (PFN_DRVVALIDATEVERSION)
        GetProcAddress(pGLDriver->hModule, "DrvValidateVersion");
    pfnDrvSetCallbackProcs = (PFN_DRVSETCALLBACKPROCS)
        GetProcAddress(pGLDriver->hModule, "DrvSetCallbackProcs");

    pproc = (PROC *)&pGLDriver->pfnDrvCreateContext;
    for (i = 0; i < DRIVER_ENTRY_POINTS; i++)
    {
        *pproc++ =
            GetProcAddress(pGLDriver->hModule, pszDriverEntryPoints[i]);
    }

    if ((pGLDriver->pfnDrvCreateContext == NULL &&
          pGLDriver->pfnDrvCreateLayerContext == NULL) ||
        pGLDriver->pfnDrvDeleteContext == NULL ||
        pGLDriver->pfnDrvSetContext == NULL ||
        pGLDriver->pfnDrvReleaseContext == NULL ||
        ((gdi.dwFlags & GLDRIVER_CLIENT_BUFFER_CALLS) &&
         (pGLDriver->pfnDrvDescribePixelFormat == NULL ||
          pGLDriver->pfnDrvSetPixelFormat == NULL ||
          pGLDriver->pfnDrvSwapBuffers == NULL)) ||
        pfnDrvValidateVersion == NULL)
    {
        WARNING("pgldrvLoadInstalledDriver: GetProcAddress failed\n");
        goto pgldrvLoadInstalledDriver_crit_exit;   // error
    }

// Validate the driver.

    //!!!XXX -- Need to define a manifest constant for the ulVersion number
    //          in this release.  Where should it go?
    if ( gdi.dwVersion != 2 || !pfnDrvValidateVersion(gdi.dwDriverVersion) )
    {
        WARNING2("pgldrvLoadInstalledDriver: bad driver version "
                 "(0x%lx, 0x%lx)\n", gdi.dwVersion, gdi.dwDriverVersion);
        goto pgldrvLoadInstalledDriver_crit_exit;   // error
    }

// Everything is golden.
// Add it to the driver list.

    pGLDriver->pGLDriver = pGLDriverList;
    pGLDriverList = pGLDriver;
    pGLDriverRet = pGLDriver;       // set return value
    DBGINFO("pgldrvLoadInstalledDriver: Loaded an OpenGL driver\n");

    // Set the callback procs for the driver if the driver supports doing so
    if (pfnDrvSetCallbackProcs != NULL)
    {
        pfnDrvSetCallbackProcs(CALLBACK_PROC_COUNT, __wglCallbackProcs);
    }

// Error clean up in the critical section.

pgldrvLoadInstalledDriver_crit_exit:
    if (pGLDriverRet == (PGLDRIVER) NULL)
    {
        if (pGLDriver != (PGLDRIVER) NULL)
        {
            if (pGLDriver->hModule != (HINSTANCE) NULL)
                if (!FreeLibrary(pGLDriver->hModule))
                    RIP("FreeLibrary failed\n");

            FREE(pGLDriver);
        }
    }

    LEAVECRITICALSECTION(&semLocal);

    return(pGLDriverRet);
}

/******************************Public*Routine******************************\
*
* CreateAnyContext
*
* Base worker function for creating all kinds of contexts
*
* History:
*  Mon Aug 26 14:41:31 1996     -by-    Drew Bliss [drewb]
*   Created
*
\**************************************************************************/

HGLRC CreateAnyContext(GLSURF *pgsurf)
{
    PLHE  plheRC;
    ULONG irc;
    HGLRC hrc;
    PLRC  plrc;

#ifndef _WIN95_
    // _OPENGL_NT_
    // On NT, client-side drivers can use special fast TEB access macros
    // which rely on glContext being at a fixed offset into the
    // TEB.  Assert that the offset is where we think it is
    // to catch any TEB changes which could break client-side
    // drivers
    // This assert is here in wglCreateContext to ensure that it
    // is checked very early in OpenGL operation
    ASSERTOPENGL(FIELD_OFFSET(TEB, glContext) == TeglContext,
                 "TEB.glContext at wrong offset\n");
    ASSERTOPENGL(FIELD_OFFSET(TEB, glDispatchTable) == TeglDispatchTable,
                 "TEB.glDispatchTable at wrong offset\n");
    ASSERTOPENGL(FIELD_OFFSET(TEB, glReserved1) == TeglReserved1,
                 "TEB.glReserved1 at wrong offset\n");
#if !defined(_WIN64)
    ASSERTOPENGL(FIELD_OFFSET(TEB, glReserved1)+(18 * sizeof(ULONG_PTR)) == TeglPaTeb,
                 "TEB.glPaTeb at wrong offset\n");
#endif
    ASSERTOPENGL(FIELD_OFFSET(TEB, glReserved2) == TeglReserved2,
                 "TEB.glReserved2 at wrong offset\n");
    ASSERTOPENGL(FIELD_OFFSET(TEB, glSectionInfo) == TeglSectionInfo,
                 "TEB.glSectionInfo at wrong offset\n");
    ASSERTOPENGL(FIELD_OFFSET(TEB, glSection) == TeglSection,
                 "TEB.glSection at wrong offset\n");
    ASSERTOPENGL(FIELD_OFFSET(TEB, glTable) == TeglTable,
                 "TEB.glTable at wrong offset\n");
    ASSERTOPENGL(FIELD_OFFSET(TEB, glCurrentRC) == TeglCurrentRC,
                 "TEB.glCurrentRC at wrong offset\n");
#endif

// Create the local RC.

    ENTERCRITICALSECTION(&semLocal);
    irc = iAllocLRC(pgsurf->ipfd);
    if (irc == INVALID_INDEX ||
        cLockHandle((ULONG_PTR)(hrc = (HGLRC) ULongToPtr(LHANDLE(irc)))) <= 0)
    {
        // cLockHandle should never fail or we will need to free the handle.
        ASSERTOPENGL(irc == INVALID_INDEX, "cLockHandle should not fail!\n");
        LEAVECRITICALSECTION(&semLocal);
        return((HGLRC) 0);
    }
    LEAVECRITICALSECTION(&semLocal);

    plheRC = &pLocalTable[irc];
    plrc = (PLRC) plheRC->pv;

    // Remember the creation DC.  This needs to be done early because
    // it is referenced in some code paths.

    plrc->gwidCreate.hdc = pgsurf->hdc;
    if (pgsurf->dwFlags & GLSURF_HDC)
    {
        plrc->gwidCreate.hwnd = pgsurf->hwnd;
        if (plrc->gwidCreate.hwnd == NULL)
        {
            plrc->gwidCreate.iType = GLWID_HDC;
        }
        else
        {
            plrc->gwidCreate.iType = GLWID_HWND;
        }
        plrc->gwidCreate.pdds = NULL;
    }
    else
    {
        plrc->gwidCreate.iType = GLWID_DDRAW;
        plrc->gwidCreate.pdds = pgsurf->dd.gddsFront.pdds;
        plrc->gwidCreate.hwnd = NULL;
    }

    if (!(pgsurf->pfd.dwFlags & PFD_GENERIC_FORMAT) &&
        !(pgsurf->pfd.dwFlags & PFD_GENERIC_ACCELERATED))
    {
    // If it is a device format, load the installable OpenGL driver.
    // Find and load the OpenGL driver referenced by this DC.

        if (!(plrc->pGLDriver = pgldrvLoadInstalledDriver(pgsurf->hdc)))
            goto wglCreateContext_error;

    // Create a driver context.

        // If the surface is a DirectDraw surface use the DirectDraw
        // entry point
        if (pgsurf->dwFlags & GLSURF_DIRECTDRAW)
        {
            if (plrc->pGLDriver->pfnDrvCreateDirectDrawContext == NULL)
            {
                SetLastError(ERROR_INVALID_FUNCTION);
                goto wglCreateContext_error;
            }

            plrc->dhrc = plrc->pGLDriver->pfnDrvCreateDirectDrawContext(
                    pgsurf->hdc, pgsurf->dd.gddsFront.pdds, pgsurf->ipfd);
            if (plrc->dhrc == 0)
            {
                WARNING("wglCreateContext: "
                        "pfnDrvCreateDirectDrawContext failed\n");
                goto wglCreateContext_error;
            }
        }
        // If the driver supports layers then create a context for the
        // given layer.  Otherwise reject all layers except for the
        // main plane and call the layer-less create
        else if (plrc->pGLDriver->pfnDrvCreateLayerContext != NULL)
        {
            if (!(plrc->dhrc =
                  plrc->pGLDriver->pfnDrvCreateLayerContext(pgsurf->hdc,
                                                            pgsurf->iLayer)))
            {
                WARNING("wglCreateContext: pfnDrvCreateLayerContext failed\n");
                goto wglCreateContext_error;
            }
        }
        else if (pgsurf->iLayer != 0)
        {
            WARNING("wglCreateContext: "
                    "Layer given for driver without layer support\n");
            SetLastError(ERROR_INVALID_FUNCTION);
            goto wglCreateContext_error;
        }
        else if (!(plrc->dhrc =
                   plrc->pGLDriver->pfnDrvCreateContext(pgsurf->hdc)))
        {
            WARNING("wglCreateContext: pfnDrvCreateContext failed\n");
            goto wglCreateContext_error;
        }
    }
    else
    {
        GLCLTPROCTABLE *pgcpt;
        GLEXTPROCTABLE *pgept;
        __GLcontext *gc;

        // Unless supported by MCD, the generic implementation doesn't
        // support layers
        if ((pgsurf->iLayer != 0) &&
            !(pgsurf->pfd.dwFlags & PFD_GENERIC_ACCELERATED))
        {
            WARNING("wglCreateContext: Layer given to generic\n");
            goto wglCreateContext_error;
        }

#ifdef GL_METAFILE
        // Create a metafile context if necessary
        if (pgsurf->dwFlags & GLSURF_METAFILE)
        {
            if (!CreateMetaRc(pgsurf->hdc, plrc))
            {
                WARNING("wglCreateContext: CreateMetaRc failed\n");
                goto wglCreateContext_error;
            }
        }
#endif

    // If it is a generic format, call the generic OpenGL server.
    // Create a server RC.

        plheRC->hgre = (ULONG_PTR) __wglCreateContext(&plrc->gwidCreate, pgsurf);
        if (plheRC->hgre == 0)
            goto wglCreateContext_error;

        // Set up the default dispatch tables for display list playback
        gc = (__GLcontext *)plheRC->hgre;
        if (gc->modes.colorIndexMode)
            pgcpt = &glCltCIProcTable;
        else
            pgcpt = &glCltRGBAProcTable;
        pgept = &glExtProcTable;
        memcpy(&gc->savedCltProcTable.glDispatchTable, &pgcpt->glDispatchTable,
               pgcpt->cEntries*sizeof(PROC));
        memcpy(&gc->savedExtProcTable.glDispatchTable, &pgept->glDispatchTable,
               pgept->cEntries*sizeof(PROC));
    }

    DBGLEVEL3(LEVEL_INFO,
        "wglCreateContext: plrc = 0x%lx, pGLDriver = 0x%lx, hgre = 0x%lx\n",
        plrc, plrc->pGLDriver, plheRC->hgre);

// Success, return the result.

    plrc->hrc = hrc;

    vUnlockHandle((ULONG_PTR)hrc);

    return hrc;

wglCreateContext_error:

// Fail, clean up and return 0.

#ifdef GL_METAFILE
    // Clean up metafile context if necessary
    if (plrc->uiGlsCaptureContext != 0)
    {
        DeleteMetaRc(plrc);
    }
#endif

    DBGERROR("wglCreateContext failed\n");
    ASSERTOPENGL(plrc->dhrc == (DHGLRC) 0, "wglCreateContext: dhrc != 0\n");
    vFreeLRC(plrc);
    vFreeHandle(irc);           // it unlocks handle too
    return NULL;
}

/******************************Public*Routine******************************\
*
* CreateMetafileSurf
*
* Fills out a GLSURF for a metafile DC
*
* History:
*  Tue Aug 27 11:41:35 1996     -by-    Drew Bliss [drewb]
*   Created
*
\**************************************************************************/

#ifdef GL_METAFILE
void CreateMetafileSurf(HDC hdc, int iLayer, GLSURF *pgsurf)
{
    pgsurf->dwFlags = GLSURF_HDC | GLSURF_METAFILE;
    pgsurf->iLayer = iLayer;

    // Metafile surfaces don't have a real pixel format
    pgsurf->ipfd = 0;

    // Create a fake format of 24-bit DIB with BGR
    memset(&pgsurf->pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
    pgsurf->pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
    pgsurf->pfd.nVersion = 1;
    pgsurf->pfd.dwFlags = PFD_DRAW_TO_BITMAP | PFD_SUPPORT_OPENGL |
        PFD_GENERIC_FORMAT;
    pgsurf->pfd.iPixelType = PFD_TYPE_RGBA;
    pgsurf->pfd.cColorBits = 24;
    pgsurf->pfd.cStencilBits = 8;
    pgsurf->pfd.cRedBits = 8;
    pgsurf->pfd.cRedShift = 16;
    pgsurf->pfd.cGreenBits = 8;
    pgsurf->pfd.cGreenShift = 8;
    pgsurf->pfd.cBlueBits = 8;
    pgsurf->pfd.cBlueShift = 0;
    pgsurf->pfd.cDepthBits = 16;
    pgsurf->pfd.iLayerType = PFD_MAIN_PLANE;

    pgsurf->hdc = hdc;
}
#endif

/******************************Public*Routine******************************\
* wglSurfacePixelFormat
*
* wglDescribePixelFormat doesn't describe the format of the surface we want
* to render into.  Some fields need to be fixed up if the surface is RGB,
* BGR, or BITFIELDS.
*
* Expects a Describe'd pixel format as input
*
\**************************************************************************/

VOID APIENTRY wglSurfacePixelFormat(HDC hdc, PIXELFORMATDESCRIPTOR *ppfd)
{
    HBITMAP hbm;
    BITMAP bm;
    ULONG cBitmapColorBits;

    hbm = CreateCompatibleBitmap(hdc, 1, 1);
    if ( hbm )
    {
        if ( GetObject(hbm, sizeof(bm), &bm) )
        {
            cBitmapColorBits = bm.bmPlanes * bm.bmBitsPixel;

#if DBG
            // If dynamic color depth caused depth mismatch one of two
            // things will happen: 1) bitmap creation will fail because
            // we failed to fill in color format, or 2) drawing will
            // be incorrect.  We will not crash.

            if (cBitmapColorBits != ppfd->cColorBits)
                WARNING("pixel format/surface color depth mismatch\n");
#endif

            if ( cBitmapColorBits >= 16 )
                __wglGetBitfieldColorFormat(hdc, cBitmapColorBits, ppfd,
                                            TRUE);
        }
        else
        {
            WARNING("wglSurfacePixelFormat: GetObject failed\n");
        }

        DeleteObject(hbm);
    }
    else
    {
        WARNING("wglSurfacePixelFormat: Unable to create cbm\n");
    }
}

/******************************Public*Routine******************************\
* bLayerPixelFormat
*
* Fake up a pixel format using the layer descriptor format.
*
* We use this to describe the layer plane in a format that the generic
* context can understand.
*
* Expects a Describe'd pixel format as input for the flags
*
\**************************************************************************/

BOOL FASTCALL bLayerPixelFormat(HDC hdc, PIXELFORMATDESCRIPTOR *ppfd,
                                int ipfd, LONG iLayer)
{
    LAYERPLANEDESCRIPTOR lpd;

    if (!wglDescribeLayerPlane(hdc, ipfd, iLayer, sizeof(lpd), &lpd))
        return FALSE;

    ppfd->nSize    = sizeof(PIXELFORMATDESCRIPTOR);
    ppfd->nVersion = 1;
    ppfd->dwFlags  = (ppfd->dwFlags & (PFD_GENERIC_FORMAT |
                                       PFD_GENERIC_ACCELERATED)) |
                     (lpd.dwFlags & ~(LPD_SHARE_DEPTH | LPD_SHARE_STENCIL |
                                      LPD_SHARE_ACCUM | LPD_TRANSPARENT));
    ppfd->iPixelType  = lpd.iPixelType;
    ppfd->cColorBits  = lpd.cColorBits;
    ppfd->cRedBits    = lpd.cRedBits   ;
    ppfd->cRedShift   = lpd.cRedShift  ;
    ppfd->cGreenBits  = lpd.cGreenBits ;
    ppfd->cGreenShift = lpd.cGreenShift;
    ppfd->cBlueBits   = lpd.cBlueBits  ;
    ppfd->cBlueShift  = lpd.cBlueShift ;
    ppfd->cAlphaBits  = lpd.cAlphaBits ;
    ppfd->cAlphaShift = lpd.cAlphaShift;
    if (!(lpd.dwFlags & LPD_SHARE_ACCUM))
    {
        ppfd->cAccumBits      = 0;
        ppfd->cAccumRedBits   = 0;
        ppfd->cAccumGreenBits = 0;
        ppfd->cAccumBlueBits  = 0;
        ppfd->cAccumAlphaBits = 0;
    }
    if (!(lpd.dwFlags & LPD_SHARE_DEPTH))
    {
        ppfd->cDepthBits = 0;
    }
    if (!(lpd.dwFlags & LPD_SHARE_STENCIL))
    {
        ppfd->cStencilBits = 0;
    }
    ppfd->cAuxBuffers = 0;

    return TRUE;
}

/******************************Public*Routine******************************\
*
* IsDirectDrawDevice
*
* Returns surface associated with HDC if such an association exists
*
* History:
*  Wed Sep 25 13:18:02 1996     -by-    Drew Bliss [drewb]
*   Created
*
\**************************************************************************/

BOOL APIENTRY IsDirectDrawDevice(HDC hdc)
{
    LPDIRECTDRAWSURFACE pdds;
    HDC hdcDevice;

    if (pfnGetSurfaceFromDC != NULL &&
        pfnGetSurfaceFromDC(hdc, &pdds, &hdcDevice) == DD_OK)
    {
        // The call gave us a reference on the surface so release it.
        pdds->lpVtbl->Release(pdds);
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

/******************************Public*Routine******************************\
*
* DdPixelDepth
*
* Determines the number of bits per pixel for a surface.
*
* History:
*  Wed Nov 20 16:57:07 1996     -by-    Drew Bliss [drewb]
*   Created
*
\**************************************************************************/

BYTE APIENTRY DdPixelDepth(DDSURFACEDESC *pddsd)
{
    if (pddsd->ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED4)
    {
        return 4;
    }
    else if (pddsd->ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED8)
    {
        return 8;
    }
    else
    {
        return (BYTE)DdPixDepthToCount(pddsd->ddpfPixelFormat.dwRGBBitCount);
    }
}

/******************************Public*Routine******************************\
*
* wglIsDirectDevice
*
* Checks to see whether the given DC is a screen DC on the
* surface for which we have direct screen access
*
* History:
*  Fri Apr 19 15:17:30 1996     -by-    Drew Bliss [drewb]
*   Created
*
\**************************************************************************/

BOOL APIENTRY wglIsDirectDevice(HDC hdc)
{
    if (wglObjectType(hdc) != OBJ_DC)
    {
        return FALSE;
    }

    // What about multiple displays?
    return GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASDISPLAY;
}

/******************************Public*Routine******************************\
*
* InitDeviceSurface
*
* Fills out a GLSURF for an HDC-based surface
*
* History:
*  Tue Aug 27 19:22:38 1996     -by-    Drew Bliss [drewb]
*   Created
*
\**************************************************************************/

BOOL APIENTRY InitDeviceSurface(HDC hdc, int ipfd, int iLayer,
                                DWORD dwObjectType, BOOL bUpdatePfd,
                                GLSURF *pgsurf)
{
    pgsurf->dwFlags = GLSURF_HDC;
    pgsurf->iLayer = iLayer;
    pgsurf->ipfd = ipfd;
    pgsurf->hdc = hdc;
    pgsurf->hwnd = NULL;

    // Determine whether direct memory access is available for this surface
    // or not.  The two cases are:
    //   It's a screen surface and we have direct screen access
    //   It's a DIBSECTION memory surface
    if (dwObjectType == OBJ_DC)
    {
        pgsurf->dwFlags |= GLSURF_DIRECTDC;

        if (wglIsDirectDevice(hdc))
        {
            pgsurf->dwFlags |= GLSURF_SCREEN | GLSURF_VIDEO_MEMORY;
            pgsurf->hwnd = WindowFromDC(hdc);

            if (GLDIRECTSCREEN)
            {
                pgsurf->dwFlags |= GLSURF_DIRECT_ACCESS;
            }
        }
    }
    else if (dwObjectType == OBJ_MEMDC)
    {
        DIBSECTION ds;

        if (GetObject(GetCurrentObject(hdc, OBJ_BITMAP), sizeof(ds), &ds) ==
            sizeof(ds) && ds.dsBm.bmBits != NULL)
        {
            pgsurf->dwFlags |= GLSURF_DIRECT_ACCESS;
        }

        if (bUpdatePfd)
        {
            // Update pixel format with true surface information rather
            // than device information
            wglSurfacePixelFormat(hdc, &pgsurf->pfd);
        }
    }

    if (bUpdatePfd &&
        iLayer > 0 &&
        !bLayerPixelFormat(hdc, &pgsurf->pfd, ipfd, iLayer))
    {
        return FALSE;
    }

    return TRUE;
}

/******************************Public*Routine******************************\
*
* InitDdSurface
*
* Completes a GLSURF for a DirectDraw-based surface.
* Pixel format information should already be filled in.
*
* History:
*  Mon Aug 26 13:50:04 1996     -by-    Drew Bliss [drewb]
*   Created
*
\**************************************************************************/

BOOL InitDdSurface(LPDIRECTDRAWSURFACE pdds, HDC hdcDevice, GLSURF *pgsurf)
{
    DDSCAPS ddscaps;
    LPDIRECTDRAWSURFACE pddsZ;
    DDSURFACEDESC *pddsd;

    pgsurf->hdc = hdcDevice;

    pgsurf->dd.gddsFront.ddsd.dwSize = sizeof(DDSURFACEDESC);
    pgsurf->dd.gddsZ.ddsd.dwSize = sizeof(DDSURFACEDESC);

    pddsd = &pgsurf->dd.gddsFront.ddsd;
    if (pdds->lpVtbl->GetSurfaceDesc(pdds, pddsd) != DD_OK)
    {
        return FALSE;
    }

    pgsurf->dwFlags = GLSURF_DIRECTDRAW | GLSURF_DIRECT_ACCESS;
    pgsurf->iLayer = 0;

    // Check for an attached Z buffer
    memset(&ddscaps, 0, sizeof(ddscaps));
    ddscaps.dwCaps = DDSCAPS_ZBUFFER;
    pddsd = &pgsurf->dd.gddsZ.ddsd;
    if (pdds->lpVtbl->GetAttachedSurface(pdds, &ddscaps, &pddsZ) == DD_OK)
    {
        if (pddsZ->lpVtbl->GetSurfaceDesc(pddsZ, pddsd) != DD_OK)
        {
            pddsZ->lpVtbl->Release(pddsZ);
            return FALSE;
        }
    }
    else
    {
        memset(&pgsurf->dd.gddsZ, 0, sizeof(pgsurf->dd.gddsZ));
    }

    // If both the color buffer and the Z buffer are in video memory
    // then hardware acceleration is possible
    if ((pgsurf->dd.gddsFront.ddsd.ddsCaps.dwCaps & DDSCAPS_VIDEOMEMORY) &&
        (pddsZ == NULL ||
         (pgsurf->dd.gddsZ.ddsd.ddsCaps.dwCaps & DDSCAPS_VIDEOMEMORY)))
    {
        pgsurf->dwFlags |= GLSURF_VIDEO_MEMORY;
    }

    pgsurf->dd.gddsFront.pdds = pdds;
    pgsurf->dd.gddsFront.dwBitDepth =
        DdPixDepthToCount(pgsurf->dd.gddsFront.
                          ddsd.ddpfPixelFormat.dwRGBBitCount);
    // GetAttachedSurface gave us a reference to the Z buffer
    pgsurf->dd.gddsZ.pdds = pddsZ;
    pgsurf->dd.gddsZ.dwBitDepth =
        DdPixDepthToCount(pgsurf->dd.gddsZ.
                          ddsd.ddpfPixelFormat.dwZBufferBitDepth);

    return TRUE;
}

/******************************Public*Routine******************************\
* wglCreateLayerContext(HDC hdc, int iLayer)
*
* Create a rendering context for a specific layer
*
* Arguments:
*   hdc        - Device context.
*   iLayer     - Layer
*
* History:
*  Tue Oct 26 10:25:26 1993     -by-    Hock San Lee    [hockl]
* Rewrote it.
\**************************************************************************/

HGLRC WINAPI wglCreateLayerContext(HDC hdc, int iLayer)
{
    DWORD dwObjectType;
    GLSURF gsurf;
    LPDIRECTDRAWSURFACE pdds;
    HDC hdcDevice;
    HGLRC hrc;

    DBGENTRY("wglCreateLayerContext\n");

// Flush OpenGL calls.

    GLFLUSH();

// Validate the DC.

    dwObjectType = wglObjectType(hdc);
    switch (dwObjectType)
    {
    case OBJ_DC:
    case OBJ_MEMDC:
        break;

    case OBJ_ENHMETADC:
#ifdef GL_METAFILE
        if (pfnGdiAddGlsRecord == NULL)
        {
            DBGLEVEL1(LEVEL_ERROR, "wglCreateContext: metafile hdc: 0x%lx\n",
                      hdc);
            SetLastError(ERROR_INVALID_HANDLE);
            return((HGLRC) 0);
        }
        break;
#else
        DBGLEVEL1(LEVEL_ERROR, "wglCreateContext: metafile hdc: 0x%lx\n", hdc);
        SetLastError(ERROR_INVALID_HANDLE);
        return((HGLRC) 0);
#endif

    case OBJ_METADC:
    default:
        // 16-bit metafiles are not supported
        DBGLEVEL1(LEVEL_ERROR, "wglCreateContext: bad hdc: 0x%lx\n", hdc);
        SetLastError(ERROR_INVALID_HANDLE);
        return((HGLRC) 0);
    }

    pdds = NULL;
    hrc = NULL;

    memset(&gsurf, 0, sizeof(gsurf));
    gsurf.ipfd = GetPixelFormat(hdc);

#ifdef GL_METAFILE
    // Skip pixel format checks for metafiles
    if (dwObjectType == OBJ_ENHMETADC)
    {
        CreateMetafileSurf(hdc, iLayer, &gsurf);
        goto NoPixelFormat;
    }
#endif

// Get the current pixel format of the window or surface.
// If no pixel format has been set, return error.

    if (gsurf.ipfd == 0)
    {
        WARNING("wglCreateContext: No pixel format set in hdc\n");
        SetLastError(ERROR_INVALID_PIXEL_FORMAT);
        return ((HGLRC) 0);
    }

    if (!DescribePixelFormat(hdc, gsurf.ipfd, sizeof(gsurf.pfd), &gsurf.pfd))
    {
        DBGERROR("wglCreateContext: DescribePixelFormat failed\n");
        return ((HGLRC) 0);
    }

    // Check for a DirectDraw surface
    if (pfnGetSurfaceFromDC != NULL &&
        pfnGetSurfaceFromDC(hdc, &pdds, &hdcDevice) == DD_OK)
    {
        // Don't allow layers for DirectDraw surfaces since
        // layering is done through DirectDraw itself.
        if (iLayer != 0 ||
            !InitDdSurface(pdds, hdcDevice, &gsurf))
        {
            goto Exit;
        }
    }
    else if (!InitDeviceSurface(hdc, gsurf.ipfd, iLayer, dwObjectType,
                                TRUE, &gsurf))
    {
        goto Exit;
    }

#ifdef GL_METAFILE
 NoPixelFormat:
#endif

    hrc = CreateAnyContext(&gsurf);

 Exit:
    if (hrc == NULL)
    {
        if (pdds != NULL)
        {
            pdds->lpVtbl->Release(pdds);

            // Release reference on Z buffer if necessary
            if (gsurf.dd.gddsZ.pdds != NULL)
            {
                gsurf.dd.gddsZ.pdds->lpVtbl->Release(gsurf.dd.gddsZ.pdds);
            }
        }
    }

    return hrc;
}

/******************************Public*Routine******************************\
* wglCreateContext(HDC hdc)
*
* Create a rendering context.
*
* Arguments:
*   hdc        - Device context.
*
* History:
*  Tue Oct 26 10:25:26 1993     -by-    Hock San Lee    [hockl]
* Rewrote it.
\**************************************************************************/

HGLRC WINAPI wglCreateContext(HDC hdc)
{
    return wglCreateLayerContext(hdc, 0);
}