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

Latest Forum Discussions

See All

Challenge those pesky wyverns to a dance...
After recently having you do battle against your foes by wildly flailing Hello Kitty and friends at them, GungHo Online has whipped out another surprising collaboration for Puzzle & Dragons. It is now time to beat your opponents by cha-cha... | Read more »
Pack a magnifying glass and practice you...
Somehow it has already been a year since Torchlight: Infinite launched, and XD Games is celebrating by blending in what sounds like a truly fantastic new update. Fans of Cthulhu rejoice, as Whispering Mist brings some horror elements, and tests... | Read more »
Summon your guild and prepare for war in...
Netmarble is making some pretty big moves with their latest update for Seven Knights Idle Adventure, with a bunch of interesting additions. Two new heroes enter the battle, there are events and bosses abound, and perhaps most interesting, a huge... | Read more »
Make the passage of time your plaything...
While some of us are still waiting for a chance to get our hands on Ash Prime - yes, don’t remind me I could currently buy him this month I’m barely hanging on - Digital Extremes has announced its next anticipated Prime Form for Warframe. Starting... | Read more »
If you can find it and fit through the d...
The holy trinity of amazing company names have come together, to release their equally amazing and adorable mobile game, Hamster Inn. Published by HyperBeard Games, and co-developed by Mum Not Proud and Little Sasquatch Studios, it's time to... | Read more »
Amikin Survival opens for pre-orders on...
Join me on the wonderful trip down the inspiration rabbit hole; much as Palworld seemingly “borrowed” many aspects from the hit Pokemon franchise, it is time for the heavily armed animal survival to also spawn some illegitimate children as Helio... | Read more »
PUBG Mobile teams up with global phenome...
Since launching in 2019, SpyxFamily has exploded to damn near catastrophic popularity, so it was only a matter of time before a mobile game snapped up a collaboration. Enter PUBG Mobile. Until May 12th, players will be able to collect a host of... | Read more »
Embark into the frozen tundra of certain...
Chucklefish, developers of hit action-adventure sandbox game Starbound and owner of one of the cutest logos in gaming, has released their roguelike deck-builder Wildfrost. Created alongside developers Gaziter and Deadpan Games, Wildfrost will... | Read more »
MoreFun Studios has announced Season 4,...
Tension has escalated in the ever-volatile world of Arena Breakout, as your old pal Randall Fisher and bosses Fred and Perrero continue to lob insults and explosives at each other, bringing us to a new phase of warfare. Season 4, Into The Fog of... | Read more »
Top Mobile Game Discounts
Every day, we pick out a curated list of the best mobile discounts on the App Store and post them here. This list won't be comprehensive, but it every game on it is recommended. Feel free to check out the coverage we did on them in the links below... | Read more »

Price Scanner via MacPrices.net

Free iPhone 15 plus Unlimited service for $60...
Boost Infinite, part of MVNO Boost Mobile using AT&T and T-Mobile’s networks, is offering a free 128GB iPhone 15 for $60 per month including their Unlimited service plan (30GB of premium data).... Read more
$300 off any new iPhone with service at Red P...
Red Pocket Mobile has new Apple iPhones on sale for $300 off MSRP when you switch and open up a new line of service. Red Pocket Mobile is a nationwide MVNO using all the major wireless carrier... Read more
Clearance 13-inch M1 MacBook Airs available a...
Apple has clearance 13″ M1 MacBook Airs, Certified Refurbished, available for $759 for 8-Core CPU/7-Core GPU/256GB models and $929 for 8-Core CPU/8-Core GPU/512GB models. Apple’s one-year warranty is... Read more
Updated Apple MacBook Price Trackers
Our Apple award-winning MacBook Price Trackers are continually updated with the latest information on prices, bundles, and availability for 16″ and 14″ MacBook Pros along with 13″ and 15″ MacBook... Read more
Every model of Apple’s 13-inch M3 MacBook Air...
Best Buy has Apple 13″ MacBook Airs with M3 CPUs in stock and on sale today for $100 off MSRP. Prices start at $999. Their prices are the lowest currently available for new 13″ M3 MacBook Airs among... Read more
Sunday Sale: Apple iPad Magic Keyboards for 1...
Walmart has Apple Magic Keyboards for 12.9″ iPad Pros, in Black, on sale for $150 off MSRP on their online store. Sale price for online orders only, in-store price may vary. Order online and choose... Read more
Apple Watch Ultra 2 now available at Apple fo...
Apple has, for the first time, begun offering Certified Refurbished Apple Watch Ultra 2 models in their online store for $679, or $120 off MSRP. Each Watch includes Apple’s standard one-year warranty... Read more
AT&T has the iPhone 14 on sale for only $...
AT&T has the 128GB Apple iPhone 14 available for only $5.99 per month for new and existing customers when you activate unlimited service and use AT&T’s 36 month installment plan. The fine... Read more
Amazon is offering a $100 discount on every M...
Amazon is offering a $100 instant discount on each configuration of Apple’s new 13″ M3 MacBook Air, in Midnight, this weekend. These are the lowest prices currently available for new 13″ M3 MacBook... Read more
You can save $300-$480 on a 14-inch M3 Pro/Ma...
Apple has 14″ M3 Pro and M3 Max MacBook Pros in stock today and available, Certified Refurbished, starting at $1699 and ranging up to $480 off MSRP. Each model features a new outer case, shipping is... Read more

Jobs Board

Omnichannel Associate - *Apple* Blossom Mal...
Omnichannel Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Operations Associate - *Apple* Blossom Mall...
Operations Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Cashier - *Apple* Blossom Mall - JCPenney (...
Cashier - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Blossom Mall Read more
IT Systems Engineer ( *Apple* Platforms) - S...
IT Systems Engineer ( Apple Platforms) at SpaceX Hawthorne, CA SpaceX was founded under the belief that a future where humanity is out exploring the stars is Read more
*Apple* Systems Administrator - JAMF - Activ...
…**Public Trust/Other Required:** None **Job Family:** Systems Administration **Skills:** Apple Platforms,Computer Servers,Jamf Pro **Experience:** 3 + years of Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.