HackTV.c

/*
  File:         HackTV.c
  Contains:     Hack TV Routines.
  Written by:   Gary Woodcock / QT Engineering
  Copyright:    © 1992-1994 by Apple Computer, Inc., all rights reserved.
  Change History (most recent first):
  <1>       12/4/94     khs     changed the format of the file to the new look and feel
  To Do:
*/
 
 
// INCLUDES
#include <Menus.h>
#include <Windows.h>
#include <QuickDraw.h>
#include <OSEvents.h>
#include <Resources.h>
#include <Desk.h>
#include <Fonts.h>
#include <ToolUtils.h>
#include <Scrap.h>
#include <Printing.h>
#include <Errors.h>
#include <SegLoad.h>
 
#include <QuickTimeComponents.h>
#include <ImageCompression.h>
 
#ifndef THINK_C
#include <Packages.h>
#endif THINK_C
 
 
// CONSTANTS
// Menu bar
enum
{
    kMenuBarID = 128
};
 
// Menus
enum
{
    kAppleID = 128, kFileID, kEditID, kMonitorID
};
 
// Apple menu items
enum
{
    kAboutItem = 1
};
 
// File menu items
enum
{
    kPageSetupItem = 1, kPrintItem, kQuitItem = 4
};
 
// Edit menu items
enum
{
    kUndoItem = 1, kCutItem = 3, kCopyItem, kPasteItem, kClearItem
};
 
// Monitor menu items
enum
{
    kVideoSettingsItem = 1, kSoundSettingsItem, kQuarterSizeItem = 4, kHalfSizeItem, kFullSizeItem
};
 
// Dialog IDs
enum
{
    kAboutDLOGID = 128, kMonitorDLOGID
};
 
// Common DITL items
enum
{
    kAboutOKButton = 1, kAboutOKButtonOutline
};
 
 
// GLOBALS
MenuHandle gAppleMenu;
MenuHandle gFileMenu;
MenuHandle gEditMenu;
MenuHandle gMonitorMenu;
EventRecord gTheEvent;
Boolean gQuitFlag;
SeqGrabComponent gSeqGrabber;
SGChannel gVideoChannel;
SGChannel gSoundChannel;
WindowPtr gMonitor;
Rect gActiveVideoRect;
PicHandle gMonitorPICT;
Boolean gFullSize;
Boolean gHalfSize;
Boolean gQuarterSize;
THPrint gPrintRec;
ICMAlignmentProcRecordPtr gSeqGrabberAlignProc;
 
 
// FUNCTION PROTOTYPES
static void DoInit(void);
 
static void DoMenuSetup(void);
 
static void HandleEvent(void);
 
static void HandleMouseDown(void);
 
static void AdjustMenus(void);
 
static void Enable(Handle menu,
                   short item,
                   Boolean ok);
 
static void HandleMenu(long menu);
 
static void DoAboutDialog(void);
 
static void DoQuit(void);
 
pascal void AboutDrawProc(DialogPtr theDialog,
                          short theItemNum);
 
static OSErr XorRectToRgn(Rect* srcRectA,
                          Rect* srcRectB,
                          RgnHandle* destRgn);
 
pascal Boolean SeqGrabberModalFilterProc(DialogPtr theDialog,
                                         EventRecord* theEvent,
                                         short* itemHit,
                                         long refCon);
 
 
// FUNCTIONS
//-----------------------------------------------------------------------
 
void main(void)
{
    // Init
    DoInit();
    DoMenuSetup();
 
    // Eat events until done
    do
    {
        HandleEvent();
    } while (!gQuitFlag);
 
    // Take off, eh?
    ExitToShell();
}
 
//-----------------------------------------------------------------------
 
static void DoInit(void)
{
    ComponentDescription theDesc;
    ComponentResult result = noErr;
    Component sgCompID = 0L;
    GrafPtr savedPort;
 
    // Set up quit flag
    gQuitFlag = false;
 
    // MacMantraª
    MaxApplZone();
    InitGraf(&qd.thePort);
    InitFonts();
    FlushEvents(everyEvent, 0);
    InitWindows();
    InitMenus();
    TEInit();
    InitDialogs(0L);
    InitCursor();
    EnterMovies();
    MoreMasters();
    MoreMasters();
    MoreMasters();
    MoreMasters();
 
    // Init stuff
    gSeqGrabber = 0L;
    gVideoChannel = 0L;
    gSoundChannel = 0L;
    gMonitorPICT = nil;
    gPrintRec = (THPrint)NewHandleClear(sizeof(TPrint));
 
    // Find and open a sequence grabber
    theDesc.componentType = SeqGrabComponentType;
    theDesc.componentSubType = 0L;
    theDesc.componentManufacturer = 'appl';
    theDesc.componentFlags = 0L;
    theDesc.componentFlagsMask = 0L;
    sgCompID = FindNextComponent(nil, &theDesc);
    if (sgCompID != 0L)
    {
        gSeqGrabber = OpenComponent(sgCompID);
    }
 
    // If we got a sequence grabber, set it up
    if (gSeqGrabber != 0L)
    {
        // Get the monitor
        gMonitor = GetNewDialog(kMonitorDLOGID, nil, (WindowPtr) - 1L);
        if (gMonitor != nil)
        {
            // Initialize the sequence grabber
            GetPort(&savedPort);
            SetPort(gMonitor);
            result = SGInitialize(gSeqGrabber);
            if (result == noErr)
            {
                result = SGSetGWorld(gSeqGrabber, (CGrafPtr)gMonitor, nil);
 
                // Get a video channel
                result = SGNewChannel(gSeqGrabber, VideoMediaType, &gVideoChannel);
                if ((gVideoChannel != nil) && (result == noErr))
                {
                    short width;
                    short height;
 
                    gQuarterSize = true;
                    gHalfSize = false;
                    gFullSize = false;
 
                    result = SGGetSrcVideoBounds(gVideoChannel, &gActiveVideoRect);
                    width = (gActiveVideoRect.right - gActiveVideoRect.left) / 4;
                    height = (gActiveVideoRect.bottom - gActiveVideoRect.top) / 4;
                    SizeWindow(gMonitor, width, height, false);
                    ShowWindow(gMonitor);
 
                    result = SGSetChannelUsage(gVideoChannel, seqGrabPreview);
                    result = SGSetChannelBounds(gVideoChannel, &(gMonitor->portRect));
                }
 
                // Get a sound channel
                result = SGNewChannel(gSeqGrabber, SoundMediaType, &gSoundChannel);
                if ((gSoundChannel != nil) && (result == noErr))
                {
                    result = SGSetChannelUsage(gSoundChannel, seqGrabPreview);
                }
 
                // Get readyÉ
                result = SGPrepare(gSeqGrabber, true, false);
            }
 
            // Go!
            result = SGStartPreview(gSeqGrabber);
            SetPort(savedPort);
        }
    }
}
 
//-----------------------------------------------------------------------
 
static void DoMenuSetup(void)
{
    Handle theMenuBar = GetNewMBar(kMenuBarID);
 
    // Set up our menus
    SetMenuBar(theMenuBar);
    gAppleMenu = GetMHandle(kAppleID);
    gFileMenu = GetMHandle(kFileID);
    gEditMenu = GetMHandle(kEditID);
    gMonitorMenu = GetMHandle(kMonitorID);
    AddResMenu(gAppleMenu, 'DRVR');
 
    // Last minute adjustmentsÉ
    AdjustMenus();
}
 
//-----------------------------------------------------------------------
 
static void HandleEvent(void)
{
    ComponentResult result = noErr;
 
    // Do system stuff
    HiliteMenu(0);
    SystemTask();
 
    // Give some time to the sequence grabber
    if (gSeqGrabber != 0L)
        result = SGIdle(gSeqGrabber);
 
    // Suck an event
    if (WaitNextEvent(everyEvent, &gTheEvent, 0, 0))
    {
        // What was it?
        switch (gTheEvent.what)
        {
            case mouseDown:
                {
                    // Handle it
                    HandleMouseDown();
                    break;
                }
            case keyDown:
            case autoKey:
                {
                    char theChar = gTheEvent.message & charCodeMask;
                    long theMenu = MenuKey(theChar);
 
                    // Handle menu command keys
                    HandleMenu(theMenu);
                    break;
                }
            case updateEvt:
                {
                    if ((gMonitor != nil) && ((WindowPtr)(gTheEvent.message) == (WindowPtr)gMonitor))
                    {
                        // Eat the update
                        BeginUpdate(gMonitor);
                        EndUpdate(gMonitor);
                    }
                    break;
                }
            default:                            // We don't really care about any other events, but you might, so feel free
                {
                    break;
                }
        }
    }
}
 
//-----------------------------------------------------------------------
 
static void HandleMouseDown(void)
{
    WindowPtr theWindow;
    short windowCode = FindWindow(gTheEvent.where, &theWindow);
 
    // Where was the mouse down?
    switch (windowCode)
    {
        case inSysWindow:
            {
                SystemClick(&gTheEvent, theWindow);
                break;
            }
        case inMenuBar:
            {
                AdjustMenus();
                HandleMenu(0L);
                break;
            }
        case inDrag:
            {
                // Was it the monitor?
                if (theWindow == gMonitor)
                {
                    ComponentResult result = noErr;
                    Rect limitRect;
                    RgnHandle grayRgn = GetGrayRgn();
                    Rect boundsRect;
 
                    // Find bounds
                    if (grayRgn != nil)
                    {
                        limitRect = (*grayRgn)->rgnBBox;
                    }
                    else
                    {
                        limitRect = qd.screenBits.bounds;
                    }
 
                    // Pause the sequence grabber
                    result = SGPause(gSeqGrabber, true);
 
                    if (gVideoChannel != nil)
                    {
                        // Get the alignment proc
                        result = SGGetAlignmentProc(gSeqGrabber, gSeqGrabberAlignProc);
 
                        // Drag it with the totally cool DragAlignedWindow
                        // Note that the sequence grabber can get real confused when you use this
                        // call if you've got multiple video channels - this'll get fixed in the 
                        // next release.  
                        result = SGGetChannelBounds(gVideoChannel, &boundsRect);
                        DragAlignedWindow(theWindow, gTheEvent.where, &limitRect, &boundsRect, gSeqGrabberAlignProc);
                    }
                    else
                    {
                        DragWindow(theWindow, gTheEvent.where, &limitRect);
                    }
 
                    // Start up the sequence grabber
                    result = SGPause(gSeqGrabber, false);
                }
                break;
            }
        default:
            {
                break;
            }
    }
}
 
//-----------------------------------------------------------------------
 
static void AdjustMenus(void)
{
    register WindowPeek wp = nil;
    short kind = 0;
    Boolean DA = false;
    ComponentResult result = noErr;
 
    // What kind of window is frontmost?
    wp = (WindowPeek)FrontWindow();
    kind = wp ? wp->windowKind : 0;
    DA = kind < 0;
 
    // Set our menu item states appropriately
 
    // Apple menu
    Enable((Handle)gAppleMenu, kAboutItem, true);
 
    // File menu
    Enable((Handle)gFileMenu, kPageSetupItem, true);
    Enable((Handle)gFileMenu, kPrintItem, (gVideoChannel != 0L ? true : false));
    Enable((Handle)gFileMenu, kQuitItem, true);
 
    // Edit menu
    Enable((Handle)gEditMenu, kUndoItem, DA);
    Enable((Handle)gEditMenu, kCutItem, DA || (gVideoChannel != 0L));
    Enable((Handle)gEditMenu, kCopyItem, DA || (gVideoChannel != 0L));
    Enable((Handle)gEditMenu, kPasteItem, DA);
    Enable((Handle)gEditMenu, kClearItem, DA);
 
    // Monitor menu
    Enable((Handle)gMonitorMenu, kVideoSettingsItem, (gVideoChannel != 0L ? true : false));
    Enable((Handle)gMonitorMenu, kSoundSettingsItem, (gSoundChannel != 0L ? true : false));
    Enable((Handle)gMonitorMenu, kQuarterSizeItem, (gVideoChannel != 0L ? true : false));
    CheckItem(gMonitorMenu, kQuarterSizeItem, gQuarterSize);
    Enable((Handle)gMonitorMenu, kHalfSizeItem, (gVideoChannel != 0L ? true : false));
    CheckItem(gMonitorMenu, kHalfSizeItem, gHalfSize);
    Enable((Handle)gMonitorMenu, kFullSizeItem, (gVideoChannel != 0L ? true : false));
    CheckItem(gMonitorMenu, kFullSizeItem, gFullSize);
 
    // Draw it
    DrawMenuBar();
}
 
//-----------------------------------------------------------------------
 
static void Enable(Handle menu,
                   short item,
                   Boolean ok)
{
    // Utility routine to enable and disable menu items
    if (ok)
    {
        EnableItem((MenuHandle)menu, item);
    }
    else
    {
        DisableItem((MenuHandle)menu, item);
    }
}
 
//-----------------------------------------------------------------------
 
static void HandleMenu(long theMenu)
{
    long mSelect;
    short menuID;
    short menuItem;
    ComponentResult result = noErr;
 
    // Did we get a menu?
    if (theMenu == 0L)
    {
        // Nope, get it from menu select
        mSelect = MenuSelect(gTheEvent.where);
    }
    else
    {
        // Yep, use it
        mSelect = theMenu;
    }
 
    // Decode it
    menuID = HiWord(mSelect);
    menuItem = LoWord(mSelect);
 
    // Which menu is it?
    switch (menuID)
    {
        case kAppleID:
            {
                if (menuItem == kAboutItem)
                {
                    // Do the boring about box
                    DoAboutDialog();
                }
                else                            // It's a DA
                    {
                        Str255 name;
                        GrafPtr savedPort;
 
                        // Open the DA
                        GetPort(&savedPort);
                        GetItem(gAppleMenu, menuItem, name);
                        OpenDeskAcc(name);
                        SetPort(savedPort);
                    }
                break;
            }
        case kFileID:
            {
                switch (menuItem)
                {
                    case kPageSetupItem:
                        {
                            // Do the page setup dialog
                            PrOpen();
                            PrStlDialog(gPrintRec);
                            PrClose();
                            break;
                        }
                    case kPrintItem:
                        {
                            TPPrPort printPort;
                            TPrStatus printStatus;
 
                            // Copy a frame from the monitor
                            if (gMonitorPICT != nil)
                            {
                                KillPicture(gMonitorPICT);
                            }
                            gMonitorPICT = nil;
                            result = SGGrabPict(gSeqGrabber, &gMonitorPICT, nil, 0, grabPictOffScreen);
                            if ((result == noErr) && (gMonitorPICT != nil))
                            {
                                // Print it
                                HLock((Handle)gMonitorPICT);
                                PrOpen();
                                if (PrJobDialog(gPrintRec))
                                {
                                    printPort = PrOpenDoc(gPrintRec, nil, nil);
                                    result = PrError();
                                    PrOpenPage(printPort, 0);
                                    result = PrError();
                                    DrawPicture(gMonitorPICT, &((**gMonitorPICT).picFrame));
                                    PrClosePage(printPort);
                                    result = PrError();
                                    PrCloseDoc(printPort);
                                    result = PrError();
                                    if ((**gPrintRec).prJob.bJDocLoop == bSpoolLoop)
                                    {
                                        PrPicFile(gPrintRec, 0, 0, 0, &printStatus);
                                        result = PrError();
                                    }
                                }
                                PrClose();
                                result = PrError();
                                HUnlock((Handle)gMonitorPICT);
                            }
                            break;
                        }
                    case kQuitItem:
                        {
                            // Let's scram
                            DoQuit();
                            break;
                        }
                }
                break;
            }
        case kEditID:
            {
                // Is this a DA kind of thing?
                if (!SystemEdit(menuItem - 1))
                {
                    // We only do cut and copy
                    if ((menuItem == kCutItem) || (menuItem == kCopyItem))
                    {
                        // Copy a frame from the monitor
                        if (gMonitorPICT != nil)
                        {
                            KillPicture(gMonitorPICT);
                        }
                        gMonitorPICT = nil;
                        result = SGGrabPict(gSeqGrabber, &gMonitorPICT, nil, 0, grabPictOffScreen);
                        if ((result == noErr) && (gMonitorPICT != nil))
                        {
                            result = ZeroScrap();
                            HLock((Handle)gMonitorPICT);
                            result = PutScrap(GetHandleSize((Handle)gMonitorPICT), 'PICT', *(Handle)gMonitorPICT);
                            HUnlock((Handle)gMonitorPICT);
                            if (result != noErr)
                            {
                                // Cut or copy failed, probably due to lack of memory
                            }
                        }
                    }
                }
                break;
            }
        case kMonitorID:
            {
                switch (menuItem)
                {
                        short width;
                        short height;
                        Rect curBounds;
                        Rect curVideoRect;
                        Rect newVideoRect;
                        Rect newBounds;
                        Rect maxBoundsRect;
                        GrafPtr savedPort;
                        RgnHandle deadRgn;
                        Rect boundsRect;
 
                    case kVideoSettingsItem:
                        {
                            if ((gSeqGrabber != 0L) && (gVideoChannel != 0L))
                            {
                                Rect newActiveVideoRect;
                                Rect adjustedActiveVideoRect;
 
                                // Get our current state
                                result = SGGetChannelBounds(gVideoChannel, &curBounds);
                                result = SGGetVideoRect(gVideoChannel, &curVideoRect);
 
                                // Pause
                                result = SGPause(gSeqGrabber, true);
 
                                // Do the dialog thang
                                result = SGSettingsDialog(gSeqGrabber, gVideoChannel, 0, nil, 0L, SeqGrabberModalFilterProc, (long)StripAddress((Ptr)gMonitor));
 
                                // What happened?
                                result = SGGetVideoRect(gVideoChannel, &newVideoRect);
                                result = SGGetSrcVideoBounds(gVideoChannel, &newActiveVideoRect);
 
                                // Set up our port
                                GetPort(&savedPort);
                                SetPort(gMonitor);
 
                                // Has our active rect changed?
                                // If so, it's because our video standard changed (e.g., NTSC to PAL),
                                // and we need to adjust our monitor window
                                if (!EqualRect(&gActiveVideoRect, &newActiveVideoRect))
                                {
                                    if (gFullSize)
                                    {
                                        width = newActiveVideoRect.right - newActiveVideoRect.left;
                                        height = newActiveVideoRect.bottom - newActiveVideoRect.top;
 
                                        gActiveVideoRect = newActiveVideoRect;
                                        SizeWindow(gMonitor, width, height, false);
                                        result = SGSetChannelBounds(gVideoChannel, &(gMonitor->portRect));
                                    }
                                    else if (gHalfSize)
                                    {
                                        width = (newActiveVideoRect.right - newActiveVideoRect.left) / 2;
                                        height = (newActiveVideoRect.bottom - newActiveVideoRect.top) / 2;
 
                                        gActiveVideoRect = newActiveVideoRect;
                                        SizeWindow(gMonitor, width, height, false);
                                        result = SGSetChannelBounds(gVideoChannel, &(gMonitor->portRect));
                                    }
                                    else if (gQuarterSize)
                                    {
                                        width = (newActiveVideoRect.right - newActiveVideoRect.left) / 4;
                                        height = (newActiveVideoRect.bottom - newActiveVideoRect.top) / 4;
 
                                        gActiveVideoRect = newActiveVideoRect;
                                        SizeWindow(gMonitor, width, height, false);
                                        result = SGSetChannelBounds(gVideoChannel, &(gMonitor->portRect));
                                    }
                                }
 
                                // Has our crop changed?
                                // This code shows how to be crop video panel friendly
                                // Two important things - 
                                // 1) Be aware that you might have been cropped and adjust your
                                //    video window appropriately
                                // 2) Be aware that you might have been adjusted and attempt to
                                //    account for this.  Adjusting refers to using the digitizer
                                //    rect to "adjust" the active source rect within the maximum
                                //    source rect.  This is useful if you're getting those nasty
                                //    black bands on the sides of your video display - you can use
                                //    the control-arrow key sequence to shift the active source 
                                //    rect around when you're in the crop video panel
 
                                adjustedActiveVideoRect = gActiveVideoRect;
                                if (!EqualRect(&curVideoRect, &newVideoRect))
                                {
                                    if ((newVideoRect.left < gActiveVideoRect.left) || (newVideoRect.right > gActiveVideoRect.right) || (newVideoRect.top < gActiveVideoRect.top) || (newVideoRect.bottom > gActiveVideoRect.bottom))
                                    {
                                        if (newVideoRect.left < gActiveVideoRect.left)
                                        {
                                            adjustedActiveVideoRect.left = newVideoRect.left;
                                            adjustedActiveVideoRect.right -= (gActiveVideoRect.left - newVideoRect.left);
                                        }
                                        if (newVideoRect.right > gActiveVideoRect.right)
                                        {
                                            adjustedActiveVideoRect.right = newVideoRect.right;
                                            adjustedActiveVideoRect.left += (newVideoRect.right - gActiveVideoRect.right);
                                        }
                                        if (newVideoRect.top < gActiveVideoRect.top)
                                        {
                                            adjustedActiveVideoRect.top = newVideoRect.top;
                                            adjustedActiveVideoRect.bottom -= (gActiveVideoRect.top - newVideoRect.top);
                                        }
                                        if (newVideoRect.bottom > gActiveVideoRect.bottom)
                                        {
                                            adjustedActiveVideoRect.bottom = newVideoRect.bottom;
                                            adjustedActiveVideoRect.top += (newVideoRect.bottom - gActiveVideoRect.bottom);
                                        }
                                        newBounds = newVideoRect;
                                        MapRect(&newBounds, &adjustedActiveVideoRect, &(gMonitor->portRect));
                                    }
                                    else        // Can't tell if we've been adjusted (digitizer rect is smaller on all sides
                                        // than the active source rect)
                                        {
                                            newBounds = newVideoRect;
                                            MapRect(&newBounds, &gActiveVideoRect, &(gMonitor->portRect));
                                        }
                                    width = newBounds.right - newBounds.left;
                                    height = newBounds.bottom - newBounds.top;
                                    result = SGSetChannelBounds(gVideoChannel, &newBounds);
                                }
 
                                // Clean out the part of the port that isn't being drawn in
                                deadRgn = NewRgn();
                                if (deadRgn != nil)
                                {
                                    result = SGGetChannelBounds(gVideoChannel, &boundsRect);
                                    result = XorRectToRgn(&boundsRect, &(gMonitor->portRect), &deadRgn);
                                    EraseRgn(deadRgn);
                                    DisposeRgn(deadRgn);
                                }
 
                                SetPort(savedPort);
 
                                // The pause that refreshes
                                result = SGPause(gSeqGrabber, false);
                            }
                            break;
                        }
                    case kSoundSettingsItem:
                        {
                            if ((gSeqGrabber != 0L) && (gSoundChannel != 0L))
                            {
                                // Do the dialog thang
                                result = SGSettingsDialog(gSeqGrabber, gSoundChannel, 0, nil, 0L, SeqGrabberModalFilterProc, (long)StripAddress((Ptr)gMonitor));
                            }
                            break;
                        }
                    case kQuarterSizeItem:
                        {
                            // New width and height
                            width = (gActiveVideoRect.right - gActiveVideoRect.left) / 4;
                            height = (gActiveVideoRect.bottom - gActiveVideoRect.top) / 4;
 
                            // Set flags and menus
                            gQuarterSize = true;
                            gHalfSize = false;
                            gFullSize = false;
                            AdjustMenus();
 
                            // Resize the monitor
                            GetPort(&savedPort);
                            SetPort(gMonitor);
                            result = SGPause(gSeqGrabber, true);
                            result = SGGetChannelBounds(gVideoChannel, &curBounds);
                            maxBoundsRect = gMonitor->portRect;
                            SizeWindow(gMonitor, width, height, false);
                            MapRect(&curBounds, &maxBoundsRect, &(gMonitor->portRect));
                            result = SGSetChannelBounds(gVideoChannel, &curBounds);
 
                            // Clean out part of port we're not drawing in
                            deadRgn = NewRgn();
                            if (deadRgn != nil)
                            {
                                result = SGGetChannelBounds(gVideoChannel, &boundsRect);
                                result = XorRectToRgn(&boundsRect, &(gMonitor->portRect), &deadRgn);
                                EraseRgn(deadRgn);
                                DisposeRgn(deadRgn);
                            }
 
                            SetPort(savedPort);
                            result = SGPause(gSeqGrabber, false);
                            break;
                        }
                    case kHalfSizeItem:
                        {
                            // New width and height
                            width = (gActiveVideoRect.right - gActiveVideoRect.left) / 2;
                            height = (gActiveVideoRect.bottom - gActiveVideoRect.top) / 2;
 
                            // Set flags and menus
                            gQuarterSize = false;
                            gHalfSize = true;
                            gFullSize = false;
                            AdjustMenus();
 
                            // Resize the monitor
                            GetPort(&savedPort);
                            SetPort(gMonitor);
                            result = SGPause(gSeqGrabber, true);
                            result = SGGetChannelBounds(gVideoChannel, &curBounds);
                            maxBoundsRect = gMonitor->portRect;
                            SizeWindow(gMonitor, width, height, false);
                            MapRect(&curBounds, &maxBoundsRect, &(gMonitor->portRect));
                            result = SGSetChannelBounds(gVideoChannel, &curBounds);
 
                            // Clean out part of port we're not drawing in
                            deadRgn = NewRgn();
                            if (deadRgn != nil)
                            {
                                result = SGGetChannelBounds(gVideoChannel, &boundsRect);
                                result = XorRectToRgn(&boundsRect, &(gMonitor->portRect), &deadRgn);
                                EraseRgn(deadRgn);
                                DisposeRgn(deadRgn);
                            }
 
                            SetPort(savedPort);
                            result = SGPause(gSeqGrabber, false);
                            break;
                        }
                    case kFullSizeItem:
                        {
                            // New width and height
                            width = gActiveVideoRect.right - gActiveVideoRect.left;
                            height = gActiveVideoRect.bottom - gActiveVideoRect.top;
 
                            // Set flags and menus
                            gQuarterSize = false;
                            gHalfSize = false;
                            gFullSize = true;
                            AdjustMenus();
 
                            // Resize the monitor
                            GetPort(&savedPort);
                            SetPort(gMonitor);
                            result = SGPause(gSeqGrabber, true);
                            result = SGGetChannelBounds(gVideoChannel, &curBounds);
                            maxBoundsRect = gMonitor->portRect;
                            SizeWindow(gMonitor, width, height, false);
                            MapRect(&curBounds, &maxBoundsRect, &(gMonitor->portRect));
                            result = SGSetChannelBounds(gVideoChannel, &curBounds);
 
                            // Clean out part of port we're not drawing in
                            deadRgn = NewRgn();
                            if (deadRgn != nil)
                            {
                                result = SGGetChannelBounds(gVideoChannel, &boundsRect);
                                result = XorRectToRgn(&boundsRect, &(gMonitor->portRect), &deadRgn);
                                EraseRgn(deadRgn);
                                DisposeRgn(deadRgn);
                            }
 
                            SetPort(savedPort);
                            result = SGPause(gSeqGrabber, false);
                            break;
                        }
                    default:
                        {
                            break;
                        }
                }
            }
        default:
            {
                break;
            }
    }
}
 
//-----------------------------------------------------------------------
 
static void DoAboutDialog(void)
{
    short itemHit;
    short itemType;
    Handle itemHandle;
    Rect itemRect;
    DialogPtr aboutDialog = GetNewDialog(kAboutDLOGID, nil, (WindowPtr) - 1L);
 
    // Do the boring about dialog
    GetDItem(aboutDialog, kAboutOKButtonOutline, &itemType, &itemHandle, &itemRect);
    SetDItem(aboutDialog, kAboutOKButtonOutline, itemType, (Handle)AboutDrawProc, &itemRect);
 
    ShowWindow(aboutDialog);
    do
    {
        ModalDialog(nil, &itemHit);
    } while (itemHit != kAboutOKButton);
    DisposDialog(aboutDialog);
}
 
//-----------------------------------------------------------------------
 
pascal void AboutDrawProc(DialogPtr theDialog,
                          short theItemNum)
{
    PenState thePenState;
    OSErr result = noErr;
    Rect itemRect;
    Handle itemHandle;
    short itemType;
 
    // Set up the pen
    GetPenState(&thePenState);
 
    GetDItem(theDialog, theItemNum, &itemType, &itemHandle, &itemRect);
 
    // What item do we need to draw?
    switch (theItemNum)
    {
        case kAboutOKButtonOutline:
            PenNormal();
            PenMode(patCopy);
            PenSize(3, 3);
            InsetRect(&itemRect, -4, -4);
            FrameRoundRect(&itemRect, 16, 16);
            break;
        default:
            break;
    }
 
    // Restore the pen
    SetPenState(&thePenState);
}
 
//-----------------------------------------------------------------------
 
static OSErr XorRectToRgn(Rect* srcRectA,
                          Rect* srcRectB,
                          RgnHandle* destRgn)
{
    RgnHandle srcRgnA = NewRgn();
    RgnHandle srcRgnB = NewRgn();
    OSErr result = noErr;
 
    if ((destRgn != nil) && (*destRgn != nil))
    {
        if ((srcRgnA != nil) && (srcRgnB != nil))
        {
            RectRgn(srcRgnA, srcRectA);
            RectRgn(srcRgnB, srcRectB);
            XorRgn(srcRgnA, srcRgnB, *destRgn);
        }
        else
        {
            result = memFullErr;
        }
    }
    else
    {
        result = nilHandleErr;
    }
    return (result);
}
 
//-----------------------------------------------------------------------
 
pascal Boolean SeqGrabberModalFilterProc(DialogPtr theDialog,
                                         EventRecord* theEvent,
                                         short* itemHit,
                                         long refCon)
{
    // Ordinarily, if we had multiple windows we cared about, we'd handle
    // updating them in here, but since we don't, we'll just clear out
    // any update events meant for us
 
    Boolean handled = false;
 
    if ((theEvent->what == updateEvt) && ((WindowPtr)theEvent->message == (WindowPtr)refCon))
    {
        BeginUpdate((WindowPtr)refCon);
        EndUpdate((WindowPtr)refCon);
        handled = true;
    }
    return (handled);
}
 
//-----------------------------------------------------------------------
 
static void DoQuit(void)
{
    ComponentResult result = noErr;
 
    // Clean up
    if (gSeqGrabber != 0L)
    {
        result = CloseComponent(gSeqGrabber);
        gSeqGrabber = 0L;
    }
    if (gMonitor != nil)
    {
        DisposeWindow(gMonitor);
    }
 
    // Set quit flag
    gQuitFlag = true;
 
    ExitMovies();
}
 
//-----------------------------------------------------------------------