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

Arq 5.10 - Online backup to Google Drive...
Arq is super-easy online backup for Mac and Windows computers. Back up to your own cloud account (Amazon Cloud Drive, Google Drive, Dropbox, OneDrive, Google Cloud Storage, any S3-compatible server... Read more
Evernote 6.13.1 - Create searchable note...
Evernote allows you to easily capture information in any environment using whatever device or platform you find most convenient, and makes this information accessible and searchable at anytime, from... Read more
Parallels Desktop 13.2.0 - Run Windows a...
Parallels allows you to run Windows and Mac applications side by side. Choose your view to make Windows invisible while still using its applications, or keep the familiar Windows background and... Read more
jAlbum Pro 15.0 - Organize your digital...
jAlbum Pro has all the features you love in jAlbum, but comes with a commercial license. You can create gorgeous custom photo galleries for the Web without writing a line of code! Beginner-friendly... Read more
iFinance 4.3.4 - Comprehensively manage...
iFinance allows you to keep track of your income and spending -- from your lunchbreak coffee to your new car -- in the most convenient and fastest way. Clearly arranged transaction lists of all your... Read more
VueScan 9.5.92 - Scanner software with a...
VueScan is a scanning program that works with most high-quality flatbed and film scanners to produce scans that have excellent color fidelity and color balance. VueScan is easy to use, and has... Read more
Scrivener 3.0 - Project management and w...
Scrivener is a project management and writing tool for writers of all kinds that stays with you from that first unformed idea all the way through to the first - or even final - draft. Outline and... Read more
SuperDuper! 3.0.1 - Advanced disk clonin...
SuperDuper! is an advanced, yet easy to use disk copying program. It can, of course, make a straight copy, or "clone" -- useful when you want to move all your data from one machine to another, or do... Read more
jAlbum 15.0 - Create custom photo galler...
With jAlbum, you can create gorgeous custom photo galleries for the Web without writing a line of code! Beginner-friendly, with pro results - Simply drag and drop photos into groups, choose a design... Read more
Scrivener 3.0 - Project management and w...
Scrivener is a project management and writing tool for writers of all kinds that stays with you from that first unformed idea all the way through to the first - or even final - draft. Outline and... Read more

Latest Forum Discussions

See All

The mobile gamer's guide to Black F...
We're starting to catch wind of some exciting deals in the mobile gaming space for Black Friday. There are big discounts on mobile phones and accessories cropping up already, so you might want to get a move on things ahead of the big day. It's... | Read more »
The best pre-Black Friday deals - Novemb...
Black Friday will soon be upon us, but online retailers are already getting a headstart on the steep discounts. Don't wait until Friday—you'll find some pretty good deals all over the internet without waiting in lines or competing with other... | Read more »
Mighty Battles guide - how to build a so...
Mighty Battles, the latest title from Hothead Games, is set to take the App Store by storm. The game puts a welcome twist on lane battlers, adding FPS elements to spice things up a bit. You'll collect cards to put your own military unit to gether,... | Read more »
Rules of Survival guide - how to be the...
The PUBG craze makes its way to mobile, with more and more battle royale games debuting on iOS and Android. Rules of Survival joins the ranks of mobile PUBG-likes, offering a classic battle royale experiences that doesn't vary too much from its... | Read more »
The best new games we played this week -...
The weekend is upon us friends, and it's time to take a look back and reflect on all of the wonderful games we've played over the past few days. This week was jam packed with new releases. There were some big, long awaited launches, some fun... | Read more »
Lineage II: Revolution guide - tips and...
At long last, Lineage II: Revolution has now come to western shores, bring Netmarble's sweeping MMORPG to mobile devices. It's an addictive, epic experience, but some of the systems in the game can be a bit overwhelming. Here are a few tips to help... | Read more »
A Boy and His Blob (Games)
A Boy and His Blob 1.0 Device: iOS Universal Category: Games Price: $4.99, Version: 1.0 (iTunes) Description: | Read more »
Fight terrible monsters and collect epic...
Released on Western markets early last month, Dragon Project, created by Japanese developer COLOPL, brings epic monster hunting action to mobile for the very first time. Collect a huge array of weapons and armor, and join up with friends to fight... | Read more »
I Am The Hero (Games)
I Am The Hero 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: I Am The Hero is a pixel art, beat 'em up, fighting game that tells the story of a "Hero" with a glorious but mysterious past.... | Read more »
Kauldron (Music)
Kauldron 1.0 Device: iOS Universal Category: Music Price: $3.99, Version: 1.0 (iTunes) Description: Kauldron is our warmest sounding, punchiest synth yet! A completely new modeling technology, combined with carefully designed... | Read more »

Price Scanner via MacPrices.net

Adorama posts Black Friday deals on Apple Mac...
Adorama has posted Black Friday sale prices on many Macs, with MacBooks and iMacs available for up to $200 off MSRP. Shipping is free, and Adorama charges sales tax in NJ and NY only: MacBook Pros... Read more
Save up to $300 on 15″ 2.2GHz MacBook Pros
B&H Photo has the 15″ 2.2GHz MacBook Pro available for $200 off MSRP including free shipping plus NY & NJ sales tax only: – 15″ 2.2GHz MacBook Pro (MJLQ2LL/A): $1799 $200 off MSRP Amazon.com... Read more
Save up to $180 with Apple Certified Refurbis...
Apple has Certified Refurbished 2017 13″ MacBook Airs available starting at $849. An Apple one-year warranty is included with each MacBook, and shipping is free: – 13″ 1.8GHz/8GB/128GB MacBook Air (... Read more
Black Friday deals on Apple Macs now live at...
Amazon has MacBook Pros, MacBook Airs, MacBooks, and iMacs on sale for up to $200 off MSRP for Black Friday week. Shipping is free. Note that stock of some Macs may come and go during the week, so... Read more
Black Friday pricing on Macs and iPads now av...
B&H Photo has lowered prices on many Macs, iPads, and iPad Pros as part of their Black Friday week sale. Save up to $200 on MacBooks and iMacs and up to $150 on iPads. B&H charges sales tax... Read more
Best Apple iPad deals this weekend, up to $80...
Apple resellers are offering 9.7″ iPads and 10.5″ iPad Pros for up to $80 off MSRP this weekend as part of their early Holiday and Black Friday sales: Adorama is offering new 2017 9.7″ 32GB WiFi... Read more
Early Black Friday sale: Apple iMacs for up t...
B&H Photo has 27-inch iMacs in stock and on sale for up $130-$150 off MSRP including free shipping. B&H charges sales tax in NY & NJ only: – 27″ 3.8GHz iMac (MNED2LL/A): $2149 $150 off... Read more
Apple restocks refurbished Mac minis starting...
Apple has restocked Certified Refurbished Mac minis starting at $419. Apple’s one-year warranty is included with each mini, and shipping is free: – 1.4GHz Mac mini: $419 $80 off MSRP – 2.6GHz Mac... Read more
Save on 12″ MacBooks, Apple refurbished model...
Apple has Certified Refurbished 2017 12″ Retina MacBooks available for $200-$240 off the cost of new models. Apple will include a standard one-year warranty with each MacBook, and shipping is free.... Read more
Early Holiday sale: 12″ iPad Pros for up to $...
B&H Photo has 12″ iPad Pros on sale today for up to $130 off MSRP. Shipping is free, and B&H collects no sales tax outside NY & NJ: – 12″ 64GB WiFi iPad Pro: $749, save $50 – 12″ 256GB... Read more

Jobs Board

*Apple* Retail - Multiple Positions - Apple,...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
Product Manager - *Apple* Pay on the *Appl...
Job Summary Apple is looking for a talented product manager to drive the expansion of Apple Pay on the Apple Online Store. This position includes a unique Read more
*Apple* Pro/Consumer Apps Support Engineer -...
…exemplify AppleCare's expert technical support paired with exceptional customer service for Apple 's software apps. This person is a problem solver, who understands Read more
Partner Marketing Manager, *Apple* Pay - Ap...
Job Summary The Apple Pay partner marketing team is looking for a Marketing Manager to develop and drive US programs. The right candidate will be passionate about Read more
*Apple* Solution Consultant - Apple (United...
# Apple Solution Consultant - Rochester, MN Job Number: 113037950 Rochester, MN, Minnesota, United States Posted: 19-Sep-2017 Weekly Hours: 40.00 **Job Summary** Are Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.