Snapshot.c

/*
    File:       Snapshot.c
 
    Contains:   This application demonstrates how to quickly and        
                efficiently capture the main device's desktop into          
                a window.  The program basically reads the image            
                stored in the the main device's pixmap then copies          
                it to a custom pixmap.  The custom pixmap is de-            
                fined at the same depth of the main device and              
                contains an identical copy of that device's color-          
                table.  This is done to provide the fastest                 
                performance possible when copying from an offscreen         
                to onscreen pixmap.  By making sure the pixel values        
                map to the exact same colors in both colortables,           
                copybits will do a direct transfer of bits without          
                wasting time remapping the colors.  Also the ctSeed         
                field for each colortable should be the same.  Finally,     
                since the main device's bounding rect is different          
                than that of the offscreen's, the copying performance       
                for the device to the offscreen is slightly affected        
                because of the scaling required.  However, the copying      
                performance for the offscreen to the window is the          
                best possible since the bounding rects for each are         
                identical.                                      
 
    Written by: EL  
 
    Copyright:  Copyright © 1991-1999 by Apple Computer, Inc., All Rights Reserved.
 
                You may incorporate this Apple sample source code into your program(s) without
                restriction. This Apple sample source code has been provided "AS IS" and the
                responsibility for its operation is yours. You are not permitted to redistribute
                this Apple sample source code as "Apple sample source 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 source
                code, but that you've made changes.
 
    Change History (most recent first):
                08/2000     JM              Major revisions made. In addition to being "carbonized"
                                            added: multiple windows, refreshing, saving, conditional
                                            menu bar for OS X and 9, Apple Event Quit support,
                                            updated about box....(bunch of extra things)
                11/6/1999   GGS             Updated to work with modern (1999) Mac OS.  
                                            Fixed a PixMap disposing bug.  Updated casts
                                            and headers.
                7/14/1999   KG              Updated for Metrowerks Codewarror Pro 2.1               
*/
 
#include "Snapshot.h"
 
// Different picture scaling constants
const int FULL_SIZE = 1;
const float HALF_SIZE = 1.41; // square root of 2, roughly
const int QUARTER_SIZE = 2;
 
/* Global Variable Definitions */
 
Rect                gBounds;                        // System resolution, effectively
Boolean             gDone = false;                  // Application termination global
Boolean             gDoingTrick = false;            // "Trick" flag
float               gScale = 2.0; // QUARTER_SIZE;          // Scaling the window, relative to resolution (default of 1/2 width and height)
Rect                gMinWindowSize;                 // The minimum window size
const unsigned char*        fileName =  "\pDesktop Pic";    // Generic filename
const unsigned char*        defaultName = "\pSnapshot";     // Default Save File Name
unsigned long       resizingDelay = 0;
 
int main()
{   
    initMac();
    setUp();
    
    doNewSnapshot();
    adjustMenus();
    doEventLoop();
    destroyAllWindows();
}
 
// Tests to see if we are running on Mac OS X.
Boolean onOSX()
{
    long response;
    OSErr   anErr = noErr;
    
    anErr = Gestalt(gestaltSystemVersion, &response);
    
    return response >= 0x01000 && (anErr == noErr);
}
 
void destroyAllWindows()
{
    WindowPtr theWindow = FrontWindow();
    while(FrontWindow() != NULL) {
        disposeWindow(theWindow);
        theWindow = FrontWindow();  
    }
}
 
void initMac()
{
    // the only "init" calls needed in carbon
    InitCursor();
    FlushEvents( 0, everyEvent );
}
 
void setUp()
{
    Handle  menuBar;
    OSErr   anErr = noErr;
    long    response;
    
    // Carbon Porting guidelines say provide alternate menu bar/menu scheme for OS X
    // This is just one way of doing a different menu for 9 and X, which is pretty static
    if (onOSX()) 
        menuBar = GetNewMBar(MENU_BAR_IDX);     //if we are running on X, need menu without a File->Quit
    else
        menuBar = GetNewMBar(MENU_BAR_ID);      //default menu bar
        
    anErr = ResError();
    
    if ( menuBar == nil || anErr != noErr )
         ExitToShell(); 
 
    SetMenuBar(menuBar);
    DisposeHandle(menuBar);
 
    DrawMenuBar();
    // Install 'quit' event handler
    // This "handler" is called when a user selects Application->Quit on OS X.
    if ((Gestalt(gestaltAppleEventsAttr, &response) == noErr)) {
            anErr = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication,
                     NewAEEventHandlerUPP(AEQuitHandler), 0, false);
            if (anErr != noErr)  
                ExitToShell();
    }
}
 
void doNewSnapshot()
{
    WindowPtr       newWindow = nil;
    PixMapHandle    pixHandle = nil;
    // Snapshots consist of a window and a PixMap, could have chosen
    // to use only pictures instead, but the existing code dealt with
    // PixMaps, so I kept the existing "concept"
    
    pixHandle = createScreenPixMap();
    newWindow = createWindow();
    // Window's refCon is its handle to a PixMap
    SetWRefCon(newWindow, (long)pixHandle);
    drawImage( newWindow );
    ShowWindow( newWindow );
}
 
WindowPtr createWindow()
{
    Rect    wBounds;
    BitMap  bitMap;
    int     top, left, width, height;
    WindowPtr newWindow;
    
    // Centering the window is a good thing....
 
    GetQDGlobalsScreenBits(&bitMap);
 
    width = ((bitMap.bounds.right - bitMap.bounds.left) / gScale);
    height = ((bitMap.bounds.bottom - bitMap.bounds.top) / gScale);
 
    left = (((bitMap.bounds.right - bitMap.bounds.left) - width) / 2);
    top = (((bitMap.bounds.bottom - bitMap.bounds.top) - height) / 2);
    
    // if scale is same size or bigger than the screen
    if (gScale <= FULL_SIZE)
        top = left = GetMBarHeight() * 3; //arbitrary position....
    
    // Create a window to display the final offscreen image. 
    SetRect( &wBounds, left, top, left + width, top + height );
    
    newWindow = NewCWindow( 0L, &wBounds, "\pSuper Snapshot!", false, kWindowFullZoomGrowDocumentProc,
                            (WindowPtr)-1L, true, 0L );
    // The refcon of the windows are going to be used to store the PixMapHandle
    // from a screen capture.  0 means no associated PixMapHandle while non-zero
    // means the value is a handle to a PixMap. A windows refcon should always be
    // a value in this program
    SetWRefCon(newWindow, 0L);
    return newWindow;
}
 
void disposeWindow(WindowPtr dieWindow)
{
    PixMapHandle pixHandle;
    
    if (dieWindow == NULL || !IsValidWindowPtr(dieWindow))
        return;
    
    // Free up the baseAddr pointer field, the PixMap itself,
    // and the window
    pixHandle = (PixMapHandle)GetWRefCon(dieWindow);
    if (pixHandle != nil) {
        LockPixels(pixHandle);
        DisposePtr((*pixHandle)->baseAddr);
        DisposeCTable((*pixHandle)->pmTable);
        // set the color table to nil since this was a "custom"
        // pixmap, otherwise DisposePixMap will not deallocate the memory
        (*pixHandle)->pmTable = nil;
        UnlockPixels(pixHandle);
        DisposePixMap(pixHandle);
    }
    DisposeWindow(dieWindow);
}
 
void calculateSystemBounds()
{
    BitMap  bitMap;
    
    // Get the resolution of the screen, save it in a global Rectangle and 
    // use the info to calculate the min/max window size
 
    GetQDGlobalsScreenBits(&bitMap);
 
    SetRect( &gBounds, 0, 0, bitMap.bounds.right, bitMap.bounds.bottom);
    SetRect(&gMinWindowSize, bitMap.bounds.right / 4, bitMap.bounds.bottom / 4, bitMap.bounds.right, bitMap.bounds.bottom);
}
 
PixMapHandle createScreenPixMap()
{
    GDHandle    mainDevice;
    CTabHandle  cTable;
    short       depth;  
    Ptr         offBaseAddr;    /* Pointer to the off-screen pixel image */
    short       bytesPerRow;
    PixMapHandle    pixHandle = nil;
    
    pixHandle = NewPixMap();
    
    /* Get a handle to the main device. */
    mainDevice = GetMainDevice();
 
    /* Store its current pixel depth. */
    depth = (**(**mainDevice).gdPMap).pixelSize;
 
    /* Make an identical copy of its pixmap's colortable. */
    cTable = (**(**mainDevice).gdPMap).pmTable;
    (void) HandToHand( (Handle*)&cTable );
    
    // Get Resolution of screen
    calculateSystemBounds();
    
    // Fill in a few of the PixMap's fields...
    // NewPixMap() is good for default initialization, simply modify
    // the new PixMap
    (*pixHandle)->pmTable = cTable;
    (*pixHandle)->bounds = gBounds;
    (*pixHandle)->pixelSize = depth;
    bytesPerRow = ((gBounds.right - gBounds.left) * depth) / 8;
    offBaseAddr = NewPtr((unsigned long) bytesPerRow * (gBounds.bottom - gBounds.top));
    (*pixHandle)->baseAddr = offBaseAddr;           // Point to image
    (*pixHandle)->rowBytes = bytesPerRow | 0x8000;  // MSB set for PixMap
    
    LockPixels(pixHandle);
 
    CopyBits( (BitMap *)*(**mainDevice).gdPMap, (BitMap *) *pixHandle,
                &(**(**mainDevice).gdPMap).bounds, &(*pixHandle)->bounds, srcCopy, 0l );
    
    UnlockPixels(pixHandle);
    
    return pixHandle;
}
 
void drawImage(WindowPtr theWindow)
{
    Rect tempRect1;
    PixMapHandle    pixHandle = (PixMapHandle) GetWRefCon(theWindow);
    GrafPtr         oldPort;
    
    GetPort(&oldPort);
    SetPortWindowPort(theWindow);
    if (theWindow == NULL || !IsValidWindowPtr(theWindow))
        return;
    if (pixHandle == NULL)
        return;
    else
        LockPixels(pixHandle);
    // Copy the offscreen image back onto the window.
 
    CopyBits( (BitMap *) *pixHandle,
        GetPortBitMapForCopyBits(GetWindowPort(theWindow)),
        &(*pixHandle)->bounds,
        GetPortBounds(GetWindowPort(theWindow),&tempRect1),
        srcCopy, 0L);
 
    UnlockPixels(pixHandle);
    SetPort(oldPort);
}
 
void saveToPICTFile(WindowPtr theWindow)
{
    PicHandle           picHandle;
    OSErr               anErr = noErr;
    NavReplyRecord      reply;
    NavDialogOptions    dialogOptions;
    FSSpec              documentFSSpec;
    OSType              fileTypeToSave = 'PICT';
    OSType              creatorType = 'ogle';       // PictureViewer
    AEKeyword           theKeyword;
    DescType            actualType;
    Size                actualSize;
    Rect                tempRect1;
    PixMapHandle        pixHandle;
    
    // This is a very basic NavServices file-saving example
    // added logic for this program specifically
    
    if (theWindow == NULL || !IsValidWindowPtr(theWindow))
        return;
    
    pixHandle = (PixMapHandle) GetWRefCon(theWindow);
    LockPixels(pixHandle);
    
    SetPortWindowPort(theWindow);
    
    GetPortBounds(GetWindowPort(theWindow), &tempRect1);
    picHandle = OpenPicture(&tempRect1);
    
    CopyBits((BitMap*) *pixHandle, GetPortBitMapForCopyBits(GetWindowPort(theWindow)), &(*pixHandle)->bounds, 
     &tempRect1, srcCopy, 0L);
     
    ClosePicture();
 
    anErr = NavGetDefaultDialogOptions(&dialogOptions); 
    dialogOptions.dialogOptionFlags |= kNavSelectDefaultLocation;
    
    // Set default name for a saved snapshot
    PLstrcpy(dialogOptions.savedFileName, defaultName);
    
    anErr = NavPutFile( nil, 
                        &reply, 
                        &dialogOptions, 
                        nil,
                        fileTypeToSave, 
                        creatorType, 
                        nil );
 
    if (anErr == noErr && reply.validRecord) {
        anErr = AEGetNthPtr(&(reply.selection), 1, typeFSS,
                                &theKeyword, &actualType,
                                &documentFSSpec, sizeof(documentFSSpec),
                                &actualSize );
        if (anErr == noErr) {
            writePictToFile(&documentFSSpec, picHandle);
        }
        reply.translationNeeded = false;
        anErr = NavCompleteSave(&reply, kNavTranslateInPlace);
    
        NavDisposeReply(&reply);
    }
    KillPicture(picHandle);
    UnlockPixels(pixHandle);
}
 
void writePictToFile(FSSpec *fspec, PicHandle picHandle)
{
    OSErr               anErr = noErr;
    long                inOutCount;
    short               refNum;
    OSType              fileTypeToSave = 'PICT';
    OSType              creatorType = 'ogle';       // PictureViewer
    int                 count;
    unsigned char       header[512];
    
    // Pict files have to have 512 bytes of "zero" data at the front.
    for (count = 0; count < 512; count++)
        header[count] = 0x00;
 
    anErr = FSpCreate(fspec, creatorType, fileTypeToSave, smSystemScript);
    if (anErr == dupFNErr) {
        anErr = FSpDelete(fspec);   //delete file if already exists
        anErr = FSpCreate(fspec, creatorType, fileTypeToSave, smSystemScript);
    }
            
    // write the file
    FSpOpenDF(fspec, fsRdWrPerm, &refNum );
    inOutCount = 512;
    anErr = FSWrite(refNum, &inOutCount, header);       // write the header
    if (anErr == noErr) {
        inOutCount = GetHandleSize((Handle)picHandle);
        anErr = FSWrite(refNum,&inOutCount,*picHandle);
    }
    FSClose( refNum );
}
 
void doTrickEventLoop()
{
    EventRecord anEvent;
    WindowPtr   evtWind = NULL;
    
    // Event loop while doing the "full screen" trick
    
    while (!gDone) {
        if (WaitNextEvent( everyEvent, &anEvent, 2, nil )) {
            if (anEvent.what == updateEvt) {
                evtWind = (WindowPtr)anEvent.message;   
                SetPortWindowPort(evtWind);
                    
                BeginUpdate(evtWind);
                drawImage(evtWind);
                EndUpdate(evtWind);
            }
            else if (anEvent.what == autoKey || anEvent.what == keyDown)
                handleKeyPress(&anEvent);
            else if (anEvent.what == kHighLevelEvent)
                AEProcessAppleEvent(&anEvent);
            }
    }
    gDone = false;
}
void doConfusion()
{
    GDHandle        mainDevice;
    PicHandle       picHandle;
    FSSpec          fspec;
    PixMapHandle    pixHandle;
    WindowPtr       theWindow;
    float           oldScale = gScale;
    OSErr           anErr = noErr;
    AEDesc          desc;
    Ptr             oldState = NULL;
    WindowPtr       bigWindow;
    RgnHandle       rgnHandle = NewRgn();
    Rect            tempRect1;
 
    gScale = 1;
    
    mainDevice = GetMainDevice();
    
    pixHandle = createScreenPixMap();
    LockPixels(pixHandle);
    
    if (!onOSX()) {
        // used a window for debugging purposes
        theWindow = createWindow();
        SetPortWindowPort(theWindow);
        SetWRefCon(theWindow, (long) pixHandle);
        picHandle = OpenPicture(&gBounds);
        drawImage(theWindow);
        ClosePicture();
        //ShowWindow(theWindow);
    
        // write pict to file
        FSMakeFSSpec(0, 0, fileName, &fspec);
        writePictToFile(&fspec, picHandle);
    
        // set desktop pict :-)
        anErr = OHMakeAliasDescFromFSSpec(&fspec, &desc);
        if (anErr == noErr) {
            SetDesktopPict(&desc, 1);
            AEDisposeDesc(&desc);
        }
        KillPicture(picHandle);
        DisposeWindow(theWindow);
        gDone = true;
    }
    else {
        // since OS X does not use the Appearance manager for the desktop background
        // we will drop into full screen mode instead
        gDoingTrick = true;
        BeginFullScreen(&oldState, nil, 0, 0, &bigWindow, 0, fullScreenAllowEvents);
        SetWRefCon(bigWindow, (long)pixHandle);
        SetPortWindowPort(bigWindow);
 
        CopyBits( (BitMap *) *pixHandle,
            GetPortBitMapForCopyBits(GetWindowPort(bigWindow)),
            &(*pixHandle)->bounds,
            GetPortBounds(GetWindowPort(bigWindow),&tempRect1),
            srcCopy, 0L);
        QDFlushPortBuffer(GetWindowPort(bigWindow), GetPortVisibleRegion(GetWindowPort(bigWindow), rgnHandle));
 
        doTrickEventLoop();
        EndFullScreen(oldState, nil);
        gDoingTrick = false;
    }
    
    // clean up
    DisposePtr((*pixHandle)->baseAddr);
    DisposeCTable((*pixHandle)->pmTable);
    UnlockPixels(pixHandle);
    DisposePixMap(pixHandle);
    DisposeRgn(rgnHandle);
    gScale = oldScale;
}
 
 
void adjustMenus()
{
    // Function simply recalculates menus based on app state
    // Apps should do better menu handling, this is quick and dirty :-)
    
    MenuHandle menuHandle = GetMenuHandle(SIZE_MENU);
    int counter;
 
    for (counter = 1; counter <= NUMBER_OF_SIZES; counter++)
        CheckMenuItem(menuHandle, counter, false);
    
    if (gScale == QUARTER_SIZE)
        CheckMenuItem(menuHandle, SIZE_QUARTER_SCALE, true);
    else if (gScale == HALF_SIZE)
        CheckMenuItem(menuHandle, SIZE_HALF_SCALE, true);
    else if (gScale == FULL_SIZE)
        CheckMenuItem(menuHandle, SIZE_FULL_SCALE, true);
 
    menuHandle = GetMenuHandle(onOSX() ? FILE_MENUX : FILE_MENU);
    
    for (counter = 2; counter <= NUMBER_OF_FILE_MENU_ITEMS; counter++)
        FrontWindow() != NULL ? EnableMenuItem(menuHandle, counter) : DisableMenuItem(menuHandle, counter);
}
 
void resizeWindow(WindowPtr theWindow)
{
    PixMapHandle pixHandle;
    Rect    originalLoc, newLoc;
    Boolean wasCollapsed;
    RgnHandle   rgnHandle = NewRgn();
    
    // Resize the window....
    if (theWindow == NULL || !IsValidWindowPtr(theWindow))
        return;
    
    wasCollapsed = IsWindowCollapsed(theWindow);
    
    if (wasCollapsed) {
        HideWindow(theWindow);
        CollapseWindow(theWindow, false);
    }
    
    pixHandle = (PixMapHandle)GetWRefCon(theWindow);
    GetWindowBounds(theWindow, kWindowContentRgn, &originalLoc);
    
    SetRect(    &newLoc, 
                originalLoc.left, originalLoc.top, 
                gBounds.right / gScale + originalLoc.left,
                gBounds.bottom / gScale + originalLoc.top);
    SetWindowBounds(theWindow, kWindowContentRgn, &newLoc);
    drawImage(theWindow);
    QDFlushPortBuffer(  GetWindowPort(theWindow), 
                        GetPortVisibleRegion(GetWindowPort(theWindow), rgnHandle));
    
    if (wasCollapsed) {
        CollapseWindow(theWindow, wasCollapsed);
        ShowWindow(theWindow);
    }
    DisposeRgn(rgnHandle);
}
 
void handleMenuSelection(long result)
{
    int menuID, menuItem;
    RgnHandle rgnHandle = NewRgn();
    DialogPtr   theDialog = NULL;
    short itemHit;
    PixMapHandle    pixHandle;
    
    menuID = HiWord(result);
    menuItem = LoWord(result);
    HiliteMenu(0);
    
    // File Menu
    if (menuID == FILE_MENU || menuID == FILE_MENUX) {
        if (menuItem == FILE_SAVE)
            saveToPICTFile(FrontWindow());
        else if (menuItem == FILE_QUIT)
            gDone = true;
        else if (menuItem == FILE_CLOSE) {
            if (gDoingTrick)
                gDone = true;
            else if (FrontWindow() != NULL)
                disposeWindow(FrontWindow());
        }
        else if (menuItem == FILE_NEW) {
            doNewSnapshot();
        }
        else if (menuItem == FILE_REFRESH) {
            if (FrontWindow() == NULL) {
                HiliteMenu(0);
                return;
            }
            pixHandle = (PixMapHandle)GetWRefCon(FrontWindow());
            DisposePtr((**pixHandle).baseAddr);
            DisposePixMap(pixHandle);
            pixHandle = createScreenPixMap();
            SetWRefCon(FrontWindow(), (long)pixHandle);
            drawImage(FrontWindow());
            QDFlushPortBuffer(GetWindowPort(FrontWindow()), GetPortVisibleRegion(GetWindowPort(FrontWindow()), rgnHandle));
        }
    }
    else if (menuID == ABOUT_MENU) {    //about menu handling
        if (menuItem == ABOUT) {
            theDialog = GetNewDialog ( ABOUTDLG, nil, (WindowPtr)-1 );
                    do {
                        ModalDialog ( nil, &itemHit );
                    } while( itemHit != ok ) ;
                    DisposeDialog ( theDialog );
        }
    }
    else if (menuID == SIZE_MENU) {     //size menu
        switch (menuItem) {
            case SIZE_QUARTER_SCALE:
                gScale = QUARTER_SIZE;
                break;
            case SIZE_HALF_SCALE:
                gScale = HALF_SIZE;
                break;
            case SIZE_FULL_SCALE:
                gScale = FULL_SIZE;
                break;
        }
        resizeWindow(FrontWindow());
    }
    else if (menuID == SPECIAL_MENU || menuID == SPECIAL_MENUX) { // "special" because depends on OS 9 or X
        switch (menuItem) {
            case SPECIAL_CONFUSING:
                doConfusion();
            break;
        }
    }
    adjustMenus();
    DisposeRgn(rgnHandle);
}
 
void handleKeyPress(EventRecord *event)
{
    char    key;
 
    key = event->message & charCodeMask;
    
    // just check to see if the command key is down, if so
    // process it as menu selection
    if ( event->modifiers & cmdKey )
        handleMenuSelection(MenuKey(key));
}
 
// Apple Event - "Quit" Handler
pascal OSErr AEQuitHandler(const AppleEvent *messagein, AppleEvent *reply, long refIn)
{
    #pragma unused (messagein,refIn,reply)
    
    // Do not call ExitToShell() in an AE Handler....
    gDone = true;
    return noErr;
}
 
void doDynamicResizing(WindowPtr theWindow)
{
    Point   currentLoc;
    Rect    windowBounds;
    long    time;
    int offsetX, offsetY;
    int windowWidth, windowHeight;
    RgnHandle   rgnHandle = NewRgn();
    EventRecord anEvent;
    
    // I could not find any code that would handle dynamic resizing using the "classic"
    // or traditional event model....so I wrote my own implementation
    GetWindowBounds(theWindow, kWindowStructureRgn, &windowBounds);
    GetMouse(&currentLoc);
    LocalToGlobal(&currentLoc);
    
    // calculate where mouse is in relation to edge of the window
    offsetX = windowBounds.right - currentLoc.h;
    offsetY = windowBounds.bottom - currentLoc.v;
    time = TickCount();
    while (Button()) {
        GetMouse(&currentLoc);
        LocalToGlobal(&currentLoc);
        GetWindowBounds(theWindow, kWindowStructureRgn, &windowBounds);
        windowBounds.bottom = currentLoc.v + offsetX;
        windowBounds.right = currentLoc.h + offsetY;
        windowWidth = windowBounds.right - windowBounds.left;
        windowHeight = windowBounds.bottom - windowBounds.top;
        
        // Error check the window rectangle
        if (windowWidth < gMinWindowSize.left)
            windowBounds.right = windowBounds.left + gMinWindowSize.left;
        if (windowHeight < gMinWindowSize.top)
            windowBounds.bottom = windowBounds.top + gMinWindowSize.top;
        SetWindowBounds(theWindow, kWindowStructureRgn, &windowBounds);
        // Draws every "resizingDelay" miliseconds, can easily change the interval
        // I've set the interval to 0 for instantaneous drawing, but different apps may want to
        // change the delay (maybe the drawing is quite complex....) or change it during execution
        if (TickCount() - time >= resizingDelay) {
            drawImage(theWindow);
            QDFlushPortBuffer(GetWindowPort(theWindow), GetPortVisibleRegion(GetWindowPort(theWindow), rgnHandle));
            time = TickCount();
        }
        WaitNextEvent( everyEvent, &anEvent, 0, nil );
        processEvent(&anEvent);
        // User may have decided to quit or close the window
        if (gDone || !IsValidWindowPtr(theWindow))
            break;
    }
    DisposeRgn(rgnHandle);
    // Update the window when all is done
    PostEvent(updateEvt, (long)theWindow);
}
 
void processEvent(EventRecord *anEvent)
{
    WindowPtr       evtWind;
    short           clickArea;
    Rect            screenRect;
    RgnHandle       rgnHandle = NewRgn();
 
    // Pretty standard event processing, save the couple checks for the
    // "trick" flag being set
    if (anEvent->what == mouseDown)
    {
        clickArea = FindWindow( anEvent->where, &evtWind );
                
        if (clickArea == inMenuBar)
            handleMenuSelection(MenuSelect(anEvent->where));
        else if (clickArea == inDrag)
        {
            GetRegionBounds(GetGrayRgn(), &screenRect);
            DragWindow( evtWind, anEvent->where, &screenRect );
        }
        else if (clickArea == inContent)
        {
            if (evtWind != FrontWindow())
                SelectWindow( evtWind );
        }
        else if (clickArea == inGoAway) {
            if (TrackGoAway( evtWind , anEvent->where )) {
                disposeWindow(evtWind);
                adjustMenus();
            }
        }
        else if (clickArea == inGrow) {
            doDynamicResizing(evtWind);
        }
        else if (clickArea == inZoomIn || clickArea == inZoomOut) {
            if (TrackBox (evtWind, anEvent->where, clickArea == inZoomOut ? inZoomOut : inZoomIn)) {
                ZoomWindow (evtWind, clickArea == inZoomOut ? inZoomOut : inZoomIn, true);
                drawImage(evtWind);
                QDFlushPortBuffer(GetWindowPort(evtWind), GetPortVisibleRegion(GetWindowPort(evtWind), rgnHandle));
            }
        }
    }
    else if (anEvent->what == updateEvt)
    {
        evtWind = (WindowPtr)anEvent->message;  
        SetPortWindowPort(evtWind);
                
        BeginUpdate(evtWind);
        drawImage(evtWind);
        EndUpdate(evtWind);
    }
    else if (anEvent->what == autoKey || anEvent->what == keyDown)
        handleKeyPress(anEvent);
    else if (anEvent->what == kHighLevelEvent)
        AEProcessAppleEvent(anEvent);
    
    DisposeRgn(rgnHandle);
}
 
void doEventLoop()
{
    EventRecord     anEvent;
    
    while (!gDone)
        if (WaitNextEvent( everyEvent, &anEvent, 10, nil))
            processEvent(&anEvent);
}