Carbon SetupGL/Carbon_SetupGL.c

/*
    File:       Carbon SetupGL.c
 
    Contains:   Functions to enable building and destorying a GL full screen or windowed context
 
    Written by: Geoff Stahl (ggs)
 
    Copyright:  Copyright © 1999 Apple Computer, Inc., All Rights Reserved
 
    Change History (most recent first):
 
         <4>     8/23/01    ggs     Fixed texture sharing and added number of bug fixes
         <3>     4/20/01    ggs     Added support for texture sharing by sharing all contexts by default
         <2>     3/26/01    ggs     Add DSp version check and other items for full screen on X
         <1>     1/19/01    ggs     Initial re-add
         <7>     3/22/00    ggs     remove extranious prototype
         <6>     3/21/00    ggs     Added windowed mode and clean up various implementation details
         <5>     1/26/00    ggs     Add fade code back in, ensure NULL pointer/context/drawable
                                    checks are in, add Preflight
         <4>     1/24/00    ggs     Added glFinish to shutdown code
         <3>     1/24/00    ggs     update to latest, better rendrere info handling for 3dfx, better
                                    checks on pause and resume, added frin devce numer and gdhandle
                                    from point
         <2.7>   11/28/99    ggs     Split out DSp and error handling.  Added texture memory
                                    considerations, assume VRAM is required if other than zero
         <2.6>   11/14/99    ggs     Fix source server copy
         <2.5>   11/13/99    ggs     fixed default pixel depth (0) condition that was causing failures
         <2.4>   11/13/99    ggs     added custom fade code
         <2.3>   11/13/99    ggs     Reset for Quake 3 use
         <2.2>   11/12/99    ggs     re-add
         <2.1>   11/12/99    ggs     added support for frequency retrieval, fixed display number
                                    output to be correct if display number input was -1
         <2>    11/12/99    ggs     1.0 functionality
         <1>    11/11/99    ggs     Initial Add
 
    Disclaimer: You may incorporate this sample code into your applications without
                restriction, though the sample code has been provided "AS IS" and the
                responsibility for its operation is 100% yours.  However, what you are
                not permitted to do is to redistribute the source as "DSC Sample Code"
                after having made changes. If you're going to re-distribute the source,
                we require that you make it clear in the source that the code was
                descended from Apple Sample Code, but that you've made changes.
*/
 
// system includes ----------------------------------------------------------
 
#ifdef __APPLE_CC__
    #include <Carbon/Carbon.h>
#else
    #include <Gestalt.h>
    #include <sound.h>
#endif
 
// project includes ---------------------------------------------------------
 
#include "Carbon_Error_Handler.h"
#include "Carbon_SetupDSp.h"
#include "Carbon_SetupGL.h"
 
 
// globals (internal/private) -----------------------------------------------
 
const RGBColor  rgbBlack    = { 0x0000, 0x0000, 0x0000 };
 
const short kWindowType = kWindowDocumentProc;
 
// prototypes (internal/private) --------------------------------------------
 
static Boolean CheckRenderer (GDHandle hGD, long *VRAM, long *textureRAM, GLint*  , Boolean fAccelMust);
static Boolean CheckAllDeviceRenderers (long* pVRAM, long* pTextureRAM, GLint* pDepthSizeSupport, Boolean fAccelMust);
static Boolean CheckWindowExtents (GDHandle hGD, short width, short height);
static void DumpCurrent (AGLDrawable* paglDraw, AGLContext* paglContext, DSpContextReference* pdspContext, pstructGLInfo pcontextInfo);
 
static OSStatus BuildGLContext (AGLDrawable* paglDraw, AGLContext* paglContext, DSpContextReference* pdspContext, GDHandle hGD, 
                                pstructGLInfo pcontextInfo, AGLContext aglShareContext);
static OSStatus BuildDrawable (AGLDrawable* paglDraw, GDHandle hGD, pstructGLInfo pcontextInfo);
static OSStatus BuildGLonDevice (AGLDrawable* paglDraw, AGLContext* paglContext, DSpContextReference* pdspContext, 
                          GDHandle hGD, pstructGLInfo pcontextInfo, AGLContext aglShareContext);
static OSStatus BuildGLonWindow (WindowPtr pWindow, AGLContext* paglContext, pstructGLWindowInfo pcontextInfo, AGLContext aglShareContext);
                          
 
// functions (internal/private) ---------------------------------------------
 
// CheckRenderer
 
// looks at renderer attributes it has at least the VRAM is accelerated
 
// Inputs:  hGD: GDHandle to device to look at
//          pVRAM: pointer to VRAM in bytes required; out is actual VRAM if a renderer was found, otherwise it is the input parameter
//          pTextureRAM:  pointer to texture RAM in bytes required; out is same (implementation assume VRAM returned by card is total so we add texture and VRAM)
//          fAccelMust: do we check for acceleration
 
// Returns: true if renderer for the requested device complies, false otherwise
 
static Boolean CheckRenderer (GDHandle hGD, long* pVRAM, long* pTextureRAM, GLint* pDepthSizeSupport, Boolean fAccelMust)
{
    AGLRendererInfo info, head_info;
    GLint inum;
    GLint dAccel = 0;
    GLint dVRAM = 0, dMaxVRAM = 0;
    Boolean canAccel = false, found = false;
    head_info = aglQueryRendererInfo(&hGD, 1);
    aglReportError ();
    if(!head_info)
    {
        ReportError ("aglQueryRendererInfo error");
        return false;
    }
    else
    {
        info = head_info;
        inum = 0;
        // see if we have an accelerated renderer, if so ignore non-accelerated ones
        // this prevents returning info on software renderer when actually we'll get the hardware one
        while (info)
        {   
            aglDescribeRenderer(info, AGL_ACCELERATED, &dAccel);
            aglReportError ();
            if (dAccel)
                canAccel = true;
            info = aglNextRendererInfo(info);
            aglReportError ();
            inum++;
        }
            
        info = head_info;
        inum = 0;
        while (info)
        {
            aglDescribeRenderer (info, AGL_ACCELERATED, &dAccel);
            aglReportError ();
            // if we can accel then we will choose the accelerated renderer 
            // how about compliant renderers???
            if ((canAccel && dAccel) || (!canAccel && (!fAccelMust || dAccel)))
            {
                aglDescribeRenderer (info, AGL_VIDEO_MEMORY, &dVRAM);   // we assume that VRAM returned is total thus add texture and VRAM required
                aglReportError ();
                if (dVRAM >= (*pVRAM + *pTextureRAM))
                {
                    if (dVRAM >= dMaxVRAM) // find card with max VRAM
                    {
                        aglDescribeRenderer (info, AGL_DEPTH_MODES, pDepthSizeSupport); // which depth buffer modes are supported
                        aglReportError ();
                        dMaxVRAM = dVRAM; // store max
                        found = true;
                    }
                }
            }
            info = aglNextRendererInfo(info);
            aglReportError ();
            inum++;
        }
    }
    aglDestroyRendererInfo(head_info);
    if (found) // if we found a card that has enough VRAM and meets the accel criteria
    {
        *pVRAM = dMaxVRAM; // return VRAM
        return true;
    }
    // VRAM will remain to same as it did when sent in
    return false;
}
 
//-----------------------------------------------------------------------------------------------------------------------
 
// CheckAllDeviceRenderers 
 
// looks at renderer attributes and each device must have at least one renderer that fits the profile
 
// Inputs:  pVRAM: pointer to VRAM in bytes required; out is actual min VRAM of all renderers found, otherwise it is the input parameter
//          pTextureRAM:  pointer to texture RAM in bytes required; out is same (implementation assume VRAM returned by card is total so we add texture and VRAM)
//          fAccelMust: do we check fro acceleration
 
// Returns: true if any renderer for on each device complies (not necessarily the same renderer), false otherwise
 
static Boolean CheckAllDeviceRenderers (long* pVRAM, long* pTextureRAM, GLint* pDepthSizeSupport, Boolean fAccelMust)
{
    AGLRendererInfo info, head_info;
    GLint inum;
    GLint dAccel = 0;
    GLint dVRAM = 0, dMaxVRAM = 0;
    Boolean canAccel = false, found = false, goodCheck = true; // can the renderer accelerate, did we find a valid renderer for the device, are we still successfully on all the devices looked at
    long MinVRAM = 0x8FFFFFFF; // max long
    GDHandle hGD = GetDeviceList (); // get the first screen
    while (hGD && goodCheck)
    {
        head_info = aglQueryRendererInfo(&hGD, 1);
        aglReportError ();
        if(!head_info)
        {
            ReportError ("aglQueryRendererInfo error");
            return false;
        }
        else
        {
            info = head_info;
            inum = 0;
            // see if we have an accelerated renderer, if so ignore non-accelerated ones
            // this prevents returning info on software renderer when actually we'll get the hardware one
            while (info)
            {
                aglDescribeRenderer(info, AGL_ACCELERATED, &dAccel);
                aglReportError ();
                if (dAccel)
                    canAccel = true;
                info = aglNextRendererInfo(info);
                aglReportError ();
                inum++;
            }
                
            info = head_info;
            inum = 0;
            while (info)
            {   
                aglDescribeRenderer(info, AGL_ACCELERATED, &dAccel);
                aglReportError ();
                // if we can accel then we will choose the accelerated renderer 
                // how about compliant renderers???
                if ((canAccel && dAccel) || (!canAccel && (!fAccelMust || dAccel)))
                {
                    aglDescribeRenderer(info, AGL_VIDEO_MEMORY, &dVRAM);    // we assume that VRAM returned is total thus add texture and VRAM required
                    aglReportError ();
                    if (dVRAM >= (*pVRAM + *pTextureRAM))
                    {
                        if (dVRAM >= dMaxVRAM) // find card with max VRAM
                        {
                            aglDescribeRenderer(info, AGL_DEPTH_MODES, pDepthSizeSupport);  // which depth buffer modes are supported
                            aglReportError ();
                            dMaxVRAM = dVRAM; // store max
                            found = true;
                        }
                    }
                }
                info = aglNextRendererInfo(info);
                aglReportError ();
                inum++;
            }
        }
        aglDestroyRendererInfo(head_info);
        if (found) // if we found a card that has enough VRAM and meets the accel criteria
        {
            if (MinVRAM > dMaxVRAM)
                MinVRAM = dMaxVRAM; // return VRAM
            
        }
        else
            goodCheck = false; // one device failed thus entire requirement fails
        hGD = GetNextDevice (hGD); // get next device
    } // while
    if (goodCheck) // we check all devices and each was good
    {
        *pVRAM = MinVRAM; // return VRAM
        return true;
    }
    return false; //at least one device failed to have mins
}
 
//-----------------------------------------------------------------------------------------------------------------------
 
// CheckWindowExtents
 
// checks to see window fits on screen completely
 
// Inputs:  hGD: GDHandle to device to look at
//          width/height: requested width and height of window
 
// Returns: true if window and borders fit, false otherwise
 
static Boolean CheckWindowExtents (GDHandle hGD, short width, short height)
{
    Rect strucRect, rectWin = {0, 0, 1, 1};
    short deviceHeight = (short) ((**hGD).gdRect.bottom - (**hGD).gdRect.top - GetMBarHeight ());   
    short deviceWidth = (short) ((**hGD).gdRect.right - (**hGD).gdRect.left);
    short windowWidthExtra, windowHeightExtra;
    // build window (not visible)
    WindowPtr pWindow = NewCWindow (NULL, &rectWin, "\p", true, kWindowType, (WindowPtr)-1, 0, 0);
    
#if !TARGET_API_MAC_CARBON
    strucRect = (**(((WindowPeek)pWindow)->strucRgn)).rgnBBox;
#else
    GetWindowBounds (pWindow, kWindowStructureRgn, &strucRect);
#endif
    windowWidthExtra = (short) ((strucRect.right - strucRect.left) - 1);
    windowHeightExtra = (short) ((strucRect.bottom - strucRect.top) - 1);
    DisposeWindow (pWindow);
    if ((width + windowWidthExtra <= deviceWidth) &&
        (height + windowHeightExtra <= deviceHeight))
        return true;
    return false;
}
 
// --------------------------------------------------------------------------
 
// DumpCurrent
 
// Kills currently allocated context
// does not care about being pretty (assumes display is likely faded)
 
// Inputs:  paglDraw, paglContext, pdspContext: things to be destroyed
 
void DumpCurrent (AGLDrawable* paglDraw, AGLContext* paglContext, DSpContextReference* pdspContext, pstructGLInfo pcontextInfo)
{
    if (*pdspContext)
        DSpReportError (DSpContext_CustomFadeGammaOut (NULL, NULL, fadeTicks));
 
    if (*paglContext)
    {
        aglSetCurrentContext (NULL);
        aglReportError ();
        aglSetDrawable (*paglContext, NULL);
        aglReportError ();
        aglDestroyContext (*paglContext);
        aglReportError ();
        *paglContext = NULL;
    }
    
    if (pcontextInfo->fmt)
    {
        aglDestroyPixelFormat (pcontextInfo->fmt); // pixel format is no longer needed
        aglReportError ();
    }
    pcontextInfo->fmt = 0;
 
    if (*paglDraw && !(pcontextInfo->fFullscreen && CheckMacOSX ())) // do not destory a window on DSp if in Mac OS X
                                                                     // since there is no window built in X
#if TARGET_API_MAC_CARBON
        DisposeWindow (GetWindowFromPort (*paglDraw));
#else
        DisposeWindow ((WindowPtr) *paglDraw);
#endif
    *paglDraw = NULL;
    DestroyDSpContext (pdspContext); // fades in, safe to call at all times
}
 
#pragma mark -
// --------------------------------------------------------------------------
 
// BuildGLContext
 
// Builds OpenGL context
 
// Inputs:  hGD: GDHandle to device to look at
//          pcontextInfo: request and requirements for cotext and drawable
 
// Outputs: paglContext as allocated
//          pcontextInfo:  allocated parameters
 
// if fail to allocate: paglContext will be NULL
// if error: will return error paglContext will be NULL
 
static OSStatus BuildGLContext (AGLDrawable* paglDraw, AGLContext* paglContext, DSpContextReference* pdspContext,
                              GDHandle hGD, pstructGLInfo pcontextInfo, AGLContext aglShareContext)
{
    OSStatus err = noErr;
    NumVersion versionDSp = GetDSpVersion ();
 
    if ((Ptr) kUnresolvedCFragSymbolAddress == (Ptr) aglChoosePixelFormat) // check for existance of OpenGL
    {
        ReportError ("OpenGL not installed");
        return noErr;
    }   
    
    // DSp has problems on Mac OS X with DSp version less than 1.99 so use agl full screen
    if ((pcontextInfo->fFullscreen) && (CheckMacOSX ()) && ((versionDSp.majorRev == 0x01) && (versionDSp.minorAndBugRev < 0x99))) // need to set pixel format for full screen
    {
        short i = 0;
        while (pcontextInfo->aglAttributes[i++] != AGL_NONE) {}
        i--; // point to AGL_NONE
        pcontextInfo->aglAttributes [i++] = AGL_FULLSCREEN;
        pcontextInfo->aglAttributes [i++] = AGL_PIXEL_SIZE;
        pcontextInfo->aglAttributes [i++] = (SInt32) pcontextInfo->pixelDepth;
        pcontextInfo->aglAttributes [i++] = AGL_NONE;
    }
 
    pcontextInfo->fmt = aglChoosePixelFormat (&hGD, 1, pcontextInfo->aglAttributes); // get an appropriate pixel format
    aglReportError ();
    if (NULL == pcontextInfo->fmt) 
    {
        ReportError("Could not find valid pixel format");
        return noErr;
    }
 
    // using a default method of sharing all the contexts enables texture sharing across these contexts by default
    *paglContext = aglCreateContext (pcontextInfo->fmt, aglShareContext);               // Create an AGL context
    if (AGL_BAD_MATCH == aglGetError())
        *paglContext = aglCreateContext (pcontextInfo->fmt, 0); // unable to sahre context, create without sharing
    aglReportError ();
    if (NULL == *paglContext) 
    {
        ReportError ("Could not create context");
        return paramErr;
    }
    if (aglShareContext == NULL)
        aglShareContext = *paglContext;
    
    // set our drawable
    
    // DSp has problems on Mac OS X use DSp only when version is not less than 1.99
    if ((pcontextInfo->fFullscreen) && (CheckMacOSX ()) && !((versionDSp.majorRev == 0x01) && (versionDSp.minorAndBugRev < 0x99))) // fullscreen X late DSp
    {
        // use DSp's front buffer on Mac OS X
        *paglDraw = GetDSpDrawable (*pdspContext);
        // there is a problem in Mac OS X GM CoreGraphics that may not size the port pixmap correctly
        // this will check the vertical sizes and offset if required to fix the problem
        // this will not center ports that are smaller then a particular resolution
        {
            short deltaV, deltaH;
            Rect portBounds;
#if TARGET_API_MAC_CARBON
            PixMapHandle hPix = GetPortPixMap (*paglDraw);
            Rect pixBounds = (**hPix).bounds;
            GetPortBounds (*paglDraw, &portBounds);
#else
            PixMapHandle hPix = (*paglDraw)->portPixMap;
            Rect pixBounds = (**hPix).bounds;
            portBounds = (*paglDraw)->portRect;
#endif
            deltaV = (short) ((portBounds.bottom - portBounds.top) - (pixBounds.bottom - pixBounds.top) +
                     (portBounds.bottom - portBounds.top - pcontextInfo->height) / 2);
            deltaH = (short) (-(portBounds.right - portBounds.left - pcontextInfo->width) / 2);
            if (deltaV || deltaH)
            {
                GrafPtr pPortSave;
                GetPort (&pPortSave);
                SetPort ((GrafPtr)*paglDraw);
                // set origin to account for CG offset and if requested drawable smaller than screen rez
                SetOrigin (deltaH, deltaV);
                SetPort (pPortSave);
            }
        }
        if (!aglSetDrawable (*paglContext, *paglDraw))          // attach the CGrafPtr to the context
            return aglReportError ();
    }
    // DSp has problems on Mac OS X with DSp version less than 1.99 so use agl full screen
    else if ((pcontextInfo->fFullscreen) && (CheckMacOSX ()) && ((versionDSp.majorRev == 0x01) && (versionDSp.minorAndBugRev < 0x99))) // fulscreen X early DSp
    {
        // use aglFullScreen
        short display = 0;
        if (!aglSetFullScreen (*paglContext, pcontextInfo->width, pcontextInfo->height, 60, display)) // attach fulls screen device to the context
        {
            ReportError ("SetFullScreen failed");
            aglReportError ();
            return paramErr;
        }
    }
    else // not Mac OS X fullscreen:  this is for three cases 1) Mac OS 9 windowed 2) Mac OS X windowed 3) Mac OS 9 fullscreen (as you need to build a window on top of DSp for GL to work correctly
    {
        // build window as late as possible
        err = BuildDrawable (paglDraw, hGD, pcontextInfo);
        if (err != noErr)
        {
            ReportError ("Could not build drawable");
            return err;
        }
        if (!aglSetDrawable (*paglContext, *paglDraw))          // attach the CGrafPtr to the context
            return aglReportError ();
    }
    if(!aglSetCurrentContext (*paglContext))                    // make the context the current context
        return aglReportError ();
    
    return err;
}
 
// --------------------------------------------------------------------------
 
// BuildDrawable
 
// Builds window to be used as drawable
 
// Inputs:  hGD: GDHandle to device to look at
//          pcontextInfo: request and requirements for cotext and drawable
 
// Outputs: paglDraw as allocated
//          pcontextInfo:  allocated parameters
 
// if fail to allocate: paglDraw will be NULL
// if error: will return error paglDraw will be NULL
 
static OSStatus BuildDrawable (AGLDrawable* paglDraw, GDHandle hGD, pstructGLInfo pcontextInfo)
{
    Rect rectWin;
    RGBColor rgbSave;
    GrafPtr pGrafSave;
    OSStatus err = noErr;
    
    // center window in our context's gdevice
    rectWin.top  = (short) ((**hGD).gdRect.top + ((**hGD).gdRect.bottom - (**hGD).gdRect.top) / 2); // v center
    rectWin.top  -= pcontextInfo->height / 2;
    rectWin.left  = (short) ((**hGD).gdRect.left + ((**hGD).gdRect.right - (**hGD).gdRect.left) / 2);   // h center
    rectWin.left  -= pcontextInfo->width / 2;
    rectWin.right = (short) (rectWin.left + pcontextInfo->width);
    rectWin.bottom = (short) (rectWin.top + pcontextInfo->height);
    
 
#if TARGET_API_MAC_CARBON
    if (pcontextInfo->fFullscreen)
        *paglDraw = GetWindowPort (NewCWindow (NULL, &rectWin, "\p", 0, plainDBox, (WindowPtr)-1, 0, 0));
    else
        *paglDraw = GetWindowPort (NewCWindow (NULL, &rectWin, "\p", 0, kWindowType, (WindowPtr)-1, 0, 0));
        ShowWindow (GetWindowFromPort (*paglDraw));
#else
    if (pcontextInfo->fFullscreen)
        *paglDraw = (AGLDrawable) NewCWindow (NULL, &rectWin, "\p", 0, plainDBox, (WindowPtr)-1, 0, 0);
    else
        *paglDraw = (AGLDrawable) NewCWindow (NULL, &rectWin, "\p", 0, kWindowType, (WindowPtr)-1, 0, 0);
        ShowWindow ((WindowPtr) *paglDraw);
#endif
    GetPort (&pGrafSave);
    SetPort ((GrafPtr)*paglDraw);
    GetForeColor (&rgbSave);
    RGBForeColor (&rgbBlack);
#if TARGET_API_MAC_CARBON
    GetWindowBounds (GetWindowFromPort (*paglDraw), kWindowContentRgn, &rectWin);
#else
    rectWin = ((GrafPtr) *paglDraw)->portRect;
#endif
    PaintRect (&rectWin);
    RGBForeColor (&rgbSave); // ensure color is reset for proper blitting
    SetPort (pGrafSave);
    return err;
}
 
// --------------------------------------------------------------------------
 
// BuildGLonDevice
 
// Takes device single device and tries to build on it
 
// Inputs:  hGD: GDHandle to device to look at
//          *pcontextInfo: request and requirements for cotext and drawable
 
// Outputs: *paglDraw, *paglContext and *pdspContext as allocated
//          *pcontextInfo:  allocated parameters
 
// if fail to allocate: paglDraw, paglContext and pdspContext will be NULL
// if error: will return error and paglDraw, paglContext and pdspContext will be NULL
// Note: *paglDraw and *pdspContext can be null is aglFullScreen is used
 
static OSStatus BuildGLonDevice (AGLDrawable* paglDraw, AGLContext* paglContext, DSpContextReference* pdspContext, 
                                 GDHandle hGD, pstructGLInfo pcontextInfo, AGLContext aglShareContext)
{
    GLint depthSizeSupport;
    OSStatus err = noErr;
    Boolean fCheckRenderer = false;
    NumVersion versionDSp = GetDSpVersion ();
 
    if (pcontextInfo->fFullscreen)
    {
        // if we are in 16 or 32 bit mode already, we can check the renderer now (we will double check later)
        if (16 <= (**(**hGD).gdPMap).pixelSize)
        {
            // check for VRAM and accelerated
            if (!CheckRenderer (hGD, &(pcontextInfo->VRAM), &(pcontextInfo->textureRAM), &depthSizeSupport, pcontextInfo->fAcceleratedMust))
            {
                ReportError ("Renderer check failed");
                return err;
            }
            else
                fCheckRenderer = true;
        }
 
        // only for  Mac OS 9 or less and greater than Mac OS X 10.0.2
        // DSp has problems on Mac OS X with DSp version less than 1.99 (10.0.2 or less)
        if ((!CheckMacOSX ()) || ((versionDSp.majorRev > 0x01) || ((versionDSp.majorRev == 0x01) && (versionDSp.minorAndBugRev >= 0x99))))  // DSp should be supported in version after 1.98
        {
            err = BuildDSpContext (pdspContext, hGD, depthSizeSupport, pcontextInfo);
            // we are now faded
            if ((err != noErr) || (*pdspContext == NULL))
            {
                if (err != noErr)
                    ReportErrorNum ("BuildDSpContext failed with error:", err);
                else
                    ReportError ("Could not build DrawSprocket context");
                if (*pdspContext)
                    DSpReportError (DSpContext_CustomFadeGammaIn (NULL, NULL, fadeTicks));
                return err;
            }
        }
        // else we are using aglFullScreen and no DSp work is required
    }
    else
    {
        if (pcontextInfo->pixelDepth == 0)  // default
        {
            pcontextInfo->pixelDepth = (**(**hGD).gdPMap).pixelSize;
            if (16 > pcontextInfo->pixelDepth)
                pcontextInfo->pixelDepth = 16;
        }
        if (pcontextInfo->fDepthMust && (pcontextInfo->pixelDepth != (**(**hGD).gdPMap).pixelSize)) // device depth must match and does not
        {
            ReportError ("Pixel Depth does not match device in windowed mode.");
            if (*pdspContext)
                DSpReportError (DSpContext_CustomFadeGammaIn (NULL, NULL, fadeTicks));
            return err;
        }
        // copy back the curretn depth
        pcontextInfo->pixelDepth = (**(**hGD).gdPMap).pixelSize;
        if (!CheckWindowExtents (hGD, pcontextInfo->width, pcontextInfo->height))
        {
            ReportError ("Window will not fit on device in windowed mode.");
            if (*pdspContext)
                DSpReportError (DSpContext_CustomFadeGammaIn (NULL, NULL, fadeTicks));
            return err;
        }
    }
    
    // if we have not already checked the renderer, check for VRAM and accelerated
    if (!fCheckRenderer)
        if (!CheckRenderer (hGD, &(pcontextInfo->VRAM), &(pcontextInfo->textureRAM), &depthSizeSupport, pcontextInfo->fAcceleratedMust))
        {
            ReportError ("Renderer check failed");
            if (*pdspContext)
                DSpReportError (DSpContext_CustomFadeGammaIn (NULL, NULL, fadeTicks));
            return err;
        }
    
    // do agl
    // need to send device #'s through this
    err = BuildGLContext (paglDraw, paglContext, pdspContext, hGD, pcontextInfo, aglShareContext);
        
    // DSp has problems on Mac OS X with DSp version less than 1.99
    if ((!CheckMacOSX ()) || ((versionDSp.majorRev > 0x01) || ((versionDSp.majorRev == 0x01) && (versionDSp.minorAndBugRev >= 0x99))))// DSp should be supported in version after 1.98
    {
        if (*pdspContext)
            DSpReportError (DSpContext_CustomFadeGammaIn (NULL, NULL, fadeTicks));
    }
    return err;
}
 
 
// --------------------------------------------------------------------------
 
// BuildGLonDrawable
 
// Takes a drawable and tries to build on it
 
// Inputs:  aglDraw: a valid AGLDrawable
//          *pcontextInfo: request and requirements for cotext and drawable
 
// Outputs: *paglContext as allocated
//          *pcontextInfo:  allocated parameters
 
// if fail to allocate: paglContext will be NULL
// if error: will return error and paglContext will be NULL
 
static OSStatus BuildGLonWindow (WindowPtr pWindow, AGLContext* paglContext, pstructGLWindowInfo pcontextInfo, AGLContext aglShareContext)
{
    GDHandle hGD = NULL;
    GrafPtr cgrafSave = NULL;
    short numDevices;
    GLint depthSizeSupport;
    OSStatus err = noErr;
    
    if (!pWindow || !pcontextInfo)
    {
        ReportError ("NULL parameter passed to BuildGLonDrawable.");
        return paramErr;
    }
    
    GetPort (&cgrafSave);
    SetPortWindowPort(pWindow);
 
    // check renderere VRAM and acceleration
    numDevices = FindGDHandleFromWindow (pWindow, &hGD);
    if (!pcontextInfo->fDraggable)  // if numDevices > 1 then we will only be using the software renderer otherwise check only window device
    {
        if ((numDevices > 1) || (numDevices == 0)) // this window spans mulitple devices thus will be software only
        {
            // software renderer
            // infinite VRAM, infinite textureRAM, not accelerated
            if (pcontextInfo->fAcceleratedMust)
            {
                ReportError ("Unable to accelerate window that spans multiple devices");
                return err;
            }
        }
        else // not draggable on single device
        {
            if (!CheckRenderer (hGD, &(pcontextInfo->VRAM), &(pcontextInfo->textureRAM), &depthSizeSupport, pcontextInfo->fAcceleratedMust))
            {
                ReportError ("Renderer check failed");
                return err;
            }
        }
    }
    // else draggable so must check all for support (each device should have at least one renderer that meets the requirements)
    else if (!CheckAllDeviceRenderers (&(pcontextInfo->VRAM), &(pcontextInfo->textureRAM), &depthSizeSupport, pcontextInfo->fAcceleratedMust))
    {
        ReportError ("Renderer check failed");
        return err;
    }
    
    // do agl
    if ((Ptr) kUnresolvedCFragSymbolAddress == (Ptr) aglChoosePixelFormat) // check for existance of OpenGL
    {
        ReportError ("OpenGL not installed");
        return NULL;
    }   
    // we successfully passed the renderer check
 
    if ((!pcontextInfo->fDraggable && (numDevices == 1)))  // not draggable on a single device
        pcontextInfo->fmt = aglChoosePixelFormat (&hGD, 1, pcontextInfo->aglAttributes); // get an appropriate pixel format
    else
        pcontextInfo->fmt = aglChoosePixelFormat (NULL, 0, pcontextInfo->aglAttributes); // get an appropriate pixel format
    aglReportError ();
    if (NULL == pcontextInfo->fmt) 
    {
        ReportError("Could not find valid pixel format");
        return NULL;
    }
 
    *paglContext = aglCreateContext (pcontextInfo->fmt, aglShareContext); // Create an AGL context
    if (AGL_BAD_MATCH == aglGetError())
        *paglContext = aglCreateContext (pcontextInfo->fmt, 0); // unable to sahre context, create without sharing
    aglReportError ();
    if (NULL == *paglContext) 
    {
        ReportError ("Could not create context");
        return NULL;
    }
    
    if (!aglSetDrawable (*paglContext, GetWindowPort (pWindow))) // attach the CGrafPtr to the context
        return aglReportError ();
    
    if(!aglSetCurrentContext (*paglContext)) // make the context the current context
        return aglReportError ();
 
    SetPort (cgrafSave);
 
    return err;
}
 
#pragma mark -
 
// functions (public) -------------------------------------------------------
 
// CheckMacOSX
 
// Runtime check to see if we are running on Mac OS X
 
// Inputs:  None
 
// Returns: 0 if < Mac OS X or version number of Mac OS X (10.0 for GM)
 
UInt32 CheckMacOSX (void)
{
    UInt32 response;
    
    if ((Gestalt(gestaltSystemVersion, (SInt32 *) &response) == noErr) && (response >= 0x01000))
        return response;
    else
        return 0;
}
 
// --------------------------------------------------------------------------
 
// PreflightGL
 
// Checks for presense of OpenGL and DSp (if required)
// Inputs: checkFullscreen: true if one wants to run fullscreen (which requires DrwSprocket currently)
// Ouputs: true if OpenGL is installed (and DrawSprocket if checkFullscreen is true
 
Boolean PreflightGL (Boolean checkFullscreen)
{
    if ((Ptr) kUnresolvedCFragSymbolAddress == (Ptr) aglChoosePixelFormat) // check for existance of OpenGL
        return false;
    if (checkFullscreen && ((Ptr) kUnresolvedCFragSymbolAddress == (Ptr) DSpStartup)) // check for existance of DSp
        return false;
    return true;
}
 
// --------------------------------------------------------------------------
 
// BuildGL
 
// Takes device and geometry request and tries to build best context and drawable
// if device does not work will walk down devices looking for first one that satisfies requirments
 
// Inputs:  *pnumDevice: 0 any device, # attempt that device first, then any device
//          *pcontextInfo: request and requirements for cotext and drawable
 
// Outputs: *paglDraw, *paglContext and *pdspContext as allocated
//          *pnumDevice to device number in list that was used 
//          *pcontextInfo:  allocated parameters
 
// if fail to allocate: paglDraw, paglContext and pdspContext will be NULL
// if error: will return error and paglDraw, paglContext and pdspContext will be NULL
 
OSStatus BuildGL (AGLDrawable* paglDraw, AGLContext* paglContext, DSpContextReference* pdspContext, 
                  short* pnumDevice, pstructGLInfo pcontextInfo, AGLContext aglShareContext)
{
    OSStatus err = noErr;
    GDHandle hGD = NULL;
    structGLInfo contextInfoSave;
    
    // clear
    *paglDraw = NULL;
    *paglContext = 0;
    *pdspContext = 0;
    contextInfoSave = *pcontextInfo; // save info to reset on failures
    
    // if we are full screen and not on Mac OS X (which will use aglFullScreen)
    if (pcontextInfo->fFullscreen)
    {
        NumVersion versionDSp = GetDSpVersion ();
        // DSp has problems on Mac OS X with DSp version less than 1.99
        if ((!CheckMacOSX ()) || ((versionDSp.majorRev > 0x01) || ((versionDSp.majorRev == 0x01) && (versionDSp.minorAndBugRev >= 0x99))))// DSp should be supported in version after 1.98
        {
            err = StartDSp ();
            if (gDSpStarted)
                gNeedFade = true;
            else
                return err;
        }
    }
    
    
    //find main device
    if (*pnumDevice == -1)
    {
        GDHandle hDevice; // check number of screens
        hGD = GetMainDevice ();
        if (NULL != hGD)
        {
            err = BuildGLonDevice (paglDraw, paglContext, pdspContext, hGD, pcontextInfo, aglShareContext);
            // find device number
            *pnumDevice = 0;
            hDevice = DMGetFirstScreenDevice (true);
            do
            {
                if (hDevice == hGD)
                    break;
                hDevice = DMGetNextScreenDevice (hDevice, true);
                (*pnumDevice)++;
            }
            while (hDevice);
            if (!hDevice)
                ReportError ("main device match not found");
        }
        else
            ReportError ("Cannot get main device");
    }
 
    if ((err != noErr) || (*paglContext == 0))
    {
        err = noErr;
        DumpCurrent (paglDraw, paglContext, pdspContext, pcontextInfo); // dump what ever partial solution we might have
        *pcontextInfo = contextInfoSave; // restore info
        //find target device and check this first is one exists
        if (*pnumDevice)
        {
            short i;
            hGD = DMGetFirstScreenDevice (true);
            for (i = 0; i < *pnumDevice; i++)
            {
                GDHandle hGDNext = DMGetNextScreenDevice (hGD, true);
                if (NULL == hGDNext) // ensure we did not run out of devices
                    break; // if no more devices drop out
                else
                    hGD = hGDNext; // otherwise continue
            }
            *pnumDevice = i; // record device we actually got
            err = BuildGLonDevice (paglDraw, paglContext, pdspContext, hGD, pcontextInfo, aglShareContext);
        }
    }
    
    // while we have not allocated a context or there were errors
    if ((err != noErr) || (*paglContext == 0))
    {
        err = noErr;
        DumpCurrent (paglDraw, paglContext, pdspContext, pcontextInfo); // dump what ever partial solution we might have
        *pcontextInfo = contextInfoSave; // restore info
        // now look through the devices in order
        hGD = DMGetFirstScreenDevice (true);    
        *pnumDevice = -1;
        do 
        {
            (*pnumDevice)++;
            err = BuildGLonDevice (paglDraw, paglContext, pdspContext, hGD, pcontextInfo, aglShareContext);
            if ((err != noErr) || (*paglDraw == NULL) || (*paglContext == 0))   // reset hGD only if we are not done
            {
                hGD = DMGetNextScreenDevice (hGD, true);
                DumpCurrent (paglDraw, paglContext, pdspContext, pcontextInfo); // dump what ever partial solution we might have
                *pcontextInfo = contextInfoSave; // restore info
            }
        }
        while (((err != noErr) || (*paglContext == 0)) && hGD);
    }
    return err;
}
 
// --------------------------------------------------------------------------
 
// DestroyGL
 
// Destroys drawable and context
// Ouputs: *paglDraw, *paglContext and *pdspContext should be 0 on exit
 
OSStatus DestroyGL (AGLDrawable* paglDraw, AGLContext* paglContext, DSpContextReference* pdspContext, pstructGLInfo pcontextInfo)
{
    if ((!paglContext) || (!*paglContext))
        return paramErr; // not a valid context
    glFinish ();
    DumpCurrent (paglDraw, paglContext, pdspContext, pcontextInfo);
    ShutdownDSp (); // safe to call anytime
    return noErr;
}
 
//-----------------------------------------------------------------------------------------------------------------------
 
// BuildGLFromWindow
 
// Takes window in the form of an AGLDrawable and geometry request and tries to build best context
 
// Inputs:  aglDraw: a valid AGLDrawable (i.e., a WindowPtr)
//          *pcontextInfo: request and requirements for cotext and drawable
 
// Outputs: *paglContext as allocated
//          *pcontextInfo:  allocated parameters
 
// if fail to allocate: paglContext will be NULL
// if error: will return error and paglContext will be NULL
 
OSStatus BuildGLFromWindow (WindowPtr pWindow, AGLContext* paglContext, pstructGLWindowInfo pcontextInfo, AGLContext aglShareContext)
{
    if (!pWindow)
        return paramErr;
    return BuildGLonWindow (pWindow, paglContext, pcontextInfo, aglShareContext);
}
 
// --------------------------------------------------------------------------
 
// DestroyGLFromWindow
 
// Destroys context that waas allocated with BuildGLFromWindow
// Ouputs: *paglContext should be NULL on exit
 
OSStatus DestroyGLFromWindow (AGLContext* paglContext, pstructGLWindowInfo pcontextInfo)
{
    OSStatus err;
    
    if ((!paglContext) || (!*paglContext))
        return paramErr; // not a valid context
    glFinish ();
    aglSetCurrentContext (NULL);
    err = aglReportError ();
    aglSetDrawable (*paglContext, NULL);
    err = aglReportError ();
    aglDestroyContext (*paglContext);
    err = aglReportError ();
    *paglContext = NULL;
 
    if (pcontextInfo->fmt)
    {
        aglDestroyPixelFormat (pcontextInfo->fmt); // pixel format is no longer valid
        err = aglReportError ();
    }
    pcontextInfo->fmt = 0;
    
    return err;
}
 
//-----------------------------------------------------------------------------------------------------------------------
 
// SuspendFullScreenGL
 
// Special suspend function to ensure the the GL window is hidden
 
// needs to be reviewed
 
OSStatus SuspendFullScreenGL (AGLDrawable aglDraw, AGLContext aglContext)
{
    if (aglDraw && aglContext) // will only have a drawable
    {
        glFinish (); // must do this to ensure the queue is complete
        aglSetCurrentContext (NULL);
#if TARGET_API_MAC_CARBON
        HideWindow (GetWindowFromPort (aglDraw));
#else
        HideWindow ((WindowPtr) aglDraw);
#endif
        return aglReportError ();
    }
    return noErr;
}
 
//-----------------------------------------------------------------------------------------------------------------------
 
// ResumeFullScreenGL
 
// Needs a special resume function to ensure the the GL window is shown
 
// needs to be reviewed
 
OSStatus ResumeFullScreenGL (AGLDrawable aglDraw, AGLContext aglContext)
{
    if (aglDraw && aglContext)
    {
#if TARGET_API_MAC_CARBON
        ShowWindow (GetWindowFromPort (aglDraw));
#else
        ShowWindow ((WindowPtr) aglDraw);
#endif
        aglSetCurrentContext (aglContext);
        aglUpdateContext (aglContext);
        return aglReportError ();
    }
    return paramErr;
}
 
//-----------------------------------------------------------------------------------------------------------------------
 
// PauseGL
 
// Pauses gl to allow toolbox drawing
 
OSStatus PauseGL (AGLContext aglContext)
{
    if (aglContext)
    {
        glFinish (); // must do this to ensure the queue is complete
        aglSetCurrentContext (NULL);
        return aglReportError ();
    }
    return paramErr;
}
 
//-----------------------------------------------------------------------------------------------------------------------
 
// ResumeGL
 
// resumes gl to allow gl drawing
 
OSStatus ResumeGL (AGLContext aglContext)
{
    if (aglContext)
    {
        aglSetCurrentContext (aglContext);
        aglUpdateContext (aglContext);
        return aglReportError ();
    }
    return paramErr;
}
 
// --------------------------------------------------------------------------
 
// FindGDHandleFromRect
 
// Inputs:  a global Rect
 
// Outputs: the GDHandle that that Rect is mostly on
 
// returns the number of devices that the Rect touches
short FindGDHandleFromRect (Rect * pRect, GDHandle * phgdOnThisDevice)
{
    Rect rectSect;
    long greatestArea, sectArea;
    short numDevices = 0;
    GDHandle hgdNthDevice;
    
    if (!phgdOnThisDevice)
        return NULL;
        
    *phgdOnThisDevice = NULL;
    
    hgdNthDevice = GetDeviceList ();
    greatestArea = 0;
    // check window against all gdRects in gDevice list and remember 
    //  which gdRect contains largest area of window}
    while (hgdNthDevice)
    {
        if (TestDeviceAttribute (hgdNthDevice, screenDevice))
            if (TestDeviceAttribute (hgdNthDevice, screenActive))
            {
                // The SectRect routine calculates the intersection 
                //  of the window rectangle and this gDevice 
                //  rectangle and returns TRUE if the rectangles intersect, 
                //  FALSE if they don't.
                SectRect (pRect, &(**hgdNthDevice).gdRect, &rectSect);
                // determine which screen holds greatest window area
                //  first, calculate area of rectangle on current device
                sectArea = (long) (rectSect.right - rectSect.left) * (rectSect.bottom - rectSect.top);
                if (sectArea > 0)
                    numDevices++;
                if (sectArea > greatestArea)
                {
                    greatestArea = sectArea; // set greatest area so far
                    *phgdOnThisDevice = hgdNthDevice; // set zoom device
                }
                hgdNthDevice = GetNextDevice(hgdNthDevice);
            }
    }
    return numDevices;
}
 
// --------------------------------------------------------------------------
 
// GetWindowDevice
 
// Inputs:  a valid WindowPtr
 
// Outputs: the GDHandle that that window is mostly on
 
// returns the number of devices that the windows content touches
 
short FindGDHandleFromWindow (WindowPtr pWindow, GDHandle * phgdOnThisDevice)
{
    GrafPtr pgpSave;
    Rect rectWind, rectSect;
    long greatestArea, sectArea;
    short numDevices = 0;
    GDHandle hgdNthDevice;
    
    if (!pWindow || !phgdOnThisDevice)
        return NULL;
        
    *phgdOnThisDevice = NULL;
    
    GetPort (&pgpSave);
    SetPortWindowPort (pWindow);
    
 
#if TARGET_API_MAC_CARBON
    GetWindowPortBounds (pWindow, &rectWind);
#else
    rectWind = pWindow->portRect;
#endif // TARGET_API_MAC_CARBON
    LocalToGlobal ((Point*)& rectWind.top); // convert to global coordinates
    LocalToGlobal ((Point*)& rectWind.bottom);
    hgdNthDevice = GetDeviceList ();
    greatestArea = 0;
    // check window against all gdRects in gDevice list and remember 
    //  which gdRect contains largest area of window}
    while (hgdNthDevice)
    {
        if (TestDeviceAttribute (hgdNthDevice, screenDevice))
            if (TestDeviceAttribute (hgdNthDevice, screenActive))
            {
                // The SectRect routine calculates the intersection 
                //  of the window rectangle and this gDevice 
                //  rectangle and returns TRUE if the rectangles intersect, 
                //  FALSE if they don't.
                SectRect (&rectWind, &(**hgdNthDevice).gdRect, &rectSect);
                // determine which screen holds greatest window area
                //  first, calculate area of rectangle on current device
                sectArea = (long) (rectSect.right - rectSect.left) * (rectSect.bottom - rectSect.top);
                if (sectArea > 0)
                    numDevices++;
                if (sectArea > greatestArea)
                {
                    greatestArea = sectArea; // set greatest area so far
                    *phgdOnThisDevice = hgdNthDevice; // set zoom device
                }
                hgdNthDevice = GetNextDevice(hgdNthDevice);
            }
    }
    
    SetPort (pgpSave);
    return numDevices;
}
 
//-----------------------------------------------------------------------------------------------------------------------
 
// FindDeviceNumFromRect
 
// returns the number of the device that the point is on (i.e., where it is in the search order)
// just a ultility to find the number of the device from a point
 
short FindDeviceNumFromRect (Rect * pRect)
{
    short displayNum = 0;
    GDHandle hgdNthDevice, hgdFoundDevice;
    
    FindGDHandleFromRect (pRect, &hgdFoundDevice);
    
    hgdNthDevice = DMGetFirstScreenDevice (true);
    while (hgdNthDevice)
    {
        if (hgdFoundDevice == hgdNthDevice)
            break;
        hgdNthDevice = DMGetNextScreenDevice(hgdNthDevice, true);
        displayNum++;
    }   // of WHILE
 
    return displayNum;
}