TweetFollow Us on Twitter

Windows Holes
Volume Number:3
Issue Number:10
Column Tag:Pascal Procedures

Blasting Holes in Windows

By Greg Marriott, MacHax™ Group, Bryan, Texas

How To Build A Laser Cannon: A Home S.D.I. Project

Bit O’ History

I wish I came up with the idea to blast holes in windows, but I didn’t. The credit goes to Andrew Donoho and Darin Adler. Andrew is the author of MacSpin, a 3-d data analysis tool. MacSpin allows a user to rotate a “cloud” of data points around any of three axes in real time. Because of its highly interactive nature, Andrew likes to refer to it as a thinking man’s video game. He and Darin got together and decided that MacSpin was missing a feature essential to successful video games: laser cannons. They envisioned a user holding down command-shift-option-capsLock and being rewarded by laser turrents appearing in the corners of the data window. Add in some crosshairs with range and elevation readouts, and the user could blast away! They figured laser cannons would be especially useful in MacSpin, where unwanted data points could be annihilated, thus saving a pet theory from bothersome reality.

Andrew continued to joke about actually implementing laser cannons. That’s how my partner, Scott Boyd, heard about it. Scott was working closely with Andrew on enhancements for MacSpin when the subject of laser cannons came up. Scott knows a good idea when he hears one (most of the time), so he immediately urged Andrew to stop joking and do it. More pressing concerns kept them both busy for a while, but the idea wouldn’t die.

Laser Cannons Are Born

I learned about laser cannons in a phone conversation with Scott. I know a good idea when I hear one, too (I hope...). They were busy. I wasn’t. By the time Scott came home from Austin a couple of days later, I had the first incarnation of Blast finished. It was an application that brought up a window, and made holes wherever the cursor was when the mouse button was pressed. The holes were plain and round, and it ran silently. The holes weren’t even centered under the cursor. But gridlines drawn in the window showed that the holes were “not there.” That is, holes had been cut out of the structure of the window. Wow laser cannons.

Fig. 1 Blasting holes in Full Paint's windows! (note how the cursor changes over a hole to the arrow, proof that it is not in the window!)

“Useless Interface” Concerns

Scott and I talked about what the holes should look like. After some discussion, he drew some sample holes in MacDraw. After a while, he had something we both liked. I took the coordinates of the vertices around the hole, and put in some code to make a region (instead of just using circles). While I was at it, I started centering the hole under the cursor. So far, so good.

Sound came next. Poking holes in things isn’t very much fun if you don’t get some sort of aural feedback. We discussed all sorts of options, from simple beeps to elaborate crashing noises. I chose the easy route and used the Sound Driver to make some square-wave sound.

Making sound on the Mac is pretty easy if you’re willing to settle for single voice tones. Each tone is described by a trio of values known as a “triplet.” These numbers describe the pitch, volume, and duration of the tones you want to play. I decided to use twenty triplets. The last triplet has zero for all three values, to tell the Sound Driver to stop making noise. For the other nineteen, I chose a starting value, then decreased the pitch and volume of each succeeding tone. This gives the impression of something moving into the distance, like a train’s whistle as it heads away from you.

Now We’re Getting Somewhere

Blast didn’t change much after I added sound. The biggest change was in “packaging.” I wanted Blast to be available from anywhere, so I decided to make it into an FKEY. But as an FKEY, Blast had to stop counting on having access to global variables and resources. QuickDraw globals such as screenBits and thePort may not be available while an FKEY is running. And, counting on certain resources being installed along with the FKEY is a sure way to see everybody’s favorite bomb box. So, Blast became a procedure in a UNIT. The procedure has everything in it Blast needs to do its dirty work. [The version presented here is compatible with both MPW Pascal and LS Pascal. The unit can be either installed as an FKEY or you can call the unit from a program. In the code listing, a USES statement is presented for both MPW and LS Pascal. Since LS Pascal requires code resources to have a MAIN entry point, a procedure with the name MAIN was added to the unit, which calls BlastIt. This MAIN can be removed for MPW. -Ed]

Gory Details

The first thing I do in Blast is allocate a non-relocatable block for the sound buffer, and fill it with triplets. Then I go about putting up the crosshair cursor. I want to be polite and give the user their original cursor back when Blast is done, so I have to know what it is. But there isn’t a call to get the current cursor. Apple seems to feel that the cursor should be “write-only.” I have to dip into low memory and suck the cursor out the hard way. I suppose this is “against the rules,” but I refuse to sit back and hope the application running is smart enough to update its cursor when Blast is through. I set up the crosshair cursor and do a SetCursor call. Remember what I said about no resources? The crosshair cursor is hard-coded into Blast and the way I set it up is by using StuffHex to load up a cursor variable. It looks kind of gnarly, but it helps make Blast self-contained.

Once the cursor is up (so the user gets some immediate feedback), I pre-fabricate the hole. To do this I open a region, and draw the outline of the hole. OpenRegion hides the QuickDraw pen, so the user doesn’t get to see any drawing happen. Then I close the region and move it to the origin, ( 0, 0 ). This makes the size calculations simple, since the top and left coordinates are both zero. I want to know how big the hole is in order to “add in” a round part (sort of like a bullet hole). I make another region shaped like a circle, and use UnionRgn to add it to the jagged part. After the composite hole is prepared, Blast is almost ready to go. First, though, I set the current port to be the window manager port (the whole screen). This is so the coordinates of the holes line up with the windows’ regions out on the desktop.

The Heart Of The Matter

The main loop is pretty straightforward. Wait for a mouse click. If the click is in the content region of any window, go to work. Initiate some asynchrounous sound by calling StartSound. Move the hole to center it under the mouse. Subtract the hole (via DiffRgn) from the content region and structure region of the window. Presto! Laser cannons. After blasting a window, all the windows underneath the blasted one need to be notified that they have newly visible areas. I use the low-level Window Manager call CalcVisBehind to do this. It recalculates the visible regions of all windows under the given window that intersect a certain region (the hole region). PaintBehind is called to paint newly visible areas, including the desktop. The new system paints windows with their background patterns, instead of always painting white like the old system.

If the user clicks anywhere but a content region, Blast exits. It throws away the hole region and the sound buffer, then restores the cursor and the old port. However, the windows retain their missing holes so you can drag a window around and see the windows behind through the holes. You can even scroll the window and watch the contents move around the hole! Only when you cause the window manager to reconstruct the window such as growing or zooming the window, do the holes go away. See figure 1.

FKEY, What’s An FKEY?

Now for a few words about FKEYs. The Mac deals with FKEYs behind the scenes by intercepting key-down events when GetNextEvent is called. If an FKEY resource with the ID number of the key that was hit is found, the system loads it in and executes it. When the FKEY is done, it passes control back to the system, which then flushes the event and pretends it never happened.

Blast can be called up from within any program that calls GetNextEvent. As a matter of fact, Blast can be called recursively from within itself, since it calls GetNextEvent. This presents no real problem, since Blast carries its own local variables around. I guess the only limit is how much stack space you have, since each activation uses up several bytes of the stack.

The Recipe for MPW

Making “non-applications” is relatively simple in MPW, as long as you pay attention to a few technical details. I made Blast part of a Pascal UNIT so the compiler wouldn’t try to create a main entry point. If it was a PROGRAM, the compiler would stick in a whole bunch of global variable initialization code and try to identify an entry point. FKEYs don’t have global variables (traditionally accessed relative to register A5), so any extra code provided by the compiler is decidedly unwelcome! [Note that for LS Pascal, a main entry point is required! But the DA PasLib file is used instead of the regular PasLib so that none of the initialization for a program is done. By clicking on the code resource option, everything gets built correctly for you. See figures 2,3 and 4. -Ed]

Once the compiler is through chewing on it, the resulting object file (the .p.o file) is passed through the linker. The linker puts in any glue code needed by functions called by Blast. Glue code is most commonly used to interface between Pascal, with its stack-based calling convention, and some parts of the Mac ROM that are register-based. The glue takes parameters off the stack and puts them in registers before calling the ROM routines. Then it takes any result from a register and puts it on the stack before returning to the main program. Another kind of glue is used for range checking by the compiler. The compiler calls glue routines to check the length of strings and such when range checking is turned on.

After the glue is taken care of, the linker expects to be told where the entry point is. The -m option on the linker let me point to the proper procedure in the Blast UNIT. The linker makes sure that the very first instruction in the resulting program jumps to the entry point. An FKEY resource is just like a CODE resource without any jump table information. With the -rt option I told the linker to create FKEY resources instead of CODE resources (the default). I also told the linker to rename the main code segment from Main to BlastKey with the -sn option. This provides a reminder when viewing the resource in ResEdit. I use ResEdit to install the FKEY in the system file. FKEY Installer and FKEY Manager should work just as well.

Adding Blast to Hypercard

Everyone I’ve talked to about WildCard (now known as HyperCard, but I think that name is really dumb) is impressed by it. One of the neatest features is the ability to compile your own commands and add them to WildCard. I recently got ahold of some sketchy documentation about these XCMD resources and decided to make Blast into a WildCard command. The only difference between an FKEY and a XCMD is that XCMDs take a single argument on the stack. WildCard passes a pointer to a block full of information to the XCMD upon activation. Inside that block of information are all sorts of goodies, like handles to command-line arguments, a place for a result code, and several fields to allow the XCMD to communicate directly with WildCard. I wasn’t really interested in all that parameter stuff (yet!), so I ignored it for the moment. All I had to do to make Blast WildCard compatible was to add a formal parameter to the procedure in the UNIT. I just changed its declaration from PROCEDURE BlastIt; to PROCEDURE BlastIt( aParam : Ptr );. Then I changed the link statement to produce XCMD resources instead of FKEY resources. Voilá, a brand new WildCard command. By the way, the name of the resource is important here, because that’s how WildCard knows which XCMD is for which command.

Up On The Soap Box

I don’t know who made up some of the “rules,” but they’re not going to be too happy with Blast. I managed to break quite a few of them along the way. The first infraction is sneaking into low memory to get the current cursor. Using any low memory globals is frowned upon. Look at all the trouble Think got into when they used BasicGlobs. For those that don’t know, BasicGlobs was to be used by MacBasic, which never made it to market. Think’s Lightspeed C generated code that used it for a scratch area. Apple started to use it in System 4.1, blowing away a whole bunch of programs. Think has corrected the problem, but they blame Apple for changing their minds about using BasicGlobs. The way I see it, anybody who uses low memory areas without express written approval from Apple deserves what they get. Apple has stated many times that low memory is off limits. In fact, the area we know as low memory is going to disappear in future architectures. But since I’m only reading (and not writing) low memory, I’ll just get trash if they ever move the cursor storage area.

The next infraction is a biggy. Blast directly modifies the fields of a windowRecord, namely the strucRgn and contRgn. Apple is really touchy about windowRecords, especially since the invention of Juggler (now known as MultiFinder, but I think that name is really dumb, too). Juggler does all sorts of neat things with windows and window lists to manage multiple applications. Blast isn’t directly affected by this, but Juggler proves that Apple is serious about changing window handling without notice. When Apple gives us windowing hardware, programs like Blast will be next to impossible (but I’m gonna try it anyway! ).

The next one is more a matter of style, but could cause problems. I just assume that Blast will have plenty of memory to make the sound buffer and the hole regions. I wouldn’t recommend using Blast when you’ve got a bunch of unsaved data and can’t afford to crash with an out of memory error.

Credits, etc...

Thanks to Andrew and Darin for having the idea, and to Scott for planting the seed in my head. Speaking of seeds, TECHNOSTUD (a.k.a. Steve Knouse) helped out by showing Blast to anyone he could drag over to a Macintosh for two minutes. Steve is sort of like a reverse Johnny Appleseed. He goes around sowing MacHax™ programs on Apples everywhere. Thanks for all your support, Steve. And thanks to Dave Smith, who put me on a panel at the Boston MacWorld Expo. That’s where Blast made its public debut. To all the attendees of that panel, here is source code, only two months late.

I can be reached at any of the following:

3420D Sandra St.

Bryan, TX 77801

(409) 846-4102

AppleLink: D0635

MCIMail: Greg Marriott

BITNet: max@tamlsr

Fig. 2 LS Pascal Project Definition

UNIT BlastStuff;

{MPW and LS Pascal Version}

INTERFACE

{ For MPW, use the following USES statement}
 USES  {$LOAD fkeyblast.dump}
      MemTypes, QuickDraw, OSIntf, ToolIntf;

{ For LS Pascal, use the following USES statement}
 USES
 ROM85;

 PROCEDURE BlastIt;
 PROCEDURE Main; {not needed for MPW}

IMPLEMENTATION

PROCEDURE BlastIt;
 CONST
 buffSize = 122; 
 { sound takes 2, 20 triplets = 120 bytes }
 VAR
 holeRgn, circleRgn : RgnHandle;
 theEvent : EventRecord;
 stopIt : Boolean;
 whichWindow : WindowPtr;
 thisWindow : WindowPeek;
 i, dur, cnt, minSize:Integer; 
 pictWidth, thePart : Integer;
 blastPict : Pichandle;
 anotherRect, circleRect, tempRect : Rect;
 mouseSpot, holeSize, middlePoint : Point;
 wPort, oldPort, tempPort : GrafPtr;
 DeskPattern : ^Pattern;
 crossHairs, oldCursor : Cursor;
 TheCurs : ^Cursor;
 squareWavePtr : SWSynthPtr;
 amp : integer;

 BEGIN

    { let’s set up sounds... }
 squareWavePtr := SWSynthPtr(NewPtr(buffSize));
 squareWavePtr^.mode := swMode; { squarewave mode }
 cnt := 400;
 amp := 150;

    { 19 triplets, with decreasing pitch and volume }
 FOR i := 0 TO 18 DO
 WITH squareWavePtr^.triplets[i] DO
 BEGIN
 count := cnt;
 amplitude := amp;
 duration := 1;
 cnt := cnt + 60;
 amp := amp - 8;
 END;

 WITH squareWavePtr^.triplets[19] DO
 BEGIN
 count := 0;     { quit }
 amplitude := 0; { making }
 duration := 0;  { sounds }
 END;

{ let’s put up our cursor, saving the old one first...}
{ there is no ‘GetCursor’ call to complement }
{ ‘SetCursor’,}
{  so we have to dip into low memory and steal it }
 TheCurs := pointer($844);
 oldCursor := TheCurs^;

{ we need to put data into our cursor variable so we}
{ can do a ‘SetCursor’ call.  stuffing the data }
{ directly into low memory doesn’t work, because the }
{ cursor on the screen wouldn’t change until mouse }
{ was moved }
 StuffHex(ptr(@crossHairs), ’71C7408140810000');
 StuffHex(ptr(longint(@crossHairs) + 8), ‘049002A041C17777’);
 StuffHex(ptr(longint(@crossHairs) + 16), ’41C102A004900000');
 StuffHex(ptr(longint(@crossHairs) + 24), ‘4081408171C70000’);
 StuffHex(ptr(longint(@crossHairs) + 32), ’01C0208210840808');
 StuffHex(ptr(longint(@crossHairs) + 40), ’07F007F047F177F7');
 StuffHex(ptr(longint(@crossHairs) + 48), ’47F107F007F00808');
 StuffHex(ptr(longint(@crossHairs) + 56), ‘1084208201C00000’);
 StuffHex(ptr(longint(@crossHairs) + 64), ‘00070008’);
 SetCursor(crossHairs);

    { make the jaggy hole }
 holeRgn := NewRgn;
 OpenRgn;
 MoveTo(250, 180);
 LineTo(260, 226);
 LineTo(230, 252);
 LineTo(260, 242);
 LineTo(232, 278);
 LineTo(270, 242);
 LineTo(286, 304); 
 { basic hole design by Scott Boyd }
 LineTo(278, 234);
 LineTo(306, 242);
 LineTo(278, 244);
 LineTo(314, 196);
 LineTo(270, 216);
 LineTo(250, 180);
 CloseRgn(holeRgn);

{ “home” the region, prepare to add circle to }
{ the middle of the hole }
 WITH holeRgn^^.rgnBBox DO
 OffsetRgn(holeRgn, -left, -top);
 { calculate size ofhole and where the middle is }
 WITH holeRgn^^.rgnBBox DO
 BEGIN
 holeSize.h := right - left;
 holeSize.v := bottom - top;
 middlePoint.h := (right - left) DIV 2;
 middlePoint.v := (bottom - top) DIV 2;
 END;
    { figure out what size to make the circle }
 IF holeSize.h > holeSize.v THEN
 minSize := holeSize.h
 ELSE
 minSize := holeSize.v;

{ we’ll make the circle 40% of the small dimension;}
{ minSize will be the radius of the circle }
 minSize := minSize DIV 5;
 WITH middlePoint DO
 SetRect(circleRect, h - minSize, v - minSize, h + minSize, v + minSize);

    { make the circle }
 circleRgn := NewRgn;
 OpenRgn;
 FrameOval(circleRect);
 CloseRgn(circleRgn);
    { add the circle to the hole }
 UnionRgn(holeRgn, circleRgn, holeRgn);
    { we don’t need this any more... }
 DisposeRgn(circleRgn);
    { make the window manager port the current port }
 GetPort(oldPort);
 GetWMgrPort(wPort);
 SetPort(wPort);

{ ok, setup’s all finished... here’s the “main loop” }
stopIt := FALSE;
REPEAT
{ accept only mouseDown events }
IF GetNextEvent(mDownMask, theEvent) THEN
 BEGIN
 { figure out where they clicked }
 thePart := FindWindow(theEvent.where, whichWindow);
 { respond only if click on content area of window }
 IF (thePart = inContent) THEN
 BEGIN
 { Make some asynchrounous noise }
 StartSound(Ptr(squareWavePtr), buffSize, NIL);
 mouseSpot := theEvent.where;
 { position hole centered over the mouse spot }
 WITH holeRgn^^.rgnBBox DO
 OffsetRgn(holeRgn, -left, -top);
 OffsetRgn(holeRgn, mouseSpot.h - holeSize.h DIV 2, mouseSpot.v - holeSize.v 
DIV 2);
 ThisWindow := WindowPeek(whichWindow);
 { blast a hole in the structure region }
 DiffRgn(ThisWindow^.contRgn, holeRgn, ThisWindow^.contRgn);
 { blast a hole in the content region }
 DiffRgn(ThisWindow^.strucRgn, holeRgn, ThisWindow^.strucRgn);

 { calculate and paint new visible regions  }
 CalcVisBehind(WindowPeek(whichWindow), holeRgn);
 PaintBehind(windowPeek(whichWindow), holeRgn);

 {since ‘PaintBehind’ messes with the port.. }
 SetPort(wPort);
 END {IF ( thePart = inContent )... }
 ELSE
 { didn’t click in a window? }
 stopIt := TRUE;
 END; {IF GetNextEvent...}
UNTIL stopIt;
  { clean up the hole }
 DisposeRgn(holeRgn);
  {  fix the port  }
 SetPort(oldPort);
  { put the old cursor back }
 SetCursor(oldCursor);
  { get rid of the sound buffer }
 DisposPtr(Ptr(squareWavePtr));
END; {BlastIt}

 PROCEDURE Main; {Not needed for MPW}
 BEGIN
 BlastIt;
 END;

END.

Fig. 3 Creating a Code Resource in LS Pascal

Listing 2: MPW Make file for Blast

Fig. 4 Installing an FKEY in the System File

UNIT BlastStuff;

{MPW and LS Pascal Version}

INTERFACE

{ For MPW, use the following USES statement}
 USES  {$LOAD fkeyblast.dump}
      MemTypes, QuickDraw, OSIntf, ToolIntf;

{ For LS Pascal, use the following USES statement}
 USES
 ROM85;

 PROCEDURE BlastIt;
 PROCEDURE Main; {not needed for MPW}

IMPLEMENTATION

PROCEDURE BlastIt;
 CONST
 buffSize = 122; 
 { sound takes 2, 20 triplets = 120 bytes }
 VAR
 holeRgn, circleRgn : RgnHandle;
 theEvent : EventRecord;
 stopIt : Boolean;
 whichWindow : WindowPtr;
 thisWindow : WindowPeek;
 i, dur, cnt, minSize:Integer; 
 pictWidth, thePart : Integer;
 blastPict : Pichandle;
 anotherRect, circleRect, tempRect : Rect;
 mouseSpot, holeSize, middlePoint : Point;
 wPort, oldPort, tempPort : GrafPtr;
 DeskPattern : ^Pattern;
 crossHairs, oldCursor : Cursor;
 TheCurs : ^Cursor;
 squareWavePtr : SWSynthPtr;
 amp : integer;

 BEGIN

    { let’s set up sounds... }
 squareWavePtr := SWSynthPtr(NewPtr(buffSize));
 squareWavePtr^.mode := swMode; { squarewave mode }
 cnt := 400;
 amp := 150;

    { 19 triplets, with decreasing pitch and volume }
 FOR i := 0 TO 18 DO
 WITH squareWavePtr^.triplets[i] DO
 BEGIN
 count := cnt;
 amplitude := amp;
 duration := 1;
 cnt := cnt + 60;
 amp := amp - 8;
 END;

 WITH squareWavePtr^.triplets[19] DO
 BEGIN
 count := 0;     { quit }
 amplitude := 0; { making }
 duration := 0;  { sounds }
 END;

{ let’s put up our cursor, saving the old one first...}
{ there is no ‘GetCursor’ call to complement }
{ ‘SetCursor’,}
{  so we have to dip into low memory and steal it }
 TheCurs := pointer($844);
 oldCursor := TheCurs^;

{ we need to put data into our cursor variable so we}
{ can do a ‘SetCursor’ call.  stuffing the data }
{ directly into low memory doesn’t work, because the }
{ cursor on the screen wouldn’t change until mouse }
{ was moved }
 StuffHex(ptr(@crossHairs), ’71C7408140810000');
 StuffHex(ptr(longint(@crossHairs) + 8), ‘049002A041C17777’);
 StuffHex(ptr(longint(@crossHairs) + 16), ’41C102A004900000');
 StuffHex(ptr(longint(@crossHairs) + 24), ‘4081408171C70000’);
 StuffHex(ptr(longint(@crossHairs) + 32), ’01C0208210840808');
 StuffHex(ptr(longint(@crossHairs) + 40), ’07F007F047F177F7');
 StuffHex(ptr(longint(@crossHairs) + 48), ’47F107F007F00808');
 StuffHex(ptr(longint(@crossHairs) + 56), ‘1084208201C00000’);
 StuffHex(ptr(longint(@crossHairs) + 64), ‘00070008’);
 SetCursor(crossHairs);

    { make the jaggy hole }
 holeRgn := NewRgn;
 OpenRgn;
 MoveTo(250, 180);
 LineTo(260, 226);
 LineTo(230, 252);
 LineTo(260, 242);
 LineTo(232, 278);
 LineTo(270, 242);
 LineTo(286, 304); 
 { basic hole design by Scott Boyd }
 LineTo(278, 234);
 LineTo(306, 242);
 LineTo(278, 244);
 LineTo(314, 196);
 LineTo(270, 216);
 LineTo(250, 180);
 CloseRgn(holeRgn);

{ “home” the region, prepare to add circle to }
{ the middle of the hole }
 WITH holeRgn^^.rgnBBox DO
 OffsetRgn(holeRgn, -left, -top);
 { calculate size ofhole and where the middle is }
 WITH holeRgn^^.rgnBBox DO
 BEGIN
 holeSize.h := right - left;
 holeSize.v := bottom - top;
 middlePoint.h := (right - left) DIV 2;
 middlePoint.v := (bottom - top) DIV 2;
 END;
    { figure out what size to make the circle }
 IF holeSize.h > holeSize.v THEN
 minSize := holeSize.h
 ELSE
 minSize := holeSize.v;

{ we’ll make the circle 40% of the small dimension;}
{ minSize will be the radius of the circle }
 minSize := minSize DIV 5;
 WITH middlePoint DO
 SetRect(circleRect, h - minSize, v - minSize, h + minSize, v + minSize);

    { make the circle }
 circleRgn := NewRgn;
 OpenRgn;
 FrameOval(circleRect);
 CloseRgn(circleRgn);
    { add the circle to the hole }
 UnionRgn(holeRgn, circleRgn, holeRgn);
    { we don’t need this any more... }
 DisposeRgn(circleRgn);
    { make the window manager port the current port }
 GetPort(oldPort);
 GetWMgrPort(wPort);
 SetPort(wPort);

{ ok, setup’s all finished... here’s the “main loop” }
stopIt := FALSE;
REPEAT
{ accept only mouseDown events }
IF GetNextEvent(mDownMask, theEvent) THEN
 BEGIN
 { figure out where they clicked }
 thePart := FindWindow(theEvent.where, whichWindow);
 { respond only if click on content area of window }
 IF (thePart = inContent) THEN
 BEGIN
 { Make some asynchrounous noise }
 StartSound(Ptr(squareWavePtr), buffSize, NIL);
 mouseSpot := theEvent.where;
 { position hole centered over the mouse spot }
 WITH holeRgn^^.rgnBBox DO
 OffsetRgn(holeRgn, -left, -top);
 OffsetRgn(holeRgn, mouseSpot.h - holeSize.h DIV 2, mouseSpot.v - holeSize.v 
DIV 2);
 ThisWindow := WindowPeek(whichWindow);
 { blast a hole in the structure region }
 DiffRgn(ThisWindow^.contRgn, holeRgn, ThisWindow^.contRgn);
 { blast a hole in the content region }
 DiffRgn(ThisWindow^.strucRgn, holeRgn, ThisWindow^.strucRgn);

 { calculate and paint new visible regions  }
 CalcVisBehind(WindowPeek(whichWindow), holeRgn);
 PaintBehind(windowPeek(whichWindow), holeRgn);

 {since ‘PaintBehind’ messes with the port.. }
 SetPort(wPort);
 END {IF ( thePart = inContent )... }
 ELSE
 { didn’t click in a window? }
 stopIt := TRUE;
 END; {IF GetNextEvent...}
UNTIL stopIt;
  { clean up the hole }
 DisposeRgn(holeRgn);
  {  fix the port  }
 SetPort(oldPort);
  { put the old cursor back }
 SetCursor(oldCursor);
  { get rid of the sound buffer }
 DisposPtr(Ptr(squareWavePtr));
END; {BlastIt}

 PROCEDURE Main; {Not needed for MPW}
 BEGIN
 BlastIt;
 END;

END.
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Apple iTunes 12.1 - Manage your music, m...
Apple iTunes lets you organize and play digital music and video on your computer. It can automatically download new music, app, and book purchases across all your devices and computers. And it's a... Read more
LibreOffice 4.4.3 - Free, open-source of...
LibreOffice is an office suite (word processor, spreadsheet, presentations, drawing tool) compatible with other major office suites. The Document Foundation is coordinating development and... Read more
FoldersSynchronizer 4.2.1 - Synchronize...
FoldersSynchronizer is a popular and useful utility that synchronizes and backs-up files, folders, disks and boot disks. On each session you can apply special options like Timers, Multiple Folders,... Read more
Simon 4.0.2 - Monitor changes and crashe...
Simon monitors websites and alerts you of crashes and changes. Select pages to monitor, choose your alert options, and customize your settings. Simon does the rest. Keep a watchful eye on your... Read more
Cocktail 8.1.2 - General maintenance and...
Cocktail is a general purpose utility for OS X that lets you clean, repair and optimize your Mac. It is a powerful digital toolset that helps hundreds of thousands of Mac users around the world get... Read more
Cyberduck 4.6.4 - FTP and SFTP browser....
Cyberduck is a robust FTP/FTP-TLS/SFTP browser for the Mac whose lack of visual clutter and cleverly intuitive features make it easy to use. Support for external editors and system technologies such... Read more
Herald 5.0.2 - Notification plugin for M...
Note: Versions 2.1.3 (for OS X 10.7), 3.0.6 (for OS X 10.8), and 4.0.8 (for OS X 10.9) are no longer supported by the developer. Herald is a notification plugin for Mail.app, Apple's Mac OS X email... Read more
DEVONthink Pro 2.8.3 - Knowledge base, i...
Save 10% with our exclusive coupon code: MACUPDATE10 DEVONthink Pro is your essential assistant for today's world, where almost everything is digital. From shopping receipts to important research... Read more
Boom 2 1.0.1 - System-wide pro audio app...
Boom 2 is a system-wide volume booster and equalizer app that is designed especially for OS X 10.10 Yosemite. It comes with a smart interface, self-calibrates itself according to your Mac, offers... Read more
Apple Security Update 2015-001 - For OS...
Apple Security Update 2015-001 is recommended for all users and improves the security of OS X. For detailed information about the security content of this update, please visit: http://support.apple.... Read more

Unlock Sunshine Girl in Ironkill with th...
Unlock Sunshine Girl in Ironkill with this special 148Apps code Posted by Rob Rich on January 29th, 2015 [ permalink ] Robo-fighter Ironkill has been out on iOS a | Read more »
Crossroad Zombies Review
Crossroad Zombies Review By Jordan Minor on January 29th, 2015 Our Rating: :: CROSSWALKING DEADiPad Only App - Designed for the iPad Crossroad Zombies is a rough draft of a cool genre mash-up.   | Read more »
Blood Brothers 2 – Tips, Cheats, and Str...
War is hell: Is it the kind of hell you want to check out? Read our Blood Brothers 2 review to find out! Blood Brothers 2, DeNA’s follow-up to the original Blood Brothers, is an intriguing card collecting / role-playing / strategy hybrid. There’s... | Read more »
Blood Brothers 2 Review
Blood Brothers 2 Review By Nadia Oxford on January 29th, 2015 Our Rating: :: AN AGGRAVATING RELATIVEUniversal App - Designed for iPhone and iPad Blood Brothers 2 is built on a simple, solid foundation, but its free-to-play system... | Read more »
I AM BREAD, the Toast of the Town, is Ro...
Have you ever dreamt of being deliciously gluten-y? Do you feel passionate about Rye and Wheat? The guys at Bossa Studios do and that is why they are bringing I AM BREAD to iOS soon. The loafy app will feature all the new content that is being... | Read more »
Fahrenheit: Indigo Prophecy Remastered R...
Fahrenheit: Indigo Prophecy Remastered Review By Jennifer Allen on January 29th, 2015 Our Rating: Universal App - Designed for iPhone and iPad 10 years after its initial release and Fahrenheit: Indigo Prophecy still feels like a... | Read more »
Evolve: Hunters Quest is a Match-3 That...
Evolve: Hunters Quest is a Match-3 That Has You Hunting Up Some Big Game. Posted by Jessica Fisher on January 29th, 2015 [ permalink ] | Read more »
Destructamundo Review
Destructamundo Review By Jordan Minor on January 29th, 2015 Our Rating: :: WRECKING CREWUniversal App - Designed for iPhone and iPad Destructamundo blows up real good.   | Read more »
The Sandbox Gets Exclusive Update, Adds...
The Sandbox Gets Exclusive Update, Adds Invincible Characters Campagin Posted by Tre Lawrence on January 29th, 2015 [ permalink ] | Read more »
Mean Girls: The Game Review
Mean Girls: The Game Review By Jordan Minor on January 29th, 2015 Our Rating: :: FETCHUniversal App - Designed for iPhone and iPad A Mean Girls tower defense game makes way more sense than you’d think.   | Read more »

Price Scanner via MacPrices.net

Back in stock: Refurbished iPod nanos for $99...
The Apple Store has Apple Certified Refurbished 16GB iPod nanos available for $99 including free shipping and Apple’s standard one-year warranty. That’s $50 off the cost of new nanos. Most colors are... Read more
New Good Management Suite Simplifies Enterpri...
Good Technology has announced the availability of the Good Management Suite, a comprehensive cross-platform solution for organizations getting started with mobile business initiatives. Built on the... Read more
15-inch 2.0GHz Retina MacBook Pro (refurbishe...
The Apple Store has Apple Certified Refurbished previous-generation 15″ 2.0GHz Retina MacBook Pros available for $1489 including free shipping plus Apple’s standard one-year warranty. Their price is... Read more
15-inch 2.5GHz Retina MacBook Pro on sale for...
Amazon has the 15″ 2.5GHz Retina MacBook Pro on sale for $2319.99 including free shipping. Their price is $180 off MSRP, and it’s the lowest price available for this model from any reseller. Read more
iPad Turns Five – The ”Book Mystique
Five years ago this week, the late Steve Jobs took the stage at an Apple special press event to unveil the first Apple iPad — a slab-shaped, hand-holdable device that was to shake the personal... Read more
Sale! 15-inch Retina MacBook Pros for up to $...
B&H Photo has the new 2014 15″ Retina MacBook Pros on sale for $150 off MSRP for a limited time. Shipping is free, and B&H charges NY sales tax only: - 15″ 2.2GHz Retina MacBook Pro: $1889.99... Read more
Apple refurbished Mac minis for up to $150 of...
The Apple Store has restocked Apple Certified Refurbished 2014 Mac minis, with models available starting at $419. Apple’s one-year warranty is included with each mini, and shipping is free: - 1.4GHz... Read more
Apple Releases New Tranche Of OS Updates For...
Apple has released incremental bugfix and security updates for its most recent Mac and iOS versions. OS X Yosemite 10.10.2 Update Apple says this update includes the following improvements: Resolves... Read more
Samsung To Be Main Supplier Of A9 Chips For i...
Korea’s Maeil Business Newspaper reports that Samsung has beat out its chipmaking rivals, Taiwan’s Qualcomm and TSMC, as supplier of chipsets for the next generations of both its own Galaxy S6 and... Read more
Save up to $340 on iMacs with Apple Certified...
The Apple Store has Apple Certified Refurbished iMacs available for up to $340 off the cost of new models. Apple’s one-year warranty is standard, and shipping is free. These are the best prices on... Read more

Jobs Board

At-Home Chat Specialist- *Apple* Online Stor...
**Job Summary** At Apple , we believe in hard work, a fun environment, and the kind of creativity and innovation that only comes about when talented people from diverse Read more
SW QA Engineer - *Apple* TV - Apple (United...
**Job Summary** The Apple TV team is looking for experienced Quality Assurance Engineers with a passion for delivering first in class home entertainment solutions. **Key Read more
*Apple* Retail - Multiple Positions(US) - Ap...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you re also the Read more
*Apple* Systems Engineer - Pre Sales, Educat...
…is responsible for proactively providing technical expertise to drive sales of Apple solutions into assigned accounts. The SE architects, validates, and assists in Read more
*Apple* Solutions Consultant - Retail Sales...
As an ambassador of the Apple brand, the ASC is accountable for driving sales performance by: Connecting with customers. Discovering customers' needs and values. Showing Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.