Feature Files/VRMovies.c

//////////
//
//  File:       VRMovies.c
//
//  Contains:   Support for QuickTime movie playback in VR nodes.
//
//  Written by: Tim Monroe
//              Some code borrowed from QTVRSamplePlayer by Bryce Wolfson.
//
//  Copyright:  © 1996-1998 by Apple Computer, Inc., all rights reserved.
//
//  Change History (most recent first):
//
//     <31>     03/17/00    rtm     made changes to get things running under CarbonLib
//     <30>     09/10/99    rtm     added code to implement hide regions
//     <29>     06/30/99    rtm     added fix to VRMoov_LoadEmbeddedMovie to call VRMoov_SetOneBalanceAndVolume
//                                  only for localized sounds
//     <28>     06/29/99    rtm     reworked VRMoov_LoopEmbeddedMovie to support palindrome looping
//     <27>     06/24/99    rtm     major overhaul of geometry handling, using newly-revised code from
//                                  VRMovies project; removed USE_COPYBITS_TO_BACKBUFFER flag, since we now
//                                  always use DecompressSequenceFrameS
//     <26>     05/17/99    rtm     reworked geometry handling to support horizontal back buffer in QT 4.0,
//                                  using code from revised VRMovies project; this code now automatically
//                                  determines whether the back buffer is oriented horizontally or not and
//                                  then does the right thing in either case
//     <25>     04/07/99    rtm     revised VRMoov_StartMovie
//     <24>     03/29/99    rtm     added VRMoov_Init and VRMoov_Stop; conditionalized out the support for
//                                  on-the-fly movie rotation (too many problems reported)
//     <23>     03/29/99    rtm     added VRMoov_StartMovie as a wrapper for StartMovie to handle streamed
//                                  movies; changed StartMovie to VRMoov_StartMovie passim
//     <22>     01/26/99    rtm     changed two occurrences of gw->portPixMap to GetGWorldPixMap(gw)
//     <21>     12/03/98    rtm     modified VRMoov_LoadEmbeddedMovie to accept a string as a parameter,
//                                  which can now be a full URL or a pathname
//     <20>     09/28/98    rtm     added call to DisposeMovie in VRMoov_PlayTransitionMovie to plug leak
//     <19>     09/11/98    rtm     added code to use DecompressImage instead of CopyBits when copying
//                                  an embedded movie frame into the backbuffer; set the compiler flag
//                                  USE_COPYBITS_TO_BACKBUFFER in VRMovies.h to select the desired call
//     <18>     05/04/98    rtm     added support for on-the-fly movie rotation
//     <17>     05/01/98    rtm     added VRMoov_DoIdle call to VRMoov_PlayTransitionMovie, to service
//                                  scene-wide sounds while a transition movie is playing; also added
//                                  call to FlushEvents and cleaned up surrounding code
//     <16>     10/29/97    rtm     modified VRMoov_SetVideoGraphicsMode to use GetMovieIndTrackType
//     <15>     09/18/97    rtm     added parameter to VRMoov_CheckForCompletedMovies
//     <14>     06/24/97    rtm     modified VRMoov_DoIdle to service QuickTime movies with video tracks 
//                                  in non-frontmost windows
//     <13>     06/19/97    rtm     added support for scene-wide sound-only movies
//     <12>     05/13/97    rtm     added VRMoov_PlayTransitionMovie
//     <11>     04/28/97    rtm     reworked to support sound-only movie files as well
//     <10>     04/28/97    rtm     added VRMoov_PlayMovie; reworked passim to use linked list
//     <9>      03/17/97    rtm     added VRMoov_SetMovieBalanceAndVolume
//     <8>      03/12/97    rtm     copied file from VRMovies project and integrated with VRScript
//     <7>      03/06/97    rtm     started to implement video masking
//     <6>      03/05/97    rtm     added VRMoov_SetChromaColor; added fChromaColor to app data record
//     <5>      03/04/97    rtm     fixed compositing problems at back buffer edges
//     <4>      03/03/97    rtm     added VRMoov_SetVideoGraphicsMode to handle compositing
//                                  without using an offscreen GWorld
//     <3>      02/27/97    rtm     further development: borrowed some ideas from QTVRSamplePlayer;
//                                  added VRMoov_SetEmbeddedMovieWidth etc.
//     <2>      12/12/96    rtm     further development: borrowed some ideas from BoxMoov demo 
//     <1>      12/11/96    rtm     first file 
//     
// This code supports playing QuickTime movies in QTVR panoramas. For movies with video tracks,
// this code draws the QuickTime movie frames into the back buffer, either directly or indirectly
// (via an offscreen GWorld). Direct drawing gives the best performance, but indirect drawing is
// necessary for some visual effects. 
//
//////////
 
// TO DO:
// + finish video masking by custom 'hide' hot spots (so video goes *behind* such hot spots)
// + verify that everything works okay if *images* are used instead of movies (scaling seems to be a problem)
 
 
//////////
//     
// header files
//     
//////////
 
#include "VRMovies.h"
#include "VRScript.h"
 
 
//////////
//     
// constants
//     
//////////
 
const RGBColor                  kBlackColor = {0x0000, 0x0000, 0x0000};     // black
const RGBColor                  kWhiteColor = {0xffff, 0xffff, 0xffff};     // white
 
 
//////////
//     
// global variables
//     
//////////
 
MoviePrePrerollCompleteUPP      gMoviePPRollCompleteProc = NULL;            // a routine descriptor for our pre-preroll completion routine
 
 
//////////
//
// VRMoov_Init
// Initialize for QuickTime movie playback.
//
//////////
 
void VRMoov_Init (void)
{
    // allocate a routine descriptor for our pre-preroll completion routine
    if (gMoviePPRollCompleteProc == NULL)
        gMoviePPRollCompleteProc = NewMoviePrePrerollCompleteUPP(VRScript_MoviePrePrerollCompleteProc);
}
 
 
//////////
//
// VRMoov_Stop
// Close down for QuickTime movie playback.
//
//////////
 
void VRMoov_Stop (void)
{
    // deallocate routine descriptor
    if (gMoviePPRollCompleteProc != NULL)
        DisposeMoviePrePrerollCompleteUPP(gMoviePPRollCompleteProc);
}
 
 
//////////
//
// VRMoov_StartMovie
// Start a QuickTime movie playing.
//
// This is essentially just a wrapper for StartMovie, to handle preprerolling of streamed movies.
//
//////////
 
void VRMoov_StartMovie (Movie theMovie)
{   
    PrePrerollMovie(theMovie, 0, GetMoviePreferredRate(theMovie), gMoviePPRollCompleteProc, (void *)0L);
}
 
 
//////////
//
// VRMoov_PlayMovie
// Start a QuickTime movie playing or stop one from playing.
//
//////////
 
void VRMoov_PlayMovie (
                WindowObject theWindowObject,
                UInt32 theNodeID,
                UInt32 theEntryID, 
                float thePanAngle,
                float theTiltAngle,
                float theScale, 
                float theWidth, 
                UInt32 theKeyRed, 
                UInt32 theKeyGreen, 
                UInt32 theKeyBlue, 
                Boolean theUseBuffer,
                Boolean theUseCenter,
                Boolean theUseKey,
                Boolean theUseHide,
                Boolean theUseDir,
                Boolean theRotate,
                float theVolAngle,
                UInt32 theMode,
                UInt32 theOptions,
                char *thePathName)
{
    ApplicationDataHdl      myAppData;
    Boolean                 myNeedPlayMovie = false;
    Boolean                 myNeedLoadMovie = false;
    VRScriptMoviePtr        myPointer = NULL;
    
    myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
    if (myAppData == NULL)
        return;
    
    // see if this movie is already in our list of playing movies               
    myPointer = (VRScriptMoviePtr)VRScript_GetObjectByEntryID(theWindowObject, kVREntry_QTMovie, theEntryID);
        
    if (myPointer == NULL) {
        // this movie isn't in our list yet, so we'll need to add it to the list
        myNeedLoadMovie = true;
        myNeedPlayMovie = true;
    } else {
        // this movie is already in our list; theOptions determines how we handle this request:
        
        switch (theOptions) {
        
            case kVRMedia_PlayNew:
                // start the movie playing (whether it's already playing or not);
                // multiple movies aren't supported in QTVR 2.0, so we just map to kVRMedia_Continue
                myNeedPlayMovie = true;
                myNeedLoadMovie = true;
                break;
                
            case kVRMedia_Restart:
                // stop the current movie and then restart it; use the existing movie data
                StopMovie(myPointer->fMovie);
                myNeedPlayMovie = true;
                myNeedLoadMovie = false;
                break;
                
            case kVRMedia_ToggleStop:
                // toggle the movie's current play/stop state
                myNeedLoadMovie = false;
                if (!IsMovieDone(myPointer->fMovie)) {
                    // stop the movie and get rid of movie list entry
                    StopMovie(myPointer->fMovie);
                    VRScript_DelistEntry(theWindowObject, (VRScriptGenericPtr)myPointer);
                    myNeedPlayMovie = false;
                } else {
                    // start the movie; use the existing movie data
                    myNeedPlayMovie = true;
                }
                break;
                
            case kVRMedia_TogglePause:
                // toggle the movie's current play/pause state
                myNeedLoadMovie = false;
                if (GetMovieRate(myPointer->fMovie) != (Fixed)0) {
                    // stop the movie
                    StopMovie(myPointer->fMovie);
                    myNeedPlayMovie = false;
                } else {
                    // restart the movie; use the existing movie data
                    myNeedPlayMovie = false;
                    StartMovie(myPointer->fMovie);
                }
                break;
                
            case kVRMedia_Continue:
                // just let the movie already in progress continue
                myNeedPlayMovie = false;
                myNeedLoadMovie = false;
                break;
                
            case kVRMedia_Stop:
                // stop the current movie
                StopMovie(myPointer->fMovie);
                myNeedPlayMovie = false;
                myNeedLoadMovie = false;
                break;
                
            default:
                // unrecognized option; start the movie playing
                myNeedPlayMovie = true;
                myNeedLoadMovie = false;
                break;
        }
    }
    
    if (myNeedLoadMovie) {
        myPointer = VRScript_EnlistMovie (
                        theWindowObject,
                        theNodeID, 
                        theEntryID,
                        thePanAngle,
                        theTiltAngle,
                        theScale, 
                        theWidth, 
                        theKeyRed, 
                        theKeyGreen, 
                        theKeyBlue, 
                        theUseBuffer,
                        theUseCenter,
                        theUseKey,
                        theUseHide,
                        theUseDir,
                        theRotate,
                        theVolAngle,
                        theMode,
                        theOptions,
                        thePathName);
    }
 
    if (myNeedPlayMovie)
        if (myPointer != NULL)
            VRMoov_LoadEmbeddedMovie(thePathName, theWindowObject, myPointer);
}
 
 
//////////
//
// VRMoov_PlayTransitionMovie
// Play a QuickTime movie as a transition from one node to another.
//
//////////
 
void VRMoov_PlayTransitionMovie (WindowObject theWindowObject, UInt32 theOptions, char *thePathName)
{
    FSSpec      myFSSpec;
    short       myMovieFileRef = 0;
    Movie       myMovie;
    Rect        myRect;
    GWorldPtr   myGWorld;
    GDHandle    myGDevice;
    long        myCount = 0;
    Boolean     myUserCancelled = false;
    OSErr       myErr = noErr;
    
    if ((theWindowObject == NULL) || (thePathName == NULL))
        goto bail;
        
    FileUtils_MakeFSSpecForPathName(0, 0L, thePathName, &myFSSpec);
    
    // open the movie
    myErr = OpenMovieFile(&myFSSpec, &myMovieFileRef, fsRdPerm);
    if (myErr != noErr)
        goto bail;
 
    myErr = NewMovieFromFile(&myMovie, myMovieFileRef, NULL, NULL, newMovieActive, NULL);
    if ((myErr != noErr) || (myMovie == NULL))
        goto bail;
 
    // set the movie GWorld
    GetMovieGWorld((**theWindowObject).fMovie, &myGWorld, &myGDevice);
    SetMovieGWorld(myMovie, myGWorld, myGDevice);
            
    // set the movie box to the size of the QTVR window         
    GetMovieBox((**theWindowObject).fMovie, &myRect);   
    SetMovieBox(myMovie, &myRect);
    
    // start the movie
    GoToBeginningOfMovie(myMovie);
    VRMoov_StartMovie(myMovie);
    
    // play the movie once thru
    while (!IsMovieDone(myMovie) && !myUserCancelled) {
        MoviesTask(myMovie, 0);
#if !TARGET_API_MAC_CARBON
        SystemTask();
#endif      
        // on very long transition movies (or very slow machines), scene-wide sound-only movies need to be serviced
        // (but not every time thru the loop, eh?)
        if (myCount % kDoIdleStep == 0)
            VRMoov_DoIdle(theWindowObject);
            
        myCount++;
        
        // see if the user has cancelled the transition movie, if so allowed
        if (theOptions == kVRMovie_PlayTilClick)
            myUserCancelled = Button();
    }
    
    StopMovie(myMovie);
    DisposeMovie(myMovie);
    
    // button clicks during a transition movie remain in the OS event queue (even if detected by the
    // Button call above); we'd better remove them (by calling FlushEvents) or they may trigger a hot
    // spot in the destination node; note that if theOptions is kVRMovie_PlayTilClick, then the first
    // click stops the movie and gets flushed, but any subsequent clicks remain in the OS event queue
    // and get processed normally....
    FlushEvents(mDownMask + mUpMask, 0);
    
bail:
    if (myMovieFileRef != 0)
        CloseMovieFile(myMovieFileRef);
}
 
 
//////////
//
// VRMoov_LoadEmbeddedMovie
// Load the QuickTime movie in the specified file.
// Returns a Boolean to indicate success (true) or failure (false).
//
//////////
 
Boolean VRMoov_LoadEmbeddedMovie (char *thePathName, WindowObject theWindowObject, VRScriptMoviePtr theEntry)
{
    FSSpec                  myFSSpec;
    short                   myMovieFileRef = 0;
    Movie                   myMovie = NULL;
    GWorldPtr               myGWorld = NULL;
    Rect                    myRect;
    ApplicationDataHdl      myAppData = NULL;
    QTVRInstance            myInstance = NULL;
    OSErr                   myErr = paramErr;
 
    //////////
    //
    // validate the window object and application data
    //
    //////////
 
    myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
    if (myAppData == NULL)
        goto bail;
        
    if (theEntry == NULL)
        goto bail;
        
    myInstance = (**theWindowObject).fInstance;
    if (myInstance == NULL)
        goto bail;
        
    HLock((Handle)myAppData);
 
    //////////
    //
    // open the movie file and the movie
    //
    //////////
    
    if (URLUtils_IsAbsoluteURL(thePathName)) {
        // we were passed a URL
        myMovie = URLUtils_NewMovieFromURL(thePathName, newMovieActive, NULL);
        if (myMovie == NULL)
            goto bail;
    } else {
        // we were passed a filename; create an FSSpec for the file
        FileUtils_MakeFSSpecForPathName(0, 0L, thePathName, &myFSSpec);
        
        // open the movie
        myErr = OpenMovieFile(&myFSSpec, &myMovieFileRef, fsRdPerm);
        if (myErr != noErr)
            goto bail;
 
        myErr = NewMovieFromFile(&myMovie, myMovieFileRef, NULL, (StringPtr)NULL, newMovieActive, NULL);
        if (myErr != noErr)
            goto bail;
    }
 
    //////////
    //
    // get the movie geometry
    //
    //////////
 
    GetMovieBox(myMovie, &myRect);
    GetMovieMatrix(myMovie, &theEntry->fOrigMovieMatrix);
    MacOffsetRect(&myRect, -myRect.left, -myRect.top);
    SetMovieBox(myMovie, &myRect);
    
    // determine the orientation of the back buffer
    (**myAppData).fBackBufferIsHoriz = QTVRUtils_IsBackBufferHorizontal((**theWindowObject).fInstance);
 
    // keep track of the movie and movie rectangle
    theEntry->fMovie = myMovie;
    theEntry->fMovieBox = myRect;
    
    //////////
    //
    // determine what kinds of media are in this movie
    //
    //////////
 
    // we cannot query a streamed movie for its media types until it's been preprerolled, which we don't want to do now;
    // as a result, we'll do the best we can
    if (QTUtils_IsStreamedMovie(myMovie)) {
        theEntry->fQTMovieHasSound = true;
        theEntry->fQTMovieHasVideo = !EmptyRect(&myRect);
    } else {
        theEntry->fQTMovieHasSound = QTUtils_IsMediaTypeInMovie(myMovie, SoundMediaType) || QTUtils_IsMediaTypeInMovie(myMovie, MusicMediaType);
        theEntry->fQTMovieHasVideo = QTUtils_IsMediaTypeInMovie(myMovie, VideoMediaType);
    }
 
    //////////
    //
    // do processing for video media
    //
    //////////
 
    if (theEntry->fQTMovieHasVideo) {
        // get rid of any existing offscreen graphics world
        if (theEntry->fOffscreenGWorld != NULL) {
            UnlockPixels(theEntry->fOffscreenPixMap);
            DisposeGWorld(theEntry->fOffscreenGWorld);
            theEntry->fOffscreenGWorld = NULL;
        }
    
        // clear out any existing custom cover function and reset the video media graphics mode
        // (these may have been modified for direct-screen drawing)
        SetMovieCoverProcs(myMovie, NULL, NULL, 0);
        VRMoov_SetVideoGraphicsMode(myMovie, theEntry, false);
        
        // if necessary, create an offscreen graphics world;
        // this is where we'll image the movie before copying it into the back buffer
        // when we want to do special effects
        if (theEntry->fUseOffscreenGWorld) {
            myErr = NewGWorld(&myGWorld, 0, &myRect, NULL, NULL, 0);
            theEntry->fOffscreenGWorld = myGWorld;
            theEntry->fOffscreenPixMap = GetGWorldPixMap(myGWorld);
            
            // make an image description, which is needed by DecompressSequenceBegin
            LockPixels(theEntry->fOffscreenPixMap);
            MakeImageDescriptionForPixMap(theEntry->fOffscreenPixMap, &(theEntry->fImageDesc));
            UnlockPixels(theEntry->fOffscreenPixMap);
        } else {
            // set the video media graphics mode to drop out the chroma key color in a movie;
            // we also need to install an uncover function that doesn't erase the uncovered region
            if (theEntry->fCompositeMovie) {
                VRMoov_SetVideoGraphicsMode(myMovie, theEntry, true);
                SetMovieCoverProcs(myMovie, NewMovieRgnCoverUPP(VRMoov_CoverProc), NULL, (long)theWindowObject);
            }
        }
        
        // turn on view-changed flag, so hide region gets calculated (if necessary)
        (**myAppData).fViewHasChanged = true;
    }
    
    //////////
    //
    // do processing for sound media
    //
    //////////
 
    if (theEntry->fQTMovieHasSound) {
        // get the sound media handler
        theEntry->fMediaHandler = QTUtils_GetSoundMediaHandler(myMovie);
 
        // set initial balance and volume for localized sounds
        if (theEntry->fSoundIsLocalized) {
            VRMoov_SetOneBalanceAndVolume(myMovie, theEntry->fMediaHandler, QTVRGetPanAngle(myInstance), QTVRGetTiltAngle(myInstance), theEntry->fMovieCenter.x, theEntry->fVolAngle);
            (**myAppData).fSoundHasChanged = true;
        }
    }
        
    //////////
    //
    // install the back-buffer imaging procedure
    //
    //////////
    
    if ((**theWindowObject).fInstance != NULL)
        VRScript_InstallBackBufferImagingProc((**theWindowObject).fInstance, theWindowObject);
        
    //////////
    //
    // start the movie playing
    //
    //////////
    
    switch (theEntry->fMode) {
        case kVRPlay_Once:                  // play the movie once thru
            GoToBeginningOfMovie(myMovie);
            VRMoov_StartMovie(myMovie);
            break;
        case kVRPlay_Loop:                  // start the movie playing in a loop
            VRMoov_LoopEmbeddedMovie(myMovie, false);
            break;
        case kVRPlay_LoopPalindrome:        // start the movie playing in a palindrome loop
            VRMoov_LoopEmbeddedMovie(myMovie, true);
            break;
    }
    
bail:
    // we don't want to edit the embedded movie, so we can close the movie file
    if (myMovieFileRef != 0)
        CloseMovieFile(myMovieFileRef);
        
    HUnlock((Handle)myAppData);
 
    return(myErr == noErr);
}
    
    
//////////
//
// VRMoov_LoopEmbeddedMovie
// Start the QuickTime movie playing in a loop.
//
//////////
 
void VRMoov_LoopEmbeddedMovie (Movie theMovie, Boolean isPalindrome)
{
    TimeBase        myTimeBase;
    long            myFlags = 0L;
 
    // set the movie's play hints to enhance looping performance
    SetMoviePlayHints(theMovie, hintsLoop, hintsLoop);
    
    // set the looping flag of the movie's time base
    myTimeBase = GetMovieTimeBase(theMovie);
    myFlags = GetTimeBaseFlags(myTimeBase);
    myFlags |= loopTimeBase;
    
    // set or clear the palindrome flag, depending on the specified setting
    if (isPalindrome)
        myFlags |= palindromeLoopTimeBase;
    else
        myFlags &= ~palindromeLoopTimeBase;
        
    SetTimeBaseFlags(myTimeBase, myFlags);
 
    // start playing the movie
    VRMoov_StartMovie(theMovie);
}
    
 
//////////
//
// VRMoov_DoIdle
// Do any movie-related processing that can or should occur at idle time.
// Returns true if the caller should call QTVRUpdate, false otherwise.
//
//////////
 
Boolean VRMoov_DoIdle (WindowObject theWindowObject)
{
    ApplicationDataHdl      myAppData;
    VRScriptMoviePtr        myPointer;
    Boolean                 myNeedUpdate = false;
 
    if ((**theWindowObject).fInstance == NULL)
        return(myNeedUpdate);
        
    myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
    if (myAppData == NULL)
        return(myNeedUpdate);
    
    // walk the linked list and service any embedded sound-only movies
    myPointer = (VRScriptMoviePtr)(**myAppData).fListArray[kVREntry_QTMovie];
    while (myPointer != NULL) {
        if (myPointer->fQTMovieHasSound && !(myPointer->fQTMovieHasVideo)) {
            MoviesTask(myPointer->fMovie, 0L);
        }
        myPointer = myPointer->fNextEntry;
    }
    
    // now service the (single) embedded video movie, if there is one   
    myPointer = VRMoov_GetEmbeddedVideo(theWindowObject);
    if (myPointer != NULL) {
        // trip the back buffer procedure, which internally calls MoviesTask;
        // note that this is necessary *only* if the window containing the embedded movie
        // isn't the frontmost window
        if ((**theWindowObject).fWindow != QTFrame_GetFrontAppWindow())
            myNeedUpdate = true;
    }
    
    return(myNeedUpdate);
}
        
                
//////////
//
// VRMoov_DumpNodeMovies
// Stop playing all movies enlisted for the current node.
//
//////////
 
void VRMoov_DumpNodeMovies (WindowObject theWindowObject)
{
    VRMoov_DumpSelectedMovies(theWindowObject, kVRSelect_Node);
}
 
 
//////////
//
// VRMoov_DumpSceneMovies
// Stop playing all movies enlisted for the current scene.
//
//////////
 
void VRMoov_DumpSceneMovies (WindowObject theWindowObject)
{
    VRMoov_DumpSelectedMovies(theWindowObject, kVRSelect_Scene);
}
 
 
//////////
//
// VRMoov_DumpSelectedMovies
// Stop any existing embedded movies from playing and then clean up.
//
//////////
 
void VRMoov_DumpSelectedMovies (WindowObject theWindowObject, UInt32 theOptions)
{
    ApplicationDataHdl      myAppData;
    VRScriptMoviePtr        myPointer;
    VRScriptMoviePtr        myNext;
 
    myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
    if (myAppData == NULL)
        return;
 
    // walk the movie list and dump any movies movies associated with this node
    myPointer = (VRScriptMoviePtr)(**myAppData).fListArray[kVREntry_QTMovie];
    while (myPointer != NULL) {
        
        myNext = myPointer->fNextEntry;
        if (((myPointer->fNodeID != kVRAnyNode) && (theOptions == kVRSelect_Node)) ||
            ((myPointer->fNodeID == kVRAnyNode) && (theOptions == kVRSelect_Scene)))
            VRScript_DelistEntry(theWindowObject, (VRScriptGenericPtr)myPointer);
        
        myPointer = myNext;
    }
    
    // clear the existing back buffer imaging proc
    QTVRSetBackBufferImagingProc((**theWindowObject).fInstance, NULL, 0, NULL, 0);
 
    // make sure the back buffer is clean
    QTVRRefreshBackBuffer((**theWindowObject).fInstance, 0);
 
    return;
}
    
 
//////////
//
// VRMoov_GetEmbeddedVideo
// Return the first embedded QuickTime movie that has a video track.
//
//////////
 
VRScriptMoviePtr VRMoov_GetEmbeddedVideo (WindowObject theWindowObject)
{
    ApplicationDataHdl      myAppData;
    VRScriptMoviePtr        myPointer = NULL;
    
    myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
    if (myAppData == NULL)
        return(myPointer);
        
    // walk our linked list of movies to find the target movie
    myPointer = (VRScriptMoviePtr)(**myAppData).fListArray[kVREntry_QTMovie];
    while (myPointer != NULL) {
        if (myPointer->fQTMovieHasVideo)
            return(myPointer);
        myPointer = myPointer->fNextEntry;
    }
    
    return(NULL);
}
 
 
//////////
//
// VRMoov_GetEmbeddedMovieWidth
// Get the width of the embedded movie.
//
//////////
 
float VRMoov_GetEmbeddedMovieWidth (WindowObject theWindowObject)
{
    float                   myWidth = 0.0;
    VRScriptMoviePtr        myPointer;
    
    myPointer = VRMoov_GetEmbeddedVideo(theWindowObject);
    if (myPointer != NULL)
        myWidth = myPointer->fMovieWidth;
            
    return(myWidth);
}
 
 
//////////
//
// VRMoov_SetEmbeddedMovieWidth
// Set the width of the embedded movie.
//
//////////
 
void VRMoov_SetEmbeddedMovieWidth (WindowObject theWindowObject, float theWidth)
{
    QTVRInstance            myInstance;
    VRScriptMoviePtr        myPointer;
    
    if (theWindowObject == NULL)
        return;
        
    myInstance = (**theWindowObject).fInstance;
    if (myInstance == NULL)
        return;
    
    myPointer = VRMoov_GetEmbeddedVideo(theWindowObject);
    if (myPointer != NULL)
        myPointer->fMovieWidth = theWidth;
 
    // clear out the existing area of interest
    QTVRRefreshBackBuffer(myInstance, 0);
 
    // reinstall the back buffer imaging procedure
    VRScript_InstallBackBufferImagingProc(myInstance, theWindowObject);
}
 
 
//////////
//
// VRMoov_GetEmbeddedMovieCenter
// Get the center of the embedded movie.
//
//////////
 
void VRMoov_GetEmbeddedMovieCenter (WindowObject theWindowObject, QTVRFloatPoint *theCenter)
{
    VRScriptMoviePtr        myPointer;
    
    myPointer = VRMoov_GetEmbeddedVideo(theWindowObject);
    if (myPointer == NULL) {
        theCenter->x = 0.0;
        theCenter->y = 0.0;
    } else {
        theCenter->x = myPointer->fMovieCenter.x;
        theCenter->y = myPointer->fMovieCenter.y;
    }       
}
 
 
//////////
//
// VRMoov_SetEmbeddedMovieCenter
// Set the center of the embedded movie.
//
//////////
 
void VRMoov_SetEmbeddedMovieCenter (WindowObject theWindowObject, const QTVRFloatPoint *theCenter)
{
    QTVRInstance            myInstance;
    float                   myX, myY;
    VRScriptMoviePtr        myPointer;
 
    if (theWindowObject == NULL)
        return;
        
    myInstance = (**theWindowObject).fInstance;
    if (myInstance == NULL)
        return;
    
    myX = theCenter->x;
    myY = theCenter->y;
    
    // subject the values passed in to the current view constraints
    QTVRWrapAndConstrain(myInstance, kQTVRPan, myX, &myX);
    QTVRWrapAndConstrain(myInstance, kQTVRTilt, myY, &myY);
            
    myPointer = VRMoov_GetEmbeddedVideo(theWindowObject);
    if (myPointer != NULL) {
        myPointer->fMovieCenter.x = myX;
        myPointer->fMovieCenter.y = myY;
    }
    
    // clear out the existing area of interest
    QTVRRefreshBackBuffer(myInstance, 0);
 
    // reinstall the back buffer imaging procedure
    VRScript_InstallBackBufferImagingProc(myInstance, theWindowObject);
}
 
 
//////////
//
// VRMoov_GetEmbeddedMovieScale
// Get the scale of the embedded movie.
//
//////////
 
float VRMoov_GetEmbeddedMovieScale (WindowObject theWindowObject)
{
    float                   myScale;
    VRScriptMoviePtr        myPointer;
    
    myPointer = VRMoov_GetEmbeddedVideo(theWindowObject);
    if (myPointer == NULL)
        myScale = 0.0;
    else
        myScale = myPointer->fMovieScale;
 
    return(myScale);
}
 
 
//////////
//
// VRMoov_SetEmbeddedMovieScale
// Set the scale factor of the embedded movie.
//
//////////
 
void VRMoov_SetEmbeddedMovieScale (WindowObject theWindowObject, float theScale)
{
    QTVRInstance            myInstance;
    VRScriptMoviePtr        myPointer;
 
    if (theWindowObject == NULL)
        return;
        
    myInstance = (**theWindowObject).fInstance;
    if (myInstance == NULL)
        return;
    
    myPointer = VRMoov_GetEmbeddedVideo(theWindowObject);
    if (myPointer != NULL)
        myPointer->fMovieScale = theScale;
    
    // clear out the existing area of interest
    QTVRRefreshBackBuffer(myInstance, 0);
 
    // reinstall the back buffer imaging procedure
    VRScript_InstallBackBufferImagingProc(myInstance, theWindowObject);
}
 
 
//////////
//
// VRMoov_GetEmbeddedMovieRect
// Get the rectangle of the embedded movie.
//
//////////
 
void VRMoov_GetEmbeddedMovieRect (WindowObject theWindowObject, Rect *theRect)
{
    VRScriptMoviePtr        myPointer;
    
    myPointer = VRMoov_GetEmbeddedVideo(theWindowObject);
    if (myPointer == NULL) {
        theRect->left = 0.0;
        theRect->top = 0.0;
        theRect->right = 0.0;
        theRect->bottom = 0.0;
    } else {
        theRect->left = myPointer->fMovieBox.left;
        theRect->top = myPointer->fMovieBox.top;
        theRect->right = myPointer->fMovieBox.right;
        theRect->bottom = myPointer->fMovieBox.bottom;
    }       
}
 
 
//////////
//
// VRMoov_SetEmbeddedMovieRect
// Set the rectangle of the embedded movie.
//
//////////
 
void VRMoov_SetEmbeddedMovieRect (WindowObject theWindowObject, const Rect *theRect)
{
    QTVRInstance            myInstance;
    VRScriptMoviePtr        myPointer;
 
    if (theWindowObject == NULL)
        return;
        
    myInstance = (**theWindowObject).fInstance;
    if (myInstance == NULL)
        return;
    
    myPointer = VRMoov_GetEmbeddedVideo(theWindowObject);
    if (myPointer != NULL) {
        myPointer->fMovieBox.right = theRect->right;
        myPointer->fMovieBox.bottom = theRect->bottom;
    }
 
    // clear out the existing area of interest
    QTVRRefreshBackBuffer(myInstance, 0);
 
    // reinstall the back buffer imaging procedure
    VRScript_InstallBackBufferImagingProc(myInstance, theWindowObject);
}
 
 
//////////
//
// VRMoov_SetAllBalanceAndVolume
// Set the balance and volume attenuation of all embedded QuickTime movies making sound.
//
//////////
 
void VRMoov_SetAllBalanceAndVolume (WindowObject theWindowObject, float thePan, float theTilt)
{
    ApplicationDataHdl      myAppData;
    VRScriptMoviePtr        myPointer;
#if QD3D_AVAIL
    VRScript3DObjPtr        my3DObjPtr;
#endif  
    
    myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
    if (myAppData == NULL)
        return;
 
    // walk our linked list of embedded QuickTime movies and set the balance and volume of any localized sounds
    myPointer = (VRScriptMoviePtr)(**myAppData).fListArray[kVREntry_QTMovie];
    while (myPointer != NULL) {
        if (myPointer->fQTMovieHasSound)
            if (myPointer->fSoundIsLocalized)
                VRMoov_SetOneBalanceAndVolume(myPointer->fMovie, myPointer->fMediaHandler, thePan, theTilt, myPointer->fMovieCenter.x, myPointer->fVolAngle);
        myPointer = myPointer->fNextEntry;
    }
    
#if QD3D_AVAIL
    // walk our linked list of 3D objects and set the balance and volume of any texture-mapped movies
    my3DObjPtr = (VRScript3DObjPtr)(**myAppData).fListArray[kVREntry_QD3DObject];
    while (my3DObjPtr != NULL) {
        if (my3DObjPtr->fTexture != NULL)
            if (my3DObjPtr->fTextureIsMovie)
                if ((**my3DObjPtr->fTexture).fMediaHandler != NULL)
                    VRMoov_SetOneBalanceAndVolume((**my3DObjPtr->fTexture).fMovie, (**my3DObjPtr->fTexture).fMediaHandler, thePan, theTilt, QTVRUtils_Point3DToPanAngle(-1.0 * my3DObjPtr->fGroupCenter.x, my3DObjPtr->fGroupCenter.y, -1.0 * my3DObjPtr->fGroupCenter.z), QTVRUtils_DegreesToRadians(kVR_TextureMovieVolAngle));
        my3DObjPtr = my3DObjPtr->fNextEntry;
    }
#endif  
}
    
    
//////////
//
// VRMoov_SetOneBalanceAndVolume
// Set the balance and volume attenuation of an embedded QuickTime movie.
//
//////////
 
void VRMoov_SetOneBalanceAndVolume (Movie theMovie, MediaHandler theMediaHandler, float thePan, float theTilt, float theMoviePan, float theVolAngle)
{
#pragma unused(theTilt)
 
    short           myValue;
    float           myPanDelta;
    float           myCosDelta;         // cosine of pan angle delta from movie center
    float           myCosLimit;         // cosine of attenuation cone limit angle
    
    myPanDelta = thePan - theMoviePan;
 
    // ***set the balance
    myValue = kQTMaxSoundVolume * sin(myPanDelta);
    MediaSetSoundBalance(theMediaHandler, myValue);
    
    // ***set the volume
    myCosDelta = cos(myPanDelta);
    myCosLimit = cos(theVolAngle);
 
    if (myCosDelta >= myCosLimit)
        // inside cone of attenuation, volume scales from 1.0 (at center) to 0.0 (at cone edge)
        myValue = kQTMaxSoundVolume * ((myCosDelta - myCosLimit) / (1 - myCosLimit));
    else
        // outside cone of attenuation, volume is 0.0;
        myValue = kNoVolume;
    
    if (myValue != GetMovieVolume(theMovie))
        SetMovieVolume(theMovie, myValue);
}
 
 
//////////
//
// VRMoov_CalcImagingMatrix
// Calculate the movie matrix required to draw the embedded movie into the specified rectangle.
//
//////////
 
OSErr VRMoov_CalcImagingMatrix (WindowObject theWindowObject, Rect *theBBufRect)
{
    ApplicationDataHdl      myAppData = NULL;
    VRScriptMoviePtr        myPointer = NULL;
    Rect                    myDestRect = *theBBufRect;
 
    myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
    if (myAppData == NULL) 
        return(paramErr);
        
    myPointer = VRMoov_GetEmbeddedVideo(theWindowObject);
    if (myPointer == NULL) 
        return(paramErr);
 
    // reset the current movie matrix with the original movie matrix of the embedded movie;
    // we need to preserve that matrix in our calculations below if we are not drawing the
    // movie into an offscreen GWorld
    myPointer->fMovieMatrix = myPointer->fOrigMovieMatrix;
    
    // in general, it's easiest to construct the desired matrix by first doing the scaling
    // and then doing the rotation and translation (if necessary); so we need to swap the
    // right and bottom edges of the back buffer rectangle before doing the scaling, if a
    // rotation will also be necessary
    if (!(**myAppData).fBackBufferIsHoriz) {
        myDestRect.bottom = theBBufRect->right;
        myDestRect.right = theBBufRect->bottom;
    }
    
    // set up the scaling matrix
    // (MapMatrix concatenates the new matrix to the existing matrix, whereas RectMatrix first
    // sets the existing matrix to the identity matrix)
    if (myPointer->fUseOffscreenGWorld)
        RectMatrix(&myPointer->fMovieMatrix, &myPointer->fMovieBox, &myDestRect);
    else
        MapMatrix(&myPointer->fMovieMatrix, &myPointer->fMovieBox, &myDestRect);
 
    // add a rotation and translation, if necessary (and if requested to do so)
    if (!(**myAppData).fBackBufferIsHoriz)
        if (myPointer->fDoRotateMovie) {
            RotateMatrix(&myPointer->fMovieMatrix, Long2Fix(-90), 0, 0);
            TranslateMatrix(&myPointer->fMovieMatrix, 0, Long2Fix(RECT_HEIGHT(*theBBufRect)));
        }
 
    return(noErr);
}
 
 
//////////
//
// VRMoov_SetupDecompSeq
// Set up the decompression sequence for DecompressSequenceFrameS.
//
// This needs to be called whenever either the rectangle or the GWorld of the back buffer changes.
//
//////////
 
OSErr VRMoov_SetupDecompSeq (VRScriptMoviePtr theEntry, GWorldPtr theDestGWorld)
{
    short                   myMode = srcCopy;
    OSErr                   myErr = noErr;
    
    if (theEntry == NULL) 
        return(paramErr);
 
    // make sure we don't have a decompression sequence already open
    VRMoov_RemoveDecompSeq(theEntry);
    
    // set up the transfer mode
    if (theEntry->fCompositeMovie)
        myMode = srcCopy | transparent;
        
    // set up the image decompression sequence
    myErr = DecompressSequenceBegin(    &theEntry->fImageSequence,
                                        theEntry->fImageDesc,
                                        (CGrafPtr)theDestGWorld,
                                        NULL,
                                        NULL,                           // entire source image
                                        &theEntry->fMovieMatrix,
                                        myMode,
                                        NULL,                           // no mask
                                        0,
                                        (**(theEntry->fImageDesc)).spatialQuality,
                                        NULL);
                                    
    return(myErr);
}
 
 
//////////
//
// VRMoov_RemoveDecompSeq
// Remove the decompression sequence for DecompressSequenceFrameS.
//
//////////
 
OSErr VRMoov_RemoveDecompSeq (VRScriptMoviePtr theEntry)
{
    OSErr                   myErr = paramErr;
    
    if (theEntry != NULL) 
        if (theEntry->fImageSequence != 0) {
            myErr = CDSequenceEnd(theEntry->fImageSequence);
            theEntry->fImageSequence = 0;
        }
    
    return(myErr);
}
 
 
//////////
//
// VRMoov_BackBufferImagingProc
// The back buffer imaging procedure:
//  * get a frame of movie and image it into the back buffer
//  * also, do any additional compositing that might be desired
// This function is called by VRScript_BackBufferImagingProc.
//
//////////
 
PASCAL_RTN OSErr VRMoov_BackBufferImagingProc (QTVRInstance theInstance, Rect *theRect, UInt16 theAreaIndex, UInt32 theFlagsIn, UInt32 *theFlagsOut, SInt32 theRefCon)
{
#pragma unused(theAreaIndex)
 
    WindowObject            myWindowObject = (WindowObject)theRefCon;
    ApplicationDataHdl      myAppData = NULL;
    Movie                   myMovie = NULL;
    Boolean                 myIsDrawing = theFlagsIn & kQTVRBackBufferRectVisible;
    VRScriptMoviePtr        myPointer = NULL;
    GWorldPtr               myBBufGWorld, myMovGWorld;
    GDHandle                myBBufGDevice, myMovGDevice;
    Rect                    myRect;
    OSErr                   myErr = paramErr;
    
    //////////
    //
    // initialize; make sure that we've got the data we need to continue
    //
    //////////
 
    // assume we're not going to draw anything
    *theFlagsOut = 0;
    
    if ((theInstance == NULL) || (myWindowObject == NULL)) 
        return(paramErr);
 
    myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(myWindowObject);
    if (myAppData == NULL) 
        return(paramErr);
 
    myPointer = VRMoov_GetEmbeddedVideo(myWindowObject);
    if (myPointer == NULL)
        return(paramErr);
        
    HLock((Handle)myAppData);
 
    myMovie = myPointer->fMovie;
    if ((myMovie == NULL) || (IsMovieDone(myMovie))) {
        // we don't have an embedded movie or it's done playing, so remove this back-buffer imaging procedure
        QTVRSetBackBufferImagingProc((**myWindowObject).fInstance, NULL, 0, NULL, 0);
        goto bail;
    }
    
    //////////
    //
    // make sure that the movie GWorld is set correctly;
    // note that we call SetMovieGWorld only if we have to (for performance reasons)
    //
    //////////
    
    // get the current graphics world
    // (on entry, the current graphics world is [usually] set to the back buffer)
    GetGWorld(&myBBufGWorld, &myBBufGDevice);
 
    // get the embedded movie's graphics world
    GetMovieGWorld(myMovie, &myMovGWorld, &myMovGDevice);
 
    if (myPointer->fUseOffscreenGWorld) {
        // we're using an offscreen graphics world, so set movie's GWorld to be that offscreen graphics world
        if (myMovGWorld != myPointer->fOffscreenGWorld)
            SetMovieGWorld(myMovie, myPointer->fOffscreenGWorld, GetGWorldDevice(myPointer->fOffscreenGWorld));         
    } else {
        // we're not using an offscreen graphics world, so set movie GWorld to be the back buffer;
        if ((myMovGWorld != myBBufGWorld) || (myMovGDevice != myBBufGDevice))
            SetMovieGWorld(myMovie, myBBufGWorld, myBBufGDevice);
    }
 
    //////////
    //
    // make sure the movie rectangle and movie matrix are set correctly
    //
    //////////
    
    if (myIsDrawing) {
        // if we weren't previously visible, make sure we are now
        GetMovieBox(myMovie, &myRect);
        if (EmptyRect(&myRect))
            SetMovieBox(myMovie, &myPointer->fMovieBox);
        
        // when no offscreen GWorld is being used...
        if (!myPointer->fUseOffscreenGWorld) {
            
            // ...make sure the movie matrix is set correctly...
            if (!MacEqualRect(theRect, &myPointer->fPrevBBufRect)) {
                VRMoov_CalcImagingMatrix(myWindowObject, theRect);
                SetMovieMatrix(myMovie, &myPointer->fMovieMatrix);
            }
            
            // ...and, if we are compositing, force QuickTime to draw a movie frame when MoviesTask is called
            // (since the previous frame was automatically erased from the back buffer by QuickTime VR)
            if (myPointer->fCompositeMovie)
                UpdateMovie(myMovie);
        }
        
    } else {
        // if we're not visible, set the movie rectangle to an empty rectangle
        // so we're not wasting time trying to draw a movie frame
        MacSetRect(&myRect, 0, 0, 0, 0);
        SetMovieBox(myMovie, &myRect);
    }
 
    //////////
    //
    // draw a new movie frame into the movie's graphics world (and play movie sound)
    //
    //////////
    
    MoviesTask(myMovie, 0);
    
    // if we got here, everything is okay so far
    myErr = noErr;
 
    //////////
    //
    // perform any additional compositing
    //
    //////////
    
    // that is, draw, using the current chroma key color, anything to be dropped out of the image;
    // note that this technique works *only* if we're using an offscreen graphics world
    if (myPointer->fUseOffscreenGWorld && myPointer->fCompositeMovie  && myPointer->fUseHideRegion && myIsDrawing) {
        RGBColor    myColor;
    
        // recalculate the hide region, but only if the view has changed
        if ((**myAppData).fViewHasChanged) {
            Handle      myHotSpotList = NewHandleClear(4);
            UInt32      myHotSpotCount = 0;
            UInt32      myHotSpotID = 0;
            OSType      myHotSpotType;
            RgnHandle   myHotSpotRgn = NULL;
        
            // dispose of any existing hide region
            if (myPointer->fHideRegion != NULL)
                DisposeRgn(myPointer->fHideRegion);
                
            // create a new, empty hide region
            myPointer->fHideRegion = NewRgn();
                
            // get a list of all visible hot spots; here, myHotSpotList is resized (if necessary) to hold
            // a list of the IDs of all visible hot spots
            myHotSpotCount = QTVRGetVisibleHotSpots((**myWindowObject).fInstance, myHotSpotList);
            if (myHotSpotCount > 0) {
                UInt32  *myHotSpotIDPtr = NULL;
                
                HLock(myHotSpotList);
                myHotSpotIDPtr = (UInt32 *)*myHotSpotList;
                while (myHotSpotCount > 0) {
                    // get the next hot spot ID in the list
                    myHotSpotID = *myHotSpotIDPtr;
                    
                    // get the type of the hot spot
                    QTVRGetHotSpotType((**myWindowObject).fInstance, myHotSpotID, &myHotSpotType);
                    if (myHotSpotType == kVRMoov_HideHotSpotType) {
                        myHotSpotRgn = NewRgn();
                        myErr = QTVRGetHotSpotRegion((**myWindowObject).fInstance, myHotSpotID, myHotSpotRgn);
                        if (myErr == noErr) {
                            // merge the existing hide region and the region of this hot spot
                            MacUnionRgn(myPointer->fHideRegion, myHotSpotRgn, myPointer->fHideRegion);
                        }
                        
                        DisposeRgn(myHotSpotRgn);
                    }
                
                    myHotSpotIDPtr++;
                    myHotSpotCount--;
                }
                
                HUnlock(myHotSpotList);
            }
            
            if (myHotSpotList != NULL)
                DisposeHandle(myHotSpotList);
        }
        
        // since we're using an offscreen graphics world, make sure we draw there
        SetGWorld(myPointer->fOffscreenGWorld, GetGWorldDevice(myPointer->fOffscreenGWorld));
 
        // set up compositing environment
        GetForeColor(&myColor);
        RGBForeColor(&myPointer->fChromaColor);
                
        // do the drawing
        if (myPointer->fHideRegion != NULL)
            MacPaintRgn(myPointer->fHideRegion);
            
        // restore original drawing environment
        RGBForeColor(&myColor);
        
        // restore original graphics world
        SetGWorld(myBBufGWorld, myBBufGDevice);
    }
 
    //////////
    //
    // if we're using an offscreen graphics world, copy it into the back buffer
    //
    //////////
    
    if (myIsDrawing) {
    
        if (myPointer->fUseOffscreenGWorld) {
            PixMapHandle    myPixMap;
            
            // if anything relevant to DecompressSequenceFrameS has changed, reset the decompression sequence
            if ((myBBufGWorld != myPointer->fPrevBBufGWorld) || !(MacEqualRect(theRect, &myPointer->fPrevBBufRect))) {
                VRMoov_CalcImagingMatrix(myWindowObject, theRect);
                VRMoov_SetupDecompSeq(myPointer, myBBufGWorld);
            }
            
            myPixMap = GetGWorldPixMap(myPointer->fOffscreenGWorld);
            LockPixels(myPixMap);
            
            // set the chroma key color, if necessary
            if (myPointer->fCompositeMovie)
                RGBBackColor(&myPointer->fChromaColor);
            
            // copy the image from the offscreen graphics world into the back buffer
            myErr = DecompressSequenceFrameS(   myPointer->fImageSequence,
#if TARGET_CPU_68K
                                                StripAddress(GetPixBaseAddr(myPixMap)),
#else
                                                GetPixBaseAddr(myPixMap),
#endif
                                                (**(myPointer->fImageDesc)).dataSize,
                                                0,
                                                NULL,
                                                NULL);
 
            // reset the chroma key color;
            // we need to do this because the buffer we just drew into might NOT actually
            // be the real back buffer (see Virtual Reality Programming With QuickTime VR, p. 1-154);
            // the copy between the intermediate buffer and the back buffer respects the current back color.
            if (myPointer->fCompositeMovie)
                RGBBackColor(&kWhiteColor);
                
            UnlockPixels(myPixMap);
        }
    }
    
    //////////
    //
    // finish up
    //
    //////////
    
    // keep track of the GWorld and rectangle passed to us this time
    myPointer->fPrevBBufGWorld = myBBufGWorld;
    myPointer->fPrevBBufRect = *theRect;
    
    // if we drew something, tell QuickTime VR
    if (myIsDrawing)
        *theFlagsOut = kQTVRBackBufferFlagDidDraw;
    
bail:   
    HUnlock((Handle)myAppData);
    return(myErr);
}
 
 
//////////
//
// VRMoov_CoverProc
// The cover function of the embedded movie.
//
//////////
 
PASCAL_RTN OSErr VRMoov_CoverProc (Movie theMovie, RgnHandle theRegion, SInt32 theRefCon)
{
#pragma unused(theMovie, theRegion, theRefCon)
 
    return(noErr);
}
 
 
//////////
//
// VRMoov_SetVideoGraphicsMode
// Set the video media graphics mode of the embedded movie.
//
//////////
 
void VRMoov_SetVideoGraphicsMode (Movie theMovie, VRScriptMoviePtr theEntry, Boolean theSetVGM)
{
    Track       myTrack;
    Media       myMedia;
 
    if (theEntry == NULL)
        return;
        
    myTrack = GetMovieIndTrackType(theMovie, 1, VideoMediaType, movieTrackMediaType | movieTrackEnabledOnly);
    if (myTrack != NULL) {
        myMedia = GetTrackMedia(myTrack);
        if (theSetVGM)
            MediaSetGraphicsMode(GetMediaHandler(myMedia), srcCopy | transparent, &theEntry->fChromaColor);
        else
            MediaSetGraphicsMode(GetMediaHandler(myMedia), srcCopy, &kWhiteColor);
    } 
        
    return;
}
 
 
//////////
//
// VRMoov_GetFinishedMovie
// Get the first enlisted movie that is done playing.
//
//////////
 
VRScriptMoviePtr VRMoov_GetFinishedMovie (WindowObject theWindowObject)
{
    ApplicationDataHdl  myAppData;
    VRScriptMoviePtr    myPointer = NULL;
 
    myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);    
    if (myAppData == NULL)
        return(NULL);
    
    // walk our linked list of movies to find the target movie
    myPointer = (VRScriptMoviePtr)(**myAppData).fListArray[kVREntry_QTMovie];
    while (myPointer != NULL) {
        if (IsMovieDone(myPointer->fMovie))
            return(myPointer);
        
        myPointer = myPointer->fNextEntry;
    }
    
    return(NULL);
}
 
 
//////////
//
// VRMoov_CheckForCompletedMovies
// Clean up any movies that are finished playing.
//
//////////
 
void VRMoov_CheckForCompletedMovies (WindowObject theWindowObject)
{
    VRScriptMoviePtr        myPointer = NULL;
    
    // delist any completed movies for the specified movie window
    if (theWindowObject != NULL)
        while ((myPointer = VRMoov_GetFinishedMovie(theWindowObject)) != NULL)
            VRScript_DelistEntry(theWindowObject, (VRScriptGenericPtr)myPointer);
}
 
 
//////////
//
// VRMoov_DumpEntryMem
// Release any memory associated with the specified list entry.
//
//////////
 
void VRMoov_DumpEntryMem (VRScriptMoviePtr theEntry)
{
    if (theEntry != NULL) {
        
        if (theEntry->fMovie != NULL) {
            StopMovie(theEntry->fMovie);
            DisposeMovie(theEntry->fMovie);
        }
            
        if (theEntry->fQTMovieHasVideo) {
            if (theEntry->fOffscreenPixMap != NULL)
                UnlockPixels(theEntry->fOffscreenPixMap);
                
            if (theEntry->fOffscreenGWorld != NULL)
                DisposeGWorld(theEntry->fOffscreenGWorld);
                
            if (theEntry->fHideRegion != NULL)
                DisposeRgn(theEntry->fHideRegion);
                
            VRMoov_RemoveDecompSeq(theEntry);
            
            if (theEntry->fImageDesc != NULL)
                DisposeHandle((Handle)theEntry->fImageDesc);
        }
        
        theEntry->fMovie = NULL;
        theEntry->fOffscreenGWorld = NULL;
        theEntry->fPrevBBufGWorld = NULL;
        theEntry->fMediaHandler = NULL;
        theEntry->fHideRegion = NULL;
        theEntry->fImageDesc = NULL;
        
        free(theEntry->fPathname);
    }
}