home *** CD-ROM | disk | FTP | other *** search
/ Otherware / Otherware_1_SB_Development.iso / mac / developm / source / ssave.src / Public Blanket.c
Encoding:
C/C++ Source or Header  |  1992-11-05  |  10.5 KB  |  287 lines

  1. /*
  2.     Blanket -- a  screen saver shell
  3.  
  4.     This program was developed by Christopher Tate at the Pennsylvania State
  5.     University, and is copyright ⌐1991 by the author.  All rights are given for anyone
  6.     to freely modify this code and use it in your own programs so long as credit is given
  7.     to Christopher Tate in your "About Box" or documentation.  The author disclaims all
  8.     damages which may occur as a result of this code; in short, use it at your own risk.
  9.  
  10.     The code has been tested on several machines under several different versions of the
  11.     system, including System 7.0b4, and seems to be quite well-behaved.  However, it's
  12.     not perfect.  I have found at least one application which seems somehow to prevent
  13.     Blanket from activating, for no known reason.  My only comfort is that After Dark 2.0
  14.     exhibits the same problem with that application.  :-)
  15.  
  16.     This file looks best in 9-point Geneva in the THINK C editor, with 3-space tabs.
  17.  
  18.     The screen saver works by patching _SystemTask, the routine that doles out time
  19.     to desk accessories and performs other periodic system tasks.  In Inside Macintosh,
  20.     Apple recommends that applications call SystemTask() as often as possible; at least
  21.     every sixtieth of a second.  Note that WaitNextEvent() calls SystemTask() itself.
  22.  
  23.     Blanket intercepts calls to _SystemTask and uses the opportunity to keep track of
  24.     the time elapsed since the user performed an action -- mouse movement or clicks, or
  25.     keypresses.  If the specified amount of time (the #define'd constant BLANK_TIME) has
  26.     elapsed since such an event occurred, the screen is erased to black.  When the user finally
  27.     causes one of the above events to occur, Blanket removes its GrafPort and causes the
  28.     Window Manager to update all windows.
  29.  
  30.     When drawing to the screen, Blanket uses Alex Chaffee's method for synchronizing
  31.     to the vertical retrace interrupt.  This method was distributed on the Usenet group
  32.     comp.sys.mac.programmer.
  33.  
  34.     Thanks to Ari Halberstadt, Michael Pierce, Jim Cathey, and Larry Rosenstein for
  35.     wonderful suggestions that I didn't wind up using.  :-)  Special thanks to
  36.     jwinterm@jarthur.claremont.edu for debugging tips.
  37.  
  38.     Blanket is written in THINK C, and takes advantage of THINK C's use of A4-relative
  39.     addressing in code resources.  Since writing an INIT is rather dependant on your
  40.     development system, porting this code to MPW C (or *any* Pascal, for that matter)
  41.     may not be trivial.
  42.  
  43.     This project must be built as an INIT resource with the "System Heap" flag set;
  44.     failure to do this will almost certainly cause all manner of interesting crashes....
  45.  
  46.     If I had time, I'd write a cdev portion to go with this screen saver to allow the user to
  47.     customize the delay time, etc.  I'd also figure out something fancier than than a single
  48.     bouncing line for it to draw while the screen is blacked out.
  49. */
  50.  
  51. #include    <StartMgr.h>
  52. #include    <VRetraceMgr.h>
  53. #include    <SetUpA4.h>        /* THINK C's mechanism for handling globals in standalone code */
  54.  
  55. #define        BLANK_TIME            120        /* 120 seconds = 2 minutes idle before blanking */
  56. #define        SYS_TASK_TRAP        0x9B4        /* the trap number for _SystemTask */
  57.  
  58. /* -----  NECESSARY global variables  ----- */
  59.  
  60. long            oldSystemTask;        /* previous _SystemTask trap address */
  61. KeyMap            theKeys;                /* current state of the keyboard */
  62. long            currentTime,            /* what time is it? */
  63.                 lastTime;                /* clock reading, the last time we did something */
  64. Point            lastMouse,                /* where was the mouse last time we checked? */
  65.                 currentMouse;            /* where is the mouse now? */
  66. Boolean            blanked, cleanUp;        /* is the screen currently blanked out? */
  67. RgnHandle        oldGrayRgn,            /* original GrayRgn */
  68.                 nullRgn;                /* null region for various stuff.... */
  69. short            oldMBarHeight;            /* save the original menu bar height */
  70. WindowPtr        frontW;                    /* temporary variable */
  71. GrafPort        myPort;                /* GrafPort for all of my drawing */
  72. GrafPtr            oldPort;                /* old GrafPort */
  73.  
  74. /* -----  global variables for anything fancy the screen saver does  ----- */
  75.  
  76. typedef struct {
  77.     long        goodA4;
  78.     VBLTask    task;
  79. } VBLSTRUCT;                /* data structure for letting a VBL task access our globals */
  80.  
  81. short            ticks, time;
  82. long            seed;
  83. DefVideoRec    video;
  84. VBLSTRUCT        myVBLRec;
  85. SysEnvRec        theSystem;
  86.  
  87. /* random number generator -- only positive numbers */
  88.  
  89. short rand()
  90. {
  91.     seed = seed * 1103515245 + 12345;
  92.     asm {
  93.         move.w        seed,d0
  94.         andi.w        #0x7FFF,d0        ;  no negative numbers!
  95.     }
  96. }
  97.  
  98. /* here's my VBL task, that just tells us when vertical retrace is */
  99.  
  100. pascal void myVBL()
  101. {
  102.     long    oldA4;
  103.  
  104.     asm {
  105.         move.l        a4,oldA4        /* save A4 */
  106.         move.l        -4(a0),a4        /* set up for globals */
  107.     }
  108.  
  109.     ticks++;                        /* increment the global "tick count" */
  110.     myVBLRec.task.vblCount = 1;    /* run again next retrace */
  111.  
  112.     asm {
  113.         move.l        oldA4,a4
  114.     }
  115. }
  116.  
  117. /* here are the routines to install and remove the VBL task */
  118.  
  119. void InstallSync()
  120. {
  121.     asm {
  122.         move.l        a4,myVBLRec.goodA4
  123.     }
  124.     myVBLRec.task.qType = vType;
  125.     myVBLRec.task.vblAddr = (ProcPtr) myVBL;
  126.     myVBLRec.task.vblCount = 1;
  127.     myVBLRec.task.vblPhase = 0;
  128.  
  129.     if (theSystem.hasColorQD)
  130.     {
  131.         GetVideoDefault(&video);
  132.         SlotVInstall(&myVBLRec.task, video.sdSlot);
  133.     }
  134.     else VInstall(&myVBLRec.task);
  135. }
  136.  
  137. void RemoveSync()
  138. {
  139.     if (theSystem.hasColorQD) SlotVRemove(&myVBLRec.task, video.sdSlot);
  140.     else VRemove(&myVBLRec.task);
  141. }
  142.  
  143. /* -----  here's the screen saver itself!  ----- */
  144.  
  145. void mySysTask()            /* this is the trap patch that actually does the work */
  146. {
  147.     asm {
  148.         clr.l        -(sp)                        /* make space for a dummy return address */
  149.         movem.l    a0-a3/d0-d2,-(sp)            /* save all registers */
  150.     }
  151.     SetUpA4();                                    /* set up for global variables (and save a4) */
  152.     asm {
  153.         move.l        oldSystemTask,a0            /* get the address of the old _SystemTask trap */
  154.         move.l        a0,32(sp)                    /* put it in the dummy space */
  155.     }
  156.  
  157. /* here, we check for any keys being pressed, using GetKeys() to include modifier keys */
  158.  
  159.     GetKeys(&theKeys);                    /* look at the keyboard for keys now being pressed */
  160.     if (theKeys.Key[0] || theKeys.Key[1] || theKeys.Key[2] || theKeys.Key[3])
  161.     {
  162.         GetDateTime(&lastTime);            /* record the time of any keypress we see */
  163.         blanked = false;                        /* turn off the blanking! */
  164.     }
  165.  
  166. /* check to see if the mouse button is down */
  167.  
  168.     if (Button())
  169.     {
  170.         GetDateTime(&lastTime);
  171.         blanked = false;    
  172.     }
  173.  
  174. /* now check to see if the mouse has been moved since the last time we came through here */
  175.  
  176.     GetMouse(¤tMouse);    
  177.     LocalToGlobal(¤tMouse);                /* always use global coordinates */
  178.  
  179.     if (!EqualPt(currentMouse, lastMouse))        /* if someone has moved the mouse... */
  180.     {
  181.         lastMouse = currentMouse;                /* remember the new mouse position */
  182.         GetDateTime(&lastTime);                /* remember when we moved the mouse */
  183.         blanked = false;                            /* turn off blanking for mouse movement, too! */
  184.     }
  185.  
  186.     if (blanked == false)
  187.     {
  188.         if (cleanUp == true)            /* if we're in the process of deactivating the screen saver */
  189.         {
  190.             GetPort(&oldPort);
  191.             SetPort(&myPort);
  192.             RemoveSync();                    /* turn off our vertical retrace thingy */
  193.             MBarHeight = oldMBarHeight;    /* restore the menu bar */
  194.             GrayRgn = oldGrayRgn;            /* restore the original GrayRgn */
  195.             frontW = FrontWindow();
  196.             PaintBehind((WindowPeek) frontW, GrayRgn);
  197.             CalcVisBehind((WindowPeek) frontW, GrayRgn);    /* force a redraw of the screen */
  198.             DrawMenuBar();                /* restore the menu bar */
  199.             ShowCursor();                    /* put the cursor back the way it was */
  200.             ClosePort(&myPort);            /* get rid of the GrafPort */
  201.             SetPort(oldPort);
  202.             cleanUp = false;                /* don't do this again */
  203.         }
  204.         GetDateTime(¤tTime);                    /* what time is it? */
  205.         if ((currentTime - lastTime) > BLANK_TIME)    /* if it's time to blank the screen...! */
  206.         {
  207.             InstallSync();                        /* turn on our vertical retrace watcher */
  208.             GetPort(&oldPort);                    /* ALWAYS save the current port! */
  209.             oldMBarHeight = MBarHeight;        /* remember the menu bar's height */
  210.             OpenPort(&myPort);                /* create the GrafPort (also calls SetPort() ) */
  211.             MBarHeight = 0;                    /* turn off things like SuperClock */
  212.             oldGrayRgn = GrayRgn;                /* look up the GrayRgn */
  213.             GrayRgn = nullRgn;                    /* set it to an empty region */
  214.             PaintRect(&(myPort.portRect));    /* black out the whole screen */
  215.  
  216. /*    the next line causes the Window Manager to tell all open windows not to draw on the screen,
  217.     because of the now-empty GrayRgn */
  218.  
  219.             CalcVisBehind((WindowPeek) FrontWindow(), oldGrayRgn);
  220.  
  221.             GetMouse(&lastMouse);            /* record mouse's location */
  222.             LocalToGlobal(&lastMouse);        /* remember to use global coordinates! */
  223.             SetPort(oldPort);                    /* restore the original GrafPort */
  224.             HideCursor();                        /* we don't want the mouse showing, either */
  225.             blanked = cleanUp = true;            /* set up to tidy up the screen when the user does something */
  226.         }
  227.     }
  228.     else        /* -----  this is where any nifty display code goes  ----- */
  229.     {
  230.         GetPort(&oldPort);                        /* save the current GrafPort */
  231.         SetPort(&myPort);                        /* make my port current */
  232.  
  233. /* Be sure to put all drawing BETWEEN the calls to GetPort(&oldPort) and SetPort(oldPort) ! */
  234.  
  235.         time = ticks;                    /* look up our custom "Ticks" variable */
  236.         while (time == ticks) {}            /* sit here until vertical retrace time */
  237.  
  238. /* Draw now, synchronized with the monitor's vertical retrace */
  239.  
  240.         SetPort(oldPort);                        /* restore the original GrafPort */
  241.     }
  242.  
  243.     RestoreA4();                                /* restore a4 */
  244.     asm {
  245.         movem.l    (sp)+,a0-a3/d0-d2            /* restore the other registers */
  246.         rts                                        /* drop through to the original trap routine */
  247.     }
  248. }
  249.  
  250. void main()                    /* this is the main INIT code which installs the trap patch */
  251. {
  252.     Handle        myHandle;
  253.     GrafPtr        savePort;
  254.  
  255.     RememberA0();            /* save our base address where we can find it */
  256.     SetUpA4();                    /* set up a4 to be our base register (and save the old a4) */
  257.  
  258.     asm
  259.     {
  260.         movem.l    a0-a3/d0-d2,-(sp)        /* save registers a0-a3 and d0-d2 */
  261.         _RecoverHandle                    /* get a handle to ourselves */
  262.         move.l        a0,myHandle            /* put it in the Handle variable */
  263.     }
  264.     DetachResource(myHandle);            /* let the Resource Mangler forget about us */
  265.     HLock(myHandle);                        /* lock ourselves down */
  266.  
  267.     InitGraf(&thePort);                        /* set up my QuickDraw globals */
  268.     (void) SysEnvirons(2, &theSystem);    /* to see if we have Color QD */
  269.     nullRgn = (RgnHandle) NewHandleSys(sizeof(Region));
  270.     HNoPurge((Handle) nullRgn);            /* just to make sure */
  271.     SetEmptyRgn(nullRgn);                    /* allocate a null region in the system heap */
  272.     blanked = cleanUp = false;                /* start out in the non-blanked state */
  273.     seed = TickCount();                        /* seed the random number generator */
  274.  
  275. /* patch SystemTask() to get periodic time */
  276.  
  277.     oldSystemTask = NGetTrapAddress(SYS_TASK_TRAP, ToolTrap);
  278.     NSetTrapAddress(mySysTask, SYS_TASK_TRAP, ToolTrap);
  279.  
  280. /* Clean up and go home */
  281.  
  282.     asm
  283.     {
  284.         movem.l    (sp)+,a0-a3/d0-d2            /* restore these saved registers */
  285.     }
  286.     RestoreA4();                                /* also restore a4 */
  287. }