home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / processes / vblsnippet / vblsnippet.c next >
Encoding:
C/C++ Source or Header  |  2000-06-23  |  8.2 KB  |  289 lines

  1. /*
  2.     File:        VBLSnippet.c
  3.  
  4.     Contains:    A simple example of a persistent VBL written in C that
  5.                 works with 68K or PowerPC.
  6.  
  7.     Written by: Jim Luther (Based on the VBL code from the Technical Note
  8.                 "TB 35 - MultiFinder Miscellanea".)     
  9.  
  10.     Copyright:    Copyright © 1995-1999 by Apple Computer, Inc., All Rights Reserved.
  11.  
  12.                 You may incorporate this Apple sample source code into your program(s) without
  13.                 restriction. This Apple sample source code has been provided "AS IS" and the
  14.                 responsibility for its operation is yours. You are not permitted to redistribute
  15.                 this Apple sample source code as "Apple sample source code" after having made
  16.                 changes. If you're going to re-distribute the source, we require that you make
  17.                 it clear in the source that the code was descended from Apple sample source
  18.                 code, but that you've made changes.
  19.  
  20.     Change History (most recent first):
  21.                 7/27/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
  22.                 
  23.  
  24. */
  25.  
  26. #include <Types.h>
  27. #include <Memory.h>
  28. #include <QuickDraw.h>
  29. #include <Fonts.h>
  30. #include <Windows.h>
  31. #include <Menus.h>
  32. #include <TextEdit.h>
  33. #include <Dialogs.h>
  34. #include <Events.h>
  35. #include <TextUtils.h>
  36. #include <Retrace.h>
  37. #include <LowMem.h>
  38. #include <Gestalt.h>
  39.  
  40. /*----------------------------------------------------------------------------*/
  41.  
  42. /*
  43. **    Define a struct to keep track of what we need in the VBL.  Put theVBLTask
  44. **    into the struct first because its address will be passed to our VBL task
  45. **    in A0.
  46. */
  47. struct VBLRec
  48. {
  49.     VBLTask        theVBLTask;        /* the VBL task itself */
  50. #if !GENERATINGCFM
  51.     long        VBLA5;            /* saved CurrentA5 where we can find it for 68K code */
  52. #endif
  53. };
  54. typedef struct VBLRec VBLRec;
  55. typedef struct VBLRec *VBLRecPtr;
  56.  
  57. /*----------------------------------------------------------------------------*/
  58.  
  59. /*
  60. **    Constants used in sample
  61. */
  62.  
  63. enum
  64. {
  65.     kInterval        =    6,        /* VBL interval */
  66.     rInfoDialog        =    140,    /* DLOG resource ID */
  67.     rStatTextItem    =    1        /* item number of counter field in dialog */
  68. };
  69.  
  70. /*----------------------------------------------------------------------------*/
  71.  
  72. /*
  73. **    Prototypes
  74. */
  75.  
  76. void        DoVBL(VBLRecPtr recPtr);
  77.  
  78. #if GENERATINGCFM
  79. void        StartVBL(VBLTaskPtr vblTaskPtr);    /* Under CFM, we're passed the VBL task pointer */
  80. #else
  81. void        StartVBL(void);                        /* Otherwise, we'll have to get it out of register A0 */ 
  82. #endif
  83.  
  84. OSErr        NewPersistentVBLProc(ProcPtr userRoutine, VBLUPP *theVBLUPP);
  85. void        DisposePersistentVBLProc(VBLUPP theVBLUPP);
  86. void        main(void);
  87.  
  88. /*----------------------------------------------------------------------------*/
  89.  
  90. /*
  91. **    A global which will be referenced from our VBL Task and the test program
  92. */
  93.  
  94. long    gCounter;        /* Counter incremented each time our VBL gets called */
  95.  
  96. /*----------------------------------------------------------------------------*/
  97.  
  98. /*
  99. **    DoVBL is called only by StartVBL()
  100. */
  101. void DoVBL(VBLRecPtr recPtr)
  102. {
  103.     gCounter++;                                    /* Show we can set a global */
  104.     recPtr->theVBLTask.vblCount = kInterval;    /* Set ourselves to run again */
  105. }
  106.  
  107. /*----------------------------------------------------------------------------*/
  108.  
  109. #if !GENERATINGCFM
  110. /*
  111. **    GetVBLRec returns the address of the VBLRec associated with our VBL task.
  112. **    This works because on entry into the VBL task, A0 points to the theVBLTask
  113. **    field in the VBLRec record, which is the first field in the record and that
  114. **    is the address we return.  Note that this method works whether the VBLRec
  115. **    is allocated globally, in the heap (as long as the record is locked in 
  116. **    memory) or if it is allocated on the stack as is the case in this example.
  117. **    In the latter case this is OK as long as the procedure which installed the
  118. **    task does not exit while the task is running.  This trick allows us to get
  119. **    to the saved A5, but it could also be used to get to anything we wanted to
  120. **    store in the record.
  121. */
  122. extern    VBLRecPtr GetVBLRec(void)
  123.     = 0x2008;    /* MOVE.L    A0,D0 */
  124. #endif
  125.  
  126. /*----------------------------------------------------------------------------*/
  127.  
  128. /*
  129. **    This is the actual VBL task code.  It uses GetVBLRec to get our VBL record
  130. **    and properly set up A5 (non-CFM only).
  131. **    Because of the vagaries of C optimization, it calls a separate routine to
  132. **    actually access global variables.  See "OV 10 - Setting and Restoring A5"
  133. **    for the reasons for this, as well as for a description of SetA5.
  134. */
  135.  
  136. #if GENERATINGCFM
  137. void StartVBL(VBLTaskPtr vblTaskPtr)
  138. {
  139.     DoVBL((VBLRecPtr)vblTaskPtr);    /* Call another routine to do actual work */
  140. }
  141. #else
  142. void StartVBL()
  143. {
  144.     long        curA5;
  145.     VBLRecPtr    recPtr;
  146.     
  147.     recPtr = GetVBLRec();            /* First get our record */
  148.     curA5 = SetA5(recPtr->VBLA5);    /* Get the saved A5 */
  149.     /* Now we can access globals */
  150.     
  151.     DoVBL(recPtr);                    /* Call another routine to do actual work */
  152.  
  153.     (void) SetA5(curA5);            /* Restore old A5 */
  154. }
  155. #endif
  156.  
  157. /*----------------------------------------------------------------------------*/
  158.  
  159. /*
  160. **    NewPersistentVBLProc allocates the VBLUPP in the System heap so the VBL
  161. **    will be persistent.
  162. */
  163. OSErr    NewPersistentVBLProc(ProcPtr userRoutine, VBLUPP *theVBLUPP)
  164. {
  165. #if GENERATINGCFM
  166.     OSErr    result;
  167.     THz        savedZone;
  168.     
  169.     savedZone = GetZone();
  170.     SetZone(SystemZone());
  171.     *theVBLUPP = NewVBLProc(userRoutine);
  172.     result = MemError();
  173.     SetZone(savedZone);
  174.     return ( result );
  175. #else
  176.     enum
  177.     {
  178.         kJMPInstr = 0x4ef9,
  179.         kJMPSize = 6
  180.     };
  181.     OSErr        result;
  182.     Ptr            sysHeapPtr;
  183.     
  184.     sysHeapPtr = NewPtrSys(kJMPSize);
  185.     result = MemError();
  186.     if ( result == noErr )
  187.     {
  188.         *(short *)sysHeapPtr = kJMPInstr;
  189.         *(ProcPtr *)(sysHeapPtr+2) = userRoutine;
  190.         FlushCodeCacheRange(sysHeapPtr, kJMPSize);
  191.         *theVBLUPP = (VBLUPP)sysHeapPtr;
  192.     }
  193.     return ( result );
  194. #endif
  195. }
  196.  
  197. /*----------------------------------------------------------------------------*/
  198.  
  199. /*
  200. **    DisposePersistentVBLProc frees up the memory used for the VBLUPP.
  201. */
  202. void    DisposePersistentVBLProc(VBLUPP theVBLUPP)
  203. {
  204. #if GENERATINGCFM
  205.     THz        savedZone;
  206.     
  207.     savedZone = GetZone();
  208.     SetZone(SystemZone());
  209.     DisposeRoutineDescriptor(theVBLUPP);
  210.     SetZone(savedZone);
  211. #else
  212.     DisposePtr((Ptr)theVBLUPP);
  213. #endif
  214. }
  215.  
  216. /*----------------------------------------------------------------------------*/
  217.  
  218. /*
  219. **    Create a dialog just to demonstrate that the global variable
  220. **    is being updated by the VBL Task.  Before installing the VBL, we store
  221. **    our A5 in the actual VBL Task record, using SetCurrentA5 described in
  222. **    OV 10 - Setting and Restoring A5.  We'll run the VBL, showing the counter
  223. **    being incremented, until the mouse button is clicked.  Then we remove
  224. **    the VBL Task, close the dialog, and remove the mouse down events to
  225. **    prevent the application from being inadvertently switched by MultiFinder.
  226. */
  227. void main (void)
  228. {
  229.     VBLRec            theVBLRec;
  230.     DialogPtr        infoDPtr;
  231.     DialogRecord    infoDStorage;
  232.     Str255            numStr = "\pTest";
  233.     OSErr            theErr;
  234.     Handle            theItemHandle;
  235.     short            theItemType;
  236.     Rect            theRect;
  237.     long            lastCount = 0;
  238.     
  239.     InitGraf(&qd.thePort);
  240.     InitFonts();
  241.     InitWindows();
  242.     InitMenus();
  243.     TEInit();
  244.     InitDialogs(NULL);
  245.     InitCursor();
  246.     MaxApplZone();
  247.     
  248. #if !GENERATINGCFM
  249.     /* Store the current value of A5 in the VBLA5 field if not CFM. */
  250.     theVBLRec.VBLA5 = SetCurrentA5 ();
  251. #endif
  252.     
  253.     gCounter = 0;    /* Initialize our global counter */
  254.     
  255.     /* Put up the dialog */
  256.     infoDPtr = GetNewDialog (rInfoDialog, (Ptr) &infoDStorage, (WindowPtr) -1);
  257.     DrawDialog (infoDPtr);
  258.     GetDialogItem (infoDPtr, rStatTextItem, &theItemType, &theItemHandle, &theRect);
  259.     
  260.     /* Set the address of our routine */
  261.     theErr = NewPersistentVBLProc((ProcPtr)StartVBL, &theVBLRec.theVBLTask.vblAddr);
  262.     theVBLRec.theVBLTask.vblCount = kInterval;    /* Frequency of task, in ticks */
  263.     theVBLRec.theVBLTask.qType = vType;            /* qElement is a VBL task */
  264.     theVBLRec.theVBLTask.vblPhase = 0;
  265.     
  266.     /* Now install the VBL task */
  267.     theErr = VInstall((QElemPtr)&theVBLRec.theVBLTask);
  268.     
  269.     /* Display the counter until the mouse button is pushed */
  270.     if ( theErr == noErr )
  271.     {
  272.         do
  273.         {
  274.             if (gCounter != lastCount)
  275.             {
  276.                 lastCount = gCounter;
  277.                 NumToString(gCounter, numStr);
  278.                 SetDialogItemText(theItemHandle, numStr);
  279.             }
  280.         } while ( !Button () );
  281.         theErr = VRemove((QElemPtr)&theVBLRec.theVBLTask); /* Remove it when done */
  282.         DisposePersistentVBLProc(theVBLRec.theVBLTask.vblAddr);    /* Dispose of the memory */
  283.     }
  284.     
  285.     /* Finish up */
  286.     CloseDialog (infoDPtr);        /* Get rid of our dialog */
  287.     FlushEvents (mDownMask, 0);    /* Flush all mouse down events */
  288. }
  289.