home *** CD-ROM | disk | FTP | other *** search
- /*
- Blanket -- a screen saver shell
-
- This program was developed by Christopher Tate at the Pennsylvania State
- University, and is copyright ⌐1991 by the author. All rights are given for anyone
- to freely modify this code and use it in your own programs so long as credit is given
- to Christopher Tate in your "About Box" or documentation. The author disclaims all
- damages which may occur as a result of this code; in short, use it at your own risk.
-
- The code has been tested on several machines under several different versions of the
- system, including System 7.0b4, and seems to be quite well-behaved. However, it's
- not perfect. I have found at least one application which seems somehow to prevent
- Blanket from activating, for no known reason. My only comfort is that After Dark 2.0
- exhibits the same problem with that application. :-)
-
- This file looks best in 9-point Geneva in the THINK C editor, with 3-space tabs.
-
- The screen saver works by patching _SystemTask, the routine that doles out time
- to desk accessories and performs other periodic system tasks. In Inside Macintosh,
- Apple recommends that applications call SystemTask() as often as possible; at least
- every sixtieth of a second. Note that WaitNextEvent() calls SystemTask() itself.
-
- Blanket intercepts calls to _SystemTask and uses the opportunity to keep track of
- the time elapsed since the user performed an action -- mouse movement or clicks, or
- keypresses. If the specified amount of time (the #define'd constant BLANK_TIME) has
- elapsed since such an event occurred, the screen is erased to black. When the user finally
- causes one of the above events to occur, Blanket removes its GrafPort and causes the
- Window Manager to update all windows.
-
- When drawing to the screen, Blanket uses Alex Chaffee's method for synchronizing
- to the vertical retrace interrupt. This method was distributed on the Usenet group
- comp.sys.mac.programmer.
-
- Thanks to Ari Halberstadt, Michael Pierce, Jim Cathey, and Larry Rosenstein for
- wonderful suggestions that I didn't wind up using. :-) Special thanks to
- jwinterm@jarthur.claremont.edu for debugging tips.
-
- Blanket is written in THINK C, and takes advantage of THINK C's use of A4-relative
- addressing in code resources. Since writing an INIT is rather dependant on your
- development system, porting this code to MPW C (or *any* Pascal, for that matter)
- may not be trivial.
-
- This project must be built as an INIT resource with the "System Heap" flag set;
- failure to do this will almost certainly cause all manner of interesting crashes....
-
- If I had time, I'd write a cdev portion to go with this screen saver to allow the user to
- customize the delay time, etc. I'd also figure out something fancier than than a single
- bouncing line for it to draw while the screen is blacked out.
- */
-
- #include <StartMgr.h>
- #include <VRetraceMgr.h>
- #include <SetUpA4.h> /* THINK C's mechanism for handling globals in standalone code */
-
- #define BLANK_TIME 120 /* 120 seconds = 2 minutes idle before blanking */
- #define SYS_TASK_TRAP 0x9B4 /* the trap number for _SystemTask */
-
- /* ----- NECESSARY global variables ----- */
-
- long oldSystemTask; /* previous _SystemTask trap address */
- KeyMap theKeys; /* current state of the keyboard */
- long currentTime, /* what time is it? */
- lastTime; /* clock reading, the last time we did something */
- Point lastMouse, /* where was the mouse last time we checked? */
- currentMouse; /* where is the mouse now? */
- Boolean blanked, cleanUp; /* is the screen currently blanked out? */
- RgnHandle oldGrayRgn, /* original GrayRgn */
- nullRgn; /* null region for various stuff.... */
- short oldMBarHeight; /* save the original menu bar height */
- WindowPtr frontW; /* temporary variable */
- GrafPort myPort; /* GrafPort for all of my drawing */
- GrafPtr oldPort; /* old GrafPort */
-
- /* ----- global variables for anything fancy the screen saver does ----- */
-
- typedef struct {
- long goodA4;
- VBLTask task;
- } VBLSTRUCT; /* data structure for letting a VBL task access our globals */
-
- short ticks, time;
- long seed;
- DefVideoRec video;
- VBLSTRUCT myVBLRec;
- SysEnvRec theSystem;
-
- /* random number generator -- only positive numbers */
-
- short rand()
- {
- seed = seed * 1103515245 + 12345;
- asm {
- move.w seed,d0
- andi.w #0x7FFF,d0 ; no negative numbers!
- }
- }
-
- /* here's my VBL task, that just tells us when vertical retrace is */
-
- pascal void myVBL()
- {
- long oldA4;
-
- asm {
- move.l a4,oldA4 /* save A4 */
- move.l -4(a0),a4 /* set up for globals */
- }
-
- ticks++; /* increment the global "tick count" */
- myVBLRec.task.vblCount = 1; /* run again next retrace */
-
- asm {
- move.l oldA4,a4
- }
- }
-
- /* here are the routines to install and remove the VBL task */
-
- void InstallSync()
- {
- asm {
- move.l a4,myVBLRec.goodA4
- }
- myVBLRec.task.qType = vType;
- myVBLRec.task.vblAddr = (ProcPtr) myVBL;
- myVBLRec.task.vblCount = 1;
- myVBLRec.task.vblPhase = 0;
-
- if (theSystem.hasColorQD)
- {
- GetVideoDefault(&video);
- SlotVInstall(&myVBLRec.task, video.sdSlot);
- }
- else VInstall(&myVBLRec.task);
- }
-
- void RemoveSync()
- {
- if (theSystem.hasColorQD) SlotVRemove(&myVBLRec.task, video.sdSlot);
- else VRemove(&myVBLRec.task);
- }
-
- /* ----- here's the screen saver itself! ----- */
-
- void mySysTask() /* this is the trap patch that actually does the work */
- {
- asm {
- clr.l -(sp) /* make space for a dummy return address */
- movem.l a0-a3/d0-d2,-(sp) /* save all registers */
- }
- SetUpA4(); /* set up for global variables (and save a4) */
- asm {
- move.l oldSystemTask,a0 /* get the address of the old _SystemTask trap */
- move.l a0,32(sp) /* put it in the dummy space */
- }
-
- /* here, we check for any keys being pressed, using GetKeys() to include modifier keys */
-
- GetKeys(&theKeys); /* look at the keyboard for keys now being pressed */
- if (theKeys.Key[0] || theKeys.Key[1] || theKeys.Key[2] || theKeys.Key[3])
- {
- GetDateTime(&lastTime); /* record the time of any keypress we see */
- blanked = false; /* turn off the blanking! */
- }
-
- /* check to see if the mouse button is down */
-
- if (Button())
- {
- GetDateTime(&lastTime);
- blanked = false;
- }
-
- /* now check to see if the mouse has been moved since the last time we came through here */
-
- GetMouse(¤tMouse);
- LocalToGlobal(¤tMouse); /* always use global coordinates */
-
- if (!EqualPt(currentMouse, lastMouse)) /* if someone has moved the mouse... */
- {
- lastMouse = currentMouse; /* remember the new mouse position */
- GetDateTime(&lastTime); /* remember when we moved the mouse */
- blanked = false; /* turn off blanking for mouse movement, too! */
- }
-
- if (blanked == false)
- {
- if (cleanUp == true) /* if we're in the process of deactivating the screen saver */
- {
- GetPort(&oldPort);
- SetPort(&myPort);
- RemoveSync(); /* turn off our vertical retrace thingy */
- MBarHeight = oldMBarHeight; /* restore the menu bar */
- GrayRgn = oldGrayRgn; /* restore the original GrayRgn */
- frontW = FrontWindow();
- PaintBehind((WindowPeek) frontW, GrayRgn);
- CalcVisBehind((WindowPeek) frontW, GrayRgn); /* force a redraw of the screen */
- DrawMenuBar(); /* restore the menu bar */
- ShowCursor(); /* put the cursor back the way it was */
- ClosePort(&myPort); /* get rid of the GrafPort */
- SetPort(oldPort);
- cleanUp = false; /* don't do this again */
- }
- GetDateTime(¤tTime); /* what time is it? */
- if ((currentTime - lastTime) > BLANK_TIME) /* if it's time to blank the screen...! */
- {
- InstallSync(); /* turn on our vertical retrace watcher */
- GetPort(&oldPort); /* ALWAYS save the current port! */
- oldMBarHeight = MBarHeight; /* remember the menu bar's height */
- OpenPort(&myPort); /* create the GrafPort (also calls SetPort() ) */
- MBarHeight = 0; /* turn off things like SuperClock */
- oldGrayRgn = GrayRgn; /* look up the GrayRgn */
- GrayRgn = nullRgn; /* set it to an empty region */
- PaintRect(&(myPort.portRect)); /* black out the whole screen */
-
- /* the next line causes the Window Manager to tell all open windows not to draw on the screen,
- because of the now-empty GrayRgn */
-
- CalcVisBehind((WindowPeek) FrontWindow(), oldGrayRgn);
-
- GetMouse(&lastMouse); /* record mouse's location */
- LocalToGlobal(&lastMouse); /* remember to use global coordinates! */
- SetPort(oldPort); /* restore the original GrafPort */
- HideCursor(); /* we don't want the mouse showing, either */
- blanked = cleanUp = true; /* set up to tidy up the screen when the user does something */
- }
- }
- else /* ----- this is where any nifty display code goes ----- */
- {
- GetPort(&oldPort); /* save the current GrafPort */
- SetPort(&myPort); /* make my port current */
-
- /* Be sure to put all drawing BETWEEN the calls to GetPort(&oldPort) and SetPort(oldPort) ! */
-
- time = ticks; /* look up our custom "Ticks" variable */
- while (time == ticks) {} /* sit here until vertical retrace time */
-
- /* Draw now, synchronized with the monitor's vertical retrace */
-
- SetPort(oldPort); /* restore the original GrafPort */
- }
-
- RestoreA4(); /* restore a4 */
- asm {
- movem.l (sp)+,a0-a3/d0-d2 /* restore the other registers */
- rts /* drop through to the original trap routine */
- }
- }
-
- void main() /* this is the main INIT code which installs the trap patch */
- {
- Handle myHandle;
- GrafPtr savePort;
-
- RememberA0(); /* save our base address where we can find it */
- SetUpA4(); /* set up a4 to be our base register (and save the old a4) */
-
- asm
- {
- movem.l a0-a3/d0-d2,-(sp) /* save registers a0-a3 and d0-d2 */
- _RecoverHandle /* get a handle to ourselves */
- move.l a0,myHandle /* put it in the Handle variable */
- }
- DetachResource(myHandle); /* let the Resource Mangler forget about us */
- HLock(myHandle); /* lock ourselves down */
-
- InitGraf(&thePort); /* set up my QuickDraw globals */
- (void) SysEnvirons(2, &theSystem); /* to see if we have Color QD */
- nullRgn = (RgnHandle) NewHandleSys(sizeof(Region));
- HNoPurge((Handle) nullRgn); /* just to make sure */
- SetEmptyRgn(nullRgn); /* allocate a null region in the system heap */
- blanked = cleanUp = false; /* start out in the non-blanked state */
- seed = TickCount(); /* seed the random number generator */
-
- /* patch SystemTask() to get periodic time */
-
- oldSystemTask = NGetTrapAddress(SYS_TASK_TRAP, ToolTrap);
- NSetTrapAddress(mySysTask, SYS_TASK_TRAP, ToolTrap);
-
- /* Clean up and go home */
-
- asm
- {
- movem.l (sp)+,a0-a3/d0-d2 /* restore these saved registers */
- }
- RestoreA4(); /* also restore a4 */
- }