home *** CD-ROM | disk | FTP | other *** search
/ Garbo / Garbo.cdr / mac / appl / globe.sit / Globe.p / Globe.p
Encoding:
Text File  |  1989-01-11  |  46.1 KB  |  1,658 lines  |  [TEXT/MPS ]

  1. (*--------------------------------------------------------------------------+
  2.  |                                                                                                                                                    |
  3.  |        Spinning Globe Graphics Demo                                                                                     |
  4.  |                                                                                                                                                    |
  5.  |        GlobeFrames (bitmaps) from an unsuspecting Sun 3                                            |
  6.  |                                                                                                                                                    |
  7.  |        Copyright (C) 1987, 1988 Paul Mercer                                                                    |
  8.  |        All rights reserved                                                                                                        |
  9.  |        Non-commercial distribution only                                                                            |
  10.  |                                                                                                                                                    |
  11.  |        Created:    pm    05/24/87                new today                                                                |
  12.  |        Modified: pm    06/03/87    v1.0    release                                                                    |
  13.  |                            pm    06/19/87    v1.1    fixed shading bug in mono pixmaps                |
  14.  |                                                                     use pixmaps only if depth > 1                    |
  15.  |                            pm    06/30/87    v1.2    works around Juggler b2 bug with                |
  16.  |                                                                      video slot VBL task switching                    |
  17.  |                            pm    01/28/88  v1.3    revved for MPW 3.0D3, misc changes            |
  18.  |                            pm    03/06/88  v1.4    added support for lsr's round WDEF,            |
  19.  |                                                                      rolled 'GlobeFrames64' file into app,    |
  20.  |                                                                     now boots in background, fixed a5            |
  21.  |                                                                     bug under Juggler, all user settings        |
  22.  |                                                                     are now saved along with window                |
  23.  |                                                                     position                                                                |
  24.  |                            pm    06/18/88    v1.41    now with 'improved' slot VBLs'                    |
  25.  |                            pm    12/26/88    v1.5    added PICT resources, imaging to target    |
  26.  |                                                                     window device; tweaked alignment;            |
  27.  |                                                                     improved shading; fixed cursors;                |
  28.  |                                                                     added palette, Palette Mgr. support;        |
  29.  |                                                                     plus lots more changes;                                |
  30.  |                                                                     thanx to Bruce and Darin                                |
  31.  |                                                                                                                                                    |
  32.  |        Globe was written as an exercise in learning Color QuickDraw.                    |
  33.  |        The goal was to write a decent demo completely in Pascal using                |
  34.  |        only Apple documented and approved techniques.  A bit of assembler        |
  35.  |        was neccessitated by the need to do VBL synchronization on the                |
  36.  |        Macintosh II.  There is also a bit of experimentation with Juggler.        |
  37.  |        Globe's heavy CPU usage makes it useful for investigating                            |
  38.  |        QuickDraw (CopyBits) performance under different conditions such as        |
  39.  |        window placement and clipping.  The sadistic will no doubt change            |
  40.  |        the depth of the screen at runtime to watch QuickDraw sweat.                    |
  41.  |                                                                                                                                                    |
  42.  |        Special thanx to Larry Rosenstein for the round WDEF.                                    |
  43.  |                                                                                                                                                    |
  44.  |        Things worth investigating include:                                                                        |
  45.  |            - this code is a mess!!                                                                                            |
  46.  |            - add dithered transfer mode option for Full Color QuickDraw                |
  47.  |            + convert the data files to PICTs to give PackBits some work                |
  48.  |            +    improve shading by stealing system colors (palette manager)                |
  49.  |            +    improve shading by checking for higher resolution/depth devices        |
  50.  |            ╨ be smart about bit depth changes (window moving, control panel)        |
  51.  |            ╨    regeneration of pixmaps to match changing screen depth                        |
  52.  |            ╨ use temporary memory calls                                                                                |
  53.  |            ╨    speed up CopyBits when shading and blitting (how?)                                |
  54.  |            ╨    anti-alias the pixmaps (scale down big bitmaps)                                        |
  55.  |            ╨    improve resolution by using more frames                                                        |
  56.  |            ╨    alleviate timing interaction while being juggled                                    |
  57.  |                                                                                                                                                    |
  58.  |        Please send your enhnancements, comments and/or questions to:                    |
  59.  |            Paul Mercer                                                                                                                    |
  60.  |            P.O. Box 160165                                                                                                            |
  61.  |            Cupertino, CA  95016-0165                                                                                        |
  62.  |                                                                                                                                                    |
  63.  |        If you really like this program (and/or this source),                                    |
  64.  |             please send $10 to your favorite charity.                                                        |
  65.  |                                                                                                                                                    |
  66.  |            AppleLink: MERCER1                                                                                                    |
  67.  |            UUCP:      {nsc,dual,sun}!apple!pmercer                                                            |
  68.  |            MCI:       SOL                                                                                                            |
  69.  |                                                                                                                                                    |
  70.  +--------------------------------------------------------------------------*)
  71.  
  72. PROGRAM Globe;
  73.  
  74.     USES
  75.         MemTypes, QuickDraw, Palettes, OSIntf, ToolIntf, MacPrint, PackIntf, CursorCtl;
  76.  
  77.     {$R-} {$OV-}    {we don't need no stinkin' range or overflow checking}
  78.     {$D+}                    { cuz we've got TMON/MacsBug}
  79.  
  80.   CONST
  81.         {$SETC Debug := True}
  82.  
  83.         {System stuff}
  84.         WaitNextEventTrap    = $60;
  85.         SlotVInstallTrap    = $6f;
  86.         UnImplentedTrap        = $9f;
  87.         PaintWhite                = $9dc;            {window manager white erase}
  88.         Ticks                            = $16a;            {tick counter in low mem}
  89.         {$IFC Debug}
  90.         MonkeyLives                = $100;
  91.         {$ENDC}
  92.         kOptionKeyCode        = 58;
  93.         CurAppName                =    $910;
  94.         CurApRefNum                =    $900;
  95.         GrayRgn                        = $9ee;
  96.  
  97.         {Program stuff}
  98.         dataID                = 128;
  99.  
  100.         {Resource IDs}
  101.         kFirstPICT        = 128;            {PICT}
  102.         kPalette            = 256;            {pltt}
  103.  
  104.         appleID                = 128;             {MENU}
  105.         fileID                = 129;
  106.         editID                = 130;
  107.         controlID            = 131;
  108.  
  109.         loadingID            = 128;            {DLOG}
  110.         coloringID        = 129;
  111.         noMemoryID        = 130;
  112.         ioErrorID            = 131;
  113.         aboutID                =    132;
  114.         kOK                        = 1;                {ok button}
  115.  
  116.         rectID                = 128;             {WIND}
  117.         roundID                =    129;
  118.  
  119.         {Menu stuff}
  120.         appleM                = 1;                {menu handle array indices}
  121.         fileM                    = 2;
  122.         editM                    = 3;
  123.         controlM            = 4;
  124.         menuCount            = 4;
  125.  
  126.         aboutCommand = 1;                    {Apple menu commands}
  127.  
  128.         closeCommand    = 1;                {File}
  129.         quitCommand        = 3;
  130.  
  131.         undoCommand        = 1;                {Edit}
  132.         cutCommand        = 3;
  133.         copyCommand        = 4;
  134.         pasteCommand    = 5;
  135.         clearCommand    = 6;
  136.  
  137.         slowerCommand        = 1;            {Control}
  138.         stepCommand            = 2;
  139.         fasterCommand        = 3;
  140.         syncCommand            = 5;
  141.         alignCommand        = 7;
  142.         timeCommand            = 9;
  143.         roundCommand        = 10;
  144.         paletteCommand    = 12;
  145.  
  146.     TYPE
  147.         LongIntPtr        =    ^LongInt;            {for accessing Ticks in WaitVBL}
  148.         IntegerPtr        = ^Integer;
  149.         BooleanPtr        = ^Boolean;            {for accessing PaintWhite in Initialize}
  150.         BitMapPtr            = ^BitMap;            {for type coercion in CopyBits}
  151.         RgnHandlePtr    = ^RgnHandle;        {for getting at GrayRgn in bounds checking}
  152.         Str31Ptr            =    ^Str31;
  153.  
  154.         DataRec            = RECORD                {Data resource format}
  155.                                         width:    Integer;    {bitmap width}
  156.                                         height:    Integer;    {bitmap height}
  157.                                         count:    Integer;    {frame count including mask}
  158.                                         sync:        Integer;    {0/1 VBL syncing}
  159.                                         round:    Integer;    {0/1 round window}
  160.                                         time:        Integer;    {0/1 frames/sec display}
  161.                                         top:        Integer;    {window position}
  162.                                         left:        Integer;
  163.                                         speed:    Integer;    {speed}
  164.                                         palette: Integer;    {use custom palette}
  165.                                     END; {DataRec}
  166.  
  167.  
  168.   VAR
  169.         {$Z+}                                                {exported for assembler access}
  170.         parisTicks:        LongInt;            {main video device tick count on Mac II}
  171.         {$Z-}
  172.  
  173.         appRefNum:        Integer;            {refNum of the application's resource fork}
  174.         frameFile:        Str255;
  175.         myData:                DataRec;            {config structure stored in as resource in data file}
  176.         frameWidth,
  177.         frameHeight,
  178.         frameCount:        Integer;
  179.         border:                Integer;            {border of gray space}
  180.         windLeft,
  181.         windTop:            Integer;            {saved default window position}
  182.  
  183.         curFrame,                                        {current frame (2 - frameCount)}
  184.         speed,                                            {frame advance delay or yieldtime w/ Juggler}
  185.         delayCount:        Integer;            {count remaining to delay}
  186.         syncVBL,                                        {sync to vbl command switch}
  187.         displayTime,                                {display frames/sec command switch}
  188.         roundWindow,                                {using Larry's round WDEF}
  189.         usePalette,                                    {using green and blue ramped palette}
  190.         doneFlag:            Boolean;            {MEL quit flag}
  191.  
  192.         timeRect:            Rect;                    {rect for drawing frames/sec}
  193.         framesDone:        Integer;            {frames/sec display vars}
  194.         targTime:            LongInt;            {time to target 'next second' used to write frames/sec}
  195.  
  196.         frameSize:        LongInt;            {size of frame bitmap}
  197.         frameBounds,                                {rect of source frame}
  198.         destBounds:        Rect;                    {centered dest rect}
  199.         pixDepth:            Integer;            {depth of PixMap}
  200.         bitMaps:            ARRAY[0..100] OF BitMap;                {the bitmaps themselves ***cheezewhiz}
  201.         pixMaps:            ARRAY[0..100] OF PixMapHandle;    {handles to pixmaps for cqd}
  202.                                                                 { 0 = scratch, 1 = mask, 2 - frameCount = bitmaps}
  203.         bigPixBlock:    Ptr;                    {pointer to big pixmap bit data}
  204.  
  205.         theWorld:            SysEnvRec;        {the world according to SysEnvirons}
  206.         usePixMaps,                                    {use pixel maps on screens deeper than 1 b/p}
  207.         useColor,                                        {only use color if 4 or more 4 bits/pix}
  208.         useShading,                                    {shade only if 8 or more bits/pix}
  209.         juggler:            Boolean;            {twitcher lives flag}
  210.         slotVBL:            Boolean;            {has slot interrupts}
  211.  
  212.         landColor,                                    {pretty colors}
  213.         waterColor,
  214.         spaceColor,
  215.         shadeColor,
  216.         myForeColor,
  217.         myBackColor:    RGBColor;
  218.         myColors:            CTabHandle;        {my color table}
  219.  
  220.         dragRect:            Rect;                    {rectangle used to mark bounds for dragging window}
  221.         myMenus:            ARRAY [1..menuCount] OF MenuHandle; {array of handles to the menus}
  222.         myWindow:            WindowPtr;
  223.         myPal:                PaletteHandle;
  224.         myEvent:            EventRecord;    {information about an event}
  225.  
  226.  
  227.   PROCEDURE _DataInit;                            EXTERNAL;
  228.     PROCEDURE    InstallVBLSync;                    EXTERNAL;
  229.     PROCEDURE RemoveVBLSync;                    EXTERNAL;
  230.     PROCEDURE AlignWindow;                        FORWARD;        {used by Initialize}
  231.     PROCEDURE UpdateWindow;                        FORWARD;        {used by DrawFrame}
  232.     PROCEDURE GetDefaults;                        FORWARD;
  233.     PROCEDURE SaveDefaults;                        FORWARD;
  234.     PROCEDURE ChangeWindow;                        FORWARD;
  235.     PROCEDURE ChangePalette;                    FORWARD;
  236.     FUNCTION    GetMyDevice: GDHandle;    FORWARD;
  237.     FUNCTION    GetMyDialog(id: Integer): DialogPtr;    FORWARD;
  238.  
  239.     {$S Initialize}        {this segment is dumped after initialization}
  240.  
  241. {+--------------------------------------------------------------------------+
  242.  |        simplistic fatal error handler with nice centering                                        |
  243.  +--------------------------------------------------------------------------+}
  244.     PROCEDURE FatalError(id: Integer);
  245.  
  246.     VAR
  247.         deviceRect:    Rect;
  248.         width:            Integer;
  249.         theDialog:    DialogPtr;
  250.         iType:            Integer;
  251.         iHandle:        Handle;
  252.         iBox:                Rect;
  253.  
  254.     BEGIN
  255.         SetCursor(arrow);
  256.         {open this window on the target device so the Palette Manager gets
  257.          to set the clut up properly for the offscreen pixmaps }
  258.       theDialog := GetNewDialog(id, Nil, WindowPtr( -1));
  259.         deviceRect := GetMyDevice^^.gdRect;
  260.         width := theDialog^.portRect.right - theDialog^.portRect.left;
  261.         MoveWindow(theDialog, (((deviceRect.right - deviceRect.left) - width) DIV 2) + deviceRect.left,
  262.                                 deviceRect.top + ((deviceRect.bottom - deviceRect.top) DIV 6), False);
  263.         ShowWindow(theDialog);
  264.  
  265.         GetDItem(theDialog, kOK, iType, iHandle, iBox);
  266.         SetPort(theDialog);
  267.         BeginUpdate(theDialog);
  268.         DrawDialog(theDialog);
  269.         PenSize(3, 3);
  270.         InsetRect(iBox, -4, -4);
  271.         FrameRoundRect(iBox, 16, 16);
  272.         EndUpdate(theDialog);
  273.  
  274.         SysBeep(10);
  275.         ModalDialog(Nil, width);
  276.  
  277.         ExitToShell;
  278.     END; {FatalError}
  279.  
  280.  
  281. {+--------------------------------------------------------------------------+
  282.  |        is the trap available?                                                                                                |
  283.  +--------------------------------------------------------------------------+}
  284.     FUNCTION TrapExist(t: Integer): Boolean;
  285.  
  286.     BEGIN
  287.         IF GetTrapAddress(t) <> GetTrapAddress(UnImplentedTrap) THEN
  288.             TrapExist := True
  289.         ELSE
  290.             TrapExist := False;
  291.     END; {TrapExist}
  292.  
  293.     FUNCTION NewOSTrapExist(t: Integer): Boolean;
  294.  
  295.     BEGIN
  296.         IF NGetTrapAddress(t, OSTrap) <> GetTrapAddress(UnImplentedTrap) THEN
  297.             NewOSTrapExist := True
  298.         ELSE
  299.             NewOSTrapExist := False;
  300.     END; {NewOSTrapExist}
  301.  
  302.  
  303. {+--------------------------------------------------------------------------+
  304.  |        return a RGBColor record                                                                                            |
  305.  +--------------------------------------------------------------------------+}
  306.     FUNCTION MakeRGBColor(r, g, b: Integer): RGBColor;
  307.  
  308.     VAR
  309.         color:    RGBColor;
  310.  
  311.     BEGIN
  312.         WITH color DO
  313.             BEGIN
  314.                 red := r;
  315.                 green := g;
  316.                 blue := b;
  317.             END;
  318.         MakeRGBColor := color;
  319.     END; {MakeRGBColor}
  320.  
  321.  
  322. {+--------------------------------------------------------------------------+
  323.  |        Get a 'STR ' Resource                                                                                                    |
  324.  +--------------------------------------------------------------------------+}
  325.     FUNCTION GetSTR(theID: Integer): Str255;
  326.  
  327.     VAR
  328.         aHandle:    Handle;
  329.         s:                Str255;
  330.  
  331.     BEGIN
  332.         aHandle := GetResource('STR ', theID);
  333.         HLock(aHandle);
  334.         BlockMove(aHandle^, @s, SizeResource(aHandle));
  335.         HUnlock(aHandle);
  336.         ReleaseResource(aHandle);
  337.         GetSTR := s;
  338.     END; {GetSTR}
  339.  
  340.  
  341. {+--------------------------------------------------------------------------+
  342.  |        give time to other apps and handle updates of 'loading' dialog                |
  343.  +--------------------------------------------------------------------------+}
  344.     PROCEDURE BeNiceToJuggler;
  345.  
  346.     VAR
  347.         savePort:    GrafPtr;
  348.         w:                WindowPtr;
  349.  
  350.     BEGIN
  351.         IF GetNextEvent(everyEvent, myEvent) THEN
  352.             IF myEvent.what = updateEvt THEN
  353.                 BEGIN
  354.                     w := FrontWindow;
  355.                     GetPort(savePort);
  356.                     SetPort(w);
  357.                     BeginUpdate(w);
  358.                     UpdtDialog(w, w^.visRgn);
  359.                     EndUpdate(w);
  360.                     SetPort(savePort);
  361.                 END;
  362.     END; {BeNiceToJuggler}
  363.  
  364.  
  365. {+------------------------------------------------------------------------+
  366.  |        load frames off disk                                                                                                |
  367.  +------------------------------------------------------------------------+}
  368.     PROCEDURE LoadFrames;
  369.  
  370.     VAR
  371.         i, j, refNum:    Integer;
  372.         dataForkSize:    longint;
  373.         myPort:                GrafPort;
  374.         oldResource:    Handle;
  375.         myPICT:                PicHandle;
  376.  
  377.         PROCEDURE IOError(err: OSErr);
  378.  
  379.         BEGIN
  380.             IF err <> noErr THEN
  381.                 BEGIN
  382.                     i := FSClose(refNum);        {close file, just in case}
  383.                     FatalError(ioErrorID);
  384.                 END;
  385.         END; {IOError}
  386.  
  387.     BEGIN
  388.         IOError(FSOpen(frameFile, 0, refNum));
  389.         IOError(GetEOF(refNum, dataForkSize));
  390.  
  391.         IF dataForkSize <> 0 THEN
  392.             FOR i := 1 TO frameCount DO
  393.                 BEGIN
  394.                     IOError(FSRead(refNum, frameSize, bitMaps[i].baseAddr));
  395.                     BeNiceToJuggler;
  396.                     SpinCursor(32);        {spin my beachball}
  397.             END;
  398.  
  399.         IOError(SetEOF(refNum, 0));                        {truncate the data file}
  400.         IOError(FSClose(refNum));
  401.  
  402.         OpenPort(@myPort);
  403.         refNum := OpenResFile(frameFile);
  404.         UseResFile(refNum);
  405.  
  406.         j := 1;
  407.         IF dataForkSize = 0 THEN
  408.             FOR i := 1 TO frameCount DO
  409.                 BEGIN
  410.                     REPEAT                                                    {this little ditty allows for sparse PICT numbering}
  411.                         myPict := PicHandle(Get1Resource('PICT', kFirstPICT + j - 1));
  412.                         IF myPICT = Nil THEN
  413.                             j := j + 1;
  414.                     UNTIL ((myPict <> Nil) OR (j = 1000));
  415.  
  416.                     j := j + 1;
  417.                     SetPortBits(bitMaps[i]);
  418.                     myPort.portRect := bitMaps[i].bounds;
  419.                     EraseRect(thePort^.portRect);
  420.                     DrawPicture(myPICT, thePort^.portRect);
  421.                     BeNiceToJuggler;
  422.                     SpinCursor(32);        {spin my beachball}
  423.             END
  424.         ELSE
  425.             FOR i := 1 TO frameCount DO
  426.                 BEGIN
  427.                     ClipRect(bitMaps[i].bounds);
  428.                     myPict := OpenPicture(bitMaps[i].bounds);
  429.                     CopyBits(bitMaps[i], thePort^.portBits, bitMaps[i].bounds, bitMaps[i].bounds, srcCopy, nil);
  430.                     ClosePicture;
  431.     
  432.                     REPEAT
  433.                         SetResLoad(False);
  434.                         oldResource := Get1Resource('PICT', kFirstPICT + i - 1);
  435.                         SetResLoad(True);
  436.                         RmveResource(oldResource);
  437.                         DisposHandle(oldResource);
  438.                     UNTIL oldResource = Nil;
  439.     
  440.                     AddResource(Handle(myPICT), 'PICT', kFirstPICT + i - 1, '');
  441.                     SetResAttrs(Handle(myPICT), resPurgeable + resChanged);
  442.     
  443.                     BeNiceToJuggler;
  444.                     SpinCursor(32);        {spin my beachball}
  445.             END;
  446.  
  447.         UpdateResFile(refNum);
  448.         IF refNum <> appRefNum THEN
  449.             CloseResFile(refNum);
  450.     END; {LoadFrames}
  451.  
  452.  
  453. {+--------------------------------------------------------------------------+
  454.  |        process mono bitmaps                                                                                                    |
  455.  +--------------------------------------------------------------------------+}
  456.     PROCEDURE ProcessBitMaps;
  457.  
  458.     VAR
  459.         tPort:    GrafPort;
  460.         i:            Integer;
  461.  
  462.     BEGIN
  463.         OpenPort(@tPort);
  464.         SetPortBits(bitMaps[0]);    {scratch map}
  465.         PenPat(ltGray);
  466.         PaintRect(frameBounds);
  467.         ClosePort(@tPort);
  468.  
  469.         CopyBits(bitMaps[0], bitMaps[1], frameBounds, frameBounds, notSrcBic, Nil);
  470.         FOR i := 2 TO frameCount DO
  471.             BEGIN
  472.                 CopyBits(bitMaps[1], bitMaps[i], frameBounds, frameBounds, srcOr, Nil);
  473.                 BeNiceToJuggler;
  474.                 SpinCursor(32);
  475.             END;
  476.     END; {ProcessBitMaps}
  477.  
  478.  
  479. {+--------------------------------------------------------------------------+
  480.  |        process mono bitmaps into pixmaps and color them if appropriate                |
  481.  +--------------------------------------------------------------------------+}
  482.     PROCEDURE ProcessPixMaps;
  483.  
  484.     CONST
  485.         kShadeRange = $0e000;                    {from white-kEdgeShade}
  486.         kEdgeShade    = $01fff;
  487.  
  488.     VAR
  489.         i, j, k:    Integer;
  490.         tRect:        Rect;
  491.         tCPort:        CGrafPort;
  492.         tHandle:    PixMapHandle;
  493.         shadePat:    PixPatHandle;
  494.         shadeStep, shade:    Fixed;
  495.         bigNum:        LongInt;
  496.         saveGDevice:    GDHandle;
  497.  
  498.     BEGIN
  499.         saveGDevice := GetGDevice;
  500.         SetGDevice(GetMyDevice);            {make sure we╒re on the target device}
  501.         ChangePalette;                                {for 5 bit inverse table to be built/current}
  502.  
  503.         OpenCPort(@tCPort);                        {open this port so we can change colors}
  504.         tHandle := tCPort.portPixMap;    {save portPixMap since it's going to change}
  505.  
  506.         {copy bitMaps to pixMaps and let CopyBits color them in the process}
  507.         IF useColor THEN
  508.             BEGIN
  509.                 RGBForeColor(landColor);
  510.                 RGBBackColor(waterColor);
  511.             END
  512.         ELSE
  513.             BEGIN
  514.                 RGBForeColor(myForeColor);    {black and white only for 2 bits/pixel}
  515.                 RGBBackColor(myBackColor);
  516.             END;
  517.         FOR i := 1 To frameCount DO
  518.             BEGIN
  519.                 CopyBits(bitMaps[i], BitMapPtr(pixMaps[i]^)^,
  520.                                     frameBounds, frameBounds, srcCopy, Nil);
  521.                 BeNiceToJuggler;
  522.                 SpinCursor(32);
  523.             END;
  524.  
  525.         {logically or the mask over (around) globe for background}
  526.         RGBForeColor(spaceColor);    {always try for gray}
  527.         FOR i := 2 To frameCount DO
  528.             BEGIN
  529.                 CopyBits(bitMaps[1], BitMapPtr(pixMaps[i]^)^,    {***quicker to use pixmaps only?}
  530.                                  frameBounds, frameBounds, srcOr, Nil);
  531.                 BeNiceToJuggler;
  532.                 SpinCursor(32);
  533.             END;
  534.  
  535.         {shade it nicely}
  536.         IF useShading THEN
  537.             BEGIN
  538.                 SetPortPix(pixMaps[0]);
  539.                 RGBBackColor(myBackColor);
  540.                 EraseRect(frameBounds);
  541.                 RGBForeColor(shadeColor);
  542.                 PenMode(addOver);                        {smear the shades}
  543.  
  544. {$IFC True}
  545.                 {draw shaded gray sphere}
  546.                 bigNum := kShadeRange * $1000;
  547.                 shadeStep := bigNum DIV (frameWidth DIV 2) * $10;
  548.                 shade := kEdgeShade * $10000;
  549.                 PenSize(1, 1);
  550.                 tRect := frameBounds;
  551.                 PenMode(srcCopy);
  552.                 FOR i := (frameWidth DIV 2) DOWNTO 1 DO                                        {this many shades}
  553.                     BEGIN
  554.                         shadeColor := MakeRGBColor(FixRound(shade), FixRound(shade), FixRound(shade));
  555.                         RGBForeColor(shadeColor);
  556.                         shade := shade + shadeStep;
  557.                         FrameOval(tRect);
  558.                         InsetRect(tRect, 1, 1);
  559.                         BeNiceToJuggler;
  560.                         SpinCursor(32);
  561.                     END;
  562. {$ELSEC}            {old drawing before Bruce changed the world}
  563.                 {draw shaded gray sphere}
  564.                 j := frameBounds.bottom DIV (16 * 2);            {pen size}
  565.                 FOR i := 1 TO 16 - 4 DO                                        {this many shades}
  566.                     BEGIN
  567.                         k := i * j;
  568.                         PenSize(k, k);
  569.                         FrameOval(frameBounds);
  570.                         BeNiceToJuggler;
  571.                         SpinCursor(32);
  572.                     END;
  573. {$ENDC}
  574.  
  575.                 {apply the shaded sphere to the frames}
  576.                 FOR i := 2 TO frameCount DO
  577.                     BEGIN
  578.                         CopyBits(BitMapPtr(pixMaps[0]^)^, BitMapPtr(pixMaps[i]^)^,
  579.                                          frameBounds, frameBounds, addMin, Nil);
  580.                         BeNiceToJuggler;
  581.                         SpinCursor(32);
  582.                     END;
  583.             END; {IF useShading}
  584.  
  585.         SetPortPix(tHandle);                    {restore portPixMap for killing}
  586.         ClosePort(GrafPtr(@tCPort));    {dispose its structures}
  587.         SetGDevice(saveGDevice);
  588.     END; {ProcessPixMaps}
  589.  
  590.  
  591. {+--------------------------------------------------------------------------+
  592.  |        initialize the frame globals                                                                                    |
  593.  +--------------------------------------------------------------------------+}
  594.     PROCEDURE InitFrameGlobals;
  595.  
  596.     VAR
  597.       i, j:             Integer;
  598.         saveGDevice,
  599.         myDevice:        GDHandle;
  600.  
  601.     BEGIN
  602.         frameBounds.top := 0;
  603.         frameBounds.left := 0;
  604.         frameBounds.bottom := frameHeight;
  605.         frameBounds.right := frameWidth;
  606.  
  607.         {allocate the bitmaps}
  608.         FOR i := 0 to frameCount DO
  609.             BEGIN
  610.                 bitMaps[i].rowBytes := frameWidth DIV 8;
  611.                 bitMaps[i].bounds := frameBounds;
  612.                 bitMaps[i].baseAddr := NewPtr(frameSize);
  613.                 SpinCursor(32);
  614.                 IF bitMaps[i].baseAddr = Nil THEN
  615.                     FatalError(noMemoryID);
  616.             END;
  617.  
  618.         {allocate the pixmaps}
  619.         IF usePixMaps THEN
  620.             BEGIN
  621.                 {clone the main device's color table for the pixMaps (tech note 120)}
  622.                 myDevice := GetMyDevice;
  623.                 myColors := myDevice^^.gdPMap^^.pmTable;        {get the color table}
  624.                 j := HandToHand(Handle(myColors));                    { and clone it}
  625.                 {now convert from device color table to screen color table - thanx DG}
  626.                 WITH myColors^^ DO
  627.                     BEGIN
  628.                         FOR i := 0 TO ctSize DO
  629.                             ctTable[i].value := i;    {put in indices}
  630.                         {now clear the high bit of ctFlags to indicate it's a screen color table}
  631.                         ctFlags := BAND(ctFlags, $7fff);
  632.                     END; {WITH}
  633.  
  634.                 bigPixBlock := NewPtr(frameSize * pixDepth * (frameCount + 1));
  635.                 IF bigPixBlock = Nil THEN
  636.                     FatalError(noMemoryID);
  637.  
  638.                 saveGDevice := GetGDevice;
  639.                 SetGDevice(myDevice);
  640.                 FOR i := 0 to frameCount DO
  641.                     BEGIN
  642.                         pixMaps[i] := NewPixMap;
  643.                         pixMaps[i]^^.pmTable := myColors;        {use my color table ***kill old color}
  644.                         pixMaps[i]^^.baseAddr := Ptr(ORD4(bigPixBlock)  + (i * frameSize * pixDepth));
  645.                         pixMaps[i]^^.rowBytes := (frameWidth DIV 8) * pixDepth + $8000;
  646.                         pixMaps[i]^^.bounds := frameBounds;
  647.                     END;
  648.                 SetGDevice(saveGDevice);
  649.             END; {IF usePixMaps}
  650.  
  651.     END; {InitFrameGlobals}
  652.  
  653.  
  654. {+--------------------------------------------------------------------------+
  655.  |        high level initializtion                                                                                            |
  656.  +--------------------------------------------------------------------------+}
  657.     PROCEDURE InitFrames;
  658.  
  659.     VAR
  660.         savePort:        GrafPtr;
  661.         theDialog:    DialogPtr;
  662.         dID:                Integer;
  663.  
  664.     BEGIN
  665.         {open this window on the target device so the Palette Manager gets
  666.          to set the clut up properly for the offscreen pixmaps }
  667.         IF useShading THEN
  668.             dID := coloringID
  669.         ELSE
  670.             dID := loadingID;
  671.  
  672.         theDialog := GetMyDialog(dID);
  673.  
  674.         GetPort(savePort);            {process the bitmap data appropriately}
  675.         InitFrameGlobals;                {initialize frame data structures}
  676.         LoadFrames;                            {load the bitmaps}
  677.  
  678.         IF usePixMaps THEN
  679.             ProcessPixMaps
  680.         ELSE
  681.             ProcessBitMaps;
  682.         SetPort(savePort);
  683.  
  684.         DisposDialog(theDialog);
  685.     END; {InitFrames}
  686.  
  687.  
  688. {+--------------------------------------------------------------------------+
  689.  |        initialize applicaton globals                                                                                    |
  690.  +--------------------------------------------------------------------------+}
  691.     PROCEDURE InitGlobals;
  692.  
  693.     VAR
  694.         i, j:            Integer;
  695.         tGDevice:    GDHandle;
  696.         refNum:        Integer;
  697.         tHandle:    Handle;
  698.         numFiles:    Integer;
  699.         message:    Integer;
  700.     document:    AppFile;
  701.         myName:        Str31Ptr;
  702.         appRefPtr: IntegerPtr;
  703.  
  704.     BEGIN
  705.         IF SysEnvirons(1, theWorld) <> noErr THEN
  706.             BEGIN
  707.                 SysBeep(5);
  708.                 SysBeep(5);
  709.                 SysBeep(5);
  710.                 ExitToShell;        {everyone should use the latest system software!}
  711.             END;
  712.  
  713.         myWindow := Nil;
  714.         appRefPtr := IntegerPtr(CurApRefNum);
  715.         appRefNum := appRefPtr^;
  716.  
  717.         IF TrapExist(WaitNextEventTrap) THEN
  718.             juggler := True
  719.         ELSE
  720.             juggler := False;
  721.  
  722.         IF  NewOSTrapExist(SlotVInstallTrap) THEN
  723.             slotVBL := True
  724.         ELSE
  725.             slotVBL := False;
  726.  
  727.         CountAppFiles(message, numfiles);        {get Finder info}
  728.         IF numFiles = 0 THEN
  729.             BEGIN
  730.                 myName := Str31Ptr(CurAppName);
  731.                 frameFile := myName^;
  732.             END
  733.         ELSE
  734.             BEGIN
  735.                 GetAppFiles(1, document);
  736.                 frameFile := document.fName;
  737.                 IF SetVol(Nil, document.vRefNum) <> noErr THEN
  738.                     FatalError(ioErrorID);
  739.             END;
  740.  
  741.         GetDefaults;
  742.  
  743.         IF theWorld.hasColorQD THEN
  744.             BEGIN
  745.                 tGDevice := GetMyDevice;
  746.                 pixDepth := tGDevice^^.GDPMap^^.pixelSize;
  747.                 usePixMaps := pixDepth > 1;
  748.                 useColor := pixDepth >= 4;
  749.                 useShading := pixDepth >= 8;
  750.             END
  751.         ELSE
  752.             BEGIN
  753.                 usePixMaps := False;
  754.                 useColor := False;
  755.                 pixDepth := 1;
  756.             END;
  757.  
  758.         frameSize := (frameWidth DIV 8) * frameHeight;
  759.         doneFlag := False;
  760.         curFrame := 2;
  761.         targTime := 0;
  762.         framesDone := 0;
  763.  
  764.         WITH screenBits.bounds DO
  765.             SetRect(dragRect, 4, 24, right - 4, bottom - 4);
  766.  
  767.         shadeColor := MakeRGBColor($1fff, $1fff, $1fff);        {light gray for additive shading}
  768.         landColor := MakeRGBColor(0, -1, 0);                                {green continents}
  769.         waterColor := MakeRGBColor(0, 0, -1);                                {blue oceans}
  770.         spaceColor := MakeRGBColor($1fff, $1fff, $1fff);        {a bit lighter than neutral gray}
  771.         myForeColor := MakeRGBColor(0, 0, 0);                                {black}
  772.         myBackColor := MakeRGBColor(-1, -1, -1);                        {white}
  773.  
  774.         myPal := GetNewPalette(kPalette);
  775.     END; {InitGlobals}
  776.  
  777.  
  778. {+--------------------------------------------------------------------------+
  779.  |        set up menus and draw menubar                                                                                    |
  780.  +--------------------------------------------------------------------------+}
  781.   PROCEDURE SetUpMenus;
  782.  
  783.     VAR
  784.       i: Integer;
  785.  
  786.     BEGIN
  787.       myMenus[appleM] := GetMenu(appleID);
  788.       AddResMenu(myMenus[appleM], 'DRVR');
  789.       myMenus[fileM] := GetMenu(fileID);
  790.       myMenus[editM] := GetMenu(editID);
  791.         myMenus[controlM] := GetMenu(controlID);
  792.  
  793.       FOR i := 1 TO menuCount DO
  794.             InsertMenu(myMenus[i], 0);
  795.       DrawMenuBar;
  796.     END; {SetUpMenus}
  797.  
  798.  
  799. {+--------------------------------------------------------------------------+
  800.  |        program initialization                                                                                                |
  801.  +--------------------------------------------------------------------------+}
  802.     PROCEDURE Initialize;
  803.  
  804.     VAR
  805.         x:    LongInt;
  806.         b:    Boolean;
  807.  
  808.      BEGIN
  809.         InitGraf(@thePort);
  810.         InitFonts;
  811.         FlushEvents(everyEvent, 0);
  812.         InitWindows;
  813.         InitMenus;
  814.         TEInit;
  815.         InitDialogs(Nil);
  816.         InitCursor;
  817.         InitCursorCtl(Nil);            {bring in acur resource now}
  818.         SetCursor(GetCursor(watchCursor)^^);
  819.  
  820.         InitGlobals;                        {initialize my globals}
  821.         SetUpMenus;                            {set up menus and menu bar}
  822.         b := GetNextEvent(everyEvent, myEvent);    {***juggler hacking so windows show up on top}
  823.         ChangePalette;                    {bring up palette (if option on) }
  824.         InitFrames;                            {load and process frames}
  825.         IF slotVBL THEN
  826.             InstallVBLSync;                {install custom VBL task if possible, do this last}
  827.         Delay(30, x);                        {wait a bit for the load dialog to go away}
  828.         ChangeWindow;                        {bring up the window invisibly}
  829.         ShowWindow(myWindow);
  830.     END; {Initialize}
  831.  
  832.  
  833. {+--------------------------------------------------------------------------+
  834.  |        return default top,left coords taking into account window type                |
  835.  +--------------------------------------------------------------------------+}
  836.     PROCEDURE DefaultWindow(VAR left, top: Integer);
  837.  
  838.     BEGIN
  839.         IF roundWindow THEN
  840.             BEGIN
  841.                 top := 64;
  842.                 left := 64;
  843.             END
  844.         ELSE
  845.             BEGIN
  846.                 top := 64 - 16;    {account for window border}
  847.                 left := 64 - 16;
  848.             END;
  849.     END; {DefaultWindow}
  850.  
  851.  
  852. {+--------------------------------------------------------------------------+
  853.  |        get defaults                                                                                                                    |
  854.  +--------------------------------------------------------------------------+}
  855.     PROCEDURE GetDefaults;
  856.  
  857.     VAR
  858.         refNum:        Integer;
  859.         tHandle:    Handle;
  860.  
  861.     BEGIN
  862.         refNum := OpenResFile(frameFile);
  863.         IF refNum = -1 THEN
  864.             FatalError(ioErrorID);
  865.         tHandle := GetResource('Data', dataID);
  866.         BlockMove(tHandle^, @myData, SIZEOF(dataRec));
  867.         IF SizeResource(tHandle) <> SIZEOF(dataRec) THEN
  868.             BEGIN
  869.                 SetHandleSize(tHandle, SIZEOF(dataRec));
  870.                 ChangedResource(tHandle);
  871.                 DefaultWindow(myData.left, myData.top);
  872.                 myData.speed := 0;
  873.                 myData.palette := 0;
  874.             END;
  875.  
  876.         IF refNum <> appRefNum THEN
  877.             CloseResFile(refNum);
  878.  
  879.         WITH myData DO
  880.             BEGIN
  881.                 frameWidth := width;
  882.                 frameHeight := width;
  883.                 frameCount := count;
  884.                 syncVBL := sync = 1;
  885.                 displayTime := time = 1;
  886.                 roundWindow := round = 1;
  887.                 usePalette := palette = 1;
  888.                 windTop := top;
  889.                 windLeft := left;
  890.             END; {WITH}
  891.         speed := myData.speed;
  892.         delayCount := speed;
  893.     END; {GetDefaults}
  894.  
  895.  
  896.   {$S Main}        {normal stuff goes here}
  897.  
  898. {+--------------------------------------------------------------------------+
  899.  |        is the Option key being held down?                                                                        |
  900.  +--------------------------------------------------------------------------+}
  901.     FUNCTION CheckOptionKey: Boolean;
  902.  
  903.     VAR
  904.         keys:    KeyMap;
  905.  
  906.     BEGIN
  907.         GetKeys(keys);
  908.         CheckOptionKey := keys[kOptionKeyCode];
  909.     END; {CheckOptionKey}
  910.  
  911.  
  912. {+--------------------------------------------------------------------------+
  913.  |        return the max device the globe window is in                                                    |
  914.  +--------------------------------------------------------------------------+}
  915.     FUNCTION GetMyDevice: GDHandle;
  916.  
  917.     VAR
  918.         windowRect: Rect;
  919.  
  920.     BEGIN
  921.         IF myWindow <> Nil THEN
  922.             BEGIN
  923.                 windowRect := myWindow^.portRect;
  924.                 LocalToGlobal(windowRect.topLeft);
  925.                 LocalToGlobal(windowRect.botRight);
  926.             END
  927.         ELSE
  928.             SetRect(windowRect, windLeft, windTop, windLeft + frameWidth, windTop + frameHeight);
  929.         GetMyDevice := GetMaxDevice(windowRect);
  930.     END; {GetMyDevice}
  931.  
  932.  
  933. {+--------------------------------------------------------------------------+
  934.  |        bring up a dialog centered on the target device                                                |
  935.  +--------------------------------------------------------------------------+}
  936.     FUNCTION GetMyDialog(id: Integer): DialogPtr;
  937.  
  938.     VAR
  939.         deviceRect:    Rect;
  940.         width:            Integer;
  941.         theDialog:    DialogPtr;
  942.  
  943.     BEGIN
  944.         SetCursor(arrow);
  945.       theDialog := GetNewDialog(id, Nil, WindowPtr( -1));
  946.         deviceRect := GetMyDevice^^.gdRect;
  947.         width := theDialog^.portRect.right - theDialog^.portRect.left;
  948.         MoveWindow(theDialog, (((deviceRect.right - deviceRect.left) - width) DIV 2) + deviceRect.left,
  949.                                 deviceRect.top + ((deviceRect.bottom - deviceRect.top) DIV 6), False);
  950.         ShowWindow(theDialog);
  951.         BeginUpdate(theDialog);
  952.         DrawDialog(theDialog);
  953.         EndUpdate(theDialog);
  954.  
  955.         GetMyDialog := theDialog;
  956.     END; {GetMyDialog}
  957.  
  958.  
  959. {+--------------------------------------------------------------------------+
  960.  |        update the 'Data' config resource                                                                            |
  961.  +--------------------------------------------------------------------------+}
  962.     PROCEDURE SaveDefaults;
  963.  
  964.     VAR
  965.         tHandle:    Handle;
  966.         refNum:        Integer;
  967.         p:                Point;
  968.  
  969.     BEGIN
  970.         IF NOT CheckOptionKey THEN        {update the Data config resource}
  971.             BEGIN
  972.                 refNum := OpenResFile(frameFile);
  973.                 IF refNum <> -1 THEN
  974.                     BEGIN
  975.                         p := myWindow^.portRect.topLeft;
  976.                         LocalToGlobal(p);
  977.                         WITH myData DO
  978.                             BEGIN
  979.                                 IF syncVBL THEN
  980.                                     sync := 1
  981.                                 ELSE
  982.                                     sync := 0;
  983.                                 IF displayTime THEN
  984.                                     time := 1
  985.                                 ELSE
  986.                                     time := 0;
  987.                                 IF roundWindow THEN
  988.                                     round := 1
  989.                                 ELSE
  990.                                     round := 0;
  991.                                 top := p.v;
  992.                                 left := p.h;
  993.                                 IF usePalette THEN
  994.                                     palette := 1
  995.                                 ELSE
  996.                                     palette := 0;
  997.                             END; {WITH}
  998.                         myData.speed := speed;
  999.                         tHandle := GetResource('Data', dataID);
  1000.                         BlockMove(@myData, tHandle^, SIZEOF(dataRec));
  1001.                         ChangedResource(tHandle);
  1002.                         IF refNum <> appRefNum THEN
  1003.                             CloseResFile(refNum)
  1004.                         ELSE
  1005.                             UpdateResFile(refNum);
  1006.                     END;
  1007.             END;
  1008.     END; {SaveDefaults}
  1009.  
  1010.  
  1011. {+--------------------------------------------------------------------------+
  1012.  |        wait for the vertical blanking for smooth blits                                                |
  1013.  +--------------------------------------------------------------------------+}
  1014.     PROCEDURE WaitVBL;
  1015.  
  1016.     VAR
  1017.         t:    LongInt;
  1018.  
  1019.     BEGIN
  1020.         IF slotVBL THEN
  1021.             BEGIN
  1022.                 t := ParisTicks;
  1023.                 REPEAT UNTIL t <> ParisTicks;
  1024.             END
  1025.         ELSE
  1026.             BEGIN
  1027.                 t := LongIntPtr(Ticks)^;
  1028.                 REPEAT UNTIL t <> LongIntPtr(Ticks)^;
  1029.             END;
  1030.     END; {WaitVBL}
  1031.  
  1032.  
  1033. {+--------------------------------------------------------------------------+
  1034.  |        ensures the ENTIRE window is visible                                                                    |
  1035.  +--------------------------------------------------------------------------+}
  1036.     PROCEDURE CheckWindowBounds(w: WindowPtr);
  1037.  
  1038.     VAR
  1039.         r:                Rect;
  1040.         rgn:            RgnHandlePtr;
  1041.  
  1042.     BEGIN
  1043.         rgn := RgnHandlePtr(GrayRgn);
  1044.         r := w^.portRect;
  1045.         IF NOT roundWindow THEN
  1046.             r.top := r.top - 20;            {account for title bar}
  1047.         SetPort(w);
  1048.         LocalToGlobal(r.topLeft);
  1049.         LocalToGlobal(r.botRight);
  1050.         IF NOT (PtInRgn(r.topLeft, rgn^) AND PtInRgn(r.botRight, rgn^)) THEN
  1051.             BEGIN
  1052.                 SysBeep(1);
  1053.                 DefaultWindow(windLeft, windTop);
  1054.                 MoveWindow(myWindow, windLeft, windTop, False);
  1055.             END;
  1056.     END; {CheckWindowBounds}
  1057.  
  1058.  
  1059. {+--------------------------------------------------------------------------+
  1060.  |        change ChangePalette                                                                                                    |
  1061.  +--------------------------------------------------------------------------+}
  1062.     PROCEDURE ChangePalette;
  1063.  
  1064.     VAR
  1065.         myDevice: GDHandle;
  1066.  
  1067.     BEGIN
  1068.         IF usePalette THEN
  1069.             BEGIN
  1070.                 SetPalette(WindowPtr(-1), myPal, True);
  1071.                 myDevice := GetMyDevice;
  1072.                 IF myDevice^^.gdPMap^^.pixelSize <= 8 THEN
  1073.                     MakeITable(myDevice^^.gdPMap^^.pmTable, myDevice^^.gdITable, 5);
  1074.             END
  1075.         ELSE
  1076.             SetPalette(WindowPtr(-1), Nil, True)
  1077.     END; {ChangePalette}
  1078.  
  1079.  
  1080. {+--------------------------------------------------------------------------+
  1081.  |        change WDEF                                                                                                                        |
  1082.  +--------------------------------------------------------------------------+}
  1083.     PROCEDURE ChangeWindow;
  1084.  
  1085.     VAR
  1086.         b:    Boolean;
  1087.         w:    Integer;
  1088.         first:    Boolean;
  1089.         p:            Point;
  1090.  
  1091.     BEGIN
  1092.         IF myWindow = Nil THEN
  1093.             first := True
  1094.         ELSE
  1095.             first := False;
  1096.         IF NOT first THEN
  1097.             BEGIN
  1098.                 p := myWindow^.portRect.topLeft;
  1099.                 LocalToGlobal(p);
  1100.                 IF roundWindow THEN                            {adjust for gray space border}
  1101.                     BEGIN
  1102.                         p.h := p.h + 16;                        {danger! danger, hardcoding ahead}
  1103.                         p.v := p.v + 16;
  1104.                     END
  1105.                 ELSE
  1106.                     BEGIN
  1107.                         p.h := p.h - 16;
  1108.                         p.v := p.v - 16;
  1109.                     END;
  1110.                 DisposeWindow(myWindow);                {waste my window prior to change, if any}
  1111.             END;
  1112.         IF roundWindow THEN
  1113.             w := roundID
  1114.         ELSE
  1115.             w := rectID;
  1116.         IF usePixMaps THEN            {bring up invisible window}
  1117.             WindowPtr(myWindow) := GetNewCWindow(w, Nil, POINTER( -1))
  1118.         ELSE
  1119.             myWindow := GetNewWindow(w, Nil, POINTER( -1));
  1120.         SetPort(myWindow);
  1121.         TextMode(srcXor);
  1122.         TextSize(9);                        {draw with small chars}
  1123.         BackPat(ltGray);                {set erase pattern}
  1124.         IF roundWindow THEN
  1125.             border := 0
  1126.         ELSE
  1127.             border := 32;
  1128.         SizeWindow(myWindow, frameBounds.right + border, frameBounds.bottom + border, False);
  1129.         destBounds := frameBounds;
  1130.         OffSetRect(destBounds, border DIV 2, border DIV 2);    {center a bit}
  1131.         IF NOT roundWindow THEN
  1132.             BEGIN
  1133.                 timeRect := myWindow^.portRect;
  1134.                 timeRect.bottom := timeRect.bottom - 5;
  1135.                 timeRect.top := timeRect.bottom - 11;
  1136.                 timeRect.left := timeRect.left + 5;
  1137.                 timeRect.right := timeRect.left + 25;
  1138.             END;
  1139.         IF first THEN
  1140.             MoveWindow(myWindow, windLeft, windTop, False)
  1141.         ELSE
  1142.             BEGIN
  1143.                 MoveWindow(myWindow, p.h, p.v, False);
  1144.                 ShowWindow(myWindow);                            {window is now visible}
  1145.             END;
  1146.  
  1147.         CheckWindowBounds(myWindow);
  1148.         b :=     BooleanPtr(PaintWhite)^;        {don't draw window in white}
  1149.         BooleanPtr(PaintWhite)^ := False;    { when making it visible}
  1150.         UpdateWindow;                                            {draw everything now}
  1151.         BooleanPtr(PaintWhite)^ := b;            {restore the global}
  1152.     END; {ChangeWindow}
  1153.  
  1154.  
  1155. {+--------------------------------------------------------------------------+
  1156.  |        write the blit speed in frames per sec                                                                |
  1157.  +--------------------------------------------------------------------------+}
  1158.     PROCEDURE WriteSpeed;
  1159.  
  1160.     VAR
  1161.         outStr:             Str255;
  1162.         r:                        Rect;
  1163.         savePort:            GrafPtr;
  1164.  
  1165.     BEGIN
  1166.         IF (targTime < TickCount) THEN
  1167.             BEGIN
  1168.                 {$IFC Debug}
  1169.                 IntegerPtr(MonkeyLives)^ := framesDone;        {*** for Bruce}
  1170.                 {$ENDC}
  1171.                 IF NOT roundWindow THEN
  1172.                     BEGIN
  1173.                         GetPort(savePort);
  1174.                         SetPort(myWindow);
  1175.                         NumToString (framesDone, outStr);
  1176.                         FrameRect(timeRect);
  1177.                         r := timeRect;
  1178.                         InsetRect(r, 1, 1);
  1179.                         PenPat(white);
  1180.                         PaintRect(r);
  1181.                         PenNormal;
  1182.                         IF framesDone < 1000 THEN            {don't draw if too long}
  1183.                             BEGIN
  1184.                                 MoveTo(timeRect.left + 4, timeRect.bottom - 2);
  1185.                                 DrawString (outStr);
  1186.                             END;
  1187.                         SetPort(savePort);
  1188.                     END;
  1189.                 framesDone := 0;
  1190.                 targTime := TickCount + 60;    {1 second into the future}
  1191.             END
  1192.         ELSE
  1193.             framesDone := framesDone + 1;
  1194.     END; {WriteSpeed}
  1195.  
  1196.  
  1197. {+------------------------------------------------------------------------+
  1198.  |        change time display                                                                                                    |
  1199.  +------------------------------------------------------------------------+}
  1200.     PROCEDURE FlipTime;
  1201.  
  1202.     VAR
  1203.         savePort:    GrafPtr;
  1204.  
  1205.     BEGIN
  1206.         IF displayTime THEN
  1207.             BEGIN
  1208.                 displayTime := False;
  1209.                 GetPort(savePort);
  1210.                 SetPort(myWindow);
  1211.                 IF usePixMaps THEN
  1212.                     BEGIN
  1213.                         RGBForeColor(spaceColor);
  1214.                         PaintRect(timeRect);
  1215.                         RGBForeColor(myForeColor);
  1216.                     END
  1217.                 ELSE
  1218.                     EraseRect(timeRect);
  1219.                 SetPort(savePort);
  1220.             END
  1221.         ELSE
  1222.             BEGIN
  1223.                 displayTime := True;
  1224.                 targTime := 0;    {draw it now}
  1225.                 WriteSpeed;
  1226.             END;
  1227.     END; {FlipTime}
  1228.  
  1229.  
  1230. {+--------------------------------------------------------------------------+
  1231.  |        blit a frame - look for updates here to minimize flicker                            |
  1232.  +--------------------------------------------------------------------------+}
  1233.     PROCEDURE DrawFrame(i: Integer);
  1234.  
  1235.     BEGIN
  1236.         IF EmptyRgn(WindowPeek(myWindow)^.updateRgn) THEN
  1237.             BEGIN
  1238.                 IF syncVBL THEN
  1239.                     WaitVBL;
  1240.                 IF usePixMaps THEN
  1241.                     {$IFC Debug}
  1242.                     IF (CheckOptionKey AND UseShading) THEN
  1243.                         CopyBits(BitMapPtr(pixMaps[0]^)^,
  1244.                                          BitMapPtr(CGrafPtr(myWindow)^.portPixMap^)^,
  1245.                                          frameBounds, destBounds, srcCopy, Nil)
  1246.                     ELSE
  1247.                     {$ENDC}
  1248.                         CopyBits(BitMapPtr(pixMaps[i]^)^,
  1249.                                          BitMapPtr(CGrafPtr(myWindow)^.portPixMap^)^,
  1250.                                          frameBounds, destBounds, srcCopy, Nil)
  1251.                 ELSE
  1252.                     CopyBits(bitMaps[i], myWindow^.portBits, frameBounds, destBounds, srcCopy, Nil);
  1253.             END {no update}
  1254.         ELSE
  1255.             UpdateWindow;
  1256.     END; {DrawFrame}
  1257.  
  1258.  
  1259. {+--------------------------------------------------------------------------+
  1260.  |        handle update events for my window                                                                        |
  1261.  +--------------------------------------------------------------------------+}
  1262.     PROCEDURE UpdateWindow;
  1263.  
  1264.     VAR
  1265.         r:    Rect;
  1266.  
  1267.     BEGIN
  1268.         targTime := 0;                            {force draw of time box now}
  1269.         BeginUpdate(myWindow);
  1270.         IF NOT roundWindow THEN            {only need to erase/gray if border exists}
  1271.             IF usePixMaps THEN
  1272.                 BEGIN
  1273.                     RGBForeColor(spaceColor);
  1274.                     PaintRect(myWindow^.portRect);
  1275.                     RGBForeColor(myForeColor);
  1276.                 END
  1277.             ELSE
  1278.                 EraseRect(myWindow^.portRect);
  1279.         r := myWindow^.portRect;        {draw some nice lines}
  1280.         PenSize(1, 1);
  1281.         FrameRect(r);
  1282.         InsetRect(r, 2, 2);
  1283.         FrameRect(r);
  1284.         PenPat(white);
  1285.         InsetRect(r, -1, -1);
  1286.         FrameRect(r);
  1287.         PenNormal;
  1288.         IF displayTime THEN
  1289.             WriteSpeed;
  1290.         DrawFrame(curFrame);
  1291.         EndUpdate(myWindow);
  1292.     END; {UpdateWindow}
  1293.  
  1294.  
  1295. {+--------------------------------------------------------------------------+
  1296.  |        advance a frame - called from MEL, handles delays                                            |
  1297.  +--------------------------------------------------------------------------+}
  1298.     PROCEDURE FrameAdvance;
  1299.  
  1300.     BEGIN
  1301.         IF (delayCount = 0) AND (speed <> -1) THEN        {handle stepping}
  1302.             BEGIN
  1303.                 delayCount := speed;
  1304.                 IF curFrame = frameCount THEN
  1305.                     curFrame := 2
  1306.                 ELSE
  1307.                     curFrame := curFrame + 1;
  1308.  
  1309.                 IF displayTime THEN
  1310.                     WriteSpeed;
  1311.                 DrawFrame(curFrame);
  1312.             END
  1313.         ELSE
  1314.             BEGIN
  1315.                 delayCount := delayCount - 1;
  1316.             END;
  1317.     END; {FrameAdvance}
  1318.  
  1319.  
  1320. {+--------------------------------------------------------------------------+
  1321.  |        about box handler                                                                                                            |
  1322.  +--------------------------------------------------------------------------+}
  1323.     PROCEDURE DoAbout;
  1324.  
  1325.     VAR
  1326.         theDialog:    DialogPtr;
  1327.         hasEvent:        Boolean;
  1328.  
  1329.     BEGIN
  1330.       theDialog := GetMyDialog(aboutID);
  1331.  
  1332.         REPEAT
  1333.             IF juggler THEN
  1334.                 hasEvent := WaitNextEvent(keyDownMask + mDownMask, myEvent, 0, Nil)
  1335.             ELSE
  1336.                 BEGIN
  1337.                     SystemTask;
  1338.                     hasEvent := GetNextEvent(keyDownMask + mDownMask, myEvent);
  1339.                 END;
  1340.                 FrameAdvance;
  1341.         UNTIL hasEvent;
  1342.  
  1343.         DisposDialog(theDialog);
  1344.     END; {DoAbout}
  1345.  
  1346.  
  1347. {+--------------------------------------------------------------------------+
  1348.  |        update menus                                                                                                                    |
  1349.  +--------------------------------------------------------------------------+}
  1350.     PROCEDURE AdjustMenus;
  1351.  
  1352.     VAR
  1353.         savePort:        GrafPtr;
  1354.         globalRect:    Rect;
  1355.         myDevice:        GDHandle;
  1356.  
  1357.     BEGIN
  1358.         GetPort(savePort);
  1359.         SetPort(myWindow);
  1360.         SetPort(savePort);
  1361.         globalRect := myWindow^.portRect;
  1362.         LocalToGlobal(globalRect.topLeft);
  1363.         LocalToGlobal(globalRect.botRight);
  1364.         myDevice := GetMyDevice;
  1365.         globalRect.left := (globalRect.left + destBounds.left) * myDevice^^.gdPMap^^.pixelSize;
  1366.         IF globalRect.left MOD 32 = 0 THEN
  1367.             DisableItem(myMenus[controlM], alignCommand)
  1368.         ELSE
  1369.             EnableItem(myMenus[controlM], alignCommand);
  1370.  
  1371.         IF speed = 0 THEN
  1372.             DisableItem(myMenus[controlM], fasterCommand)
  1373.         ELSE
  1374.             EnableItem(myMenus[controlM], fasterCommand);
  1375.         IF roundWindow THEN
  1376.             DisableItem(myMenus[controlM], timeCommand)
  1377.         ELSE
  1378.             EnableItem(myMenus[controlM], timeCommand);
  1379.         CheckItem(myMenus[controlM], syncCommand, syncVBL);
  1380.         CheckItem(myMenus[controlM], timeCommand, displayTime AND NOT roundWindow);
  1381.         CheckItem(myMenus[controlM], roundCommand, roundWindow);
  1382.         CheckItem(myMenus[controlM], paletteCommand, usePalette);
  1383.  
  1384.         IF (FrontWindow = myWindow) THEN
  1385.             BEGIN
  1386.                 DisableItem(myMenus[fileM], closeCommand);
  1387.                 DisableItem(myMenus[editM], undoCommand);
  1388.                 DisableItem(myMenus[editM], cutCommand);
  1389.                 DisableItem(myMenus[editM], copyCommand);
  1390.                 DisableItem(myMenus[editM], pasteCommand);
  1391.                 DisableItem(myMenus[editM], clearCommand);
  1392.             END
  1393.         ELSE
  1394.             BEGIN
  1395.                 EnableItem(myMenus[fileM], closeCommand);
  1396.                 EnableItem(myMenus[editM], undoCommand);
  1397.                 EnableItem(myMenus[editM], cutCommand);
  1398.                 EnableItem(myMenus[editM], copyCommand);
  1399.                 EnableItem(myMenus[editM], pasteCommand);
  1400.                 EnableItem(myMenus[editM], clearCommand);
  1401.             END;
  1402.     END; {AdjustMenus}
  1403.  
  1404.  
  1405. {+--------------------------------------------------------------------------+
  1406.  |     longword align the window to help out quickdraw                                                |
  1407.  +--------------------------------------------------------------------------+}
  1408.     PROCEDURE AlignWindow;
  1409.  
  1410.     VAR
  1411.         i:                Integer;
  1412.         myDevice:    GDHandle;
  1413.         grid:            Integer;
  1414.         globalRect:    Rect;
  1415.  
  1416.     BEGIN
  1417.         globalRect := myWindow^.portRect;
  1418.         LocalToGlobal(globalRect.topLeft);
  1419.         LocalToGlobal(globalRect.botRight);
  1420.  
  1421.         myDevice := GetMyDevice;
  1422.         grid := 32 DIV myDevice^^.gdPMap^^.pixelSize;
  1423.  
  1424.         i := (((globalRect.left + destBounds.left) + grid DIV 2) DIV grid) * grid - destBounds.left;
  1425.         MoveWindow(myWindow, i, globalRect.top, False);
  1426.     END; {AlignWindow}
  1427.  
  1428.  
  1429. {+--------------------------------------------------------------------------+
  1430.  |        handle control menu commands                                                                                    |
  1431.  +--------------------------------------------------------------------------+}
  1432.     PROCEDURE DoControlMenu(i: Integer);
  1433.  
  1434.     VAR
  1435.         savePort:    GrafPtr;
  1436.  
  1437.     BEGIN
  1438.         CASE i OF
  1439.             slowerCommand:
  1440.                 BEGIN
  1441.                     IF speed = -1 THEN
  1442.                         speed := 50            {magic numbers...}
  1443.                     ELSE
  1444.                         speed := speed + 1;
  1445.                     delayCount := speed;
  1446.                 END;
  1447.  
  1448.             stepCommand:
  1449.                 BEGIN
  1450.                     delayCount := 0;    {advance frame now}
  1451.                     speed := 0;                {step now}
  1452.                     FrameAdvance;
  1453.                     speed := -1;            {don't advance in loop}
  1454.                 END;
  1455.  
  1456.             fasterCommand:
  1457.                 BEGIN
  1458.                     speed := speed - 1;
  1459.                     IF speed < 0 THEN
  1460.                         speed := 0;
  1461.                     delayCount := speed;
  1462.                 END;
  1463.  
  1464.             syncCommand:
  1465.                 syncVBL := NOT syncVBL;
  1466.  
  1467.             alignCommand:
  1468.                 AlignWindow;
  1469.  
  1470.             timeCommand:
  1471.                 FlipTime;
  1472.  
  1473.             roundCommand:
  1474.                 BEGIN
  1475.                     roundWindow := NOT roundWindow;
  1476.                     ChangeWindow;
  1477.                 END;
  1478.  
  1479.             paletteCommand:
  1480.                 BEGIN
  1481.                     usePalette := NOT usePalette;
  1482.                     ChangePalette;
  1483.                 END;
  1484.  
  1485.         END; {CASE}
  1486.         AdjustMenus;
  1487.     END; {DoControlMenu}
  1488.  
  1489.  
  1490. {+--------------------------------------------------------------------------+
  1491.  |        handle menu and menu key commands                                                                            |
  1492.  +--------------------------------------------------------------------------+}
  1493.   PROCEDURE DoCommand(mResult: LONGINT);
  1494.  
  1495.     VAR
  1496.       theItem:    Integer;                {menu item number from mResult low-order word}
  1497.       theMenu:    Integer;                {menu number from mResult high-order word}
  1498.       name:            Str255;                    {desk accessory name}
  1499.       w:                WindowPtr;
  1500.         i:                Integer;
  1501.  
  1502.     BEGIN
  1503.       theItem := LoWord(mResult);       {call Toolbox Utility routines to}
  1504.       theMenu := HiWord(mResult);       {set menu item number and menu}
  1505.       {number}
  1506.  
  1507.       CASE theMenu OF
  1508.             appleID:
  1509.                     BEGIN
  1510.                         IF (theItem = aboutCommand) THEN
  1511.                             DoAbout
  1512.                         ELSE
  1513.                             BEGIN
  1514.                                 GetItem(myMenus[appleM], theItem, name);
  1515.                                 i := OpenDeskAcc(name);
  1516.                                 SetPort(myWindow);
  1517.                             END;
  1518.                     END; {CASE appleID}
  1519.  
  1520.             fileID:
  1521.                 CASE theItem OF
  1522.                     closeCommand:
  1523.                         BEGIN
  1524.                             w := FrontWindow;
  1525.                             IF WindowPeek(w)^.windowKind < 0 THEN
  1526.                                 CloseDeskAcc(WindowPeek(w)^.windowKind);
  1527.                         END;
  1528.                     quitCommand:
  1529.                         doneFlag := True;
  1530.                 END; {CASE fileID}
  1531.  
  1532.             editID:
  1533.                 IF NOT SystemEdit(theItem - 1) THEN
  1534.                     BEGIN
  1535.                     END;
  1536.  
  1537.             controlID:
  1538.                 DoControlMenu(theItem);
  1539.  
  1540.       END; {CASE theMenu}
  1541.  
  1542.       HiliteMenu(0);
  1543.     END; {DoCommand}
  1544.  
  1545.  
  1546. {+--------------------------------------------------------------------------+
  1547.  |        main event loop                                                                                                                |
  1548.  +--------------------------------------------------------------------------+}
  1549.     PROCEDURE MainEventLoop;
  1550.  
  1551.     VAR
  1552.         hasEvent:            Boolean;
  1553.         whichWindow:    WindowPtr;
  1554.  
  1555.     BEGIN
  1556.         REPEAT
  1557.             FrameAdvance;                            {draw my stuff}
  1558.  
  1559.             IF juggler THEN
  1560.                 hasEvent := WaitNextEvent(everyEvent, myEvent, 0, Nil)
  1561.             ELSE
  1562.                 BEGIN
  1563.                     SystemTask;                        {service DAs}
  1564.                     hasEvent := GetNextEvent(everyEvent, myEvent);
  1565.                 END;
  1566.  
  1567.             IF hasEvent THEN
  1568.                 CASE myEvent.what OF
  1569.  
  1570.                     mouseDown:
  1571.                         CASE FindWindow(myEvent.where, whichWindow) OF
  1572.  
  1573.                             inSysWindow:
  1574.                                 SystemClick(myEvent, whichWindow);
  1575.  
  1576.                             inMenuBar:
  1577.                                 BEGIN
  1578.                                     AdjustMenus;    {pretty up the menus}
  1579.                                     DoCommand(MenuSelect(myEvent.where));
  1580.                                 END;
  1581.  
  1582.                             inDrag:
  1583.                                 BEGIN
  1584.                                     DragWindow(whichWindow, myEvent.where, dragRect);
  1585.                                     AdjustMenus;    {align command may change}
  1586.                                 END;
  1587.  
  1588.                             inGoAway:
  1589.                                 IF whichWindow = myWindow THEN
  1590.                                     IF TrackGoAway(myWindow, myEvent.where) THEN
  1591.                                         doneFlag := True;
  1592.  
  1593.                             inContent:
  1594.                                 BEGIN
  1595.                                     IF whichWindow <> FrontWindow THEN
  1596.                                         SelectWindow(whichWindow);
  1597.                                     IF roundWindow THEN
  1598.                                         BEGIN
  1599.                                             DragWindow(whichWindow, myEvent.where, dragRect);
  1600.                                             AdjustMenus;    {align command may change}
  1601.                                         END;
  1602.                                 END;
  1603.                         END; {CASE mouseDown}
  1604.  
  1605.                     keyDown, autoKey:            {key pressed once or held down to repeat}
  1606.                         IF (myWindow = frontWindow) AND
  1607.                              (BAnd(myEvent.modifiers, cmdKey) <> 0) THEN
  1608.                             DoCommand(MenuKey(CHR(BAnd(myEvent.message, charCodeMask))));
  1609.  
  1610.                     activateEvt:
  1611.                         IF (WindowPtr(myEvent.message) = myWindow) AND
  1612.                              (BAnd(myEvent.modifiers, activeFlag) <> 0) THEN
  1613.                             SetCursor(arrow);
  1614.  
  1615.                     updateEvt:
  1616.                         IF WindowPtr(myEvent.message) = myWindow THEN
  1617.                             UpdateWindow;
  1618.  
  1619.                 END; {CASE myEvent.what}
  1620.         UNTIL doneFlag;
  1621.   END; {MainEventLoop}
  1622.  
  1623.  
  1624. {+--------------------------------------------------------------------------+
  1625.  |        recursively chow da windows                                                                                        |
  1626.  +--------------------------------------------------------------------------+}
  1627.     PROCEDURE CloseAllWindows(w: WindowPtr);
  1628.  
  1629.     BEGIN
  1630.         IF WindowPeek(w)^.nextWindow <> Nil THEN
  1631.             CloseAllWindows(WindowPtr(WindowPeek(w)^.nextWindow));
  1632.         IF WindowPeek(w)^.windowKind < 0 THEN
  1633.             CloseDeskAcc(WindowPeek(w)^.windowKind);
  1634.     END; {CloseAllWindows}
  1635.  
  1636.  
  1637. {+--------------------------------------------------------------------------+
  1638.  |        Main Program                                                                                                                    |
  1639.  +--------------------------------------------------------------------------+}
  1640.     BEGIN
  1641.         UnLoadSeg(@_DataInit);                {remove Pascal data initialization code}
  1642.         MaxApplZone;                                    {fully expand the heap}
  1643.         Initialize;                                        {run once-only code}
  1644.         UnLoadSeg(@Initialize);                {remove once-only code}
  1645.  
  1646.         AdjustMenus;                                    {adjust once now}
  1647.         MainEventLoop;                                {run the main loop}
  1648.  
  1649.         IF NOT juggler THEN
  1650.             SetCursor(GetCursor(watchCursor)^^);
  1651.         IF slotVBL THEN
  1652.             RemoveVBLSync;                            {remove custom VBL task if on Mac II}
  1653.         SaveDefaults;                                    {save the program settings}
  1654.         DisposeWindow(myWindow);
  1655.         IF FrontWindow <> Nil THEN
  1656.             CloseAllWindows(FrontWindow);
  1657.     END. {PROGRAM Globe}
  1658.