QTBigScreen.c

//////////
//
//  File:       QTBigScreen.c
//
//  Contains:   Code for playing QuickTime movies fullscreen.
//
//  Written by: Tim Monroe
//
//  Copyright:  © 2002 by Apple Computer, Inc., all rights reserved.
//
//  Change History (most recent first):
//
//     <1>      07/01/02    rtm     first file
//
//////////
 
//////////
//
// header files
//
//////////
 
#include "QTBigScreen.h"
 
 
//////////
//
// global variables
//
//////////
 
WindowObject                    gFullScreenWindowObject = NULL;
Boolean                         gEndingFullScreen = false;
 
 
//////////
//
// QTBig_InitWindowData
// Do any fullscreen-specific initialization for the specified window.
//
//////////
 
ApplicationDataHdl QTBig_InitWindowData (WindowObject theWindowObject)
{
    ApplicationDataHdl      myAppData = NULL;
 
    // if we already have some window data, dump it
    myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
    if (myAppData != NULL)
        QTBig_DumpWindowData(theWindowObject);
 
    // allocate a new application data handle
    myAppData = (ApplicationDataHdl)NewHandleClear(sizeof(ApplicationDataRecord));
        
    return(myAppData);
}
 
 
//////////
//
// QTBig_DumpWindowData
// Do any fullscreen-specific tear-down for the specified window.
//
//////////
 
void QTBig_DumpWindowData (WindowObject theWindowObject)
{
    ApplicationDataHdl      myAppData = NULL;
        
    myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
    if (myAppData != NULL) {
        if ((**myAppData).fFullScreenWindow != NULL)
            QTBig_StopFullscreen(theWindowObject);
        
        DisposeHandle((Handle)myAppData);
        (**theWindowObject).fAppData = NULL;
    }
}
 
 
//////////
//
// QTBig_StartFullscreen
// Start fullscreen playback.
//
//////////
 
OSErr QTBig_StartFullscreen (WindowObject theWindowObject)
{
    MovieController         myMC = NULL;
    Movie                   myMovie = NULL;
    ApplicationDataHdl      myAppData = NULL;
    long                    myFlags = fullScreenAllowEvents;
    OSErr                   myErr = paramErr;
        
    if (theWindowObject == NULL)
        goto bail;
 
    myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
    myMovie = (**theWindowObject).fMovie;
    myMC = (**theWindowObject).fController;
    
    if ((myAppData == NULL) || (myMovie == NULL) || (myMC == NULL))
        goto bail;
    
    if ((**myAppData).fFullScreenWindow == NULL) {
        short               myOrigScreenWidth = 0;
        short               myOrigScreenHeight = 0;
        short               myNewScreenWidth = 0;
        short               myNewScreenHeight = 0;
        short               myScreenWidth = 0;
        short               myScreenHeight = 0;
        short               myMovieWidth = 0;
        short               myMovieHeight = 0;
        Fixed               myScreenRatio;
        Fixed               myMovieRatio;
        Rect                myRect;
        RGBColor            myColor = {0x0000, 0x0000, 0x0000}; // black
            
        // remember some of the current state
        GetMovieGWorld(myMovie, &(**myAppData).fOrigMovieGWorld, NULL);
        GetMovieBox(myMovie, &(**myAppData).fOrigMovieRect);
        MCGetControllerBoundsRect(myMC, &(**myAppData).fOrigControllerRect);
        (**myAppData).fOrigControllerVis = MCGetVisible(myMC);
        (**myAppData).fOrigControllerAttached = MCIsControllerAttached(myMC);
        (**myAppData).fOrigWindow = (**theWindowObject).fWindow;
        
        // get the current screen resolution
        myErr = BeginFullScreen(&(**myAppData).fRestoreState, NULL, &myScreenWidth, &myScreenHeight, NULL, NULL, fullScreenPreflightSize);
        if (myErr != noErr)
            goto bail;
        
        // keep track of the original screen resolution
        myOrigScreenHeight = myScreenHeight;
        myOrigScreenWidth = myScreenWidth;
        
        // calculate the destination rectangle
        GetMovieNaturalBoundsRect(myMovie, &myRect);
        MacOffsetRect(&myRect, -myRect.left, -myRect.top);
        
        myMovieWidth = myRect.right;
        myMovieHeight = myRect.bottom;
        
        myMovieRatio = FixRatio(myMovieWidth, myMovieHeight);
        myScreenRatio = FixRatio(myScreenWidth, myScreenHeight);
 
        // scale the movie rectangle to fit the screen ratio
        if (myMovieRatio > myScreenRatio) {
            myMovieHeight = (myScreenWidth * myMovieHeight) / myMovieWidth;
            myMovieWidth = myScreenWidth;
        } else {
            myMovieWidth = (myScreenHeight * myMovieWidth) / myMovieHeight;
            myMovieHeight = myScreenHeight;
        }
        
        MacSetRect(&myRect, 0, 0, myMovieWidth, myMovieHeight);
 
        myScreenWidth = myMovieWidth;
        myScreenHeight = myMovieHeight;
 
        // begin full-screen display
        myErr = BeginFullScreen(&(**myAppData).fRestoreState, NULL, &myScreenWidth, &myScreenHeight, &(**myAppData).fFullScreenWindow, &myColor, myFlags); 
        if (myErr != noErr)
            goto bail;
 
        // determine whether the resolution changed; if it has changed, we must have passed in a supported resolution,
        // so we want the movie to fill the screen; otherwise, we need to offset the movie to center it in the screen
        if ((myScreenWidth == myOrigScreenWidth) && (myScreenHeight == myOrigScreenHeight))
            MacOffsetRect(&myRect, (myScreenWidth - myMovieWidth) / 2, (myScreenHeight - myMovieHeight) / 2);
            
#if TARGET_OS_WIN32
        // on Windows, set a window procedure for the new window
        QTMLSetWindowWndProc((**myAppData).fFullScreenWindow, QTBig_HandleMessages);
#endif
 
        // hide the original window
        QTFrame_SetWindowVisState(theWindowObject, false);
        
        // attach the existing window object to the new window
#if TARGET_OS_MAC
        SetWRefCon((**myAppData).fFullScreenWindow, (long)theWindowObject);
#endif
#if TARGET_OS_WIN32
        SetWindowLong(GetPortNativeWindow((GrafPtr)(**myAppData).fFullScreenWindow), GWL_USERDATA, (LPARAM)theWindowObject);
#endif
        
        // set the movie and movie controller state to the new window and rectangle 
        SetGWorld(GetWindowPort((**myAppData).fFullScreenWindow), NULL);
        SetMovieGWorld(myMovie, GetWindowPort((**myAppData).fFullScreenWindow), NULL);
        MCSetControllerPort(myMC, GetWindowPort((**myAppData).fFullScreenWindow));
        MCSetControllerAttached(myMC, false);
        MCSetControllerBoundsRect(myMC, &myRect);
        
        SetMovieBox(myMovie, &myRect);
        MCSetVisible(myMC, false);
        MCActivate(myMC, (**myAppData).fFullScreenWindow, true);
        
        (**theWindowObject).fWindow = QTFrame_GetWindowReferenceFromWindow((**myAppData).fFullScreenWindow);
 
#if TARGET_API_MAC_CARBON
        HiliteWindow((**myAppData).fFullScreenWindow, true);
#endif
 
#if END_FULLSCREEN_AT_MOVIE_END
        // install a callback procedure to return linear, non-looping movies
        // to normal mode at the end of the movie
        if (QTBig_MovieIsStoppable(myMC))
            QTBig_InstallCallBack(theWindowObject);
#endif
    }
    
    gFullScreenWindowObject = theWindowObject;
    
bail:
    return(myErr);
}
 
 
//////////
//
// QTBig_StopFullscreen
// Stop fullscreen playback.
//
//////////
 
OSErr QTBig_StopFullscreen (WindowObject theWindowObject)
{
    ApplicationDataHdl      myAppData = NULL;
    OSErr                   myErr = paramErr;
        
    if (theWindowObject == NULL)
        goto bail;
 
    myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
    if (myAppData != NULL) {
        if ((**myAppData).fFullScreenWindow != NULL) {
 
            // restore the original settings
            (**theWindowObject).fWindow = (**myAppData).fOrigWindow;
 
            MacSetPort(QTFrame_GetPortFromWindowReference((**theWindowObject).fWindow));
            
            SetMovieGWorld((**theWindowObject).fMovie, (CGrafPtr)(**myAppData).fOrigMovieGWorld, GetGWorldDevice((CGrafPtr)(**myAppData).fOrigMovieGWorld));
            SetMovieBox((**theWindowObject).fMovie, &(**myAppData).fOrigMovieRect);
 
            MCSetControllerPort((**theWindowObject).fController, (CGrafPtr)(**myAppData).fOrigMovieGWorld);
            MCSetControllerAttached((**theWindowObject).fController, (**myAppData).fOrigControllerAttached);
            MCSetVisible((**theWindowObject).fController, (**myAppData).fOrigControllerVis);
            MCSetControllerBoundsRect((**theWindowObject).fController, &(**myAppData).fOrigControllerRect);
 
            gEndingFullScreen = true;
 
            // end fullscreen playback
            myErr = EndFullScreen((**myAppData).fRestoreState, 0L);
            
            gEndingFullScreen = false;
 
            // empty out the data structures and global variables
            (**myAppData).fOrigWindow = NULL;
            (**myAppData).fFullScreenWindow = NULL;
            (**myAppData).fRestoreState = NULL;
            (**myAppData).fOrigMovieGWorld = NULL;
            
            gFullScreenWindowObject = NULL;
                        
#if END_FULLSCREEN_AT_MOVIE_END
            // dispose of the CallMeWhen callback and the callback UPP
            if ((**myAppData).fCallBack != NULL)
                DisposeCallBack((**myAppData).fCallBack);
                
            if ((**myAppData).fCallBackUPP != NULL)
                DisposeQTCallBackUPP((**myAppData).fCallBackUPP);
                
            (**myAppData).fCallBack = NULL;
            (**myAppData).fCallBackUPP = NULL;
#endif
                
            // make sure the movie window is the correct size and then show it again
            QTFrame_SizeWindowToMovie(theWindowObject);
            QTFrame_SetWindowVisState(theWindowObject, true);
        }
    }
 
bail:
    return(myErr);
}
 
 
#if END_FULLSCREEN_AT_MOVIE_END
//////////
//
// QTBig_MovieIsStoppable
// Is the specified movie non-looping and non-interactive?
//
//////////
 
Boolean QTBig_MovieIsStoppable (MovieController theMC)
{
    long        myFlags = 0L;
 
    MCGetControllerInfo(theMC, &myFlags);
    
    if ((myFlags & mcInfoIsLooping) || (myFlags & mcInfoMovieIsInteractive))
        return(false);
    else
        return(true);
}
 
 
//////////
//
// QTBig_InstallCallBack
// Install a CallMeWhen callback procedure.
//
//////////
 
void QTBig_InstallCallBack (WindowObject theWindowObject)
{
    ApplicationDataHdl      myAppData = NULL;
    QTCallBack              myCallBack = NULL;
        
    if (theWindowObject == NULL)
        return;
 
    if ((**theWindowObject).fMovie == NULL)
        return;
        
    myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(theWindowObject);
    if (myAppData == NULL)
        return;
    
    myCallBack = NewCallBack(GetMovieTimeBase((**theWindowObject).fMovie), callBackAtExtremes);
    if (myCallBack != NULL) {
        (**myAppData).fCallBack = myCallBack;
        (**myAppData).fCallBackUPP = NewQTCallBackUPP(QTBig_FullscreenCallBack);
        CallMeWhen(myCallBack, (**myAppData).fCallBackUPP, (long)theWindowObject, triggerAtStop, 0, 0);
    }
}
 
 
//////////
//
// QTBig_FullscreenCallBack
// A CallMeWhen callback procedure that ends fullscreen playback.
//
// The theRefCon parameter is assumed to be a WindowObject.
//
//////////
 
PASCAL_RTN void QTBig_FullscreenCallBack (QTCallBack theCallBack, long theRefCon)
{
    WindowObject            myWindowObject = (WindowObject)theRefCon;
    ApplicationDataHdl      myAppData = NULL;
    QTCallBack              myCallBack = NULL;
        
    if (myWindowObject == NULL)
        return;
 
    myAppData = (ApplicationDataHdl)QTFrame_GetAppDataFromWindowObject(myWindowObject);
    if (myAppData == NULL)
        return;
 
    if ((**myAppData).fCallBack != theCallBack)
        return;
 
    // mark this window for ending fullscreen mode
    (**myAppData).fEndFullscreenNeeded = true;
 
    // clean up the callback stuff
    if ((**myAppData).fCallBack != NULL)
        DisposeCallBack((**myAppData).fCallBack);
        
    if ((**myAppData).fCallBackUPP != NULL)
        DisposeQTCallBackUPP((**myAppData).fCallBackUPP);
        
    (**myAppData).fCallBack = NULL;
    (**myAppData).fCallBackUPP = NULL;
}
#endif  // END_FULLSCREEN_AT_MOVIE_END
 
 
#if TARGET_OS_WIN32
//////////
//
// QTBig_HandleMessages
// Handle Windows messages for the full-screen window.
// 
//////////
 
LRESULT CALLBACK QTBig_HandleMessages (HWND theWnd, UINT theMessage, UINT wParam, LONG lParam)
{
    MovieController         myMC = NULL;
    Movie                   myMovie = NULL;
    WindowObject            myWindowObject = NULL;
    MSG                     myMsg = {0};
    EventRecord             myMacEvent;
    LONG                    myPoints = GetMessagePos();
    BOOL                    myIsHandled = false;
 
    if (gFullScreenWindowObject == NULL)
        goto bail;
 
    // make sure we don't get called while the movie is returning to normal state
    if (gEndingFullScreen)
        goto bail;
    
    // get the window object, movie, and movie controller for this window
    myWindowObject = gFullScreenWindowObject;       
    if (myWindowObject != NULL) {
        myMC = (**myWindowObject).fController;
        myMovie = (**myWindowObject).fMovie;
    }
        
    // give the movie controller this message first
    if (myMC != NULL) {
        LONG            myPoints = GetMessagePos();
 
        myMsg.hwnd = theWnd;
        myMsg.message = theMessage;
        myMsg.wParam = wParam;
        myMsg.lParam = lParam;
        myMsg.time = GetMessageTime();
        myMsg.pt.x = LOWORD(myPoints);
        myMsg.pt.y = HIWORD(myPoints);
 
        // translate a Windows event to a Mac event
        WinEventToMacEvent(&myMsg, &myMacEvent);
 
        // let the application-specific code have a chance to intercept the event
        myIsHandled = QTApp_HandleEvent(&myMacEvent);
    }
 
    switch (theMessage) {
        case WM_CHAR:
            // do any application-specific key press handling
            myIsHandled = QTApp_HandleKeyPress((char)wParam);
            break;
    }
    
bail:
 
    return(DefWindowProc(theWnd, theMessage, wParam, lParam));
}
#endif  // TARGET_OS_WIN32
 
 
//////////
//
// QTFrame_SetWindowVisState
// Show or hide the window associated with the specified window object.
//
//////////
 
void QTFrame_SetWindowVisState (WindowObject theWindowObject, Boolean theState)
{
    // make sure we have a non-NULL window object and window
    if (theWindowObject == NULL)
        return;
        
    if ((**theWindowObject).fWindow == NULL)
        return;
        
    // set the visibility state of the window
#if TARGET_OS_MAC
    if (theState)
        MacShowWindow((**theWindowObject).fWindow);
    else
        HideWindow((**theWindowObject).fWindow);
#endif
#if TARGET_OS_WIN32
    ShowWindow((**theWindowObject).fWindow, theState);      
#endif
}