DrawSprocketTest.c

/*
    File:       DrawSprocketTest.c
    
    Description: Simple implementation of DrawSprcoket functions for testing
 
    Author:     cjd
 
    Copyright:  © Copyright 1999 Apple Computer, Inc. All rights reserved.
    
    Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
                ("Apple") in consideration of your agreement to the following terms, and your
                use, installation, modification or redistribution of this Apple software
                constitutes acceptance of these terms.  If you do not agree with these terms,
                please do not use, install, modify or redistribute this Apple software.
 
                In consideration of your agreement to abide by the following terms, and subject
                to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs
                copyrights in this original Apple software (the "Apple Software"), to use,
                reproduce, modify and redistribute the Apple Software, with or without
                modifications, in source and/or binary forms; provided that if you redistribute
                the Apple Software in its entirety and without modifications, you must retain
                this notice and the following text and disclaimers in all such redistributions of
                the Apple Software.  Neither the name, trademarks, service marks or logos of
                Apple Computer, Inc. may be used to endorse or promote products derived from the
                Apple Software without specific prior written permission from Apple.  Except as
                expressly stated in this notice, no other rights or licenses, express or implied,
                are granted by Apple herein, including but not limited to any patent rights that
                may be infringed by your derivative works or by other works in which the Apple
                Software may be incorporated.
 
                The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
                WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
                WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
                PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
                COMBINATION WITH YOUR PRODUCTS.
 
                IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
                CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
                GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
                OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
                (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
                ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
                
    Change History (most recent first):
 
      <SP33>      2/3/99    cjd     Adding DSpGetVersion test
      <SP32>     1/26/99    cjd     Removing code from GSp, overlays, scaling
      <SP31>    10/19/98    cjd     Updating so that it is buildable by MPW
 
*/
 
//¥ ------------------------------  Includes
 
#include <CodeFragments.h>
#include <Dialogs.h>
#include <Displays.h>
#include <DrawSprocket.h>
#include <Events.h>
#include <Fonts.h>
#include <MacMemory.h>
#include <MacWindows.h>
#include <Menus.h>
#include <Quickdraw.h>
#include <Resources.h>
#include <TextEdit.h>
#include <Timer.h>
 
#if defined (__MWERKS__)
//#include <Math Routines.h>
#endif
 
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
 
#if defined (__MWERKS__)
#include <sioux.h>
#else
#include <SIOW.h>
#endif
 
//¥ ------------------------------  Private Definitions
 
#define kRectSize           16
#define kDisplayWidth       640
#define kDisplayHeight      480
#define kDisplayDepth       8
 
//¥ ------------------------------  Private Types
//¥ ------------------------------  Private Variables
 
static Boolean  gVBLSync = true;
static char     gTextBuffer[512];
 
//¥ ------------------------------  Private Functions
 
void main(void);
static void ErrorMessage(char *inMessage, OSStatus inError);
static void DumpContextAttributes(DSpContextAttributes *inAttributes);
static void MyInitAttributes(DSpContextAttributes *inAttributes);
 
static void TestDrawSprocket(void);
static void TestContextIteration(void);
static void TestContextSearchingAuto(void);
static void TestContextSearchingManual(void);
static void TestContextBuffering(Boolean inUseUnderlay, Boolean inUseSingleBuffer);
static void TestContextCLUT(void);
static void TestUserSelectContext(void);
static void TestFlatContexts(void);
static void TestGetVersion(void);
 
//¥ ------------------------------  Public Variables
 
#pragma mark ##### support routines #####
 
//¥ --------------------    main
 
void
main(void)
{
OSStatus    theError;
    
#if defined (__MWERKS__)
    /* tell SIOUX to shut up */
    SIOUXSettings.autocloseonquit = true;
    SIOUXSettings.asktosaveonclose = false;
#endif
    
    /* startup DrawSprocket */
    theError = DSpStartup();
 
    if (theError)
    {
        ErrorMessage("DSpStartup()", theError);
        return;
    }
    
    /* main test menu */
    TestDrawSprocket();
    
    /* shutdown draw sprocket */
    theError = DSpShutdown();
 
    if (theError)
        ErrorMessage("DSpShutdown()", theError);
}
 
//¥ --------------------    ErrorMessage
 
void
ErrorMessage(
    char        *inMessage,
    OSStatus    inError
)
{
UInt32  theIndex;
 
static char *theErrorTexts[] =
{
    "Unknown Error",
    "kDSpNotInitializedErr",
    "kDSpSystemSWTooOldErr",
    "kDSpInvalidContextErr",
    "kDSpInvalidAttributesErr",
    "kDSpContextAlreadyReservedErr",
    "kDSpContextNotReservedErr",
    "kDSpContextNotFoundErr",
    "kDSpFrameRateNotReadyErr",
    "kDSpConfirmSwitchWarning",
    "kDSpInternalErr"
};
    
    /* convert the error code into an array index */    
    if (inError <= kDSpNotInitializedErr && inError >= kDSpInternalErr)
        theIndex = kDSpNotInitializedErr - inError + 1;
    else
        theIndex = 0;
    
    /* print the message */
    printf("# Error %d (%s) encountered: %s\n", inError, theErrorTexts[theIndex], inMessage);
}
 
//¥ --------------------    DumpContextAttributes
 
void
DumpContextAttributes(DSpContextAttributes *inAttributes)
{
    printf("\t             frequency : ");
 
    if (inAttributes->frequency)
        printf("%dhz\n", inAttributes->frequency>>16);
    else
        printf("(unknown)\n");
 
    printf("\t          displayWidth : %d\n", inAttributes->displayWidth);
    printf("\t         displayHeight : %d\n", inAttributes->displayHeight);
    
    printf("\t            colorNeeds : ");
 
    switch (inAttributes->colorNeeds)
    {
        case kDSpColorNeeds_DontCare:
            printf("kDSpColorNeeds_DontCare\n");
            break;
    
        case kDSpColorNeeds_Request:
            printf("kDSpColorNeeds_Request\n");
            break;
    
        case kDSpColorNeeds_Require:
            printf("kDSpColorNeeds_Require\n");
            break;
    
        default:
            printf(" %d (Unknown)\n", inAttributes->colorNeeds);
            break;
    }
    
    printf("\t            colorTable : 0x%X\n", inAttributes->colorTable);
    
    printf("\t        contextOptions : \n");
 
    if (inAttributes->contextOptions & kDSpContextOption_PageFlip)
        printf("\t                         kDSpContextOption_PageFlip\n");
 
    if (0 == inAttributes->contextOptions)
        printf("\t                     (no options set)\n");
        
    printf("\t   backBufferDepthMask : 0x%X\n", inAttributes->backBufferDepthMask);
    printf("\t      displayDepthMask : 0x%X\n", inAttributes->displayDepthMask);
    printf("\t   backBufferBestDepth : %d\n", inAttributes->backBufferBestDepth);
    printf("\t      displayBestDepth : %d\n", inAttributes->displayBestDepth);
    printf("\t             pageCount : %d\n", inAttributes->pageCount);
    
    printf("\t gameMustConfirmSwitch : ");
 
    if (inAttributes->gameMustConfirmSwitch)
        printf("TRUE\n");
    else
        printf("FALSE\n");
}
 
//¥ --------------------    MyInitAttributes
 
void
MyInitAttributes(DSpContextAttributes *inAttributes)
{
    if (nil == inAttributes)
        DebugStr("\pStimpy! You Idiot!");
        
    inAttributes->frequency                 = 0;
    inAttributes->displayWidth              = 0;
    inAttributes->displayHeight             = 0;
    inAttributes->reserved1                 = 0;
    inAttributes->reserved2                 = 0;
    inAttributes->colorNeeds                = 0;
    inAttributes->colorTable                = nil;
    inAttributes->contextOptions            = 0;
    inAttributes->backBufferDepthMask       = 0;
    inAttributes->displayDepthMask          = 0;
    inAttributes->backBufferBestDepth       = 0;
    inAttributes->displayBestDepth          = 0;
    inAttributes->pageCount                 = 0;
    inAttributes->gameMustConfirmSwitch     = false;
    inAttributes->reserved3[0]              = 0;
    inAttributes->reserved3[1]              = 0;
    inAttributes->reserved3[2]              = 0;
    inAttributes->reserved3[3]              = 0;
}
 
#pragma mark ##### test routines #####
 
//¥ --------------------    TestDrawSprocket
 
void
TestDrawSprocket(void)
{
Boolean theDoneFlag = false;
    
    while (false == theDoneFlag)
    {
    UInt32  theChoice;
        
        printf("\n\n############################################################\n");
        printf("DrawSprocket test application.\n");
        printf("Please select from the following tests:\n");        
        
        printf("\t 1. Exit\n");
        printf("\t 2. Get Version\n");
        printf("\t 4. Display/context iteration\n");
        printf("\t 5. Context searching (auto selection of w/h/d/d)\n");
        printf("\t 6. Context searching (you specify w/h/d/d)\n");
        printf("\t 7. Multiple-buffering/page flipping (640x480x8)\n");
        printf("\t 8. CLUT operations\n");
        printf("\t 9. DSpUserSelectContext\n");
        printf("\t10. Underlays\n");
        printf("\t14. Single Buffering\n");
 
        if (gVBLSync)
            printf("\t15. Disable VBL Sync\n");
        else
            printf("\t15. Enable VBL Sync\n");
 
        printf("\t16. Test context flattening\n");
        
        printf("\n\tSelection: ");
        gets(gTextBuffer);
        theChoice = atoi(gTextBuffer);
 
        switch (theChoice)
        {
            case 1:     //¥ Exit
                theDoneFlag = true;
                break;
                
            case 2:     //¥ Get version
                TestGetVersion();
                break;
 
            case 4:     //¥ Context iteration
                TestContextIteration();
                break;          
            
            case 5:     //¥ Auto context searching
                TestContextSearchingAuto();
                break;
 
            case 6:     //¥ Manual context searching
                TestContextSearchingManual();
                break;
                
            case 7:     //¥ Buffering
                TestContextBuffering(false, false);
                break;
            
            case 8:     //¥ CLUT
                TestContextCLUT();
                break;
            
            case 9:     //¥ User select context
                TestUserSelectContext();
                break;
                        
            case 10:    //¥ Underlay
                TestContextBuffering(true, false);
                break;
            
            case 14:    //¥ Single buffer
                TestContextBuffering(false, true);
                break;
                
            case 15:    //¥ Toggle sync
                gVBLSync ^= 1;
                break;
            
            case 16:    //¥ Context flattening
                TestFlatContexts();
                break;
 
            default:    //¥ Unknown
                printf("\nhuh?\n");
                break;          
        }
        
        printf("\nThank you, please drive through.\n");
    }
}
 
//¥ --------------------    TestContextInteration
 
void
TestContextIteration(void)
{
GDHandle    theGDevice;
OSStatus    theError;
    
    printf("\n\n");
    printf("############################################################\n");
    printf("###          Testing Display/Context Iteration           ###\n");
    printf("############################################################\n");
    printf("\n\n");
 
    /*
    ** Walk the list of display devices in the system.  DrawSprocket is
    ** centered around the DisplayIDType, which is used by the Display
    ** Manager.  The GDevice records are going to be in flux with future
    ** versions of the system software, so it is best to make the change
    ** now and make your software DisplayManager-centric.
    */
    theGDevice = DMGetFirstScreenDevice(false);
 
    while (theGDevice)
    {
    DisplayIDType       theDisplayID;
    DSpContextReference theContext;
    UInt32              theContextIndex = 0;
        
        /* get the display ID */
        theError = DMGetDisplayIDByGDevice(theGDevice, &theDisplayID, false);
 
        if (theError)
        {
            ErrorMessage("DMGetDisplayIDByGDevice", theError);
            return;
        }
        
        /* walk the list of contexts for this display */
        theError = DSpGetFirstContext(theDisplayID, &theContext);
 
        while (noErr == theError)
        {
        DSpContextAttributes    theAttributes;
            
            /* obtain the context attributes */
            theError = DSpContext_GetAttributes(theContext, &theAttributes);
 
            if (theError)
            {
                ErrorMessage("DSpContext_GetAttributes", theError);
                return;
            }
            
            /* display the attributes structure */
            printf("Display %d, Context %d capabilities:\n", theDisplayID, theContextIndex++);
 
            DumpContextAttributes(&theAttributes);
 
            /*
            ** get the next context.  when a kDSpContextNotFoundErr code
            ** is returned we have hit the end of the context list.  if
            ** a different error code is returned, something went wrong.
            */
            theError = DSpGetNextContext(theContext, &theContext);
 
            if (theError && (kDSpContextNotFoundErr != theError))
            {
                ErrorMessage("DSpGetNextContext", theError);
                return;
            }
        }
 
        /* next device */
        theGDevice = DMGetNextScreenDevice(theGDevice, false);
    }   
}
 
//¥ --------------------    TestContextSearchingAuto
 
void
TestContextSearchingAuto(void)
{
DSpContextAttributes    theDesiredAttributes;
DSpContextReference     theContext;
OSStatus                theError;
    
    printf("\n\n");
    printf("############################################################\n");
    printf("###          Testing Display/Context Searching           ###\n");
    printf("############################################################\n");
    printf("\n\n");
 
    /*
    ** Find a 640x480x8 mode
    **
    ** First, init the attributes so that there will be no garbage in any
    ** of the fields (and which will cause DS to return an error).  This
    ** is especially important for fields that DS can't check, such as
    ** the colorTable.  If the colorTable is bogus when a call to reserve
    ** a context is made, then it will try to use the bogus color table
    ** (causing unpleasant visits to the debugger).
    **
    ** When using DSpFindBestContext(), the attributes are interpreted as
    ** *REQUIREMENTS*, unlike when you call DSpContext_Reserve(), when
    ** they are interpreted as *REQUESTS*.  If I specify a contextOption
    ** of kDSpContextOption_PageFlip here, only contexts with the ability
    ** to page flip will be considered in the search, however if I specify
    ** the same option when reserving the context, and page flipping is not
    ** available, then DS will use software double/triple buffering.  This
    ** is so to make it easy for the game to always request that DS use
    ** page flipping if it is there (at reservation time), but still continue
    ** if it is not.  Conversely, the game can search for only contexts
    ** that meet a specific criteria, and know that any matches found will
    ** meet those specifications.
    */
    MyInitAttributes(&theDesiredAttributes);
 
    theDesiredAttributes.displayWidth           = 640;
    theDesiredAttributes.displayHeight          = 480;
    theDesiredAttributes.colorNeeds             = kDSpColorNeeds_Require;
    theDesiredAttributes.backBufferDepthMask    = kDSpDepthMask_8;
    theDesiredAttributes.displayDepthMask       = kDSpDepthMask_8;
    theDesiredAttributes.backBufferBestDepth    = kDisplayDepth;
    theDesiredAttributes.displayBestDepth       = kDisplayDepth;
    theDesiredAttributes.pageCount              = 2;
 
    theError = DSpFindBestContext(&theDesiredAttributes, &theContext);
 
    if (theError && kDSpContextNotFoundErr != theError)
    {
        ErrorMessage("DSpFindBestContext 640x480x8", theError);
        return;
    }
 
    if (kDSpContextNotFoundErr == theError)
    {
        printf("# Unable to find a matching context for the following attributes:\n");
        DumpContextAttributes(&theDesiredAttributes);
        return;
    }
    else
    {
    DSpContextAttributes    theActualAttributes;
    DisplayIDType           theDisplayID;
        
        /* get the actual attributes for the context */
        theError = DSpContext_GetAttributes(theContext, &theActualAttributes);
 
        if (theError)
        {
            ErrorMessage("DSpContext_GetAttributes", theError);
            return;
        }
 
        /* get the display id for the context */
        theError = DSpContext_GetDisplayID(theContext, &theDisplayID);
 
        if (theError)
        {
            ErrorMessage("DSpContext_GetDisplayID", theError);
            return;
        }
 
        /* tell the user about the results */
        printf("Best matching Context for the following attributes...\n");
        DumpContextAttributes(&theDesiredAttributes);
 
        printf("...is the context on display id %d with these attributes:\n", theDisplayID);
 
        DumpContextAttributes(&theActualAttributes);
    }
}
 
//¥ --------------------    TestContextSearchingManual
 
void
TestContextSearchingManual(void)
{
DSpContextAttributes    theDesiredAttributes;
DSpContextReference     theContext;
OSStatus                theError;
    
    printf("\n\n");
    printf("############################################################\n");
    printf("###          Testing Display/Context Searching           ###\n");
    printf("############################################################\n");
    printf("\n\n");
 
    /* get the context attributes */
    MyInitAttributes(&theDesiredAttributes);
    theDesiredAttributes.pageCount              = 2;
 
    printf("enter the display width: ");
    gets(gTextBuffer);
    theDesiredAttributes.displayWidth           = atoi(gTextBuffer);
 
    if (0 == theDesiredAttributes.displayWidth)
    {
        printf("bogus value!\n");
        return;
    }
    
    printf("enter the display height: ");
    gets(gTextBuffer);
    theDesiredAttributes.displayHeight          = atoi(gTextBuffer);
 
    if (0 == theDesiredAttributes.displayHeight)
    {
        printf("bogus value!\n");
        return;
    }
    
    printf("enter the back buffer best depth: ");
    gets(gTextBuffer);
    theDesiredAttributes.backBufferBestDepth    = atoi(gTextBuffer);
 
    if (0 == theDesiredAttributes.backBufferBestDepth)
    {
        printf("bogus value!\n");
        return;
    }
    
    printf("enter the display best depth: ");
    gets(gTextBuffer);
    theDesiredAttributes.displayBestDepth       = atoi(gTextBuffer);
 
    if (0 == theDesiredAttributes.displayBestDepth)
    {
        printf("bogus value!\n");
        return;
    }
    
    theDesiredAttributes.colorNeeds             = kDSpColorNeeds_Require;
    theDesiredAttributes.backBufferDepthMask    = theDesiredAttributes.backBufferBestDepth;
    theDesiredAttributes.displayDepthMask       = theDesiredAttributes.displayBestDepth;
 
    /* find the context */
    theError = DSpFindBestContext(&theDesiredAttributes, &theContext);
 
    if (theError && kDSpContextNotFoundErr != theError)
    {
        ErrorMessage("DSpFindBestContext (Manual)", theError);
        return;
    }
 
    if (kDSpContextNotFoundErr == theError)
    {
        printf("# Unable to find a matching context for the following attributes:\n");
        DumpContextAttributes(&theDesiredAttributes);
        return;
    }
    else
    {
    DSpContextAttributes    theActualAttributes;
    DisplayIDType           theDisplayID;
        
        /* get the actual attributes for the context */
        theError = DSpContext_GetAttributes(theContext, &theActualAttributes);
 
        if (theError)
        {
            ErrorMessage("DSpContext_GetAttributes", theError);
            return;
        }
 
        /* get the display id for the context */
        theError = DSpContext_GetDisplayID(theContext, &theDisplayID);
 
        if (theError)
        {
            ErrorMessage("DSpContext_GetDisplayID", theError);
            return;
        }
 
        /* tell the user about the results */
        printf("Best matching Context for the following attributes...\n");
        DumpContextAttributes(&theDesiredAttributes);
 
        printf("...is the context on display id %d with these attributes:\n", theDisplayID);
        DumpContextAttributes(&theActualAttributes);
    }
}
 
//¥ --------------------    TestContextBuffering
 
void
TestContextBuffering(
    Boolean inUseUnderlay,
    Boolean inUseSingleBuffer
)
{
DSpContextAttributes    theDesiredAttributes;
DSpContextReference     theContext;
OSStatus                theError;
DSpAltBufferReference   theUnderlay;
UInt32                  theDisplayWidth, theDisplayHeight;
CGrafPtr                theAltBufferPort;
GDHandle                theAltBufferGDevice, theOldGDevice;
GrafPtr                 theOldPort;
    
    printf("\n\n");
    printf("############################################################\n");
    printf("###                  Testing Buffering                   ###\n");
    printf("############################################################\n");
    printf("\n\n");
 
 
    /* find the context */
    MyInitAttributes(&theDesiredAttributes);
 
    theDesiredAttributes.displayWidth           = kDisplayWidth;
    theDesiredAttributes.displayHeight          = kDisplayHeight;
    theDesiredAttributes.colorNeeds             = kDSpColorNeeds_Require;
    theDesiredAttributes.backBufferDepthMask    = kDSpDepthMask_8;
    theDesiredAttributes.displayDepthMask       = kDSpDepthMask_8;
    theDesiredAttributes.backBufferBestDepth    = kDisplayDepth;
    theDesiredAttributes.displayBestDepth       = kDisplayDepth;
    theDesiredAttributes.pageCount              = 2;
 
    if (inUseSingleBuffer)
        theDesiredAttributes.pageCount          = 1;
 
    theError = DSpFindBestContext(&theDesiredAttributes, &theContext);
 
    if (theError && kDSpContextNotFoundErr != theError)
    {
        ErrorMessage("DSpFindBestContext", theError);
        return;
    }
 
    if (kDSpContextNotFoundErr == theError)
    {
        printf("# Unable to find a matching context for the following attributes:\n");
        DumpContextAttributes(&theDesiredAttributes);
        return;
    }
 
    theDisplayWidth = kDisplayWidth;
    theDisplayHeight = kDisplayHeight;
    
    /*
    ** Here is where I need to OR in the value to use page flipping.  If
    ** I had used the value when calling DSpFindBestContext() then it would
    ** have only considered displays that have page flipping hardware, but
    ** I want to run with software buffering too.
    */
    theDesiredAttributes.contextOptions |= kDSpContextOption_PageFlip;
 
    /*
    ** If page flipping isn't available, then software buffering will
    ** be used.  DS can use double or triple buffering depending on the
    ** state of the kDSpContextOption_TripleBuffer option bit.
    **
    ** If you only want double buffering, perhaps because of memory
    ** constraints then you should turn off the option bit.
    **
    ** If you leave the bit on and there is hardware page flipping
    ** available (and you haven't turned off that option bit), but
    ** there are only 2 video pages (not three) then DS will consider
    ** page flipping to be more important of an option than triple
    ** buffering and will drop you down to 2 VRAM pages (leaving you
    ** still page flipping).
    **
    */
 
    //¥ theDesiredAttributes.contextOptions &= ~kDSpContextOption_TripleBuffer;
 
    //¥ set vbl sync
    if (false == gVBLSync)
        theDesiredAttributes.contextOptions |= kDSpContextOption_DontSyncVBL;
            
    //¥ reset page count
    theDesiredAttributes.pageCount              = 2;
    if (inUseSingleBuffer)
        theDesiredAttributes.pageCount          = 1;
        
    /* reserve the context */   
    theError = DSpContext_Reserve(theContext, &theDesiredAttributes);
 
    if (theError)
    {
        ErrorMessage("DSpContext_Reserve", theError);
        return;
    }
        
    /*
    ** If you are in a debug cycle, you may want to enable debugging mode
    ** in DrawSprocket so that a fade out, followed by a break in the
    ** debugger, won't leave you with nothing to see.
    **
    ** You can also create a folder in same folder as your game, and name
    ** it "DSpSetDebugMode", this will also cause DSp to enter debug mode.
    ** This method is handy if you don't want to rebuild your game with
    ** the call just to debug it.
    */
 
    //¥ DSpSetDebugMode(true);
    
    /*
    ** fade out all displays to black, you must have at least one reserved
    ** context to do this or you will get an error.  A game should always
    ** fade to black before activating a context because if the activation
    ** causes a resolution change the user will see a very ugly twitch in
    ** the display.
    */
 
    theError = DSpContext_FadeGammaOut(nil, nil);
 
    if (theError)
    {
        ErrorMessage("DSpContext_FadeGammaOut", theError);
        DSpContext_FadeGammaIn(nil, nil);
        DSpContext_Release(theContext);
 
        return;
    }
    
    /* put the context into the active state */
    theError = DSpContext_SetState(theContext, kDSpContextState_Active);
 
    if (theError)
    {
        ErrorMessage("DSpContext_SetState", theError);
        DSpContext_FadeGammaIn(nil, nil);
        DSpContext_Release(theContext);
 
        return;
    }
 
    /* fade back in */
    theError = DSpContext_FadeGammaIn(nil, nil);
 
    if (theError)
    {
        ErrorMessage("DSpContext_FadeGammaIn", theError);
        DSpContext_Release(theContext);
 
        return;
    }
    
    /*
    ** allocate an alt buffer that will be used for the underlay.  An
    ** underlay is useful in games that have a need to restore from a
    ** static background.
    */
 
    theError = DSpAltBuffer_New(theContext, false, 0, &theUnderlay);
 
    if (theError)
    {
        DSpContext_Release(theContext);
        ErrorMessage("DSpAltBuffer_New (underlay)", theError);
 
        return;
    }
    
    theError = DSpContext_SetUnderlayAltBuffer(theContext, theUnderlay);
 
    if (theError)
    {
        DSpContext_Release(theContext);
        ErrorMessage("DSpContext_SetUnderlayAltBuffer", theError);
 
        return;
    }
 
    //¥ do the double buffering
    {   
    UInt32  theColorIndex, theXPos, theYPos;
    Rect    theRect;
        
        GetPort(&theOldPort);
        theOldGDevice = GetGDevice();
        
        /* set the alt buffer to be the current port */
        theError = DSpAltBuffer_GetCGrafPtr(theUnderlay, kDSpBufferKind_Normal, &theAltBufferPort, &theAltBufferGDevice);
 
        if (theError)
        {
            ErrorMessage("DSpAltBuffer_GetCGrafPtr (underlay)", theError);
            DSpContext_FadeGammaIn(nil, nil);
            DSpContext_Release(theContext);
 
            return;
        }
        
        /*
        ** if you want to use QuickDraw or any other toolbox rendering
        ** code, you must setup the GDevice as well as the port when
        ** working with AltBuffers!
        */
 
        SetPort((GrafPtr)theAltBufferPort);
        SetGDevice(theAltBufferGDevice);
 
        /*
        ** fill the underlay buffer with a pattern or clear it
        */
 
        if (inUseUnderlay)
        {
            /*
            ** draw a pattern into the underlay buffer. this pattern will be
            ** restored every time I get the back buffer
            */
            theColorIndex = 0;
 
            for (theYPos = 0; theYPos < theDesiredAttributes.displayHeight; theYPos += kRectSize)
            {
                for(theXPos = 0; theXPos < theDesiredAttributes.displayWidth; theXPos += kRectSize)
                {
                    SetRect(&theRect, 0, 0, kRectSize, kRectSize);
                    OffsetRect(&theRect, theXPos, theYPos);
                    theAltBufferPort->fgColor = theColorIndex % 255;
                    
                    PaintRect(&theRect);
 
                    theColorIndex++;            
                }
            }
        }
        else
        {
            ForeColor(blackColor);
            BackColor(whiteColor);
            SetRect(&theRect, 0, 0, theDesiredAttributes.displayWidth, theDesiredAttributes.displayHeight);
            EraseRect(&theRect);
        }
            
        SetPort(theOldPort);
        SetGDevice(theOldGDevice);
    }
        
    /*
    ** this test slides a black vertical bar left and right on the
    ** display, producing an image where any tearing is amplified.
    */
 
    {
    RGBColor    theColor, theTextColor;
    Rect        theRectangleRect, theRect;
    UInt32      theStartTick, theMaxTick, theCurrentTick;
    UInt32      theCount, theRectangleDelta;
    UInt32      theWidth, theXPos, theYPos;
    CGrafPtr    theBackBuffer;
    UInt32      theXSquares, theYSquares;
    
    theXSquares = theDesiredAttributes.displayWidth / kRectSize;
    theYSquares = theDesiredAttributes.displayHeight / kRectSize;
    
    theColor.red = 0;
    theColor.green = 0;
    theColor.blue = 0;
    theTextColor.red = 0xFFFF;
    theTextColor.green = 0xFFFF;
    theTextColor.blue = 0xFFFF;
 
    /* rectangle will be 1/8 the display width, and all of the height */
    SetRect(&theRectangleRect, 0, 0, theDesiredAttributes.displayWidth >> 3,
        theDesiredAttributes.displayHeight);
    
    theStartTick = theCurrentTick = TickCount();
    theMaxTick = theStartTick + (15 * 60);
    theCount = 0;
    theRectangleDelta = 3;
 
    while (theCurrentTick < theMaxTick)
    {
        /*
        ** every so often change the color of one of the squares
        ** in the underlay
        */
 
        if ((theCurrentTick % 10) == 0)
        {
        RGBColor    theNewColor;
        UInt32      theX, theY;
        Rect        theRect;
            
            SetPort((GrafPtr)theAltBufferPort);
            SetGDevice(theAltBufferGDevice);
            
            theNewColor.red = Random();
            theNewColor.green = Random();
            theNewColor.blue = Random();
            
            theX = Random() % theXSquares;
            theY = Random() % theYSquares;
            
            theRect.left = theX * kRectSize;
            theRect.right = theRect.left + kRectSize;
            theRect.top = theY * kRectSize;
            theRect.bottom = theRect.top + kRectSize;
            
            RGBForeColor(&theNewColor);
            PaintRect(&theRect);
            DSpAltBuffer_InvalRect(theUnderlay, &theRect);
        }
        
        /* get the back buffer */
        theError = DSpContext_GetBackBuffer(theContext, kDSpBufferKind_Normal, &theBackBuffer);
 
        if (theError)
        {
            ErrorMessage("DSpContext_GetBackBuffer", theError);
            DSpContext_Release(theContext);
 
            return;
        }
        
        /* set the back buffer to be the current port */
        SetPort((GrafPtr)theBackBuffer);
        
        /* fill the display with a sliding vertical rectangle */
        RGBForeColor(&theColor);        
        PaintRect(&theRectangleRect);
 
        /* inval the new rect position */       
        theError = DSpContext_InvalBackBufferRect(theContext, &theRectangleRect);
            
        if ((theRectangleRect.right + theRectangleDelta) > theDesiredAttributes.displayWidth)
            theRectangleDelta = -theRectangleDelta;
        
        if ((theRectangleRect.left + theRectangleDelta) < 0)
            theRectangleDelta = -theRectangleDelta;
 
        theRectangleRect.left += theRectangleDelta;
        theRectangleRect.right += theRectangleDelta;
 
        // draw a frame counter
        sprintf((char *)&gTextBuffer[1],
            "This is frame %d (%d ticks remaining, %.1f fps)",
            theCount + 1, theMaxTick - theCurrentTick,
            (float)(theCount / ((theCurrentTick - theStartTick) / 60.0)));
 
        gTextBuffer[0] = strlen((char *)&gTextBuffer[1]);
        theWidth = StringWidth((ConstStr255Param)gTextBuffer);
        theXPos = (theDisplayWidth >> 1) - (theWidth >> 1);
        theYPos = theDisplayHeight - 20;
 
        theTextColor.red = 0;
        theTextColor.green = 0;
        theTextColor.blue = 0;
        RGBForeColor(&theTextColor);
        SetRect(&theRect, theXPos - 10, theYPos - 20, theXPos + theWidth + 10, theYPos + 10);
        PaintRect(&theRect);
        
        theError = DSpContext_InvalBackBufferRect(theContext, &theRect);
 
        theTextColor.red = 0;
        theTextColor.green = 0xFFFF;
        theTextColor.blue = 0xFFFF;
        RGBForeColor(&theTextColor);
        FrameRect(&theRect);
        
        MoveTo(theXPos, theYPos);
        DrawString((ConstStr255Param)gTextBuffer);
        
        // swap the buffers
        theError = DSpContext_SwapBuffers(theContext, nil, 0);
 
        if (theError)
        {
            ErrorMessage("DSpContext_SwapBuffers", theError);
            DSpContext_Release(theContext);
            return;
        }
        
        /* increase frame counter */
        theCount++;
        theCurrentTick = TickCount();
    }
 
    }
    
    /*
    ****************************************************************************
    ** cleanup
    ****************************************************************************
    */  
 
    if (inUseUnderlay)
    {
        SetPort(theOldPort);
        SetGDevice(theOldGDevice);
    }
    
    /* fade to black */
    theError = DSpContext_FadeGammaOut(nil, nil);
 
    if (theError)
    {
        ErrorMessage("DSpContext_FadeGammaIn", theError);
        DSpContext_Release(theContext);
        return;
    }
 
    /* remove the underlay & release it */
    DSpContext_SetUnderlayAltBuffer(theContext, nil);
    DSpAltBuffer_Dispose(theUnderlay);
    theUnderlay = nil;
    
    /* put the context into the inactive state */
    theError = DSpContext_SetState(theContext, kDSpContextState_Inactive);
 
    if (theError)
    {
        ErrorMessage("DSpContext_SetState", theError);
        DSpContext_FadeGammaIn(nil, nil);
        DSpContext_Release(theContext);
 
        return;
    }
 
    /* fade back in */
    theError = DSpContext_FadeGammaIn(nil, nil);
 
    if (theError)
    {
        ErrorMessage("DSpContext_FadeGammaIn", theError);
        DSpContext_Release(theContext);
 
        return;
    }
    
    /* release the context */
    theError = DSpContext_Release(theContext);
 
    if (theError)
    {
        ErrorMessage("DSpContext_Release", theError);
        return;
    }
}
 
//¥ --------------------    TextContextCLUT
 
void
TestContextCLUT(void)
{
DSpContextAttributes    theDesiredAttributes;
DSpContextReference     theContext;
OSStatus                theError;
RGBColor                theBlankingColor;
    
    printf("\n\n");
    printf("############################################################\n");
    printf("###                     Testing CLUT                     ###\n");
    printf("############################################################\n");
    printf("\n\n");
 
 
    /* find the context */
    MyInitAttributes(&theDesiredAttributes);
 
    theDesiredAttributes.displayWidth           = kDisplayWidth;
    theDesiredAttributes.displayHeight          = kDisplayHeight;
    theDesiredAttributes.colorNeeds             = kDSpColorNeeds_Require;
    theDesiredAttributes.backBufferDepthMask    = kDSpDepthMask_8;
    theDesiredAttributes.displayDepthMask       = kDSpDepthMask_8;
    theDesiredAttributes.backBufferBestDepth    = kDisplayDepth;
    theDesiredAttributes.displayBestDepth       = kDisplayDepth;
    theDesiredAttributes.pageCount              = 2;
 
    theError = DSpFindBestContext(&theDesiredAttributes, &theContext);
 
    if (theError && kDSpContextNotFoundErr != theError)
    {
        ErrorMessage("DSpFindBestContext", theError);
        return;
    }
 
    if (kDSpContextNotFoundErr == theError)
    {
        printf("# Unable to find a matching context for the following attributes:\n");
        DumpContextAttributes(&theDesiredAttributes);
 
        return;
    }
 
    /* reserve the context */   
    theError = DSpContext_Reserve(theContext, &theDesiredAttributes);
 
    if (theError)
    {
        ErrorMessage("DSpContext_Reserve", theError);
        return;
    }
 
    /* set the blanking color to white, just for the heck of it */
    theBlankingColor.red = 0xFFFFF; 
    theBlankingColor.green = 0xFFFFF;   
    theBlankingColor.blue = 0xFFFFF;    
 
    theError = DSpSetBlankingColor(&theBlankingColor);
 
    if (theError)
    {
        ErrorMessage("DSpSetBlankingColor", theError);
        return;
    }
 
    /* fade out all displays to black */
    theError = DSpContext_FadeGammaOut(nil, nil);
 
    if (theError)
    {
        ErrorMessage("DSpContext_FadeGammaOut", theError);
        DSpContext_FadeGammaIn(nil, nil);
        DSpContext_Release(theContext);
 
        return;
    }
    
    /* put the context into the active state */
    theError = DSpContext_SetState(theContext, kDSpContextState_Active);
 
    if (theError)
    {
        ErrorMessage("DSpContext_SetState", theError);
        DSpContext_FadeGammaIn(nil, nil);
        DSpContext_Release(theContext);
 
        return;
    }
 
    /* fade back in */
    theError = DSpContext_FadeGammaIn(nil, nil);
 
    if (theError)
    {
        ErrorMessage("DSpContext_FadeGammaIn", theError);
        DSpContext_Release(theContext);
 
        return;
    }
    
    /*
    ****************************************************************************
    ** do the CLUT testing
    ****************************************************************************
    */
    
    /* draw a pattern into the back buffer and cycle it */
    {
    UInt32      thePosition, theColorIndex, theXPos, theYPos;
    CGrafPtr    theBackBuffer;
    ColorSpec   theOriginalColors[256];
    
    /* get the back buffer */
    theError = DSpContext_GetBackBuffer(theContext, kDSpBufferKind_Normal, &theBackBuffer);
 
    if (theError)
    {
        ErrorMessage("DSpContext_GetBackBuffer", theError);
        DSpContext_Release(theContext);
 
        return;
    }
    
    /* set the back buffer to be the current port */
    SetPort((GrafPtr)theBackBuffer);
 
    /* draw a pattern into the back buffer */
    theColorIndex = 0;
 
    for(theYPos = 0; theYPos < theDesiredAttributes.displayHeight; theYPos += kRectSize)
    {
        for(theXPos = 0; theXPos < theDesiredAttributes.displayWidth; theXPos += kRectSize)
        {
        Rect    theRect;
            
            SetRect(&theRect, 0, 0, kRectSize, kRectSize);
            OffsetRect(&theRect, theXPos, theYPos);
            theBackBuffer->fgColor = theColorIndex % 255;
            
            PaintRect(&theRect);
 
            theColorIndex++;            
        }
    }
    
    /* bring the back buffer to the front */
    theError = DSpContext_SwapBuffers(theContext, nil, 0);
 
    if (theError)
    {
        ErrorMessage("DSpContext_SwapBuffers", theError);
        DSpContext_Release(theContext);
 
        return;
    }
    
    /*
    ** get the original color table
    **
    ** unfortunately, these probably aren't the real colors, since
    ** the way that gamma works on the Mac is to remap the indexed
    ** color table to new colors.  In other words, these colors are
    ** gamma corrected versions of the ones that were originally set.
    **
    */
 
    theError = DSpContext_GetCLUTEntries(theContext, theOriginalColors, 0, 255);
 
    if (theError)
    {
        ErrorMessage("DSpContext_GetCLUTEntries", theError);
        DSpContext_Release(theContext);
 
        return;
    }
    
    /* cycle the colors */
    while (! Button())
    {
    ColorSpec   theSpec[256], theTempSpec;
        
        /* rotate them */
        theTempSpec = theOriginalColors[0];
        
        for(theColorIndex = 0; theColorIndex < 255; theColorIndex++)
            theSpec[theColorIndex].rgb = theOriginalColors[(theColorIndex + thePosition) % 255].rgb;
 
        theSpec[255].rgb = theTempSpec.rgb;
        
        thePosition++;
 
        if (thePosition > 255)
            thePosition = 0;
        
        /* set the entries */
        theError = DSpContext_SetCLUTEntries(theContext, theSpec, 0, 255);
 
        if (theError)
        {
            ErrorMessage("DSpContext_SetCLUTEntries", theError);
            DSpContext_Release(theContext);
 
            return;
        }
    }
 
    }
    
    /*
    ****************************************************************************
    ** cleanup
    ****************************************************************************
    */  
 
    /* fade to black */
    theError = DSpContext_FadeGammaOut(nil, nil);
 
    if (theError)
    {
        ErrorMessage("DSpContext_FadeGammaIn", theError);
        DSpContext_Release(theContext);
 
        return;
    }
 
    /* put the context into the inactive state */
    theError = DSpContext_SetState(theContext, kDSpContextState_Inactive);
 
    if (theError)
    {
        ErrorMessage("DSpContext_SetState", theError);
        DSpContext_FadeGammaIn(nil, nil);
        DSpContext_Release(theContext);
 
        return;
    }
 
    /* fade back in */
    theError = DSpContext_FadeGammaIn(nil, nil);
 
    if (theError)
    {
        ErrorMessage("DSpContext_FadeGammaIn", theError);
        DSpContext_Release(theContext);
 
        return;
    }
    
    /* release the context */
    theError = DSpContext_Release(theContext);
 
    if (theError)
    {
        ErrorMessage("DSpContext_Release", theError);
        return;
    }
}
 
//¥ --------------------    TestUserSelectContext
 
void
TestUserSelectContext(void)
{
DSpContextAttributes    theDesiredAttributes;
DSpContextAttributes    theActualAttributes;
DSpContextReference     theContext;
OSStatus                theError;
Boolean                 theShowDialogFlag;
DisplayIDType           theDisplayID;
    
    printf("\n\n");
    printf("############################################################\n");
    printf("###                     Testing CLUT                     ###\n");
    printf("############################################################\n");
    printf("\n\n");
 
    /* find the context */
    MyInitAttributes(&theDesiredAttributes);
 
    theDesiredAttributes.displayWidth           = kDisplayWidth;
    theDesiredAttributes.displayHeight          = kDisplayHeight;
    theDesiredAttributes.colorNeeds             = kDSpColorNeeds_Require;
    theDesiredAttributes.backBufferDepthMask    = kDSpDepthMask_8;
    theDesiredAttributes.displayDepthMask       = kDSpDepthMask_8;
    theDesiredAttributes.backBufferBestDepth    = kDisplayDepth;
    theDesiredAttributes.displayBestDepth       = kDisplayDepth;
    theDesiredAttributes.pageCount              = 2;
 
    /*
    ** see if there are enough choices available for the user to
    ** choose from, this can be used if I needed to determine
    ** whether or not to enable a menu item, etc.
    */
    theError = DSpCanUserSelectContext(&theDesiredAttributes, &theShowDialogFlag);
 
    if (theError)
    {
        ErrorMessage("DSpCanUserSelectContext", theError);
        return;
    }
 
    if (false == theShowDialogFlag)
    {
        printf("There are not enough choices to warrant asking the user.\n");
        return;
    }
    
    /*
    ** put up the choice dialog, I don't care about what display it
    ** appears on, nor do I care to know about update events
    */
    theError = DSpUserSelectContext(&theDesiredAttributes, 0, nil, &theContext);
 
    if (theError)
    {
        /*
        ** since we know that there were possible matches available (because
        ** we called DSpCanUserSelectContext), we know that a context-not-found
        ** error means that the user canceled the dialog
        */
        if (kDSpContextNotFoundErr == theError)
            printf("The user canceled the dialog.\n");
        else
            ErrorMessage("DSpUserSelectContext", theError);
 
        return;
    }
 
    /* tell the user about the results */
    printf("User-Selected Context for the following attributes...\n");
    DumpContextAttributes(&theDesiredAttributes);
    
    theError = DSpContext_GetDisplayID(theContext, &theDisplayID);
 
    if (theError)
    {
        ErrorMessage("DSpContext_GetDisplayID", theError);
        return;
    }
 
    theError = DSpContext_GetAttributes(theContext, &theActualAttributes);
 
    if (theError)
    {
        ErrorMessage("DSpContext_GetAttributes", theError);
        return;
    }
 
    printf("...is the context on display id %d with these attributes:\n", theDisplayID);
    DumpContextAttributes(&theActualAttributes);
}
 
//¥ --------------------    TestFlatContexts
 
void
TestFlatContexts(void)
{
DSpContextAttributes    theDesiredAttributes;
DSpContextReference     theContext, theRestoredContext;
OSStatus                theError;
UInt32                  theFlatContextSize;
Ptr                     theFlatContextBuffer = nil;
    
    printf("\n\n");
    printf("############################################################\n");
    printf("###                 Testing Flat Contexts                ###\n");
    printf("############################################################\n");
    printf("\n\n");
 
 
    /* find the context */
    MyInitAttributes(&theDesiredAttributes);
 
    theDesiredAttributes.displayWidth           = kDisplayWidth;
    theDesiredAttributes.displayHeight          = kDisplayHeight;
    theDesiredAttributes.colorNeeds             = kDSpColorNeeds_Require;
    theDesiredAttributes.backBufferDepthMask    = kDSpDepthMask_8;
    theDesiredAttributes.displayDepthMask       = kDSpDepthMask_8;
    theDesiredAttributes.backBufferBestDepth    = kDisplayDepth;
    theDesiredAttributes.displayBestDepth       = kDisplayDepth;
    theDesiredAttributes.pageCount              = 2;
 
    theError = DSpFindBestContext(&theDesiredAttributes, &theContext);
 
    if (theError && kDSpContextNotFoundErr != theError)
    {
        ErrorMessage("DSpFindBestContext", theError);
        return;
    }
 
    if (kDSpContextNotFoundErr == theError)
    {
        printf("# Unable to find a matching context for the following attributes:\n");
        DumpContextAttributes(&theDesiredAttributes);
 
        return;
    }
    
    /* get the flattened size & allocate space for it */
    theError = DSpContext_GetFlattenedSize(theContext, &theFlatContextSize);
 
    if (theError)
    {
        ErrorMessage("DSpContext_GetFlattenedSize", theError);
        return;
    }
 
    theFlatContextBuffer = NewPtr(theFlatContextSize);
 
    if (nil == theFlatContextBuffer)
    {
        ErrorMessage("not enough memory to allocate flat context", 0);
        return;
    }
    
    /* flatten the context */
    theError = DSpContext_Flatten(theContext, theFlatContextBuffer);
 
    if (theError)
        ErrorMessage("DSpContext_Flatten", theError);
    
    /* restore the context from the flattened version */
    if (noErr == theError)
    {
        theError = DSpContext_Restore(theFlatContextBuffer, &theRestoredContext);
 
        if (theError)
            ErrorMessage("DSpContext_Flatten", theError);
 
        /* same context? */
        if (theRestoredContext == theContext)
            printf("restored context successfully\n");
        else
            printf("Error: restored context didn't match!\n");
        
    }
    
    /* cleanup */
    DisposePtr(theFlatContextBuffer);
}
 
//¥ --------------------    TestGetVersion
 
static void
TestGetVersion(void)
{
NumVersion  theVersion;
char        buffer[128];
 
    printf("\n\n");
    printf("############################################################\n");
    printf("###                 Testing DSpGetVersion                ###\n");
    printf("############################################################\n");
    printf("\n\n");
 
    if ((Ptr) kUnresolvedCFragSymbolAddress == (Ptr) DSpGetVersion)
    {
        printf("Error: DSpGetVersion not present.  Your DrawSprocket is too old.");
        return;
    }
    
    theVersion = DSpGetVersion();
 
    sprintf(buffer, "DrawSprocket v%d.%d.%d",
        theVersion.majorRev,
        theVersion.minorAndBugRev >> 4,
        theVersion.minorAndBugRev & 0x0F);
 
    switch (theVersion.stage)
    {
        case 0x20:
            sprintf(buffer, "%s development %d\n\n", buffer, theVersion.nonRelRev);
            break;
            
        case 0x40:
            sprintf(buffer, "%s alpha %d\n\n", buffer, theVersion.nonRelRev);
            break;
            
        case 0x60:
            sprintf(buffer, "%s beta %d\n\n", buffer, theVersion.nonRelRev);
            break;
            
        case 0x80:
            sprintf(buffer, "%s final %d\n\n", buffer, theVersion.nonRelRev);
            break;
            
    }
 
    printf(buffer);
}