/******************************Module*Header*******************************\
* Module Name: pixelfmt.c
*
* This contains the pixel format functions.
*
* Created: 15-Dec-1994 00:28:39
* Author: Gilman Wong [gilmanw]   --   ported from gdi\gre\pixelfmt.cxx
*
* Copyright (c) 1994 Microsoft Corporation
\**************************************************************************/

#include "precomp.h"
#pragma hdrstop

//#define DBG_WINDOW
//#define DBG_REFCOUNTS

#ifdef _CLIENTSIDE_
// Need for glsbAttention declaration
#include "glsbcltu.h"
#include "glscreen.h"
#endif

#ifdef _MCD_
#include "mcd.h"
#endif

#define SAVE_ERROR_CODE(x)  SetLastError((x))

// Number of generic pixel formats.  There are 5 pixel depths (4,8,16,24,32).
// This is to convert BMF constants into # bits per pel

#define BMF_COUNT (BMF_32BPP+1)

ULONG gaulConvert[BMF_COUNT] =
{
    0,
    1,
    4,
    8,
    16,
    24,
    32
};

#define MIN_GENERIC_PFD  1
#define MAX_GENERIC_PFD  36

LRESULT CALLBACK
wglWndProc(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam);

#define PALETTE_WATCHER_CLASS __TEXT("Palette Watcher")
static ATOM aPaletteWatcherClass = 0;

DWORD tidPaletteWatcherThread = 0;
ULONG ulPaletteWatcherCount = 0;
HANDLE hPaletteWatcherThread = 0;
HWND hwndPaletteWatcher = 0;
LONG lPaletteWatcherUsers = 0;

/******************************Public*Routine******************************\
* pwndNew
*
* Allocate a new GLGENwindow, initialize it (from input structure), and
* insert it into the global linked list.
*
* Returns:
*   Pointer to structure if successful, NULL otherwise.
*
* History:
*  01-Nov-1994 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

GLGENwindow * APIENTRY pwndNew(GLGENwindow *pwndInit)
{
    GLGENwindow *pwndRet = (GLGENwindow *) NULL;
    BOOL bDirectScreen = GLDIRECTSCREEN && pwndInit->gwid.hwnd;
    LPDIRECTDRAWCLIPPER pddClip = (LPDIRECTDRAWCLIPPER) NULL;

// If using direct access, retrieve or create a clipper object to track
// vis rgn changes.

    if (pwndInit->gwid.iType == GLWID_DDRAW)
    {
        HRESULT hr;
        
        hr = pwndInit->gwid.pdds->lpVtbl->
            GetClipper(pwndInit->gwid.pdds, &pddClip);
        if (hr != DD_OK && hr != DDERR_NOCLIPPERATTACHED)
        {
            return NULL;
        }
    }
        
    if ( !bDirectScreen ||
         pwndInit->gwid.iType == GLWID_DDRAW ||
         (GLSCREENINFO->pdd->lpVtbl->
          CreateClipper(GLSCREENINFO->pdd, 0, &pddClip, NULL) == DD_OK &&
          pddClip->lpVtbl->SetHWnd(pddClip, 0, pwndInit->gwid.hwnd) == DD_OK) )
    {
        pwndInit->pddClip = pddClip;
        
    // Allocate a new GLGENwindow.

        pwndRet = (GLGENwindow *)ALLOC(sizeof(GLGENwindow));
        if (pwndRet)
        {
            // Initialize from input structure.
            *pwndRet = *pwndInit;

            // Initialize per-window semaphore.
            __try
            {
                InitializeCriticalSection(&pwndRet->sem);
            }
            __except(EXCEPTION_EXECUTE_HANDLER)
            {
                FREE(pwndRet);
                pwndRet = NULL;
            }

            if (pwndRet)
            {
                // Set initial usage count to one
                pwndRet->lUsers = 1;

                // Insert into linked list.
                EnterCriticalSection(&gwndHeader.sem);
                {
                    pwndRet->pNext = gwndHeader.pNext;
                    gwndHeader.pNext = pwndRet;
                }
                LeaveCriticalSection(&gwndHeader.sem);
            }
        }
    }
    else
    {
       WARNING("pwndNew: Clipper setup failed\n");

       if (pddClip != NULL)
       {
           pddClip->lpVtbl->Release(pddClip);
       }
    }

#ifdef DBG_WINDOW
    if (pwndRet != NULL)
    {
        DbgPrint("Alloc window %p, type %d, hdc %p, hwnd %p, pdds %p\n",
                 pwndRet, pwndRet->gwid.iType, pwndRet->gwid.hdc,
                 pwndRet->gwid.hwnd, pwndRet->gwid.pdds);
    }
#endif
    
    return pwndRet;
}

/******************************Public*Routine******************************\
*
* pwndUnsubclass
*
* Removes OpenGL's subclassing set when windows are created
*
* History:
*  Mon May 20 14:05:23 1996	-by-	Drew Bliss [drewb]
*   Created
*
\**************************************************************************/

void pwndUnsubclass(GLGENwindow *pwnd, BOOL bProcessExit)
{
    WNDPROC wpCur;
        
    // We only restore the original WNDPROC if the current WNDPROC
    // is one of ours.  This prevents us from stomping on the WNDPROC
    // pointer if somebody else has changed it.

    if ((pwnd->ulFlags & GLGENWIN_OTHERPROCESS) == 0)
    {
        wpCur = (WNDPROC)GetWindowLongPtr(pwnd->gwid.hwnd, GWLP_WNDPROC);
        if (wpCur == wglWndProc)
        {
            SetWindowLongPtr(pwnd->gwid.hwnd, GWLP_WNDPROC,
                          (LONG_PTR) pwnd->pfnOldWndProc);
        }
    }
    else
    {
        // Clean up the palette watcher window if this is the last user.
        EnterCriticalSection(&gcsPaletteWatcher);

        ASSERTOPENGL(lPaletteWatcherUsers > 0,
                     "lPaletteWatcherUsers too low\n");
        
        if (--lPaletteWatcherUsers == 0)
        {
            if( PostMessage(hwndPaletteWatcher, WM_CLOSE, 0, 0) == FALSE)
            {
                DbgPrint( "PostMessage to hwnd: %08x failed with error: %08x\n",
                          hwndPaletteWatcher, GetLastError() );

                // Check if the thread is still alive
                
                if( WaitForSingleObject( hPaletteWatcherThread, 100 ) !=
                    WAIT_OBJECT_0 )
                {
                    // This means that the thread is still alive and 
                    // somehow the window is invalid.
                    // Kill this thread or else GL will keep waiting.
                    TerminateThread( hPaletteWatcherThread, 0 );
                }
                
                // Should be safe to do. If the thread is alive, it 
                // was killed above else, someone else killed it
                tidPaletteWatcherThread = 0;
            }
            CloseHandle( hPaletteWatcherThread );
            
            // We don't want to zero the palette watcher's thread ID
            // at process exit because we use it to wait for the
            // thread to die.
            if (!bProcessExit)
            {
                tidPaletteWatcherThread = 0;
            }
        }
        
        LeaveCriticalSection(&gcsPaletteWatcher);
    }
}

/******************************Public*Routine******************************\
* pwndFree
*
* Frees the specified GLGENwindow.
*
* Returns:
*   NULL if successful, pointer to structure otherwise
*
* History:
*  07-Nov-1994 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

GLGENwindow * APIENTRY pwndFree(GLGENwindow *pwndVictim, BOOL bProcessExit)
{
    BOOL bDirectScreen = GLDIRECTSCREEN && pwndVictim->gwid.hwnd;

#ifdef DBG_WINDOW
    DbgPrint("Free  window %p\n", pwndVictim);
#endif

    // Check for a stray screen lock and release if necessary.

    if (pwndVictim->ulFlags & GLGENWIN_DIRECTSCREEN)
        EndDirectScreenAccess(pwndVictim);

    // Free clipper object.

    if (bDirectScreen)
    {
        pwndVictim->pddClip->lpVtbl->Release(pwndVictim->pddClip);
    }

    // Cleanup visible region caches if they exist.

    if ( pwndVictim->prgndat )
        FREE(pwndVictim->prgndat);

    if ( pwndVictim->pscandat )
        FREE(pwndVictim->pscandat);
    
    // Restore original WNDPROC in window.
    if (pwndVictim->gwid.hwnd != NULL)
        pwndUnsubclass(pwndVictim, bProcessExit);

    // Cleanup GLGENlayers.

    if (pwndVictim->plyr)
    {
        int i;

        for (i = 0; i < 15; i++)
        {
            if (pwndVictim->plyr->overlayInfo[i])
                FREE(pwndVictim->plyr->overlayInfo[i]);

            if (pwndVictim->plyr->underlayInfo[i])
                FREE(pwndVictim->plyr->underlayInfo[i]);
        }

        FREE(pwndVictim->plyr);
    }

    // Notify MCD that this window has gone away
    if (pwndVictim->dwMcdWindow != 0)
    {
        GenMcdDestroyWindow(pwndVictim);
    }
        
    // Delete victim.

    DeleteCriticalSection(&pwndVictim->sem);
    FREE(pwndVictim);

    return NULL;
}

/******************************Public*Routine******************************\
*
* pwndCleanup
*
* Does all cleanup necessary for window destruction
*
* History:
*  Mon Mar 18 17:30:49 1996	-by-	Drew Bliss [drewb]
*   Created
*
\**************************************************************************/

void APIENTRY pwndCleanup(GLGENwindow *pwndVictim)
{
    GLGENwindow *pwnd, *pwndPrev;
#if DBG
    ULONG ulLoops;
#endif

#ifdef DBG_WINDOW
    DbgPrint("Clean window %p\n", pwndVictim);
#endif
    
    EnterCriticalSection(&gwndHeader.sem);

    // Search for victim.  Maintain a prev pointer so we can do
    // removal from linked list.

    for (
         pwndPrev = &gwndHeader, pwnd = pwndPrev->pNext;
         pwnd != &gwndHeader;
         pwndPrev = pwnd, pwnd = pwndPrev->pNext
         )
    {
        if (pwnd == pwndVictim)
            break;
    }

    // If victim was found, take it out.

    if (pwnd == pwndVictim)
    {
        // Excise victim from linked list.
        
        pwndPrev->pNext = pwnd->pNext;
    }
    
    LeaveCriticalSection(&gwndHeader.sem);

    if (pwnd == NULL)
    {
        WARNING("pwndFree: pwndVictim not found in list\n");
        return;
    }

    // If victim was found, it's out of the list so nobody
    // new can get access to it.
            
    // Wait for all current accessors to go away before cleaning up
    // the window

#if DBG
    ulLoops = 0;
#endif
    
    for (;;)
    {
        if (pwndVictim->lUsers == 1)
        {
            break;
        }

#if DBG
        if (++ulLoops == 1000)
        {
            DbgPrint("Spinning on window %p\n", pwndVictim);
#ifdef DBG_WINDOW
            DebugBreak();
#endif
        }
#endif
        
        // Wait on the critical section as a delay
        // Acquiring it doesn't guarantee that we're the last
        // accessor, but it does kill time in the case where
        // another accessor is already holding it
        EnterCriticalSection(&pwndVictim->sem);
        LeaveCriticalSection(&pwndVictim->sem);

        // Allow other threads time to run so we don't starve
        // anybody while we're waiting
        Sleep(0);
    }

    if (pwndVictim->buffers != NULL)
    {
        __glGenFreeBuffers(pwndVictim->buffers);
        wglCleanupWindow(pwndVictim);
    }
    
    if (pwndFree(pwndVictim, FALSE))
        WARNING("window deletion failed\n");
}

/******************************Public*Routine******************************\
* vCleanupWnd
*
* Removes and deletes all GLGENwindow structures from the linked list.
* Must *ONLY* be called from process detach (GLUnInitializeProcess).
*
* History:
*  25-Jul-1995 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

VOID APIENTRY vCleanupWnd()
{
    GLGENwindow *pwndNext;
    
    EnterCriticalSection(&gwndHeader.sem);

    while ( gwndHeader.pNext != &gwndHeader )
    {
        pwndNext = gwndHeader.pNext->pNext;
        pwndFree(gwndHeader.pNext, TRUE);
        gwndHeader.pNext = pwndNext;
    }

    LeaveCriticalSection(&gwndHeader.sem);

    // Wait for the palette watcher thread to die.  This ensures
    // that the palette watcher critical section can be deleted
    // safely in process detach.
    // We don't use a critical section at this point because of
    // the special critsec rules during DLL detach processing.
    while (tidPaletteWatcherThread != 0)
    {
        Sleep(50);
    }
    // Give the palette watcher thread some time to exit after
    // clearing the thread ID.
    Sleep(50);
}

/******************************Public*Routine******************************\
* pwndGetFromHWND
*
* Finds the corresponding GLGENwindow for the given window handle.
*
* Returns:
*   Pointer to GLGENwindow if sucessful; NULL otherwise.
*
* History:
*  19-Oct-1994 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

GLGENwindow * APIENTRY pwndGetFromHWND(HWND hwnd)
{
    GLGENwindow *pwndRet = (GLGENwindow *) NULL;
    GLGENwindow *pwnd = (GLGENwindow *) NULL;

    EnterCriticalSection(&gwndHeader.sem);
    {
        for (pwnd = gwndHeader.pNext; pwnd != &gwndHeader; pwnd = pwnd->pNext)
            if (pwnd->gwid.hwnd == hwnd)
            {
                pwndRet = pwnd;
                InterlockedIncrement(&pwnd->lUsers);
                break;
            }
    }
    LeaveCriticalSection(&gwndHeader.sem);

#ifdef DBG_REFCOUNTS
    if (pwndRet != 0)
    {
        DbgPrint("GetHWND %p to %d\n", pwndRet, pwndRet->lUsers);
    }
#endif
    
    return pwndRet;
}

/******************************Public*Routine******************************\
* pwndGetFromMemDC
*
* Finds the corresponding GLGENwindow for the given mem DC handle.
*
* Returns:
*   Pointer to GLGENwindow if sucessful; NULL otherwise.
*
* History:
*  21-Jan-1995 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

GLGENwindow * APIENTRY pwndGetFromMemDC(HDC hdcMem)
{
    GLGENwindow *pwndRet = (GLGENwindow *) NULL;
    GLGENwindow *pwnd = (GLGENwindow *) NULL;

    EnterCriticalSection(&gwndHeader.sem);
    {
        for (pwnd = gwndHeader.pNext; pwnd != &gwndHeader; pwnd = pwnd->pNext)
        {
            // If the pwnd has an HWND then the DC used at its
            // creation was associated with a window.  If we're
            // in this routine, though, that means hdcMem is
            // not associated with a window, so there's been
            // a reuse of the HDC handle and even though
            // we match DCs we can't return the pwnd.
            
            if (pwnd->gwid.hdc == hdcMem && pwnd->gwid.hwnd == NULL)
            {
                pwndRet = pwnd;
                InterlockedIncrement(&pwndRet->lUsers);
                break;
            }
        }
    }
    LeaveCriticalSection(&gwndHeader.sem);

#ifdef DBG_REFCOUNTS
    if (pwndRet != 0)
    {
        DbgPrint("GetDC   %p to %d\n", pwndRet, pwndRet->lUsers);
    }
#endif
    
    return pwndRet;
}

/******************************Public*Routine******************************\
*
* pwndGetFromDdraw
*
* Looks up a window by its DirectDraw surface
*
* History:
*  Wed Aug 28 18:15:40 1996	-by-	Drew Bliss [drewb]
*   Created
*
\**************************************************************************/

GLGENwindow *pwndGetFromDdraw(LPDIRECTDRAWSURFACE pdds)
{
    GLGENwindow *pwndRet = (GLGENwindow *) NULL;
    GLGENwindow *pwnd = (GLGENwindow *) NULL;

    EnterCriticalSection(&gwndHeader.sem);
    {
        for (pwnd = gwndHeader.pNext; pwnd != &gwndHeader; pwnd = pwnd->pNext)
            if (pwnd->gwid.pdds == pdds)
            {
                pwndRet = pwnd;
                InterlockedIncrement(&pwndRet->lUsers);
                break;
            }
    }
    LeaveCriticalSection(&gwndHeader.sem);

#ifdef DBG_REFCOUNTS
    if (pwndRet != 0)
    {
        DbgPrint("GetDD   %p to %d\n", pwndRet, pwndRet->lUsers);
    }
#endif
    
    return pwndRet;
}

/******************************Public*Routine******************************\
* pwndGetFromID
*
* Finds the corresponding GLGENwindow for the given window ID.
*
* Returns:
*   Pointer to GLGENwindow if sucessful; NULL otherwise.
*
* History:
*  19-Oct-1994 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

GLGENwindow * APIENTRY pwndGetFromID(GLWINDOWID *pgwid)
{
    GLGENwindow *pwndRet = (GLGENwindow *) NULL;

    switch (pgwid->iType)
    {
    case GLWID_HWND:
        pwndRet = pwndGetFromHWND(pgwid->hwnd);
        break;
    case GLWID_HDC:
        pwndRet = pwndGetFromMemDC(pgwid->hdc);
        break;
    case GLWID_DDRAW:
        pwndRet = pwndGetFromDdraw(pgwid->pdds);
        break;
    }

    return pwndRet;
}

/******************************Public*Routine******************************\
*
* pwndRelease
*
* Decrements the user count of a window
*
* History:
*  Mon Mar 18 19:35:28 1996	-by-	Drew Bliss [drewb]
*   Created
*
\**************************************************************************/

#if DBG
void APIENTRY pwndRelease(GLGENwindow *pwnd)
{
    ASSERTOPENGL(pwnd->lUsers > 0, "Decrement lUsers below zero\n");
    
    InterlockedDecrement(&pwnd->lUsers);
    
#ifdef DBG_REFCOUNTS
    DbgPrint("Release %p to %d\n", pwnd, pwnd->lUsers);
#endif
}
#endif

/******************************Public*Routine******************************\
*
* pwndUnlock
*
* Releases an owner of a window
*
* History:
*  Mon Mar 18 17:25:56 1996	-by-	Drew Bliss [drewb]
*   Created
*
\**************************************************************************/

void APIENTRY pwndUnlock(GLGENwindow *pwnd, __GLGENcontext *gengc)
{
    ASSERTOPENGL(pwnd != NULL, "Unlocking NULL window\n");

    LEAVE_WINCRIT_GC(pwnd, gengc);
    pwndRelease(pwnd);
}


/******************************Public*Routine******************************\
*
* ENTER_WINCRIT_GC
* LEAVE_WINCRIT_GC
*
* Window lock tracking routines.  The pwnd and gengc are validated
* and updated to reflect current locks.
*
* If the gengc is non-NULL then recursion is not allowed.  This is
* to prevent difficulties with maintaining gengc->pwndLocked correctly
* during recursion.  Recursing with gengc == NULL is not a problem.
*
* No ASSERTOPENGL usage so these can be enabled on free builds.
*
* History:
*  Wed Jul 02 12:57:26 1997	-by-	Drew Bliss [drewb]
*
\**************************************************************************/

void ENTER_WINCRIT_GC(GLGENwindow *pwnd, __GLGENcontext *gengc)
{
    EnterCriticalSection(&pwnd->sem);

    if (pwnd->owningThread == 0)
    {
#if DBG || defined(TRACK_WINCRIT)
        if (pwnd->lockRecursion != 0)
        {
            DbgPrint("Unowned window 0x%08lX has recursion count %d\n",
                     pwnd, pwnd->lockRecursion);
            DebugBreak();
        }
        if (pwnd->gengc != NULL)
        {
            DbgPrint("Unowned window 0x%08lX has gengc 0x%08lX\n",
                     pwnd, pwnd->gengc);
            DebugBreak();
        }
        if (gengc != NULL && gengc->pwndLocked != NULL)
        {
            DbgPrint("gengc 0x%08lX has pwnd 0x%08lX while locking 0x%08lX\n",
                     gengc, gengc->pwndLocked, pwnd);
            DebugBreak();
        }
#endif

        pwnd->owningThread = GetCurrentThreadId();
        if (gengc != NULL)
        {
            gengc->pwndLocked = pwnd;
            pwnd->gengc = gengc;
        }
    }
    else
    {
        // Make sure this thread is really the one holding the lock.
        ASSERT_WINCRIT(pwnd);
        
#if DBG || defined(TRACK_WINCRIT)
        // Recursion is only allowed with gengc == NULL.
        if (gengc != NULL)
        {
            DbgPrint("Window 0x%08lX recursing with gengc 0x%08lX\n",
                     pwnd, gengc);
            DebugBreak();
        }
#endif
    }

    pwnd->lockRecursion++;
}

void LEAVE_WINCRIT_GC(GLGENwindow *pwnd, __GLGENcontext *gengc)
{
    ASSERT_WINCRIT(pwnd);
    
#if 0
// Currently turned off because of difference in RTL_CRITICAL_SECTION
// RecursionCount between x86 and Alpha
#if !defined(_WIN95_) && (DBG || defined(TRACK_WINCRIT))
    // Check and make sure that our tracking information is following
    // what the system thinks.
    if (pwnd->sem.OwningThread != (HANDLE)pwnd->owningThread ||
        (DWORD)pwnd->sem.RecursionCount != pwnd->lockRecursion)
    {
        DbgPrint("pwnd 0x%08lX critsec information mismatch\n", pwnd);
        DebugBreak();
    }
#endif
#endif

#if DBG || defined(TRACK_WINCRIT)
    if (gengc != NULL)
    {
        if (pwnd->gengc != gengc || gengc->pwndLocked != pwnd)
        {
            DbgPrint("pwnd 0x%08lX:%08lX mismatch with gengc 0x%08lX:%08lX\n",
                     pwnd, pwnd->gengc, gengc, gengc->pwndLocked);
            DebugBreak();
        }
        if (pwnd->lockRecursion != 1)
        {
            DbgPrint("gengc 0x%08lX leaving window 0x%08lX with "
                     "recursion count of %d\n",
                     gengc, pwnd, pwnd->lockRecursion);
            DebugBreak();
        }
    }
#endif
    
    if (--pwnd->lockRecursion == 0)
    {
        if (gengc != NULL)
        {
            gengc->pwndLocked = NULL;
        }

        pwnd->gengc = NULL;
        pwnd->owningThread = 0;
    }
    
    LeaveCriticalSection(&pwnd->sem);
}

/******************************Public*Routine******************************\
*
* wglValidateWindows
*
* Walks the window list and prunes away any DC-based windows with
* invalid DCs.  This is necessary because, unlike window-based
* windows, we usually aren't notified when a memory DC goes away
* so if it has a window it just hangs around
*
* History:
*  Thu May 02 17:44:23 1996	-by-	Drew Bliss [drewb]
*   Created
*
\**************************************************************************/

void APIENTRY wglValidateWindows(void)
{
    GLGENwindow *pwnd, *pwndNext;
    BOOL bValid;

    EnterCriticalSection(&gwndHeader.sem);
    for (pwnd = gwndHeader.pNext; pwnd != &gwndHeader; pwnd = pwndNext)
    {
        pwndNext = pwnd->pNext;

        switch(pwnd->gwid.iType)
        {
        case GLWID_HDC:
            bValid = GetObjectType(pwnd->gwid.hdc) != 0;
            break;
            
        case GLWID_DDRAW:
            // Better validation?  Not really necessary since properly
            // behaved apps will have the genwin cleaned up on
            // context destruction.
            bValid = !IsBadReadPtr(pwnd->gwid.pdds, sizeof(void *)) &&
                *(void **)pwnd->gwid.pdds == pwnd->pvSurfaceVtbl;
            break;
            
        default:
            // No validation for HWNDs necessary
            bValid = TRUE;
            break;
        }

        if (!bValid)
        {
            // Increment so users count is one
            InterlockedIncrement(&pwnd->lUsers);
            pwndCleanup(pwnd);
        }
    }
    LeaveCriticalSection(&gwndHeader.sem);
}

/******************************Public*Routine******************************\
* plyriGet
*
* Returns the GLGENlayerInfo for the specified layer plane from the pwnd.
* If it doesn't yet exist, the GLGENlayer and/or GLGENlayerInfo structure(s)
* are allocated.
*
* Returns:
*   A non-NULL pointer if successful; NULL otherwise.
*
* History:
*  16-May-1996 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

GLGENlayerInfo * APIENTRY plyriGet(GLGENwindow *pwnd, HDC hdc, int iLayer)
{
    GLGENlayerInfo *plyriRet = (GLGENlayerInfo * ) NULL;
    GLGENlayerInfo **pplyri;

    ASSERTOPENGL(pwnd, "plyriGet: bad pwnd\n");

// Allocate plyr if needed.

    if (!pwnd->plyr)
    {
        pwnd->plyr = (GLGENlayers *) ALLOCZ(sizeof(GLGENlayers));

        if (!pwnd->plyr)
        {
            WARNING("plyriGet: alloc failed (GLGENlayers)\n");
            goto plyriGet_exit;
        }
    }

// Get info for the specified layer (positive values are overlay planes,
// negative values are underlay planes).

    if (iLayer > 0)
        pplyri = &pwnd->plyr->overlayInfo[iLayer - 1];
    else if (iLayer < 1)
        pplyri = &pwnd->plyr->underlayInfo[(-iLayer) - 1];
    else
    {
        WARNING("plyriGet: no layer plane info for main plane!\n");
        goto plyriGet_exit;
    }

// Allocate plyri if needed.

    if (!(*pplyri))
    {
        LAYERPLANEDESCRIPTOR lpd;

        if (!wglDescribeLayerPlane(hdc, pwnd->ipfd, iLayer, sizeof(lpd), &lpd))
        {
            WARNING("plyriGet: wglDescribeLayerPlane failed\n");
            goto plyriGet_exit;
        }

        *pplyri = (GLGENlayerInfo *)
            ALLOC((sizeof(COLORREF) * (1 << lpd.cColorBits))
                       + sizeof(GLGENlayerInfo));


        if (*pplyri)
        {
            int i;

        // Initialize the new GLGENlayerInfo.
        // Note that the palette is initialized with all white colors.

            (*pplyri)->cPalEntries = 1 << lpd.cColorBits;
            for (i = 0; i < (*pplyri)->cPalEntries; i++)
                (*pplyri)->pPalEntries[i] = RGB(255, 255, 255);
        }
        else
        {
            WARNING("plyriGet: alloc failed (GLGENlayerInfo)\n");
            goto plyriGet_exit;
        }
    }

// Success.

    plyriRet = *pplyri;

plyriGet_exit:

    return plyriRet;
}

/******************************Public*Routine******************************\
* GetScreenRect
*
* Get the screen rectangle by accessing the virtual screen metrics. 
*
* If the system does not understand multimon, such as old Win95 or NT,
* just use the device caps.  This code technically isn't necessary but
* it's useful.
*
\**************************************************************************/

static void GetScreenRect( HDC hdc, LPRECTL pRect )
{
    // If SM_CMONITORS is not understood the system returns zero,
    // so this if test works for both old and new systems.
    if (GetSystemMetrics(SM_CMONITORS) > 1)
    {
        pRect->left   = GetSystemMetrics( SM_XVIRTUALSCREEN );
        pRect->top    = GetSystemMetrics( SM_YVIRTUALSCREEN );
        pRect->right  = pRect->left + GetSystemMetrics( SM_CXVIRTUALSCREEN );
        pRect->bottom = pRect->top  + GetSystemMetrics( SM_CYVIRTUALSCREEN );
    }
    else
    {
        pRect->left   = 0;
        pRect->top    = 0;
        pRect->right  = GetDeviceCaps(hdc, HORZRES);
        pRect->bottom = GetDeviceCaps(hdc, VERTRES);
    }
}

/******************************Public*Routine******************************\
* bClipToScreen(prclDst, prclSrc)
*
* Clip source rectangle to screen bounds and store in destination rectangle.
*
* Returns:
*   TRUE if resultant prclDst == prclSrc; FALSE otherwise.
*
\**************************************************************************/

BOOL bClipToScreen(RECTL *prclDst, RECTL *prclSrc)
{
    BOOL bRet;
    HDC hdc;

    if (hdc = GetDC(NULL))
    {
        RECTL rclScreen;

        GetScreenRect( hdc, &rclScreen );

        prclDst->left   = max(prclSrc->left  , rclScreen.left  );
        prclDst->top    = max(prclSrc->top   , rclScreen.top   );
        prclDst->right  = min(prclSrc->right , rclScreen.right );
        prclDst->bottom = min(prclSrc->bottom, rclScreen.bottom);

        if ((prclDst->left >= prclDst->right) ||
            (prclDst->top >= prclDst->bottom))
        {
            prclDst->left   = 0;
            prclDst->top    = 0;
            prclDst->right  = 0;
            prclDst->bottom = 0;
        }

        ReleaseDC(NULL, hdc);
    }
    else
    {
        prclDst->left   = 0;
        prclDst->top    = 0;
        prclDst->right  = 0;
        prclDst->bottom = 0;
    }

    if ((prclDst->left   == prclSrc->left  ) &&
        (prclDst->top    == prclSrc->top   ) &&
        (prclDst->right  == prclSrc->right ) &&
        (prclDst->bottom == prclSrc->bottom))
        bRet = TRUE;
    else
        bRet = FALSE;

    return bRet;
}

/******************************Public*Routine******************************\
*
* PaletteWatcherProc
*
* Window proc for the palette watcher
*
* History:
*  Mon Oct 14 15:29:10 1996	-by-	Drew Bliss [drewb]
*   Created
*
\**************************************************************************/

LRESULT WINAPI PaletteWatcherProc(HWND hwnd, UINT uiMsg,
                                  WPARAM wpm, LPARAM lpm)
{
    switch(uiMsg)
    {
    case WM_PALETTECHANGED:
        InterlockedIncrement((LONG *)&ulPaletteWatcherCount);
        return 0;
        
    default:
        return DefWindowProc(hwnd, uiMsg, wpm, lpm);
    }
}

/******************************Public*Routine******************************\
*
* PaletteWatcher
*
* Thread routine for the palette change monitor.  Creates a hidden
* top level window and looks for WM_PALETTECHANGED.
*
* History:
*  Mon Oct 14 15:16:02 1996	-by-	Drew Bliss [drewb]
*   Created
*
\**************************************************************************/

DWORD WINAPI PaletteWatcher(LPVOID pvArg)
{
    HWND hwnd;

    hwnd = CreateWindow(PALETTE_WATCHER_CLASS,
                        PALETTE_WATCHER_CLASS,
                        WS_OVERLAPPED,
                        0, 0, 1, 1,
                        NULL,
                        NULL,
                        (HINSTANCE)GetModuleHandle(NULL),
                        NULL);
    if (hwnd != NULL)
    {
        HDC hdc;
        HPALETTE hpal;

        // Select a palette into the window DC.  This is necessary
        // to get around an optimization introduced into NT5 where
        // WM_PALETTECHANGED is only sent to windows that have selected
        // a palette.
        
        hpal = NULL;
        
        hdc = GetDC(hwnd);
        if (hdc != NULL)
        {
            hpal = SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), FALSE);
            ReleaseDC(hwnd, hdc);
        }

        if (hpal == NULL)
        {
            goto EH_Exit;
        }
            
        hwndPaletteWatcher = hwnd;
        
        for (;;)
        {
            MSG msg;

            if (GetMessage(&msg, hwnd, 0, 0) > 0)
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
            else
            {
                break;
            }
        }

    EH_Exit:
        DestroyWindow(hwnd);
        hwndPaletteWatcher = 0;
    }

    EnterCriticalSection(&gcsPaletteWatcher);
        
    // Some kind of problem occurred or this thread is dying.
    // Indicate that this thread is going away and that a
    // new watcher needs to be created.
    if (tidPaletteWatcherThread == GetCurrentThreadId())
    {
        tidPaletteWatcherThread = 0;
    }
        
    LeaveCriticalSection(&gcsPaletteWatcher);
    
    return 0;
}

/******************************Public*Routine******************************\
*
* StartPaletteWatcher
*
* Spins up a thread to watch for palette change events
*
* History:
*  Mon Oct 14 15:11:35 1996	-by-	Drew Bliss [drewb]
*   Created
*
\**************************************************************************/

BOOL StartPaletteWatcher(void)
{
    BOOL bRet;
    
    EnterCriticalSection(&gcsPaletteWatcher);

    bRet = FALSE;
    if (tidPaletteWatcherThread == 0)
    {
        HANDLE h;

        if (aPaletteWatcherClass == 0)
        {
            WNDCLASS wc;

            wc.style = 0;
            wc.lpfnWndProc = PaletteWatcherProc;
            wc.cbClsExtra = 0;
            wc.cbWndExtra = 0;
            wc.hInstance = (HINSTANCE)GetModuleHandle(NULL);
            wc.hIcon = NULL;
            wc.hCursor = NULL;
            wc.hbrBackground = NULL;
            wc.lpszMenuName = NULL;
            wc.lpszClassName = PALETTE_WATCHER_CLASS;

            aPaletteWatcherClass = RegisterClass(&wc);
        }
        
        if (aPaletteWatcherClass != 0)
        {
            h = CreateThread(NULL, 4096, PaletteWatcher,
                             NULL, 0, &tidPaletteWatcherThread);
            if (h != NULL)
            {
                hPaletteWatcherThread = h;
                bRet = TRUE;
            }
        }
    }
    else
    {
        bRet = TRUE;
    }

    if (bRet)
    {
        lPaletteWatcherUsers++;
    }
    
    LeaveCriticalSection(&gcsPaletteWatcher);
    
    // Make sure that the Palette watcher window is created.
    // Dont need to be in the CritSec for this.
    while( (hwndPaletteWatcher == 0) && 
           (tidPaletteWatcherThread != 0) ) Sleep( 100 );

    return bRet;
}

/******************************Public*Routine******************************\
* wglWndProc
*
* Handle window events for keeping GLGENwindows current
*
* History:
*  19-Oct-1994 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

LRESULT CALLBACK
wglWndProc(HWND hwnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
    GLGENwindow *pwnd;
    LRESULT lRet = 0;
    WORD width, height;
    __GLGENcontext *gengc;

    pwnd = pwndGetFromHWND(hwnd);

    if (pwnd)
    {
        __GLGENbuffers *buffers = (__GLGENbuffers *) NULL;
        // Cache old WNDPROC because we may delete pwnd
        WNDPROC pfnWndProc = pwnd->pfnOldWndProc;

        // If WM_NCDESTROY, do OpenGL housekeeping after
        // calling original WndProc.
        // NOTE - We shouldn't really need this special case.
        // It's present in order to allow apps to do things like
        // wglDeleteContext in NCDESTROY which wouldn't work if
        // we cleaned up the window before we passed on the message
        // This used to be done in WM_DESTROY where apps do work,
        // but now that it's on NCDESTROY it's much less likely that
        // an app is doing anything.  We preserved the old behavior
        // for safety, though.

        if (uiMsg == WM_NCDESTROY)
        {
            // Subclassing is supposed to be removed during NCDESTROY
            // processing and order is important.  Remove our
            // subclassing before passing on the message.
            pwndUnsubclass(pwnd, FALSE);

            if (pfnWndProc)
            {
                lRet = CallWindowProc(pfnWndProc, hwnd,
                                      uiMsg, wParam, lParam);
            }
        }

    // OpenGL housekeeping in response to windowing system messages.

        switch (uiMsg)
        {
            case WM_SIZE:
                width  = LOWORD(lParam);
                height = HIWORD(lParam);
                gengc = (__GLGENcontext *)GLTEB_SRVCONTEXT();

                // Use the non-gc enter to allow recursion.
                ENTER_WINCRIT(pwnd);
                {
                    POINT pt;
                    
                    // Convert client coordinates to screen coordinates
                    // as genwin information is always in screen coordinates.
                    // The given lParam information may be parent-relative
                    // for child windows so it can't be used directly.
                    pt.x = 0;
                    pt.y = 0;
                    ClientToScreen(hwnd, &pt);

                    pwnd->rclClient.left   = pt.x;
                    pwnd->rclClient.right  = pt.x + width;
                    pwnd->rclClient.top    = pt.y;
                    pwnd->rclClient.bottom = pt.y + height;

#if 0
                    DbgPrint("size %d,%d - %d,%d\n",
                             pwnd->rclClient.left,
                             pwnd->rclClient.top,
                             pwnd->rclClient.right,
                             pwnd->rclClient.bottom);
#endif
                    
                    // At least clip to screen.

                    if (bClipToScreen(&pwnd->rclBounds,
                                      &pwnd->rclClient))
                        pwnd->clipComplexity = CLC_TRIVIAL;
                    else
                        pwnd->clipComplexity = CLC_RECT;

                    buffers = pwnd->buffers;
                    if (buffers)
                    {
                        buffers->WndUniq++;

                        buffers->WndSizeUniq++;

                    // Don't let it hit -1.  -1 is special and is used by
                    // MakeCurrent to signal that an update is required

                        if (buffers->WndUniq == -1)
                            buffers->WndUniq = 0;

                        if (buffers->WndSizeUniq == -1)
                            buffers->WndSizeUniq = 0;

                        if ((gengc != NULL) && (pwnd == gengc->pwndLocked))
                            UpdateWindowInfo(gengc);
                    }
                }
                LEAVE_WINCRIT(pwnd);

                break;

            case WM_MOVE:
                gengc = (__GLGENcontext *)GLTEB_SRVCONTEXT();
                
                // Use the non-gc enter to allow recursion.
                ENTER_WINCRIT(pwnd);
                {
                    POINT pt;
                    
                    // Convert client coordinates to screen coordinates
                    // as genwin information is always in screen coordinates.
                    // The given lParam information may be parent-relative
                    // for child windows so it can't be used directly.
                    pt.x = 0;
                    pt.y = 0;
                    ClientToScreen(hwnd, &pt);

                    width  = (WORD) (pwnd->rclClient.right -
                                     pwnd->rclClient.left);
                    height = (WORD) (pwnd->rclClient.bottom -
                                     pwnd->rclClient.top);

                    ASSERTOPENGL(
                        (pwnd->rclClient.right -
                         pwnd->rclClient.left) <= 0x0FFFF &&
                        (pwnd->rclClient.bottom -
                         pwnd->rclClient.top) <= 0x0FFFF,
                        "wglWndProc(): WM_MOVE - width/height overflow\n"
                        );

                    pwnd->rclClient.left   = pt.x;
                    pwnd->rclClient.right  = pt.x + width;
                    pwnd->rclClient.top    = pt.y;
                    pwnd->rclClient.bottom = pt.y + height;

#if 0
                    DbgPrint("move %d,%d - %d,%d\n",
                             pwnd->rclClient.left,
                             pwnd->rclClient.top,
                             pwnd->rclClient.right,
                             pwnd->rclClient.bottom);
#endif
                    
                    // At least clip to screen.

                    if (bClipToScreen(&pwnd->rclBounds,
                                      &pwnd->rclClient))
                        pwnd->clipComplexity = CLC_TRIVIAL;
                    else
                        pwnd->clipComplexity = CLC_RECT;

                    buffers = pwnd->buffers;
                    if (buffers)
                    {
                        buffers->WndUniq++;

                    // Don't let it hit -1.  -1 is special and is used by
                    // MakeCurrent to signal that an update is required

                        if (buffers->WndUniq == -1)
                            buffers->WndUniq = 0;

                        if ((gengc != NULL) && (pwnd == gengc->pwndLocked))
                            UpdateWindowInfo(gengc);
                    }
                }
                LEAVE_WINCRIT(pwnd);

                break;

            case WM_PALETTECHANGED:
                gengc = (__GLGENcontext *)GLTEB_SRVCONTEXT();
                
                // Use the non-gc enter to allow recursion.
                ENTER_WINCRIT(pwnd);
                {
                    pwnd->ulPaletteUniq++;
                    if ((gengc != NULL) && (pwnd == gengc->pwndLocked))
                        HandlePaletteChanges(gengc, pwnd);
                }
                LEAVE_WINCRIT(pwnd);

                break;

            case WM_NCDESTROY:
                pwndCleanup(pwnd);

            // WM_NCDESTROY (and WM_DESTROY) are sent after the window has
            // been removed from the screen.  The window area is invalid
            // but there is no API that allows us to dertermine that. This
            // allows multithreaded drawing to draw on the screen area
            // formerly occupied by the window.  On Win95, DirectDraw does
            // not force a repaint of the system when a window is destroyed.
            // Therefore, if we are running multiple threads on Win95,
            // we force a repaint of the desktop.  Note that multithreaded
            // does not mean that we are doing multithreaded drawing, but
            // its a reasonable approximation.

                if (WIN95_PLATFORM && (lThreadsAttached > 1))
                {
                    InvalidateRect(NULL, NULL, FALSE);
                }

                return lRet;

            default:
                break;
        }

    // If !WM_NCDESTROY, do OpenGL housekeeping before calling original
    // WndProc.

        ASSERTOPENGL(uiMsg != WM_NCDESTROY,
                     "WM_NCDESTROY processing didn't terminate\n");

        pwndRelease(pwnd);

        if (pfnWndProc)
            lRet = CallWindowProc(pfnWndProc, hwnd,
                                  uiMsg, wParam, lParam);
    }

    return lRet;
}

/******************************Public*Routine******************************\
*
* CreatePwnd
*
* Creates a window for the given surface
*
* History:
*  Thu Aug 29 10:33:59 1996	-by-	Drew Bliss [drewb]
*   Created
*
\**************************************************************************/

GLGENwindow * APIENTRY CreatePwnd(GLWINDOWID *pgwid, int ipfd, int ipfdDevMax,
                                  DWORD dwObjectType, RECTL *prcl, BOOL *pbNew)
{
    GLGENwindow *pwnd;
    GLGENwindow wndInit;
    
    pwnd = pwndGetFromID(pgwid);

    if ( !pwnd )
    {
        memset(&wndInit, 0, sizeof(wndInit));

        wndInit.gwid = *pgwid;
        wndInit.ipfd = ipfd;
        wndInit.ipfdDevMax = ipfdDevMax;

        //!!!client driver
        //!!!dbug -- Move SetWindowLong call to pwndNew?!? Maybe move
        //!!!dbug    everything from this if.. clause to pwndNew?!?
        if ( wndInit.gwid.hwnd )
        {
            DWORD dwPid;

            if (GetWindowThreadProcessId(wndInit.gwid.hwnd,
                                         &dwPid) == 0xffffffff)
            {
                return NULL;
            }

            if (dwPid == GetCurrentProcessId())
            {
                wndInit.pfnOldWndProc =
                    (WNDPROC) SetWindowLongPtr(wndInit.gwid.hwnd,
                                            GWLP_WNDPROC, (LONG_PTR) wglWndProc);
            }
            else
            {
                wndInit.ulFlags |= GLGENWIN_OTHERPROCESS;

                // Start a thread to watch for palette changes
                if (!StartPaletteWatcher())
                {
                    return NULL;
                }
            }
            
            // Get *SCREEN* coordinates of client rectangle.

            GetClientRect(wndInit.gwid.hwnd, (LPRECT) &wndInit.rclClient);
            ClientToScreen(wndInit.gwid.hwnd, (LPPOINT) &wndInit.rclClient);
            wndInit.rclClient.right += wndInit.rclClient.left;
            wndInit.rclClient.bottom += wndInit.rclClient.top;
        }
        else if (dwObjectType == OBJ_DC)
        {
            // A direct DC without a window is treated like a DFB
            GetScreenRect( pgwid->hdc, &wndInit.rclClient );
        }
        else if (dwObjectType == OBJ_MEMDC)
        {
            DIBSECTION bmi;

        // Get bitmap dimensions.

            if ( !GetObject(GetCurrentObject(pgwid->hdc, OBJ_BITMAP),
                            sizeof(DIBSECTION), (LPVOID) &bmi) )
            {
                WARNING("wglSetPixelFormat(): GetObject failed\n");
                return NULL;
            }

            wndInit.rclClient.left   = 0;
            wndInit.rclClient.top    = 0;
            wndInit.rclClient.right  = bmi.dsBm.bmWidth;
            wndInit.rclClient.bottom = abs(bmi.dsBm.bmHeight);
        }
        else if (dwObjectType == OBJ_DDRAW)
        {
            // DirectDraw surface, use passed in rectangle
            ASSERTOPENGL(prcl != NULL, "NULL rect for DDraw surface\n");
            wndInit.rclClient = *prcl;

            // Record the surface vtbl pointer for later validation
            wndInit.pvSurfaceVtbl = *(void **)pgwid->pdds;
        }
        else
        {
            ASSERTOPENGL(dwObjectType == OBJ_ENHMETADC,
                         "Bad dwType in SetPixelFormat\n");
            
            // Initialize metafile DC's to have no size so all output
            // is clipped.  This is good because there's no surface
            // to draw on
            wndInit.rclClient.left   = 0;
            wndInit.rclClient.top    = 0;
            wndInit.rclClient.right  = 0;
            wndInit.rclClient.bottom = 0;
        }

        if (wndInit.gwid.hwnd)
        {
            // To be safe, at least clip bounds to screen.

            if (bClipToScreen(&wndInit.rclBounds,
                              &wndInit.rclClient))
                wndInit.clipComplexity = CLC_TRIVIAL;
            else
                wndInit.clipComplexity = CLC_RECT;
        }
        else
        {
            // Make bounds the same as client.
            wndInit.rclBounds = wndInit.rclClient;
            wndInit.clipComplexity = CLC_TRIVIAL;
        }

        pwnd = pwndNew(&wndInit);
        if (pwnd == NULL)
        {
            WARNING("wglSetPixelFormat: Unable to allocate new window\n");

            if ( wndInit.gwid.hwnd )
            {
                pwndUnsubclass(&wndInit, FALSE);
            }
        }

        *pbNew = TRUE;
    }
    else
    {
    // If the given pixel format is the same as the previous one, return
    // success.  Otherwise, as the pixel format can be set only once,
    // return error.

        if ( pwnd->ipfd != ipfd )
        {
            WARNING("wglSetPixelFormat: Attempt to set pixel format twice\n");
            SAVE_ERROR_CODE(ERROR_INVALID_PIXEL_FORMAT);
            pwndRelease(pwnd);
            pwnd = NULL;
        }

        *pbNew = FALSE;
    }

    return pwnd;
}

/******************************Public*Routine******************************\
* wglGetPixelFormat
*
* Get the pixel format for the window or surface associated with the given
* DC.
*
* Returns:
*   0 if error or no pixel format was previously set in the window or
*   surface; current pixel format index otherwise
*
* History:
*  19-Oct-1994 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

int WINAPI wglGetPixelFormat(HDC hdc)
{
    GLGENwindow *pwnd;
    int iRet = 0;
    GLWINDOWID gwid;

    WindowIdFromHdc(hdc, &gwid);
    pwnd = pwndGetFromID(&gwid);

    if (pwnd)
    {
        iRet = pwnd->ipfd;
        pwndRelease(pwnd);
    }
    else
    {
#if 0
	// Too noisy for normal operation
        WARNING("wglGetPixelFormat: No window for DC\n");
#endif
        SAVE_ERROR_CODE(ERROR_INVALID_PIXEL_FORMAT);
    }

    return iRet;
}

/*****************************Private*Routine******************************\
*
* EnterPixelFormatSection
*
* Enters pixel format exclusive code
*
* NOTE - Pixel format information is maintained in the client process
* so it is not synchronized between processes.  This means that two
* processes could successfully set the pixel format for a window.
* If the list becomes global, this synchronization code should also become
* cross-process aware.
*
* History:
*  Mon Jun 26 17:49:04 1995	-by-	Drew Bliss [drewb]
*   Created
*
\**************************************************************************/

#define EnterPixelFormatSection() \
    (EnterCriticalSection(&gcsPixelFormat), TRUE)

/*****************************Private*Routine******************************\
*
* LeavePixelFormatSection
*
* Leaves pixel format exclusive code
*
* History:
*  Mon Jun 26 17:55:20 1995	-by-	Drew Bliss [drewb]
*   Created
*
\**************************************************************************/

#define LeavePixelFormatSection() \
    LeaveCriticalSection(&gcsPixelFormat)

/******************************Public*Routine******************************\
* wglNumHardwareFormats
*
* Returns the number of hardware formats (ICD and MCD), supported on the
* specified hdc.
*
* History:
*  17-Apr-1996 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

VOID APIENTRY wglNumHardwareFormats(HDC hdc, DWORD dwType,
                                    int *piMcd, int *piIcd)
{
// It is assumed that the caller has already validated the DC.

    ASSERTOPENGL((dwType == OBJ_DC) ||
                 (dwType == OBJ_MEMDC) ||
                 (dwType == OBJ_ENHMETADC) ||
                 (dwType == OBJ_DDRAW),
                 "wglNumHardwareFormats: bad hdc\n");

// Do not call MCD or ICD for enhanced metafile DCs.  In such a
// case, the code in ntgdi\client\output.c will return a non-zero value
// even if there are no ICD or MCD pixelformats.
#if _WIN32_WINNT >= 0x0501
    {
        BOOL wow64Process;

        if (IsWow64Process(GetCurrentProcess(), &wow64Process) && wow64Process)
            dwType = OBJ_ENHMETADC;
    }
#endif

    if ( dwType == OBJ_ENHMETADC )
    {
    // It's a metafile DC.  Therefore it cannot support MCD or ICD
    // (current OpenGL metafiling support would have to be modified
    // to allow this).

        *piIcd = 0;
        *piMcd = 0;
    }
    else
    {
    // Get ICD pixelformat count.

        *piIcd = __DrvDescribePixelFormat(hdc, 1, 0, NULL);

    // Get MCD pixelformat count.

#ifdef _MCD_
        if ( gpMcdTable || bInitMcd(hdc) )
            *piMcd = (gpMcdTable->pMCDDescribePixelFormat)(hdc, 1, NULL);
        else
            *piMcd = 0;
#else
        *piMcd = 0;
#endif
    }
}

/******************************Public*Routine******************************\
*
* GetCompatibleDevice
*
* Returns an HDC appropriate for making escape calls on.
* In the memdc case it returns a DC for the screen.
*
* History:
*  Wed Nov 20 17:48:57 1996	-by-	Drew Bliss [drewb]
*   Created
*
\**************************************************************************/

HDC GetCompatibleDevice(HDC hdc, DWORD dwObjectType)
{
    HDC hdcDriver;
    int iTech;
    
    hdcDriver = hdc;
    iTech = GetDeviceCaps(hdc, TECHNOLOGY);
    if ((dwObjectType == OBJ_MEMDC) && (iTech != DT_PLOTTER) &&
        (iTech != DT_RASPRINTER))
    {
        hdcDriver = GetDC(NULL);
    }

    return hdcDriver;
}

/******************************Public*Routine******************************\
* wglSetPixelFormat
*
* Set the pixel format for the window or surface associated with the given
* DC.
*
* Note:
* Since the pixel format is per-window data (per-DC for non-display DCs), a
* side effect of this call is to create a GLGENwindow structure.
*
* Note:
* For an installable client driver, a GLGENwindow structure is still created
* to track the pixel format and the driver structure (GLDRIVER).
*
* History:
*  19-Oct-1994 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

BOOL WINAPI wglSetPixelFormat(HDC hdc, int ipfd,
                              CONST PIXELFORMATDESCRIPTOR *ppfd)
{
    GLGENwindow *pwnd = NULL;
    int   ipfdDevMax, ipfdMcdMax;
    DWORD dwObjectType;
    BOOL  bRet = FALSE;
    GLWINDOWID gwid;
    BOOL  bNew;
    HDC hdcDriver;
    LPDIRECTDRAWSURFACE pdds;
    RECTL rcl, *prcl;
    DDSURFACEDESC ddsd;
        
//DBGPRINT1("wglSetPixelFormat: ipfd = %ld\n", ipfd);

// Validate DC.

    switch (dwObjectType = wglObjectType(hdc))
    {
    case OBJ_DC:
    case OBJ_MEMDC:
    case OBJ_ENHMETADC:
        break;
    default:
        WARNING1("wglSetPixelFormat: Attempt to set format of %d type DC\n",
                 dwObjectType);
        SAVE_ERROR_CODE(ERROR_INVALID_HANDLE);
        return(FALSE);
    }

// Take the pixel format mutex

    if (!EnterPixelFormatSection())
    {
        WARNING("wglSetPixelFormat: Unable to take pixel format mutex\n");
        return FALSE;
    }

// Get the number of hardware supported formats.

    if (pfnGetSurfaceFromDC != NULL &&
        pfnGetSurfaceFromDC(hdc, &pdds, &hdcDriver) == DD_OK)
    {
        // Get the surface dimensions
        memset(&ddsd, 0, sizeof(ddsd));
        ddsd.dwSize = sizeof(ddsd);
        if (pdds->lpVtbl->GetSurfaceDesc(pdds, &ddsd) != DD_OK)
        {
            goto LeaveSection;
        }

        rcl.left = 0;
        rcl.top = 0;
        rcl.right = ddsd.dwWidth;
        rcl.bottom = ddsd.dwHeight;
        prcl = &rcl;

        // Switch object type to identify this as a DirectDraw surface
        dwObjectType = OBJ_DDRAW;
    }
    else
    {
        pdds = NULL;
        prcl = NULL;
        
        hdcDriver = GetCompatibleDevice(hdc, dwObjectType);
        if (hdcDriver == NULL)
        {
            goto LeaveSection;
        }
    }
        
    wglNumHardwareFormats(hdcDriver, dwObjectType,
                          &ipfdMcdMax, &ipfdDevMax);

// Filter out invalid (out of range) pixel format indices.

    if ( (ipfd < 1) || (ipfd > (ipfdDevMax + ipfdMcdMax + MAX_GENERIC_PFD)) )
    {
        WARNING1("wglSetPixelFormat: ipfd %d out of range\n", ipfd);
        SAVE_ERROR_CODE(ERROR_INVALID_PARAMETER);
        goto LeaveSection;
    }

// If it exists, grab pwnd.  Otherwise, create one.

    WindowIdFromHdc(hdc, &gwid);
    pwnd = CreatePwnd(&gwid, ipfd, ipfdDevMax, dwObjectType, prcl, &bNew);
    if (pwnd == NULL)
    {
        goto LeaveSection;
    }

    if (bNew)
    {
// Dispatch driver formats.
// Driver is responsible for doing its own validation of the pixelformat.
// For generic formats, we call wglValidPixelFormat to validate.
// We do not send DirectDraw pixel format calls to the driver
// so that we avoid having new pixel format calls.

        if (dwObjectType != OBJ_DDRAW && ipfd <= ipfdDevMax)
        {
            bRet = __DrvSetPixelFormat(hdc, ipfd, (PVOID) pwnd);
#if DBG
            if (!bRet)
            {
                WARNING("__DrvSetPixelFormat failed\n");
            }
#endif
        }
        else
        {
            bRet = wglValidPixelFormat(hdc, ipfd, dwObjectType,
                                       pdds, &ddsd);
#if DBG
            if (!bRet)
            {
                WARNING("wglValidPixelFormat failed\n");
            }
#endif
        }

// If the pixel format is not valid or could not be set in the driver,
// cleanup and return error.

        if (!bRet)
        {
            goto FreeWnd;
        }
    }
    else
    {
        bRet = TRUE;
    }

    pwndRelease(pwnd);
    
LeaveSection:
    LeavePixelFormatSection();

    if (pdds != NULL)
    {
        pdds->lpVtbl->Release(pdds);
    }
    else if (hdcDriver != hdc)
    {
        ReleaseDC((HWND) NULL, hdcDriver);
    }
    
    return bRet;

FreeWnd:
    pwndCleanup(pwnd);
    goto LeaveSection;
}

/******************************Public*Routine******************************\
* wglChoosePixelFormat
*
* Choose the pixel format.
*
* Returns: 0 if error; best matching pixel format index otherwise
*
* History:
*
*  Sat Feb 10 11:55:22 1996     -by-    Hock San Lee    [hockl]
* Chose generic 16-bit depth buffer over 32-bit depth buffer.
* Added PFD_DEPTH_DONTCARE flag.
*
*  19-Oct-1994 Gilman Wong [gilmanw]
* Taken from GreChoosePixelFormat (gdi\gre\pixelfmt.cxx).
*
* History for gdi\gre\pixelfmt.cxx:
*  Tue Sep 21 14:25:04 1993     -by-    Hock San Lee    [hockl]
* Wrote it.
\**************************************************************************/

// Reserve some PFD_SUPPORT flags for other potential graphics systems
// such as PEX, HOOPS, Renderman etc.

#define PFD_SUPPORT_OTHER1         0x01000000
#define PFD_SUPPORT_OTHER2         0x02000000
#define PFD_SUPPORT_OTHER3         0x04000000
#define PFD_SUPPORT_OTHER4         0x08000000

// Scores for matching pixel formats

#define PFD_DRAW_TO_WINDOW_SCORE   0x10000    /* must match */
#define PFD_DRAW_TO_BITMAP_SCORE   0x01000
#define PFD_PIXEL_TYPE_SCORE       0x01000
#define PFD_SUPPORT_SCORE          0x01000
#define PFD_DOUBLEBUFFER_SCORE1    0x01000
#define PFD_DOUBLEBUFFER_SCORE2    0x00001
#define PFD_STEREO_SCORE1          0x01000
#define PFD_STEREO_SCORE2          0x00001
#define PFD_BUFFER_SCORE1          0x01010
#define PFD_BUFFER_SCORE2          0x01001
#define PFD_BUFFER_SCORE3          0x01000
// #define PFD_LAYER_TYPE_SCORE    0x01000
#define PFD_DEVICE_FORMAT_SCORE    0x00100
#define PFD_ACCEL_FORMAT_SCORE     0x00010
#define PFD_SUPPORT_DDRAW_SCORE    0x10000    /* must match */

//!!! Add code to choose overlays?

int WINAPI wglChoosePixelFormat(HDC hdc, CONST PIXELFORMATDESCRIPTOR *ppfd)
{
    PIXELFORMATDESCRIPTOR pfdIn = *ppfd;
    PIXELFORMATDESCRIPTOR pfdCurrent;

// Enumerate and find the best match.

    int ipfdBest = 1;           // assume the default is the best
    int iScoreBest = -1;
    int ipfdMax;
    int ipfd = 1;

    do
    {
        int iScore = 0;

        ipfdMax = wglDescribePixelFormat(hdc,ipfd,sizeof(PIXELFORMATDESCRIPTOR),&pfdCurrent);

        if (ipfdMax == 0)
            return(0);          // something went wrong

        if (pfdIn.iPixelType == pfdCurrent.iPixelType)
            iScore += PFD_PIXEL_TYPE_SCORE;

        if ((pfdIn.cColorBits == 0)
         || (pfdIn.cColorBits == pfdCurrent.cColorBits))
            iScore += PFD_BUFFER_SCORE1;
        else if (pfdIn.cColorBits < pfdCurrent.cColorBits)
            iScore += PFD_BUFFER_SCORE2;
        else if (pfdCurrent.cColorBits != 0)
            iScore += PFD_BUFFER_SCORE3;

        if (!(pfdIn.dwFlags & PFD_DRAW_TO_WINDOW)
         || (pfdCurrent.dwFlags & PFD_DRAW_TO_WINDOW))
            iScore += PFD_DRAW_TO_WINDOW_SCORE;

        if (!(pfdIn.dwFlags & PFD_DRAW_TO_BITMAP)
         || (pfdCurrent.dwFlags & PFD_DRAW_TO_BITMAP))
            iScore += PFD_DRAW_TO_BITMAP_SCORE;

        if (!(pfdIn.dwFlags & PFD_SUPPORT_GDI)
         || (pfdCurrent.dwFlags & PFD_SUPPORT_GDI))
            iScore += PFD_SUPPORT_SCORE;

        if (!(pfdIn.dwFlags & PFD_SUPPORT_OPENGL)
         || (pfdCurrent.dwFlags & PFD_SUPPORT_OPENGL))
            iScore += PFD_SUPPORT_SCORE;

        if ((pfdIn.dwFlags & PFD_SUPPORT_DIRECTDRAW) == 0 ||
            (pfdCurrent.dwFlags & PFD_SUPPORT_DIRECTDRAW))
        {
            iScore += PFD_SUPPORT_DDRAW_SCORE;
        }
        
        if (!(pfdIn.dwFlags & PFD_SUPPORT_OTHER1)
         || (pfdCurrent.dwFlags & PFD_SUPPORT_OTHER1))
            iScore += PFD_SUPPORT_SCORE;

        if (!(pfdIn.dwFlags & PFD_SUPPORT_OTHER2)
         || (pfdCurrent.dwFlags & PFD_SUPPORT_OTHER2))
            iScore += PFD_SUPPORT_SCORE;

        if (!(pfdIn.dwFlags & PFD_SUPPORT_OTHER3)
         || (pfdCurrent.dwFlags & PFD_SUPPORT_OTHER3))
            iScore += PFD_SUPPORT_SCORE;

        if (!(pfdIn.dwFlags & PFD_SUPPORT_OTHER4)
         || (pfdCurrent.dwFlags & PFD_SUPPORT_OTHER4))
            iScore += PFD_SUPPORT_SCORE;

        if (pfdCurrent.dwFlags & PFD_GENERIC_ACCELERATED)
            iScore += PFD_ACCEL_FORMAT_SCORE;
        else if (!(pfdCurrent.dwFlags & PFD_GENERIC_FORMAT))
            iScore += PFD_DEVICE_FORMAT_SCORE;

        if ((pfdIn.dwFlags & PFD_DOUBLEBUFFER_DONTCARE)
         || ((pfdIn.dwFlags & PFD_DOUBLEBUFFER)
          == (pfdCurrent.dwFlags & PFD_DOUBLEBUFFER)))
            iScore += PFD_DOUBLEBUFFER_SCORE1;
        else if (pfdCurrent.dwFlags & PFD_DOUBLEBUFFER)
            iScore += PFD_DOUBLEBUFFER_SCORE2;

        if ((pfdIn.dwFlags & PFD_STEREO_DONTCARE)
         || ((pfdIn.dwFlags & PFD_STEREO)
          == (pfdCurrent.dwFlags & PFD_STEREO)))
            iScore += PFD_STEREO_SCORE1;
        else if (pfdCurrent.dwFlags & PFD_STEREO)
            iScore += PFD_STEREO_SCORE2;

        if ((pfdIn.cAlphaBits == 0)
         || (pfdIn.cAlphaBits == pfdCurrent.cAlphaBits))
            iScore += PFD_BUFFER_SCORE1;
        else if (pfdIn.cAlphaBits < pfdCurrent.cAlphaBits)
            iScore += PFD_BUFFER_SCORE2;
        else if (pfdCurrent.cAlphaBits != 0)
            iScore += PFD_BUFFER_SCORE3;

        if ((pfdIn.cAccumBits == 0)
         || (pfdIn.cAccumBits == pfdCurrent.cAccumBits))
            iScore += PFD_BUFFER_SCORE1;
        else if (pfdIn.cAccumBits < pfdCurrent.cAccumBits)
            iScore += PFD_BUFFER_SCORE2;
        else if (pfdCurrent.cAccumBits != 0)
            iScore += PFD_BUFFER_SCORE3;

// Some applications (e.g. GLview browser) specifies a 0-bit depth buffer
// but expect this function to return a pixel format with a depth buffer.
// This works in NT 3.51 since all pixel formats have a depth buffer.
// When pixel formats with no depth buffer were added in NT 4.0, these
// applications stopped working.  The flag PFD_DEPTH_DONTCARE is added to
// indicate that no depth buffer is required.  If this flags is not given,
// this function will attempt to select a pixel format with a depth buffer.

	if (pfdIn.dwFlags & PFD_DEPTH_DONTCARE)
	{
	    if (pfdCurrent.cDepthBits == 0)
		iScore += PFD_BUFFER_SCORE1;
	    else
		iScore += PFD_BUFFER_SCORE2;
	}
	else if (pfdCurrent.cDepthBits != 0)
	{
	    if ((pfdIn.cDepthBits == 0)
	     || (pfdIn.cDepthBits == pfdCurrent.cDepthBits))
		iScore += PFD_BUFFER_SCORE1;
	    else if (pfdIn.cDepthBits < pfdCurrent.cDepthBits)
		iScore += PFD_BUFFER_SCORE2;
	    else if (pfdCurrent.cDepthBits != 0)
		iScore += PFD_BUFFER_SCORE3;
	}

        if ((pfdIn.cStencilBits == 0)
         || (pfdIn.cStencilBits == pfdCurrent.cStencilBits))
            iScore += PFD_BUFFER_SCORE1;
        else if (pfdIn.cStencilBits < pfdCurrent.cStencilBits)
            iScore += PFD_BUFFER_SCORE2;
        else if (pfdCurrent.cStencilBits != 0)
            iScore += PFD_BUFFER_SCORE3;

        if ((pfdIn.cAuxBuffers == 0)
         || (pfdIn.cAuxBuffers == pfdCurrent.cAuxBuffers))
            iScore += PFD_BUFFER_SCORE1;
        else if (pfdIn.cAuxBuffers < pfdCurrent.cAuxBuffers)
            iScore += PFD_BUFFER_SCORE2;
        else if (pfdCurrent.cAuxBuffers != 0)
            iScore += PFD_BUFFER_SCORE3;

#if 0
        DbgPrint("%2d: score is %8X, best %8X (%2d)\n",
                 ipfd, iScore, iScoreBest, ipfdBest);
#endif
        
        if (iScore > iScoreBest)
        {
            iScoreBest = iScore;
            ipfdBest = ipfd;
        }
        else if (iScore == iScoreBest)
        {
// When everything is equal, we should choose the pixel format with a
// smaller depth size for better performance, provided that the smaller
// depth buffer satisfies the request.  The best way to do this is to
// order pixel formats such that one with smaller depth buffer comes
// first.  In NT 3.51, however, the generic pixel format was not ordered
// this way.  As a result, pixel formats with 32-bit depth buffer are
// choosen by default.  To maintain compatibility, we modify the selection
// here without reordering generic pixel formats.

            if ((pfdCurrent.dwFlags & PFD_GENERIC_FORMAT) &&
#ifdef _MCD_
                !(pfdCurrent.dwFlags & PFD_GENERIC_ACCELERATED) &&
#endif
                (pfdIn.cDepthBits < 16 || pfdIn.dwFlags & PFD_DEPTH_DONTCARE) &&
                (pfdCurrent.cDepthBits == 16) &&
                (ipfd == ipfdBest + 1))
            {
                ipfdBest = ipfd;
            }
        }

        ipfd++;
    } while (ipfd <= ipfdMax);

    return(ipfdBest);
}

/******************************Public*Routine******************************\
*
* MaskToBitsAndShift
*
* Counts bits in a mask and determines shift
* Set bits must be contiguous
*
* History:
*  Mon Aug 26 14:16:28 1996	-by-	Drew Bliss [drewb]
*   Created
*
\**************************************************************************/

void APIENTRY MaskToBitsAndShift(DWORD dwMask, BYTE *pbBits, BYTE *pbShift)
{
    DWORD dwBit;

    *pbBits = 0;
    *pbShift = 0;
    
    /* Determine first set bit and accumulate shift count */
    dwBit = 0x1;
    while ((dwMask & dwBit) == 0)
    {
        dwBit <<= 1;
        (*pbShift)++;
    }

    /* Count set bits */
    while ((dwMask & dwBit) != 0)
    {
        dwBit <<= 1;
        (*pbBits)++;
    }

    /* No other bits in the mask can be set */
    ASSERTOPENGL(((*pbBits+*pbShift) == (sizeof(dwMask)*8)) ||
                 ((dwMask >> (*pbBits+*pbShift)) == 0),
                 "Invalid mask\n");
}

/*****************************Private*Routine******************************\
*
* ComputeBitsFromMasks
*
* Determines the values for c*Bits and c*Shift from BI_BITFIELD
* channel masks
*
* History:
*  Tue Feb 14 10:50:10 1995     -by-    Drew Bliss [drewb]
*   Created by pulling out duplicated code
*
\**************************************************************************/

static void ComputeBitsFromMasks(PIXELFORMATDESCRIPTOR *ppfd,
                                 DWORD dwRedMask, DWORD dwGreenMask,
                                 DWORD dwBlueMask)
{
    /* Masks can't be zero and they can't overlap */
    ASSERTOPENGL(dwRedMask != 0 &&
                 dwGreenMask != 0 &&
                 dwBlueMask != 0,
                 "Bitfield mask is zero");
    ASSERTOPENGL((dwRedMask & dwGreenMask) == 0 &&
                 (dwRedMask & dwBlueMask) == 0 &&
                 (dwGreenMask & dwBlueMask) == 0,
                 "Bitfield masks overlap");

    MaskToBitsAndShift(dwRedMask, &ppfd->cRedBits, &ppfd->cRedShift);
    MaskToBitsAndShift(dwGreenMask, &ppfd->cGreenBits, &ppfd->cGreenShift);
    MaskToBitsAndShift(dwBlueMask, &ppfd->cBlueBits, &ppfd->cBlueShift);
}

/******************************Public*Routine******************************\
* __wglGetDdFormat
*
* Special case of __wglGetBitfieldColorFormat to support DirectDraw
* surfaces.  Fills in the cRedBits, cRedShift, cGreenBits, etc. fields
* of the PIXELFORMATDESCRIPTOR for 16, 24, and 32bpp direct surfaces.
*
* This is done by interpreting the given surface information
*
* History:
*  07-Jun-1995 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

void __wglGetDdFormat(DDSURFACEDESC *pddsd,
                      PIXELFORMATDESCRIPTOR *ppfd)
{
    // This routine should only be called for bitfield formats, but
    // random mode changes in the middle of certain calls could cause
    // it to be called with non-bitfield formats.
    //
    // When such a mode change occurs OpenGL should not crash but
    // does not necessarily have to produce correct output
    
    if ((pddsd->ddpfPixelFormat.dwFlags & DDPF_RGB) == 0 ||
        (pddsd->ddpfPixelFormat.dwFlags & (DDPF_PALETTEINDEXED1 |
                                           DDPF_PALETTEINDEXED2 |
                                           DDPF_PALETTEINDEXED4 |
                                           DDPF_PALETTEINDEXED8)))
    {
        WARNING1("__wglGetDdFormat called with 0x%08lX ddpf flags\n",
                 pddsd->ddpfPixelFormat.dwFlags);
        
        ppfd->cRedBits = 8;
        ppfd->cRedShift = 0;
        ppfd->cGreenBits = 8;
        ppfd->cGreenShift = 0;
        ppfd->cBlueBits = 8;
        ppfd->cBlueShift = 0;
    }
    else
    {
        ComputeBitsFromMasks(ppfd,
                             pddsd->ddpfPixelFormat.dwRBitMask,
                             pddsd->ddpfPixelFormat.dwGBitMask,
                             pddsd->ddpfPixelFormat.dwBBitMask);
    }
}

/******************************Public*Routine******************************\
* __wglGetBitfieldColorFormat
*
* Fills in the cRedBits, cRedShift, cGreenBits, etc. fields of the
* PIXELFORMATDESCRIPTOR for 16, 24, and 32bpp surfaces (either device
* or bitmap surfaces).
*
* This is done by creating a compatible bitmap and calling GetDIBits
* to return the color masks.  This is done with two calls.  The first
* call passes in biBitCount = 0 to GetDIBits which will fill in the
* base BITMAPINFOHEADER data.  The second call to GetDIBits (passing
* in the BITMAPINFO filled in by the first call) will return the color
* table or bitmasks, as appropriate.
*
* This function is used to describe the color format for both the underlying
* surface and for the device.  This is the same thing if the DC is a
* display DC.  However, for a memory DC, the surface and the device may have
* different formats.  The bDescribeSurf flag indicates whether the caller
* wants the decription of the device (FALSE) or the surface (TRUE).
*
* Returns:
*   TRUE if successful, FALSE otherwise.
*
* History:
*  07-Jun-1995 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

BOOL APIENTRY
__wglGetBitfieldColorFormat(HDC hdc, UINT cColorBits, PIXELFORMATDESCRIPTOR *ppfd,
                            BOOL bDescribeSurf)
{
    HBITMAP hbm = (HBITMAP) NULL;
    BOOL    bRet = FALSE;
    HDC hdcDriver;

#if DBG
// Dynamic color depth changes can cause this.  It will not cause us to crash,
// but drawing (color) may be incorrect.

    if ((GetObjectType(hdc) == OBJ_DC) &&
        (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE))
    {
        WARNING("Palette managed device that is greater than 8 bits\n");
    }

    if (cColorBits < 16)
    {
        WARNING("__wglGetBitfieldColorFormat with cColorBits < 16\n");
    }
#endif

// Handle direct case.

    if ( GLDIRECTSCREEN && wglIsDirectDevice(hdc) )
    {
        __wglGetDdFormat(&GLSCREENINFO->gdds.ddsd, ppfd);
        return TRUE;
    }

// Create a dummy bitmap from which we can query color format info.
//
// If we want a device format AND its a MEM_DC AND NOT a printer or plotter,
// then we need to create a compatible bitmap from a display DC (not the mem
// DC passed into this function).
//
// Otherwise, the format of the surface (whether bitmap or device) associated
// with the DC passed in will suffice.
//
// WinNT does not care, but the Win95 GetDIBits call might
// fail if we use a memory DC.  Specifically, if the memory
// DC contains a surface that does not match the display
// (remember, the new bitmap is compatible with the display)
// the Win95 GetDIBits call will fail.
//
// So use the display DC.  It works on both platforms.

    if (!bDescribeSurf)
    {
        hdcDriver = GetCompatibleDevice(hdc, GetObjectType(hdc));
        if (hdcDriver == NULL)
        {
            return FALSE;
        }
    }
    else
    {
        hdcDriver = hdc;
    }
    
    hbm = CreateCompatibleBitmap(hdcDriver, 1, 1);
    if ( !hbm )
    {
        WARNING("__wglGetBitfieldColorFormat: "
                "CreateCompatibleBitmap failed\n");
    }

// Get the color format by calling GetDIBits.

    else
    {
        BYTE ajBitmapInfo[sizeof(BITMAPINFO) + 3*sizeof(DWORD)];
        BITMAPINFO *pbmi = (BITMAPINFO *) ajBitmapInfo;
        int iRet;

        //!!!dbug -- Init masks to zero so we can
        // tell if they are set by GetDIBits.
        memset(pbmi, 0, sizeof(ajBitmapInfo));
        pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);

        // Call first time to fill in BITMAPINFO header.
        iRet = GetDIBits(hdc, hbm, 0, 0, NULL, pbmi, DIB_RGB_COLORS);

#if DBG
        if (pbmi->bmiHeader.biBitCount != cColorBits)
            WARNING2("__wglGetBitfieldColorFormat: bit count != BITSPIXEL "
                     " (%ld, %ld)\n", pbmi->bmiHeader.biBitCount, cColorBits);
#endif

        switch ( pbmi->bmiHeader.biCompression )
        {
        case BI_RGB:

#if DBG
        // Dynamic color depth changes can cause this.  It will not cause
        // us to crash, but drawing (color) may be incorrect.

            if (pbmi->bmiHeader.biBitCount != cColorBits)
            {
                WARNING("__wglGetBitfieldColorFormat(): bit count mismatch\n");
            }
#endif

        // Default DIB format.  Color masks are implicit for each bit depth.

            switch ( pbmi->bmiHeader.biBitCount )
            {
            case 16:
                // 16bpp default is 555 BGR-ordering
                ppfd->cRedBits   = 5; ppfd->cRedShift   = 10;
                ppfd->cGreenBits = 5; ppfd->cGreenShift =  5;
                ppfd->cBlueBits  = 5; ppfd->cBlueShift  =  0;
                bRet = TRUE;
                break;

            case 24:
            case 32:
                // 24 and 32bpp default is 888 BGR-ordering
                ppfd->cRedBits   = 8; ppfd->cRedShift   = 16;
                ppfd->cGreenBits = 8; ppfd->cGreenShift =  8;
                ppfd->cBlueBits  = 8; ppfd->cBlueShift  =  0;
                bRet = TRUE;
                break;

            default:
                break;
            }

            break;

        case BI_BITFIELDS:

        // Some drivers seem to return bitfields for everything that's
        // not paletted.  They return correct BGR bitfields so we
        // operate correctly, so remove this assert
#ifdef STRICT_BITFIELD_CHECK
            ASSERTOPENGL(
                    cColorBits == 16 || cColorBits == 32,
                    "__wglGetBitfieldColorFormat(): "
                    "BI_BITFIELDS surface not 16 or 32bpp\n"
                );
#endif

            // Call a second time to get the color masks.
            // It's a GetDIBits Win32 "feature".
            iRet = GetDIBits(hdc, hbm, 0, pbmi->bmiHeader.biHeight, NULL,
                             pbmi, DIB_RGB_COLORS);

            ComputeBitsFromMasks(ppfd,
                                 *(DWORD *)&pbmi->bmiColors[0],
                                 *(DWORD *)&pbmi->bmiColors[1],
                                 *(DWORD *)&pbmi->bmiColors[2]);

            bRet = TRUE;
            break;

        default:
            RIP("__wglGetBitfieldColorFormat(): bad biCompression\n");
            break;
        }

        DeleteObject(hbm);
    }

    if ( hdcDriver != hdc )
    {
        ReleaseDC((HWND) NULL, hdcDriver);
    }

    return bRet;
}

/******************************Public*Routine******************************\
*
* wglGetDeviceDepth
*
* Returns the depth of the given HDC
* Primarily used to workaround potential problems with printers
* that lie about their depth in GetDeviceCaps
*
* History:
*  Tue Apr 09 16:52:47 1996	-by-	Drew Bliss [drewb]
*   Created
*
\**************************************************************************/

int wglGetDeviceDepth(HDC hdc)
{
    int iTech;

    // If this is an enhanced metafile it should return the technology
    // of the reference device
    iTech = GetDeviceCaps(hdc, TECHNOLOGY);
    if (iTech == DT_PLOTTER || iTech == DT_RASPRINTER)
    {
        HBITMAP hbm;
        BYTE ajBitmapInfo[sizeof(BITMAPINFO) + 3*sizeof(DWORD)];
        BITMAPINFO *pbmi = (BITMAPINFO *) ajBitmapInfo;
        int iRet;
        
        // We're dealing with a printer or a metafile that has a printer
        // as a reference device
        // Find out the true depth by creating a compatible
        // bitmap and querying its format
        if ( (hbm = CreateCompatibleBitmap(hdc, 1, 1)) != NULL )
        {
            memset(pbmi, 0, sizeof(ajBitmapInfo));
            pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
            iRet = GetDIBits(hdc, hbm, 0, 0, NULL, pbmi, DIB_RGB_COLORS);
            
            DeleteObject(hbm);
            
            return iRet != 0 ? pbmi->bmiHeader.biBitCount : -1;
        }

        // Failure
        return -1;
    }
    else
    {
        // We're dealing with a well-behaved DC so just return
        // the normal depth
        return GetDeviceCaps(hdc, BITSPIXEL)*GetDeviceCaps(hdc, PLANES);
    }
}

/******************************Public*Routine******************************\
* wglDescribePixelFormat
*
* Describe the pixel format.
* If cjpfd is 0, just return the maximum pixel format index.
*
* Returns: 0 if error; maximum pixel format index otherwise
*
* History:
*  19-Oct-1994 Gilman Wong [gilmanw]
* Adapted from GreDescribePixelFormat (gdi\gre\pixelfmt.cxx).
*
* History for gdi\gre\pixelfmt.cxx:
*  Mon Apr 25 15:34:32 1994     -by-    Hock San Lee    [hockl]
* Added 16-bit Z buffer formats and removed double buffered formats for bitmaps.
*  Tue Sep 21 14:25:04 1993     -by-    Hock San Lee    [hockl]
* Wrote it.
\**************************************************************************/

// Here are the generic formats that we enumerate.  ChoosePixelFormat code
// assumes that generic pixel formats with z32 comes before z16 as given below:
//
// I. Native formats:
//
//   1. rgb.sb.z32.a0
//   2. rgb.sb.z16.a0
//   3. rgb.db.z32.a0
//   4. rgb.db.z16.a0
//   5. rgb.sb.z32.a8
//   6. rgb.sb.z16.a8
//   7. rgb.db.z32.a8
//   8. rgb.db.z16.a8
//   9.  ci.sb.z32
//   10. ci.sb.z16
//   11. ci.db.z32
//   12. ci.db.z16
//
// II. Other formats:
//
//   1. rgb.sb.z32.a0
//   2. rgb.sb.z16.a0
//   3. rgb.sb.z32.a8
//   4. rgb.sb.z16.a8
//   5.  ci.sb.z32
//   6.  ci.sb.z16
//
// We always enumerate the native formats first followed by other formats
// in the BPP order {24, 32, 16, 8, 4} for a total of 1 * 12 + 4 * 6 = 36
// pixel formats.

// Highest native format generic pixel format index.
#define MAX_NATIVE_GENERIC_PFD 12
// Number of non-native formats in a non-native group.
#define NON_NATIVE_PFD_GROUP 6

static BYTE aabPixelBits[BMF_COUNT][4] =
{
    {24, 32, 16, 8},    // error
    {24, 32, 16, 8},    // 1 bpp
    {24, 32, 16, 8},    // 4 bpp
    {24, 32, 16, 4},    // 8 bpp
    {24, 32,  8, 4},    // 16 bpp
    {32, 16,  8, 4},    // 24 bpp
    {24, 16,  8, 4}     // 32 bpp
};

static BYTE abPixelType[MAX_GENERIC_PFD] =
{
    PFD_TYPE_RGBA, PFD_TYPE_RGBA, PFD_TYPE_RGBA, PFD_TYPE_RGBA,
    PFD_TYPE_RGBA, PFD_TYPE_RGBA, PFD_TYPE_RGBA, PFD_TYPE_RGBA,
    PFD_TYPE_COLORINDEX,PFD_TYPE_COLORINDEX, PFD_TYPE_COLORINDEX,PFD_TYPE_COLORINDEX,
    PFD_TYPE_RGBA, PFD_TYPE_RGBA, PFD_TYPE_RGBA, PFD_TYPE_RGBA,
    PFD_TYPE_COLORINDEX, PFD_TYPE_COLORINDEX,
    PFD_TYPE_RGBA, PFD_TYPE_RGBA, PFD_TYPE_RGBA, PFD_TYPE_RGBA,
    PFD_TYPE_COLORINDEX, PFD_TYPE_COLORINDEX,
    PFD_TYPE_RGBA, PFD_TYPE_RGBA, PFD_TYPE_RGBA, PFD_TYPE_RGBA,
    PFD_TYPE_COLORINDEX, PFD_TYPE_COLORINDEX,
    PFD_TYPE_RGBA, PFD_TYPE_RGBA, PFD_TYPE_RGBA, PFD_TYPE_RGBA,
    PFD_TYPE_COLORINDEX, PFD_TYPE_COLORINDEX
};

int WINAPI InternalDescribePixelFormat(HDC hdc, HDC hdcDriver,
                                       int ipfd, UINT cjpfd,
                                       LPPIXELFORMATDESCRIPTOR ppfd,
                                       int ipfdDevMax, int ipfdMcdMax,
                                       LPDIRECTDRAWSURFACE pdds,
                                       DDSURFACEDESC *pddsd)
{
    int iRet = 0;
    int ipfdGen;
    UINT iDitherFormat;
    BYTE cColorBitsNative;
    
// If cjpfd is 0, just return the maximum pixel format index.

    if (cjpfd == 0 || ppfd == NULL)
    {
        iRet = MAX_GENERIC_PFD + ipfdDevMax + ipfdMcdMax;
        goto wglDescribePixelFormat_cleanup;
    }

// Validate the size of the pixel format descriptor.

    if (cjpfd < sizeof(PIXELFORMATDESCRIPTOR))
    {
        SAVE_ERROR_CODE(ERROR_INVALID_PARAMETER);
        goto wglDescribePixelFormat_cleanup;
    }

// Validate pixel format index.
// If a driver support device pixel formats 1..ipfdDevMax, the generic
// pixel formats will be (ipfdDevMax+1)..(ipfdDevMax+MAX_GENERIC_PFD).
// Otherwise, ipfdDevMax is 0 and the generic pixel formats are
// 1..MAX_GENERIC_PFD.

    if ((ipfd < 1) || (ipfd > ipfdDevMax + ipfdMcdMax + MAX_GENERIC_PFD))
    {
        SAVE_ERROR_CODE(ERROR_INVALID_PARAMETER);
        goto wglDescribePixelFormat_cleanup;
    }

// Dispatch ICD driver formats.

    if (ipfd <= ipfdDevMax)
    {
        int iDrvRet = __DrvDescribePixelFormat(hdcDriver,ipfd,cjpfd,ppfd);
        if (iDrvRet)
        {
            ASSERTOPENGL(iDrvRet == ipfdDevMax,
                         "wglDescribePixelFornat: Bad ipfdDevMax");
            iRet = MAX_GENERIC_PFD + ipfdDevMax + ipfdMcdMax;
        }

        goto wglDescribePixelFormat_cleanup;
    }

#ifdef _MCD_
// Dispatch MCD driver formats.

    ipfdGen = ipfd - ipfdDevMax;
    if (ipfdGen <= ipfdMcdMax)
    {
        int iMcdRet;

    // Note: don't need to check if gpMcdTable is valid because we can't get
    // here unless ipfdDevMax is non-zero and that can't happen unless the
    // the table is valid.

        ASSERTOPENGL(gpMcdTable, "wglDescribePixelFormat: bad MCD table\n");

        iMcdRet = (gpMcdTable->pMCDDescribePixelFormat)(hdcDriver, ipfdGen,
                                                        ppfd);
        if (iMcdRet)
        {
            ASSERTOPENGL(iMcdRet == ipfdMcdMax,
                         "wglDescribePixelFornat: Bad ipfdMcdMax");
            iRet = MAX_GENERIC_PFD + ipfdDevMax + ipfdMcdMax;
        }

        goto wglDescribePixelFormat_cleanup;
    }

// Generic implementation.
// Normalize the generic pixel format index to 0..(MAX_GENERIC_PFD-1).

    ipfdGen = ipfdGen - ipfdMcdMax - 1;
#else
// Generic implementation.
// Normalize the generic pixel format index to 0..(MAX_GENERIC_PFD-1).

    ipfdGen = ipfd - ipfdDevMax - 1;
#endif

// Get the native BPP format.

    if (pdds != NULL)
    {
        cColorBitsNative = DdPixelDepth(pddsd);
    }
    else
    {
        cColorBitsNative = (BYTE)wglGetDeviceDepth(hdc);
    }
    
    if (cColorBitsNative < 1)
    {
        SAVE_ERROR_CODE(ERROR_INVALID_PARAMETER);
        goto wglDescribePixelFormat_cleanup;
    }

    if (cColorBitsNative <= 1)
    {
        cColorBitsNative = 1;
        iDitherFormat = BMF_1BPP;
    }
    else if (cColorBitsNative <= 4)
    {
        cColorBitsNative = 4;
        iDitherFormat = BMF_4BPP;
    }
    else if (cColorBitsNative <= 8)
    {
        cColorBitsNative = 8;
        iDitherFormat = BMF_8BPP;
    }
    else if (cColorBitsNative <= 16)
    {
        cColorBitsNative = 16;
        iDitherFormat = BMF_16BPP;
    }
    else if (cColorBitsNative <= 24)
    {
        cColorBitsNative = 24;
        iDitherFormat = BMF_24BPP;
    }
    else
    {
        cColorBitsNative = 32;
        iDitherFormat = BMF_32BPP;
    }

// Fill in the pixel format descriptor.

    ppfd->nSize      = sizeof(PIXELFORMATDESCRIPTOR);
    ppfd->nVersion   = 1;
    ppfd->iPixelType = abPixelType[ipfdGen];

    if (ipfdGen < MAX_NATIVE_GENERIC_PFD)
    {
        ppfd->cColorBits = max(cColorBitsNative, 4);    // 1 bpp not supported
    }
    else
    {
        ppfd->cColorBits = aabPixelBits[iDitherFormat]
            [(ipfdGen - MAX_NATIVE_GENERIC_PFD) / NON_NATIVE_PFD_GROUP];
    }

// If the color format is compatible to that of the device and the
// color bits is 16 or greater, use the device description.
// Otherwise, use the generic format.

    if (ipfdGen < MAX_NATIVE_GENERIC_PFD && cColorBitsNative >= 16)
    {
// Handle compatible formats that are greater than 16-bits.

        if (pdds != NULL)
        {
            __wglGetDdFormat(pddsd, ppfd);
        }
        else if ( !__wglGetBitfieldColorFormat(hdc, cColorBitsNative,
                                               ppfd, FALSE) )
        {
        // Don't know how to deal with this device!

            WARNING("Unknown device format");
            SAVE_ERROR_CODE(ERROR_NOT_SUPPORTED);
            goto wglDescribePixelFormat_cleanup;
        }
    }
    else
    {
// Handle generic formats.

        switch (ppfd->cColorBits)
        {
        case 4:
            ppfd->cRedBits   = 1; ppfd->cRedShift   = 0;
            ppfd->cGreenBits = 1; ppfd->cGreenShift = 1;
            ppfd->cBlueBits  = 1; ppfd->cBlueShift  = 2;
            break;
        case 8:
            ppfd->cRedBits   = 3; ppfd->cRedShift   = 0;
            ppfd->cGreenBits = 3; ppfd->cGreenShift = 3;
            ppfd->cBlueBits  = 2; ppfd->cBlueShift  = 6;
            break;
        case 16:
            /*
            ** Even though Win95 allows arbitrary bitfield definitions
            ** for 16bpp DIBs, only 555BGR is usable by Win95's GDI.
            */
            ppfd->cRedBits   = 5; ppfd->cRedShift   = 10;   // 555BGR
            ppfd->cGreenBits = 5; ppfd->cGreenShift =  5;
            ppfd->cBlueBits  = 5; ppfd->cBlueShift  =  0;
            break;
        case 24:
        case 32:
            /*
            ** Even though Win95 allows arbitrary bitfield definitions
            ** for 32bpp, only 888BGR is usable by Win95's GDI.  Similarly,
            ** NT has the concept of a RGB 24bpp DIB, but Win95 does not.
            */
            ppfd->cRedBits   = 8; ppfd->cRedShift   = 16;   // 888BGR
            ppfd->cGreenBits = 8; ppfd->cGreenShift =  8;
            ppfd->cBlueBits  = 8; ppfd->cBlueShift  =  0;
            break;
        default:
            ASSERTOPENGL(FALSE, "wglDescribePixelFornat: Unknown format");
            break;
        }
    }

    ppfd->cAlphaBits    = 0;
    ppfd->cAlphaShift   = 0;
    if ( ipfdGen < MAX_NATIVE_GENERIC_PFD)
    {
        // Only report alpha bits if the DirectDraw surface has them.
        if (pdds != NULL)
        {
            if (pddsd->ddpfPixelFormat.dwFlags & DDPF_ALPHAPIXELS)
            {
                ASSERTOPENGL(pddsd->dwFlags & DDSD_ALPHABITDEPTH,
                             "Surface with alpha but no bit depth set\n");
                
                ppfd->cAlphaBits = (BYTE)pddsd->dwAlphaBitDepth;
            }
        }
        else if ( (ipfdGen > 3) && (ipfdGen < 8) )
        {
            ppfd->cAlphaBits = 8;
        }
    }
    else
    {
        int ipfd = (ipfdGen - MAX_NATIVE_GENERIC_PFD) % NON_NATIVE_PFD_GROUP;
        if ( (ipfd == 2) || (ipfd == 3) )
        {
            ppfd->cAlphaBits = 8;
        }
    }

    if (ppfd->iPixelType == PFD_TYPE_RGBA)
    {
        if (ppfd->cColorBits <= 16)
        {
            if (ppfd->cColorBits < 8)
            {
                // !!! Internally now, we will actually be using a 32-bit accum
                // buffer, but the user will think it's 16 (This is for 
                // backwards compatibility).
                ppfd->cAccumBits = 16;
                if( ppfd->cAlphaBits )
                {
                    ppfd->cAccumRedBits   = 4;
                    ppfd->cAccumGreenBits = 4;
                    ppfd->cAccumBlueBits  = 4;
                    ppfd->cAccumAlphaBits = 4;
                }
                else
                {
                    ppfd->cAccumRedBits   = 5;
                    ppfd->cAccumGreenBits = 6;
                    ppfd->cAccumBlueBits  = 5;
                    ppfd->cAccumAlphaBits = 0;
                }
            }
            else 
            {
                ppfd->cAccumBits = 32;
                if( ppfd->cAlphaBits )
                {
                    ppfd->cAccumRedBits   = 8;
                    ppfd->cAccumGreenBits = 8;
                    ppfd->cAccumBlueBits  = 8;
                    ppfd->cAccumAlphaBits = 8;
                }
                else
                {
                    ppfd->cAccumRedBits   = 11;
                    ppfd->cAccumGreenBits = 11;
                    ppfd->cAccumBlueBits  = 10;
                    ppfd->cAccumAlphaBits = 0;
                }
            }
        }
        else
        {
            ppfd->cAccumBits = 64;

            if( ppfd->cAlphaBits )
            {
                ppfd->cAccumRedBits   = 16;
                ppfd->cAccumGreenBits = 16;
                ppfd->cAccumBlueBits  = 16;
                ppfd->cAccumAlphaBits = 16;
            }
            else
            {
                ppfd->cAccumRedBits   = 16;
                ppfd->cAccumGreenBits = 16;
                ppfd->cAccumBlueBits  = 16;
                ppfd->cAccumAlphaBits = 0;
            }
        }
    }
    else
    {
        ppfd->cAccumBits      = 0;
        ppfd->cAccumRedBits   = 0;
        ppfd->cAccumGreenBits = 0;
        ppfd->cAccumBlueBits  = 0;
        ppfd->cAccumAlphaBits = 0;
    }

// Generic formats alternate between 16- and 32-bit depth buffer.  Evens
// are 32-bit, odds are 16-bit.
// DirectDraw surfaces always report the depth of the attached Z buffer
// for the native format indices.

    if (pdds != NULL && ipfdGen < MAX_NATIVE_GENERIC_PFD)
    {
        DDSCAPS ddscaps;
        LPDIRECTDRAWSURFACE pddsZ;
        
        // DDraw surfaces may not have attached Z buffers, in which case
        // we should not report depth bits.  If one is attached, its
        // depth should be reported.
        // We only do this processing for native pixel formats.
        
        memset(&ddscaps, 0, sizeof(ddscaps));
        ddscaps.dwCaps = DDSCAPS_ZBUFFER;
        if (pdds->lpVtbl->
            GetAttachedSurface(pdds, &ddscaps, &pddsZ) == DD_OK)
        {
            HRESULT hr;
            DDSURFACEDESC ddsdZ;
                
            memset(&ddsdZ, 0, sizeof(ddsdZ));
            ddsdZ.dwSize = sizeof(ddsdZ);
                
            hr = pddsZ->lpVtbl->GetSurfaceDesc(pddsZ, &ddsdZ);
            
            pddsZ->lpVtbl->Release(pddsZ);

            if (hr != DD_OK)
            {
                goto wglDescribePixelFormat_cleanup;
            }

            ppfd->cDepthBits =
                (BYTE)DdPixDepthToCount(ddsdZ.ddpfPixelFormat.
                                        dwZBufferBitDepth);
        }
        else
        {
            ppfd->cDepthBits = 0;
        }
    }
    else if (ipfdGen & 0x1)
    {
        ppfd->cDepthBits = 16;
    }
    else
    {
        ppfd->cDepthBits = 32;
    }
    
    ppfd->cStencilBits  = 8;
    ppfd->cAuxBuffers   = 0;
    ppfd->iLayerType    = PFD_MAIN_PLANE;
    ppfd->bReserved     = 0;
    ppfd->dwLayerMask   = 0;
    ppfd->dwVisibleMask = 0;
    ppfd->dwDamageMask  = 0;

// Compute the buffer flags.
// Support OpenGL in all generic formats.

    ppfd->dwFlags = PFD_SUPPORT_OPENGL | PFD_GENERIC_FORMAT;

    // Indicate DirectDraw support on native pixel formats for DD surfaces.
    if (pdds != NULL && ipfdGen < MAX_NATIVE_GENERIC_PFD)
    {
        ppfd->dwFlags |= PFD_SUPPORT_DIRECTDRAW;
    }
    
// Bitmaps and GDI drawing are available in single buffered mode only.

    if (pdds == NULL &&
        (ipfdGen == 2 || ipfdGen == 3 || ipfdGen == 6 || ipfdGen == 7 ||
         ipfdGen == 10 || ipfdGen == 11))
    {
        ppfd->dwFlags |= PFD_DOUBLEBUFFER | PFD_SWAP_COPY;
    }
    else
    {
        ppfd->dwFlags |= PFD_DRAW_TO_BITMAP | PFD_SUPPORT_GDI;
    }

// Draw to window or device surface only if the format is compatible.

    if (ipfdGen < MAX_NATIVE_GENERIC_PFD)
    {
        ppfd->dwFlags |= PFD_DRAW_TO_WINDOW;

// Need a palette if it is a RGBA pixel type on a palette managed device.

        if (ppfd->cColorBits == 8 && ppfd->iPixelType == PFD_TYPE_RGBA)
        {
            ppfd->dwFlags |= PFD_NEED_PALETTE;
        }
    }

// If this is a 1 bpp surface, we don't support drawing to window and
// double buffered mode.  Re-set the buffer flags.

    if (cColorBitsNative < 4)
    {
#ifndef GL_METAFILE
        ASSERTOPENGL(ppfd->cColorBits == 4,
            "wglDescribePixelFormat: bad cColorBits for 1 bpp surface\n");
#endif

        ppfd->dwFlags = PFD_DRAW_TO_BITMAP | PFD_SUPPORT_GDI |
            PFD_SUPPORT_OPENGL | PFD_GENERIC_FORMAT;
    }

// To support other potential graphics systems, we reserve the following
// flags in the pixel format descriptor.  For example, PEX may use
// PFD_SUPPORT_OTHER1 on the system that supports PEX.  Since we don't
// support these other systems in the generic implementation, they belong
// to the device pixel format descriptor.

    ASSERTOPENGL(!(ppfd->dwFlags & (PFD_SUPPORT_OTHER1 | PFD_SUPPORT_OTHER2 |
                                    PFD_SUPPORT_OTHER3 | PFD_SUPPORT_OTHER4)),
                 "dwFlags reserved for device formats\n");

    iRet = MAX_GENERIC_PFD + ipfdDevMax + ipfdMcdMax;

wglDescribePixelFormat_cleanup:

    return iRet;
}

int WINAPI wglDescribePixelFormat(HDC hdc, int ipfd, UINT cjpfd,
                                  LPPIXELFORMATDESCRIPTOR ppfd)
{
    int iRet = 0;
    int ipfdDevMax, ipfdMcdMax;
    DWORD dwObjectType;
    HDC hdcDriver = NULL;
    LPDIRECTDRAWSURFACE pdds;
    DDSURFACEDESC ddsd;

// Validate DC.

    switch (dwObjectType = wglObjectType(hdc))
    {
    case OBJ_DC:
    case OBJ_MEMDC:
    case OBJ_ENHMETADC:
        break;
    default:
        SAVE_ERROR_CODE(ERROR_INVALID_HANDLE);
        return(0);
    }

    // Check to see if this is a DirectDraw DC.  If it is, use the
    // device DC returned from DirectDraw.
    if (pfnGetSurfaceFromDC != NULL &&
        pfnGetSurfaceFromDC(hdc, &pdds, &hdcDriver) == DD_OK)
    {
        // pdds reference must be released before exiting this function.

        // Retrieve surface description for later use.
        memset(&ddsd, 0, sizeof(ddsd));
        ddsd.dwSize = sizeof(ddsd);
        if (pdds->lpVtbl->GetSurfaceDesc(pdds, &ddsd) != DD_OK)
        {
            goto wglDescribePixelFormat_cleanup;
        }
    }
    else
    {
        pdds = NULL;
        
        hdcDriver = GetCompatibleDevice(hdc, dwObjectType);
        if (hdcDriver == NULL)
        {
            goto wglDescribePixelFormat_cleanup;
        }

        // NOTE: From this point on, all exit cases must cleanup hdcDriver
    }

// Get the number of hardware supported formats.

    wglNumHardwareFormats(hdcDriver, dwObjectType, &ipfdMcdMax, &ipfdDevMax);

    iRet = InternalDescribePixelFormat(hdc, hdcDriver, ipfd, cjpfd, ppfd,
                                       ipfdDevMax, ipfdMcdMax,
                                       pdds, &ddsd);
    
wglDescribePixelFormat_cleanup:

    if (pdds != NULL)
    {
        pdds->lpVtbl->Release(pdds);
    }
    else if (hdcDriver != hdc)
    {
        ReleaseDC((HWND) NULL, hdcDriver);
    }

    return iRet;
}

#ifdef _MCD_
/******************************Public*Routine******************************\
* GenMcdGenericCompatibleFormat
*
* Determines if pixelformat in gengc can be supported by generic code.
*
* Note:
*   The implication of not being compatible is that generic cannot be
*   used for driver kickbacks and MCD contexts cannot be converted.
*
* Returns:
*   TRUE if compatible, FALSE otherwise.
*
* History:
*  04-Jun-1996 -by- Gilman Wong [gilmanw]
* Wrote it.
\**************************************************************************/

BOOL FASTCALL GenMcdGenericCompatibleFormat(__GLGENcontext *gengc)
{
    PIXELFORMATDESCRIPTOR *ppfd;

// Software-only formats are definitely supported.

    ppfd = &gengc->gsurf.pfd;
    if ((ppfd->dwFlags & (PFD_GENERIC_FORMAT|PFD_GENERIC_ACCELERATED))
        == PFD_GENERIC_FORMAT)
        return TRUE;

// Layer planes are not supported.

    if (gengc->iLayerPlane)
        return FALSE;

// Generic is PFD_SWAP_COPY only.  There can't be many apps that rely
// on PFD_SWAP_EXCHANGE behavior (usually they look for PFD_SWAP_COPY
// so the back buffer can be used as backing store), but for now I think
// we should be conservative.
//
// Note: most MGA cards will set PFD_SWAP_COPY or neither (i.e., either
// style might be used depending on window size).

    if (ppfd->dwFlags & PFD_SWAP_EXCHANGE)
        return FALSE;

// Can only support 8bpp stencils.

    if ((ppfd->cStencilBits != 0) && (ppfd->cStencilBits != 8))
        return FALSE;

// Passed all the checks, we're compatible.

    return TRUE;
}
#endif

/******************************Public*Routine******************************\
* wglSwapBuffers
*
\**************************************************************************/

BOOL WINAPI wglSwapBuffers(HDC hdc)
{
    int  ipfd;
    BOOL bRet = FALSE;
    GLGENwindow *pwnd;
    GLWINDOWID gwid;

// Validate the DC.

    if (IsDirectDrawDevice(hdc))
    {
        SetLastError(ERROR_INVALID_FUNCTION);
        return FALSE;
    }
    
    switch ( wglObjectType(hdc) )
    {
    case OBJ_DC:
        break;
    case OBJ_MEMDC:
        return(TRUE);           // early out -- nothing to do if memory DC
    default:
        WARNING("wglSwapBuffers(): invalid hdc\n");
        SAVE_ERROR_CODE(ERROR_INVALID_HANDLE);
        return(FALSE);
    }

// Validate pixel format.

    WindowIdFromHdc(hdc, &gwid);
    pwnd = pwndGetFromID(&gwid);
    if ( pwnd )
    {
        if (pwnd->ipfd > 0)
        {
            // Dispatch to driver or generic.  Which one can be determined by
            // the pixel format.

            if ( pwnd->ipfd <= pwnd->ipfdDevMax )
            {
                // Some ICDs do not need glFinish synchronization so
                // we don't do it here.  __DrvSwapBuffers will call
                // it if necessary.
                bRet = __DrvSwapBuffers(hdc, TRUE);
            }
            else
            {
                // Finish OpenGL calls in this thread before doing the swap.
                // We use glFinish instead of glFlush to ensure that all
                // OpenGL operations are completed.
                glFinish();

                ENTER_WINCRIT(pwnd);
                
                // Can't rely on the pwnd's HDC because it may have
                // been released since SetPixelFormat.  Always use
                // the DC passed in as the target.
                bRet = glsrvSwapBuffers(hdc, pwnd);

                LEAVE_WINCRIT(pwnd);
            }
        }

        pwndRelease(pwnd);
    }
    else
    {
        SAVE_ERROR_CODE(ERROR_INVALID_HANDLE);
    }

    return bRet;
}