home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Libraries / Spin Cursor Lib / main.c < prev    next >
Encoding:
Text File  |  1992-09-09  |  11.0 KB  |  357 lines  |  [TEXT/KAHL]

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. // SpinCursor Library
  4. // ------------------
  5. //
  6. // Add this library to your project, and #include "SpinCursor.h", and you
  7. // too can have smooth, spinning cursors in your programs!
  8. //
  9. //
  10. // NOTICE:
  11. //
  12. // There is no legal problem if you include this library or header file in
  13. // your own projects.  The only problem that may occur is if you attempt
  14. // to sell any or all of this code, or if you edit it and repost it anywhere. 
  15. //
  16. // If you distribute this code, which I strongly urge, please leave the
  17. // folders, files, and libraries untouched. If you want to mess around, make
  18. // a duplicate copy and edit that one.
  19. //
  20. // If you have questions, comments, or suggestions, please feel free to write
  21. // me (although I should mention that this address is only valid until May
  22. // of 1993; after that I'm a certified gradge-U-aite of the University of
  23. // Michigan.
  24. //
  25. // EMAIL: rudman@engin.umich.edu
  26. //
  27. // Modifications:
  28. //
  29. // 09/09/92        DER        Added global installation variable to avoid multiple
  30. //                        installations of a cursor task.
  31. //
  32. ///////////////////////////////////////////////////////////////////////////////
  33. #include "CSpinner.h"
  34.  
  35. CursorTask            gCursTask;                                /* Global structure */
  36. Boolean                gCursInstalled = false;                    /* installation global */
  37.  
  38. /* --------- */
  39. /* SetCQDVar */
  40. /* --------- */
  41. /*
  42.     This assertains whether or not Color QuickDraw is available.
  43.     Obviously, this affects whether or not we can utilize
  44.     color cursor calls. We store the results in the global
  45.     structure so that we can access it during the main calls.
  46. */
  47.  
  48. void
  49. SetCQDVar (void)
  50. {
  51.     OSErr        err;
  52.     long int    result;
  53.     SysEnvRec    sysEnv;
  54.             
  55.     if (WeCanUseGestalt ()) {
  56.         err = Gestalt (gestaltQuickdrawVersion, &result);
  57.         gCursTask.cQD = (result >= 0x100);
  58.     }
  59.     else {
  60.         err = SysEnvirons (1, &sysEnv);
  61.         gCursTask.cQD = sysEnv.hasColorQD;
  62.     }
  63. }
  64.  
  65. /* ---------------- */
  66. /* BeginResSpinning */
  67. /* ---------------- */
  68. /*
  69.     This is a higher level call to BeginSpinning() which will
  70.     utilize a 'SPIN' resource of resID.  All color checking
  71.     and error checking is handled within this call, and the
  72.     error is returned in the form of a CursErr (see the
  73.     header file for #defines). A call to BeginResSpinning()
  74.     should be accompanied by a later call to StopSpinning().
  75. */
  76.  
  77. CursErr
  78. BeginResSpinning (short int resID)
  79. {
  80.     Handle        spinH;
  81.     short int    attrs;
  82.     
  83.     Boolean        color;
  84.     short int     initResID;
  85.     short int    numCursors;
  86.     short int     initDelay;
  87.     short int    frequency;
  88.     
  89.     if (gCursInstalled)                                    /* if we are already installed, */
  90.         return (cursAlreadyInstalled);                    /* return an error! */
  91.         
  92.     gCursInstalled = false;                                /* make sure we know we are not in yet */
  93.     
  94.     spinH = GetResource ('SPIN', resID);                /* grab the 'SPIN' resource */
  95.     
  96.     if (spinH == nil)                                    /* if we grabbed a nil handle, */
  97.         return (cursResNotFound);                        /* return an error */
  98.         
  99.     if (ResError () != noErr)                            /* if we grabbed a handle but */
  100.         return (cursSpinCursResErr);                    /* had an error, return it */
  101.     
  102.     attrs = GetResAttrs (spinH);                        /* store original attribs */
  103.     HLock (spinH);                                        /* lock the handle down */
  104.     HNoPurge (spinH);                                    /* make it unpurgeable */
  105.     
  106.     BlockMove (&((*spinH)[0]), &color, 1);                /* pull 1 byte */
  107.     BlockMove (&((*spinH)[2]), &initResID, 2);            /* pull 2 bytes */
  108.     BlockMove (&((*spinH)[4]), &numCursors, 2);            /* pull 2 bytes */
  109.     BlockMove (&((*spinH)[6]), &initDelay, 2);            /* pull 2 bytes */
  110.     BlockMove (&((*spinH)[8]), &frequency, 2);            /* pull 2 bytes */
  111.         
  112.     SetResAttrs (spinH, attrs);                            /* restore original attribs */
  113.     ReleaseResource (spinH);                            /* let go of the resource */
  114.     
  115.     if (ResError () != noErr)                            /* if we get any errors, */
  116.         return (cursSpinCursResErr);                    /* return them */
  117.     
  118.     return (BeginSpinning (color, initResID,
  119.             numCursors, initDelay, frequency));            /* call BeginSpinning () */
  120. }
  121.  
  122. /* ------------- */
  123. /* BeginSpinning */
  124. /* ------------- */
  125. /*
  126.     This call is identical to BeginResSpinning(), except that you
  127.     specify all of the cursor information yourself. Again, all
  128.     error handling is done for you; it will return a CursErr, but
  129.     you really need not worry about this.
  130. */
  131.  
  132. CursErr
  133. BeginSpinning (    Boolean                color,
  134.                 short int             initResID,
  135.                 short int            numCursors,
  136.                 short int             initDelay,
  137.                 short int            frequency)
  138. {
  139.     OSErr        err;                                    /* returnable error */
  140.     short int    i;                                        /* dummy counter */
  141.     CursorList    *cp;                                    /* pointing index */
  142.     
  143.     if (gCursInstalled)                                    /* if we are already installed, */
  144.         return (cursAlreadyInstalled);                    /* return an error! */
  145.     
  146.     gCursInstalled = false;                                /* we are not installed yet */
  147.     SetCQDVar ();                                        /* set our CQD variable */
  148.     gCursTask.grec.cursors =
  149.             (CursorList *)NewPtr (sizeof (CursorList));    /* allocate cursor memory */
  150.     
  151.                                                         /* now set up the first field */
  152.     
  153.     if (gCursTask.cQD && color) {
  154.         (gCursTask.grec.cursors)->curs = (CursHandle)GetCCursor (initResID);
  155.     }
  156.     else {                        
  157.         (gCursTask.grec.cursors)->curs = GetCursor (initResID);
  158.     }
  159.         
  160.     if ((gCursTask.grec.cursors)->curs == nil)            /* if we did not get a cursor, */
  161.         return (cursResNotFound);                        /* return an error */
  162.             
  163.     HNoPurge ((Handle)(gCursTask.grec.cursors)->curs);    /* set it unpurgeable */
  164.     HLockHi ((Handle)(gCursTask.grec.cursors)->curs);    /* and lock it high in memory */
  165.         
  166.     gCursTask.grec.frame = cp = gCursTask.grec.cursors;    /* set the frame to the first field */
  167.     
  168.     for (i=1; i < numCursors; i++) {                    /* cycle through the rest */
  169.         
  170.     cp->nextCurs = (struct CursorList *)NewPtr (sizeof (CursorList));
  171.     cp = (CursorList *)cp->nextCurs;                    /* allocate cursor space and jump */
  172.         
  173.     if ((MemError () != noErr) || (cp == nil))            /* check for any errors, */
  174.         return (cursMemReqFailed);                        /* and return them */
  175.         
  176.     if (gCursTask.cQD && color) {
  177.         cp->curs = (CursHandle)GetCCursor (initResID+i);/* color cursors if desired */
  178.     }
  179.     else {
  180.         cp->curs = GetCursor (initResID+i);                /* black and white otherwise */
  181.     }
  182.         
  183.     if (cp->curs == nil)                                /* did we get the cursor */
  184.         return (cursResNotFound);                        /* if no, return error */
  185.             
  186.     HNoPurge ((Handle)cp->curs);                        /* force it unpurgeable */
  187.     HLockHi ((Handle)cp->curs);                            /* and lock it high */
  188.     }
  189.     
  190.     cp->nextCurs = nil;                                    /* set the last field; EOL */
  191.         
  192.     gCursTask.grec.frequency = frequency;                /* set the frequency field */
  193.     gCursTask.color = color;                            /* set the color field */
  194.             
  195.     gCursTask.vblTask.qType = vType;                    /* set up the VBL task record */
  196.     gCursTask.vblTask.vblAddr = (ProcPtr)SpinCursor;
  197.     gCursTask.vblTask.vblCount = initDelay;
  198.     gCursTask.vblTask.vblPhase = 0;
  199.  
  200.     err = VInstall ((QElemPtr)&gCursTask.vblTask);        /* install the VBL task */
  201.         
  202.     if (err)
  203.         return (cursVBLReqFailed);                        /* if errors, return them, else */
  204.     else {
  205.         gCursInstalled = true;                            /* declare the task installed */
  206.         return (cursNoErr);                                /* and return no error */
  207.     }
  208. }
  209.  
  210. /* ---------- */
  211. /* SpinCursor */
  212. /* ---------- */
  213. /*
  214.     This is the meat and bones of the spinning cursor calls.
  215.     When the VBL task count (vblCount) goes to zero, the
  216.     Vertical Retrace Manager will call SpinCursor(). Our job
  217.     will be to set the cursor to the current frame, and then
  218.     to advance the frame. The thing to remember is that we
  219.     are executed during interrupt, and we are not allowed to
  220.     use Memory Manager calls... as well as a billion other
  221.     things.
  222.     
  223.     Another cool thing to note is that at the time of our
  224.     interrupt, a pointer to the installed VBL task will be
  225.     placed in A0. Since we installed with some more globals
  226.     attached, these will also be available to us; kind of
  227.     a sneaky way around the messed up A5 problem.
  228.     
  229.     Also remember that the vblCount is zero at this time. If
  230.     we leave this function without returning a nonzero value,
  231.     the task will never return to SpinCursor again. So, we
  232.     articulately return the vblCount to frequency, which is
  233.     why the task happens so regulary...
  234. */
  235.  
  236. pascal void
  237. SpinCursor (void)
  238. {
  239.     CursorTaskPtr        taskPtr;
  240.     Boolean                busy;
  241.  
  242.     asm {
  243.         move.l a0, taskPtr;                                    /* grab our globals from A0 */
  244.     }
  245.     
  246.     busy = (*(Boolean *)CrsrBusy);                            /* determine the cursor state */
  247.         
  248.     if (!busy) {                                            /* if it is available to us */
  249.                                                             /* set the cursor and frame advance */
  250.  
  251.         if (taskPtr->color && taskPtr->cQD) {    
  252.             SetCCursor ((CCrsrHandle)(taskPtr->grec.frame)->curs);
  253.         }
  254.         else {
  255.             SetCursor ((CursPtr)(*((taskPtr->grec.frame)->curs)));
  256.         }    
  257.             
  258.         taskPtr->grec.frame = (CursorList *)(taskPtr->grec.frame)->nextCurs;
  259.  
  260.         if (taskPtr->grec.frame == nil)                        /* if at EOL, */
  261.             taskPtr->grec.frame = taskPtr->grec.cursors;    /* return us back to first frame */
  262.             
  263.     }
  264.  
  265.     taskPtr->vblTask.vblCount = taskPtr->grec.frequency;    /* restore the VBL task */
  266. }
  267.  
  268. /* ------------ */
  269. /* StopSpinning */
  270. /* ------------ */
  271. /*
  272.     When you have finished whatever task you were doing when
  273.     you decided to BeginSpinning(), call StopSpinning() to
  274.     end the spinning cursor, and to deallocate the memory and
  275.     remove the VBL task. It would be unwise to exit your
  276.     program without removing the VBL task. Why? Because if
  277.     you do not do it, who will? Of course, the vblCount will
  278.     zero out and the call to SpinCursor will never happen,
  279.     but hey.. we program here, right?
  280. */
  281.  
  282. CursErr
  283. StopSpinning (void)
  284. {
  285.     OSErr        err;
  286.     short int    i;
  287.     CursorList    *cp, *temp;
  288.             
  289.     if (!gCursInstalled)                                    /* if we were not installed, */
  290.         return (cursTaskNotInstalled);                        /* alert them and exit */
  291.     
  292.     err = VRemove ((QElemPtr)&gCursTask.vblTask);            /* remove the VBL task */
  293.     
  294.     cp = gCursTask.grec.cursors;                            /* set the pointing index */
  295.     
  296.     while (cp) {                                            /* while the index is not empty, */
  297.     
  298.         HUnlock ((Handle)(cp->curs));                        /* unlock the cursors */
  299.         HPurge ((Handle)(cp->curs));                        /* make them purgeable */
  300.                                                             /* and dispose of them */
  301.         
  302.         if (gCursTask.color && gCursTask.cQD) {                
  303.             DisposCCursor ((CCrsrHandle)(cp->curs));
  304.             err = noErr;
  305.         }
  306.         else {
  307.             ReleaseResource ((Handle)(cp->curs));
  308.             err = ResError ();
  309.         }
  310.         
  311.         if (err != noErr)                                    /* any errors, we will return */
  312.             return (cursCouldNotRelease);
  313.             
  314.         temp = (CursorList *)cp->nextCurs;                    /* set a pointer to the next field */
  315.         DisposPtr ((Ptr)cp);                                /* deallocate the current index */
  316.         err = MemError ();                                    /* grab any errors */
  317.         
  318.         if (err != noErr)                                    /* and return them */
  319.             return (err);
  320.         cp = temp;                                            /* set the index to the next field */
  321.     }
  322.     
  323.     InitCursor ();                                            /* restore the cursor to an arrow */
  324.     return (cursNoErr);                                        /* and return no error */
  325. }
  326.  
  327. Boolean
  328. WeCanUseGestalt (void)
  329. {
  330.     return (IsThisTrapAvailable (_Gestalt));
  331. }
  332.  
  333. Boolean
  334. IsThisTrapAvailable (unsigned long int trap)
  335. {
  336.     TrapType                trapType = ToolTrap;
  337.     unsigned long int        numToolBoxTraps;
  338.  
  339.     if (NGetTrapAddress (_InitGraf, ToolTrap) ==
  340.         NGetTrapAddress (0xAA6E, ToolTrap))
  341.         numToolBoxTraps = 0x200;
  342.     else
  343.         numToolBoxTraps = 0x400;
  344.  
  345.     if (BitAnd (trap, 0x0800) > 0)
  346.         trapType = ToolTrap;
  347.     else
  348.         trapType = OSTrap;
  349.         
  350.     if (trapType == ToolTrap) {
  351.         trap = BitAnd (trap, 0x07FF);
  352.         if (trap >= numToolBoxTraps)
  353.             trap = _Unimplemented;
  354.     }
  355.     return (NGetTrapAddress(trap, trapType) != 
  356.             NGetTrapAddress(_Unimplemented, ToolTrap));
  357. }