home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Think Class Libraries / CAnimCursor / CAnimCursor.c < prev    next >
Encoding:
Text File  |  1994-11-30  |  18.5 KB  |  733 lines  |  [TEXT/KAHL]

  1. /*
  2.  * CAnimCursor.c
  3.  * Easy, flexible handling of animated cursors.  No TCL required.
  4.  * Version 1.0b4, 14 September 1992
  5.  *
  6.  * Jon Pugh's Pascal "Watch" unit, which he posted to the tcl-talk
  7.  * discussion group on 92 Apr 27, was the jumping-off point for
  8.  * this class.  Thanks, Jon.
  9.  * Debugging help was provided by Daryl Spitzer.  Thanks, Daryl.
  10.  * Written by Jamie McCarthy.
  11.  * Internet: k044477@kzoo.edu                AppleLink: j.mccarthy
  12.  * Telephone:  800-421-4157 (9:00-5:00 Eastern time)
  13.  * I'm releasing this code with the hope that someone will get something
  14.  * out of it.  Feedback of any sort, even just letting me know that you're
  15.  * using it, is greatly appreciated!
  16.  *
  17.  * CAnimCursor's source code is in the public domain.  If you make changes,
  18.  * please do me the courtesy of letting me know before you redistribute it.
  19.  *
  20.  * HOW TO USE IT
  21.  *
  22.  * Add an 'acur' resource to your program, and pass its ID to
  23.  * gAnimCursor->IAnimCursor().  Then call startAnimating() when you want
  24.  * to start animating--for example, when you begin a lengthy operation--
  25.  * and call stopAnimating() when you're done with it.  Call Dispose()
  26.  * at the appropriate time, of course.
  27.  *
  28.  * You may change which cursors you're using, even during animation, by
  29.  * calling useAnimCursorID().  You may change the animation speed with
  30.  * setTicksBetweenCursors().  There is a delay between calling
  31.  * startAnimating() and the appearance of the first cursor, which you
  32.  * may change with setInitialDelay().  And you can turn the interrupt-
  33.  * driven animation on or off with setUsingInterrupts().
  34.  *
  35.  * To release the memory used by the cursors without disposing of the
  36.  * entire object, call stopUsingAnimCursor().  Thereafter, you must call
  37.  * useAnimCursorID() before you start animating again.
  38.  *
  39.  * Any of these methods may be called at any time, and it will do what
  40.  * you'd expect;  for example, if you call startAnimating() twice, the
  41.  * second call will simply have no effect.
  42.  *
  43.  * THE THINK CLASS LIBRARY AND THE TWO MODES
  44.  *
  45.  * If you're using the TCL, there are three additional classes that you
  46.  * probably want to patch into your project.  To understand why, you should
  47.  * be aware that CSwitchboard and the CView hierarchy reset the cursor
  48.  * every time through the event loop.  For further information, read the
  49.  * source to CSwitchboard::ProcessEvent() and CDesktop::DispatchCursor().
  50.  * There are two ways of dealing with this, called "modes."
  51.  *
  52.  * The first, "interrupted," is to accept it, and turn off animation each
  53.  * time through ProcessEvent().  This is best if your animated cursor
  54.  * doesn't resemble the arrow, and if you spend a long time away from the
  55.  * event loop, because then it indicates "I'm busy, don't try clicking
  56.  * or anything."  If this is all you want, add CAcurSwitchboard and
  57.  * CAcurError to your project and follow the instructions given in
  58.  * the source files.
  59.  *
  60.  * The second mode, "continuous," is to fight it, and keep the animation
  61.  * going through the event loop.  This is best if your cursor is similar
  62.  * to the arrow, and if you go through the event loop relatively quickly,
  63.  * like once every few seconds or faster.  To use this mode, you must
  64.  * curse Symantec up and down for not including multiple inheritance,
  65.  * and for not writing the cursor-dispatching methods with a good
  66.  * solid OOP paradigm.  After you get that out of your system, you must
  67.  * call gAnimCursor->setMode(kCACModeContinuous), add CAcurSwitchboard,
  68.  * CAcurError, and CAcurDesktop to your project, and follow the
  69.  * instructions in those source files.
  70.  *
  71.  * You may switch back and forth between the two modes at any time, assuming
  72.  * you're using all three "CAcur" subclasses in your project.
  73.  *
  74.  * By the way, remember not to call ForgetObject() on gAnimCursor.  That
  75.  * function assumes it's being passed a (CObject*), which, despite
  76.  * appearances, gAnimCursor is not.  You'll get a bus error or somesuch.
  77.  *
  78.  * COLOR CURSORS
  79.  *
  80.  * The action routine, animateCursor(), is constantly called by the interrupt
  81.  * routine, but may also be called from "normal" program code.  In fact,
  82.  * it _must_ be called from "normal" code if you want color cursors,
  83.  * since SetCCursor may not be called from an interrupt.
  84.  *
  85.  * If you have color cursors, but they're showing as B&W part or all of the
  86.  * time, then the interrupt routine, not you, is calling animateCursor()
  87.  * first.  You may either (1) give your non-interrupt code a better chance
  88.  * of being called, by increasing the slop value with setSlopTicks(),
  89.  * or (2) call animateCursor() more often, or (3) turn interrupts off
  90.  * entirely with setUsingInterrupts(FALSE), or any combination thereof.
  91.  * If you have the ticks per cursor cranked way fast, you won't be able to
  92.  * get the color displayed no matter what you do.
  93.  *
  94.  * Because I call Count1Resources('crsr') (instead of CountResources()) to
  95.  * determine whether there are color cursors that can be shown, the file with
  96.  * the cursors must be the current resource file when IAnimCursor() is
  97.  * called.
  98.  *
  99.  * OH AND BY THE WAY
  100.  *
  101.  * In case you were wondering, there _is_ protection against animateCursor()
  102.  * being simultaneously called from the "normal" and the interrupt level.
  103.  * This is the very first time I've written "dining philosophers" code--
  104.  * whoopie.
  105.  *
  106.  * VERSION HISTORY
  107.  *
  108.  * Changes from 1.0b3 to 1.0b4:
  109.  * Added itsAcurID, to prevent unnecessary reloading of resources.
  110.  * Removed _nooptimize and put interrupt-level global access in separate function.
  111.  * Modified a few comments.
  112.  * Put in the resource-file-tracking code.
  113.  *
  114.  * Changes from 1.0b1 to 1.0b3:
  115.  * Daryl Spitzer (Daryl_Spitzer@mindlink.bc.ca) fixed the A5 bug for me.
  116.  * Made nextCursor() a method.
  117.  * Made installVBLTask() and removeVBLTask() methods.
  118.  * Added the continuous mode, and CAcurDesktop.
  119.  * Changed the "set" methods to use ticks.
  120.  * Got the 'acur' with a Get1Resource() instead of GetResource().
  121.  * Fixed a few miscellaneous bugs.
  122.  * Changed a few variables' names around.
  123.  * Added some comments.
  124.  * Made a few code touch-ups.
  125.  *
  126.  *
  127.  * It's laughable that someone wants Apple to animate their cursor for them.
  128.  * I guess the Magic Toolbox has made us spoiled children.  If you need an
  129.  * attitude adjustment, try programming an MS-DOS boat anchor.  That'll
  130.  * animate your cursor!
  131.  *   - "The Midnight Hacker," Letter to MacTutor, May 1988
  132.  *
  133.  */
  134.  
  135.  
  136.  
  137. /********************************/
  138.  
  139. #include "CAnimCursor.h"
  140.  
  141. /********************************/
  142.  
  143. #include <GestaltEqu.h>
  144. #include <QuickDraw.h>
  145. #include <Retrace.h>
  146.  
  147. /********************************/
  148.  
  149.     /*
  150.      * Some people use SysEqu.h, others use LoMem.h.  The two cannot both
  151.      * be used (see "Mac #includes.h").  The former has CrsrBusy defined,
  152.      * while the latter does not.  To avoid inclusion confusion, I'll
  153.      * just do the simplest thing:  hard-code it!
  154.      */
  155. #define cursorIsBusy() ( (* (unsigned char*) 0x08CD) != 0x00)
  156.  
  157. /********************************/
  158.  
  159. static pascal void doAnimateCursor(void);
  160.  
  161. /********************************/
  162.  
  163. extern Boolean gInBackground;
  164.  
  165. CAnimCursor *gAnimCursor = NULL;
  166.  
  167. /********************************/
  168.  
  169.  
  170.  
  171. void CAnimCursor::IAnimCursor(short rsrcID)
  172. {
  173.     MoveHHi((Handle) this);
  174.     
  175.     setTopResFile(CurResFile());
  176.     
  177.     determineTryToUseColor();
  178.     
  179.     isInAnInterrupt = FALSE;
  180.     
  181.     isAnimating = FALSE;
  182.     vblTaskIsInstalled = FALSE;
  183.     itsAcurID = MININT;
  184.     itsAcurHndl = NULL;
  185.     itsLastTick = 0;
  186.     
  187.     setTicksBetweenCursors(kCACDefaultTicksBetweenCursors);
  188.     setInitialDelayTicks(kCACDefaultInitialDelayTicks);
  189.     setUsingInterrupts(TRUE);
  190.     setSlopTicks(kCACDefaultColorInterruptSlop);
  191.     setMode(kCACModeInterrupted);
  192.     
  193.     useAnimCursorID(rsrcID);
  194. }
  195.  
  196.  
  197.  
  198. void CAnimCursor::Dispose(void)
  199. {
  200.     stopUsingAnimCursor();
  201.     delete(this);
  202. }
  203.  
  204.  
  205.  
  206. void CAnimCursor::useAnimCursorID(short rsrcID)
  207. {
  208.     OSErr theOSErr;
  209.     short oldState;
  210.     short oldResFile;
  211.     Boolean wasAnimating;
  212.     
  213.     if (itsAcurID == rsrcID) return;
  214.     
  215.     oldResFile = CurResFile();
  216.     useTopResFile();
  217.     
  218.     wasAnimating = getIsAnimating();
  219.     
  220.     stopUsingAnimCursor();
  221.     
  222.     itsAcurHndl = (acurHndl) Get1Resource('acur', rsrcID);
  223.     if (itsAcurHndl == NULL || ResError()) return;
  224.     
  225.     DetachResource((Handle) itsAcurHndl);
  226.     MoveHHi((Handle) itsAcurHndl);
  227.     HLock((Handle) itsAcurHndl);
  228.     
  229.     loadIndividualCursors();
  230.     
  231.     itsAcurID = rsrcID;
  232.     
  233.     if (wasAnimating) {
  234.         startAnimating();
  235.     }
  236.     
  237.     UseResFile(oldResFile);
  238. }
  239.  
  240.  
  241.  
  242. void CAnimCursor::stopUsingAnimCursor(void)
  243. {
  244.     if (itsAcurHndl == NULL) return;
  245.     
  246.     stopAnimating();
  247.     
  248.     disposeIndividualCursors();
  249.     
  250.         /* Dispose of the 'acur' handle. */
  251.     DisposHandle((Handle) itsAcurHndl);
  252.     itsAcurHndl = NULL;
  253. }
  254.  
  255.  
  256.  
  257. void CAnimCursor::startAnimating(void)
  258. {
  259.     OSErr theOSErr;
  260.     
  261.     if (itsAcurHndl == NULL) return;
  262.     if (getIsAnimating()) return;
  263.     
  264.     itsInitialTick = TickCount();
  265.     
  266.     theOSErr = noErr;
  267.     if (usingInterrupts) {
  268.         theOSErr = installVBLTask();
  269.     }
  270.     
  271.     if (theOSErr == noErr) {
  272.         isAnimating = TRUE;
  273.     }
  274. }
  275.  
  276.  
  277.  
  278. void CAnimCursor::stopAnimating(void)
  279. {
  280.     OSErr theOSErr;
  281.     
  282.     theOSErr = removeVBLTask();
  283.     
  284.     if (!getIsAnimating()) return;
  285.     isAnimating = FALSE;
  286.     itsInitialTick = 0;
  287. }
  288.  
  289.  
  290.  
  291. Boolean CAnimCursor::getIsAnimating(void)
  292. {
  293.     return isAnimating;
  294. }
  295.  
  296.  
  297.  
  298. void CAnimCursor::animateCursor(void)
  299. {
  300.     register long cTickCount;
  301.     
  302.     if (isInAnimateCursor) return;
  303.     if (!getIsAnimating()) return;
  304.     if (gInBackground) return;
  305.     
  306.         /* "Lock" the routine, so that the interrupt can't re-enter it. */
  307.     isInAnimateCursor = TRUE;
  308.     
  309.         /*
  310.          * Put the current TickCount into a variable.  This is for speed--
  311.          * calling TickCount() takes time--but also because strange things
  312.          * might happen if the Ticks were incremented in the middle of this
  313.          * routine...
  314.          */
  315.     cTickCount = TickCount();
  316.     
  317.     if ((itsLastTick+itsTicksBetweenCursorsForInterrupt <= cTickCount)
  318.         || (!isInAnInterrupt &&
  319.             itsLastTick+itsTicksBetweenCursorsForNormalCode <= cTickCount)
  320.         ) {
  321.         
  322.         if (cursorIsBusy()) {
  323.             
  324.                 /* Sigh.  Just my luck.  Let's try again next time around. */
  325.             itsVBLTask.vblCount = 1;
  326.             
  327.         } else {
  328.             
  329.             register acurPtr theAcurPtr;
  330.             
  331.             theAcurPtr = *itsAcurHndl;
  332.             
  333.             nextCursor();
  334.             
  335.             if (usingColorCursors) {
  336.                 
  337.                 if (isInAnInterrupt) {
  338.                         /*
  339.                          * We can't call SetCCursor at interrupt time, so fake it:
  340.                          * draw the B&W cursor that's built into the color one.
  341.                          */
  342.                     SetCursor( (CursPtr) &
  343.                         (*((CCrsrHandle) theAcurPtr->cursor[theAcurPtr->cCursor]))
  344.                             ->crsr1Data);
  345.                 } else {
  346.                         /*
  347.                          * We're OK; animateCursor() was not called from an
  348.                          * interrupt, so we can use the real color cursor.
  349.                          */
  350.                     SetCCursor( (CCrsrHandle) theAcurPtr->cursor[theAcurPtr->cCursor] );
  351.                 }
  352.                 
  353.             } else {
  354.                 
  355.                 SetCursor( * theAcurPtr->cursor[theAcurPtr->cCursor] );
  356.                 
  357.             }
  358.             
  359.             itsLastTick = cTickCount;
  360.             
  361.             itsVBLTask.vblCount = itsTicksBetweenCursorsForInterrupt;
  362.             
  363.         }
  364.         
  365.     }
  366.     
  367.         /* We're done, so the interrupt routine is again free to call. */
  368.     isInAnimateCursor = FALSE;
  369. }
  370.  
  371.  
  372.  
  373. void CAnimCursor::setTicksBetweenCursors(short newTicksBetweenCursors)
  374. {
  375.         // A quick sanity check.  No fewer than 0 ticks, no more than 20 seconds.
  376.     ASSERT(newTicksBetweenCursors >= 0 && newTicksBetweenCursors <= 1200);
  377.     
  378.     if (newTicksBetweenCursors != itsTicksBetweenCursors) {
  379.         itsTicksBetweenCursors = newTicksBetweenCursors;
  380.         setSlopTicks(getSlopTicks());
  381.     }
  382. }
  383.  
  384.  
  385.  
  386. void CAnimCursor::setInitialDelayTicks(short newInitialDelayTicks)
  387. {
  388.         // A quick sanity check.  No fewer than 0 ticks, no more than one minute.
  389.     ASSERT(newInitialDelayTicks >= 0 && newInitialDelayTicks <= 3600);
  390.     
  391.     itsInitialDelayTicks = newInitialDelayTicks;
  392. }
  393.  
  394.  
  395.  
  396. void CAnimCursor::setUsingInterrupts(Boolean newUsingInterrupts)
  397. {
  398.     newUsingInterrupts = (newUsingInterrupts != FALSE);
  399.     
  400.     if (usingInterrupts == newUsingInterrupts) return;
  401.     
  402.     if (usingInterrupts && getIsAnimating()) {
  403.         OSErr theOSErr;
  404.         theOSErr = removeVBLTask();
  405.     }
  406.     
  407.     usingInterrupts = newUsingInterrupts;
  408.     
  409.     if (usingInterrupts && getIsAnimating()) {
  410.         OSErr theOSErr;
  411.         theOSErr = installVBLTask();
  412.     }
  413. }
  414.  
  415.  
  416.  
  417. Boolean CAnimCursor::getUsingInterrupts(void)
  418. {
  419.     return usingInterrupts;
  420. }
  421.  
  422.  
  423.  
  424. void CAnimCursor::setSlopTicks(short newSlopTicks)
  425. {
  426.     ASSERT(newSlopTicks >= 0);
  427.     itsTicksBetweenCursorsForNormalCode = itsTicksBetweenCursors - (newSlopTicks+1)/3;
  428.     if (itsTicksBetweenCursorsForNormalCode < 1) {
  429.         itsTicksBetweenCursorsForNormalCode = 1;
  430.     }
  431.     if (itsTicksBetweenCursorsForNormalCode < (itsTicksBetweenCursors*2)/3) {
  432.         itsTicksBetweenCursorsForNormalCode = (itsTicksBetweenCursors*2)/3;
  433.     }
  434.     itsTicksBetweenCursorsForInterrupt =
  435.         itsTicksBetweenCursorsForNormalCode + newSlopTicks;
  436. }
  437.  
  438.  
  439.  
  440. short CAnimCursor::getSlopTicks(void)
  441. {
  442.     return itsTicksBetweenCursorsForInterrupt - itsTicksBetweenCursorsForNormalCode;
  443. }
  444.  
  445.  
  446.  
  447. void CAnimCursor::setMode(short newMode)
  448. {
  449.     ASSERT(newMode >= kCACModeInterrupted && newMode <= kCACModeContinuous);
  450.     itsMode = newMode;
  451. }
  452.  
  453.  
  454.  
  455. short CAnimCursor::getMode(void)
  456. {
  457.     return itsMode;
  458. }
  459.  
  460.  
  461. void CAnimCursor::setTopResFile(short newTopResFile)
  462. {
  463.     itsTopResFile = newTopResFile;
  464. }
  465.  
  466.  
  467.  
  468. void CAnimCursor::useTopResFile(void)
  469. {
  470.     UseResFile(itsTopResFile);
  471. }
  472.  
  473.  
  474.  
  475. /********************************/
  476.  
  477.  
  478.  
  479. void CAnimCursor::determineTryToUseColor(void)
  480. {
  481.     long theQDVersion;
  482.     OSErr theOSErr;
  483.     
  484.     theOSErr = Gestalt(gestaltQuickdrawVersion, &theQDVersion);
  485.     if (theOSErr == noErr) {
  486.         tryToUseColor = (theQDVersion >= gestalt8BitQD);
  487.     } else {
  488.         SysEnvRec theWorld;
  489.         theOSErr = SysEnvirons(2, &theWorld);
  490.         if (theOSErr == noErr) {
  491.             tryToUseColor = theWorld.hasColorQD;
  492.         } else {
  493.             tryToUseColor = FALSE;
  494.         }
  495.     }
  496. }
  497.  
  498.  
  499.  
  500. void CAnimCursor::loadIndividualCursors(void)
  501. {
  502.     register acurPtr theAcurPtr;
  503.     short nColorCursors;
  504.     short cCursor;
  505.     short oldState;
  506.     short oldResFile;
  507.     
  508.     if (itsAcurHndl == NULL) return;
  509.     
  510.         /*
  511.          * A quick sanity check.
  512.          * If the handle is too short to be a real 'acur', bail out.
  513.          */
  514.     ASSERT(GetHandleSize((Handle) itsAcurHndl) >= sizeof(acurStruct));
  515.     
  516.     oldResFile = CurResFile();
  517.     useTopResFile();
  518.     
  519.     oldState = HGetState((Handle) itsAcurHndl);
  520.     HLock((Handle) itsAcurHndl);
  521.     theAcurPtr = *itsAcurHndl;
  522.     
  523.     if (tryToUseColor) {
  524.         nColorCursors = Count1Resources('crsr');
  525.         usingColorCursors = (nColorCursors > 0);
  526.     }
  527.     
  528.     for (cCursor = 0;  cCursor < theAcurPtr->nCursors;  ++cCursor) {
  529.         
  530.         short theID;
  531.         
  532.             /* Determine the ID. */
  533.         theID = * (short*) &(theAcurPtr->cursor[cCursor]);
  534.         
  535.             /* Load in the cursor from disk. */
  536.         if (usingColorCursors) {
  537.             theAcurPtr->cursor[cCursor] = (CursHandle) GetCCursor(theID);
  538.             DetachResource( (Handle) theAcurPtr->cursor[cCursor] );
  539.         } else {
  540.             CursHandle theCursHndl;
  541.             theCursHndl = GetCursor(theID);
  542.             
  543.             if (theCursHndl != NULL) {
  544.                 
  545.                 DetachResource( (Handle) theCursHndl );
  546.                 
  547.             } else {
  548.                 
  549.                     /*
  550.                      * If there wasn't a (B&W) 'CURS', try to fake it with 
  551.                      * a (color) 'crsr'.  I'm still waiting for that "true stud"
  552.                      * certificate, Jon...
  553.                      */
  554.                 CCrsrHandle theCCrsrHndl;
  555.                 
  556.                 theCCrsrHndl = GetCCursor(theID);
  557.                 if (theCCrsrHndl != NULL) {
  558.                     
  559.                         /*
  560.                          * Put this stuff on the stack, to avoid hassle with
  561.                          * memory allocation.  (It's OK, it's only about 70 bytes.)
  562.                          */
  563.                     Bits16 theData, theMask;
  564.                     Point theHotSpot;
  565.                     
  566.                     BlockMove(&(**theCCrsrHndl).crsr1Data, &theData, sizeof(Bits16));
  567.                     BlockMove(&(**theCCrsrHndl).crsrMask, &theMask, sizeof(Bits16));
  568.                     theHotSpot = (**theCCrsrHndl).crsrHotSpot;
  569.                     DisposeCCursor(theCCrsrHndl);
  570.                     
  571.                     theCursHndl = (CursHandle) NewHandle(sizeof(Cursor));
  572.                     if (theCursHndl != NULL) {        // only a paranoid would check this
  573.                         BlockMove(&theData, &(**theCursHndl).data, sizeof(Bits16));
  574.                         BlockMove(&theMask, &(**theCursHndl).mask, sizeof(Bits16));
  575.                         (**theCursHndl).hotSpot = theHotSpot;
  576.                     }
  577.                     
  578.                 }
  579.                 
  580.             }
  581.             
  582.             theAcurPtr->cursor[cCursor] = theCursHndl;
  583.         }
  584.         
  585.             /* If an error was found, just continue. */
  586.         if (theAcurPtr->cursor[cCursor] != NULL && ResError() == noErr) {
  587.             MoveHHi((Handle) theAcurPtr->cursor[cCursor]);
  588.             HLock((Handle) theAcurPtr->cursor[cCursor]);
  589.         }
  590.         
  591.     }
  592.     
  593.         /* Start with the first cursor. */
  594.     theAcurPtr->cCursor = 0;
  595.     
  596.     HSetState((Handle) itsAcurHndl, oldState);
  597.     
  598.     UseResFile(oldResFile);
  599. }
  600.  
  601.  
  602.  
  603. void CAnimCursor::disposeIndividualCursors(void)
  604. {
  605.     register acurPtr theAcurPtr;
  606.     register short cCursor;
  607.     
  608.     if (itsAcurHndl == NULL) return;
  609.     
  610.     HLock((Handle) itsAcurHndl);
  611.     theAcurPtr = *itsAcurHndl;
  612.     
  613.     for (cCursor = 0;  cCursor < theAcurPtr->nCursors;  ++cCursor) {
  614.         
  615.             /* Dispose of an individual cursor. */
  616.         if (usingColorCursors) {
  617.             DisposeCCursor((CCrsrHandle) theAcurPtr->cursor[cCursor]);
  618.         } else {
  619.             HUnlock((Handle) theAcurPtr->cursor[cCursor]);
  620.             DisposHandle((Handle) theAcurPtr->cursor[cCursor]);
  621.         }
  622.         
  623.     }
  624.     
  625.     HUnlock((Handle) itsAcurHndl);
  626. }
  627.  
  628.  
  629.  
  630. OSErr CAnimCursor::installVBLTask(void)
  631. {
  632.     OSErr theOSErr;
  633.     unsigned long theInitialVBLCount;
  634.     
  635.     if (vblTaskIsInstalled) return noErr;
  636.     
  637.     MoveHHi((Handle) this);
  638.     HLock((Handle) this);
  639.     
  640.     itsA5 = SetCurrentA5();
  641.     itsVBLTask.qType = vType;
  642.     itsVBLTask.vblAddr = (ProcPtr) StripAddress(doAnimateCursor);
  643.     theInitialVBLCount = itsInitialTick + itsInitialDelayTicks - TickCount();
  644.     if (theInitialVBLCount < 1) theInitialVBLCount = 1;
  645.     itsVBLTask.vblCount = theInitialVBLCount;
  646.     itsVBLTask.vblPhase = 0;
  647.     theOSErr = VInstall( (QElemPtr) StripAddress(&itsVBLTask) );
  648.     
  649.     if (theOSErr != noErr) {
  650.         HUnlock((Handle) this);
  651.     } else {
  652.         vblTaskIsInstalled = TRUE;
  653.     }
  654.     
  655.     return theOSErr;
  656. }
  657.  
  658.  
  659.  
  660. OSErr CAnimCursor::removeVBLTask(void)
  661. {
  662.     OSErr theOSErr;
  663.     
  664.     if (!vblTaskIsInstalled) return noErr;
  665.     
  666.     theOSErr = VRemove( (QElemPtr) StripAddress(&itsVBLTask) );
  667.     
  668.     if (theOSErr == noErr) {
  669.         HUnlock((Handle) this);
  670.         vblTaskIsInstalled = FALSE;
  671.     }
  672.     
  673.     return theOSErr;
  674. }
  675.  
  676.  
  677.  
  678. void CAnimCursor::nextCursor(void)
  679. {
  680.     short cCursor;
  681.     register acurPtr theAcurPtr;
  682.     
  683.     theAcurPtr = *itsAcurHndl;
  684.     
  685.     cCursor = theAcurPtr->cCursor;
  686.     ++cCursor;
  687.     if (cCursor >= theAcurPtr->nCursors) {
  688.         cCursor = 0;
  689.     }
  690.     theAcurPtr->cCursor = cCursor;
  691. }
  692.  
  693.  
  694.  
  695.     static long getVBLRec(void) = 0x2008;        // MOVE.L A0,D0
  696.     static void doActualAnimateCursor(void);
  697.     
  698. static pascal void doAnimateCursor(void)
  699. {
  700.         /* See Tech Notes #180 and #208. */
  701.     long myVBLRecAddress;
  702.     long oldA5;
  703.     
  704.         /*
  705.          * The address of the queue entry is stored in A0.
  706.          * The application's A5 is stored one longword before 
  707.          * the VBL queue entry.
  708.          */
  709.     myVBLRecAddress = getVBLRec();
  710.     oldA5 = SetA5( * ( (long*)myVBLRecAddress - 1 ) );
  711.     
  712.         /*
  713.          * OK, globals are valid now, so we can get at the object.  But
  714.          * the compiler may have decided to resolve any global references
  715.          * _before_ A5 was set up.  To forestall that, all such references
  716.          * are in doActualAnimateCursor().  See Tech Note #180.
  717.          */
  718.     doActualAnimateCursor();
  719.     
  720.         /* Restore the A5 to what it was before we did our thing. */
  721.     SetA5(oldA5);
  722. }
  723.  
  724.  
  725.  
  726. static void doActualAnimateCursor(void)
  727. {
  728.     if (!gAnimCursor->isInAnInterrupt) {
  729.         gAnimCursor->isInAnInterrupt = TRUE;
  730.         gAnimCursor->animateCursor();
  731.         gAnimCursor->isInAnInterrupt = FALSE;
  732.     }
  733. }