Life
 Volume Number: 3 Issue Number: 9 Column Tag: Macintosh II

Life, Quickdraw & The Picker

By Steven Sheets, Contributing Editor, Hoffman Estates,. IL

Steve wins our Program of the Month award for his excellent treatment of the whole subject of color quickdraw as presented in this example program. Enjoy an extra \$50 on MacTutor, and thanks for sharing the technology!

Color Life

Life is one of the oldest computer “Games”. Besides being an extremely interesting mathematical puzzle, even a black and white Life program can provide hypnotising graphic animation. It is almost appropriate that this column’s first Color Quickdraw example is this classic program. Using the full color of the Macintosh //, Life is more spell binding than ever! [Yeah, now if Apple would ever ship us some color monitors maybe we’ll see what this program can really do some day! -Ed]

This article will detail the development of a Color Quickdraw program, Color Life. It will explain how to check for Color Quickdraw, how create Color Grafports/Windows, how the Color RGB Model works, how Color Drawing works, how to use the Color Picker Package and how to create Pop Up Menus. First however it will explain what the game of Life is about....

Game of Life

Life simulates the life and death of a group of cells from one generation to the next. Usually the goal is to find some stable life form (ie. one that will not die). Life was originally designed by Prof. John Conway at the University of Cambridge. It first appeared in Martin Gardner’s “Mathematical Games” column of Scientific American in October 1970. Scientific American and Byte Magazine are two of the main sources of good articles on Life.

The rules of Life are simple. The game is played on a two dimensional grid of a certain size. Each spot (or cell) on the grid can be alive or dead (empty). Also each cell is surrounded by it’s 8 neighboring cells, which can also be alive or dead. The number of living cells around the center cell becomes very important in calculating deaths and births each turn.

Every turn (usually called a generation), a living cell can die or live on to the next generation. Also a empty cell can have a birth (ie. cell becomes alive). If a live cell is surrounded by 2 or 3 cells this generation, it will survive till the next generation. If a live cell is surrounded by 1 or less cells this generation, it dies (from starvation). If the cell is surrounded by 4 or more cells this generation, it dies (from over crowding). Finally if a empty cell is surrounded by exactly 3 living neighbors, there is a birth there. Next generation a new cell exists at that spot.

From these simple rules, Life emerges.

Fig. 1 Color Quickdraw (cmd-shift-3 only works in black and white!)

Color QuickDraw

The very first thing a Macintosh // Color program has to check for is what environment the program is running in. The program is not actually looking for a Macintosh //; it requires Color Quickdraw to run. While the Macintosh // is now the only computer with Color Quickdraw, it may not be in the future (upgraded Mac //, Mac SE with Color Quickdraw expansion card or even a Mac ///).

No matter what the computer, if Color Quickdraw exists on the machine, the two high bits of the low-memory global ROM85 (word at \$028E) will be cleared (set to zero). By examining this memory location, a program can discover if Color Quickdraw exists. The check for Color Quickdraw should be done after the normal Mac Initialization (InitGraf, InitFont, InitWindow, etc.), but before any Color Quickdraw commands are called (creating Color Grafports or color Pixel Patterns). If Color Quickdraw does not exist, the program should politely inform the user that he needs a Macintosh //. A system bomb is not a polite way of informing the user!

Color Grafports & Color Windows

Once the program knows Color Quickdraw exists, it can create and work with Color Grafports. All the new Color Quickdraw commands must be done on a Color Grafport, not an old-style Grafport. All the older Quickdraw commands have been expanded so they can work with either a Grafport or a Color Grafport (also called cGrafport). The cGrafport data structure is the same size as the older Grafport structure. While many data fields are the same, some have been changed. The exact format is not important for this program since none of the fields are accessed directly. The new cGrafports are allocated in manners similar to the old Grafports (either a NewPtr call or a pointer to a holding Variable). However, instead of using the Quickdraw commands OpenPort, InitPort and ClosePort, the new Color Quickdraw commands OpenCPort, InitCPort and CloseCPort are used to open, init and close a Color Grafport.

In practise, the new Color Quickdraw Port commands are used as often as the older Quickdraw Port commands; that is almost never. The vast majority of the Macintosh programs draw on a Window (which keeps track of the Grafport itself). Generally the only time a Macintosh program directly manipulates a Grafport is when using an off screen bitmap.

The Window Manager has been expanded to include Color Windows. The changes in the Window Manager are similar to the changes in Color Quickdraw. There is a new Color Window Record of the same size as the old Window Record. All of the field are the same except the Port field now contains a cGrafPort instead of a Grafport. All existing Window Manager calls have been expanded to so that either a Window Pointer or a Color Window Pointer can be used with them. Where the NewWindow or GetNewWindow command was used to create a window, the NewCWindow and GetNewCWindow commands create a color window. The two new commands even have the same parameters and purpose (create a window from scratch or from a window resource) as the old commands. They just return a Color Window instead (complete with a correctly created Color Grafport).

RGB Color

To understand exactly how Color Drawing is handled on the newly created Color Grafport/Windows, the RGB color model must be reviewed. Color Quickdraw uses a RGB color model as an conceptional model for all drawing. A color is defined as 3 non-signed integer values (0-65535). This defines the strength of the Red, Green and Blue portions of the color. A Black color would have the values 0,0,0, while white would have 65535, 65535, 65535. Blue would be 0, 65535, 0, while Purple would be 65535, 0, 65535. There are 1,777,216 distinct colors in this model. When something is conceptionally drawn, it must be in a color defined by this model.

In reality, the number of colors that can be drawn at one time, is dependent on the pixel depth of the color device. Most graphics devices are video card, but they can be almost anything (printer, off screen bit map, etc.). A color device that can allocate 4 bits of memory for each pixel (pixel depth of 4), can use 16 colors at one time (2 to the 4th power). A color device that has pixel depth of 8, can have 256 colors at one time (2 to the 8th power). Remember that usually the user can set what pixel depth the video card is using by using the Monitor portion of the Control Panel. A 8 pixel depth card may be only using 4, 2 or even 1 pixels at a specific moment. When color Quickdraw tries to draw in a specific RGB color, it uses the Color Manager to find the closest match on the graphics device. That is the color that is actually displayed.

A good example of this would be trying to display a light, medium and dark shade of red. A video card which has been set (and as enough memory) for a pixel depth of 8 would probably have a few shades of red. Even if the shades were not the exact RGB colors requested, the user would at least know that one shade was darker and one shade lighter than regular red when the colors were displayed on the screen. Suppose then the user set the video card pixel level to 4, thus only having 16 colors at one time. There most likely would only be one red color being used by the video card at one time. Chances are that the programs 3 red RGB shades would match to the cards single red color (it would be the closest match for any of them). The user would then not be able to tell the 3 colors apart on the screen.

The Color Quickdraw commands RGBForeColor and RGBBackColor set the RGB value of the Foreground and Background color. Remember that when called, these routine use the Color Manager to determine the best RGB color match of the graphics device that is being drawn on. This is the true RGB color that will be displayed on the screen.

All text is drawn in the Foreground Color. Copybits will display bitmaps in the Foreground and Background Colors . Set pixels will appear in the Foreground Color; unset pixels will appear in the Background Color. The Foreground and Background Colors may also effect the drawing of a new Color Quickdraw data structure, the Pix Pattern.

Fig. 2 Our menus for the game of life

Color Pix Patterns

Most commonly used Quickdraw drawing commands are done with the Pen. The Move and Moveto commands use the current setting of the pen. The various Paint commands (PaintRect, PaintOval, PaintPoly, etc.) also use the pen’s settings. Old Quickdraw would draw the pen using the Pen Pattern; Color Quickdraw draws using the Pen’s Color Pattern or Pix Pattern. In old Quickdraw, similar to the Pen Pattern, there is Background Pattern. All Erase commands (EraseRect, EraseOval, ErasePoly, etc.) used the Background Pattern to draw with. With Color Quickdraw, similar to the Pen’s Pix Pattern, there is a Background Pix Pattern. What these two Pix Pattern displays depend on how the Pattern was created and thus what type of Pix Pattern it is.

There are 3 types of Pix Patterns. A Pix Pattern variable is a handled to a very elaborate data structure (including handles to other more complex data structures). For this discussion the actual values and formats of the data type is not important.

The simplest Pix Pattern is a monochrome Pix Pattern. Basically the old Quickdraw’s 8 by 8 Pattern is expanded to a Pix Pattern with no preset colors. Instead the monochrome Pix Pattern always uses the current RGB Foreground and Background color to draw in. Where bits (pixels) in the pattern are set, the pixel is drawn in the Foreground color, where they are cleared, it is drawn in the Background color. Changing the Foreground or Background color, does not change the Pix Pattern. Any future drawing with the Pix Pattern will be in the new colors.

The Foreground and Background Pix Patterns can be reset by using the old PenPat and BackPat command. On a Color Quickdraw Grafport/Window, these commands reset the Foreground and Background Pix Pattern to a newly created monochrome Pix Pattern (using the Pattern that is passed as the model). Thus by setting RGB Foreground Color to red, the RGB Background color to white and the PenPat to a brick pattern, the various Pen Drawing commands would draw with a red and white brick pattern. If RGB Foreground Color was suddenly set to Blue, the Pen Drawing commands would draw with a blue and white brick pattern. Thus the PenPat and BackPat commands are two methods to set the Pen Pix Pattern and the Background Pix Pattern.

The second type of Pix Pattern is the RGB Pix Pattern. This Pix Pattern contains an approximation of a certain RGB color. To make it, first a empty Pix Pattern must be created using the NewPixPat function. This Pix Pattern has no value at this point. Then the Pix Pattern and a specific RGB value is passed to MakeRGBPat. This reconstructs the Pix Pattern (it is a handle, remember) into a RGB Pix Pattern. If there is an RGB color used by the Graphics device that is close enough to the RGB color, that entire Pix Pattern is set to that color. If there is not an RGB color that close, Quickdraw tries to construct a Pix Pattern of that approximates that RGB pattern. It does this by mixing 2 RGB colors the graphic device allows in the Pix Pattern. This process is called dithering. In the above shades of Red example, imagine if the three red shades were used to create three Pix Patterns. On a 8 Pixel video card, the three Pix Patterns would probably display a solid red pattern, each red a different shade. When the video card was set to 4 Pixel mode, the medium shade would probably still remain a solid red pattern. However the lighter red Pix Pattern would display a pattern mixing red and white, while the darker red Pix Pattern would display a pattern mixing red and black. When running the example program, play with the Pixel depth of the video Card (using the Monitor portion of the Control Panel) to get an idea of how these Pix Patterns appear. Notice that every time the Pixel depth changes, the RGB Pix Pattern seems to be recalculated without ever calling the MakeRGBPat routine.

The last type of Pix Pattern is the Full Color Pix Pattern. This is the most powerful Pix Pattern. It can have almost any size and any color or combination of colors. This Pix Pattern can be created using the NewPixPat, and then having the data structure stuffed with the correct values. The other, easier method of creating this Pix Pattern is to read it in from a resource file using the GetPixPat function. The Pix Pattern read in can be of any type, but usually GetPixPat is used only for Full Color ones. This type of Pix Pattern (and example of the resource data structure) is not used in the example program.

Once the Pix Patterns are created, the new Color Quickdraw commands PenPixPat and BackPixPat can be used to set the Pen Pix Pattern and Background Pix Pattern. Remember Pix Patterns are handles. Calling the PenPixPat and BackPixPat command (or the PenPat and BackPat command) does not dispose of the old Pix Pattern if it was set using PenPixPat and BackPixPat. The call only resets the handle in use; the original data remains unchanged. Pix Pattern handles created by the program (using NewPixPattern or GetPixPattern) are safe and can be reused. This is not true of Pix Patterns created using PenPat or BackPat. Somehow Color Quickdraw keeps track of Pix Patterns created this way, and disposes of them when they are not needed (the program does not have to do this).

Fig. 3 The Life Game in Living(?) Color(Pictured is the seventh generation; the other generations are in color and cannot be captured)

Other Color Commands

Besides using the older Quickdraw commands, Color Quickdraw has also been expanded to include six new color operations; FillCRect, FillCOval, FillCRoundRect, FillCArc, FillCRgn, FillCPoly. They are similar to the old Quickdraw Fill commands (FillRect, FillOval, FillRoundRect, FillArc, FillRgn, FillPoly). However instead of being passed a specific Pattern to fill the graphics area, they are passed a specific color Pix Pattern (of any of the 3 defined types). The graphic area is then filled with the specific color Pattern. These commands completely by pass the existing Foreground RGB Color, Background RGB Color, Foreground Pix Pattern and Background Pix Pattern.

Color Picker Package

While the program has select a specific RGB color, a user may wish to pick a new color (ex. a paint program). Having the user pick the exact color he wants and provide a good interface to do this, is not a trivial task. Fortunately (and after a lot of good forethought by who ever developed Color Quickdraw) there exists a standardized way of letting the user to this; the Color Picker Package. The Color Picker Package, like all Packages, is not a set of Rom resident routines. The Package is stored as a system resource, to be read in when the program needs it. But like the Standard File Package (with it’s SFGetFile and SFPutFile dialogs), the Color Picker can be used easily by any program. The Picker will be the standard way of selecting RGB colors and should be quickly learned by all Macintosh users, just as SFGetFile and SFPutFile were. Also like SFGetFile and SFPutFile, if there are any upgrades to the Color Picker Package, a program using the Picker will automatically use the new version without having to be modified.

The main call in the Color Picker Package is the GetColor function. The routine is passed a point at which the dialog is drawn (the dialog is centered if the point is set to 0,0), a prompt string, an in going RGB color and a VAR to an out going RGB color. The routine will display and run the Picker dialog. The function will return False if the user presses Cancel. If he presses OK, the function will return True and the out going RGB variable will contain the new RGB color. If that RGB color is being used for something, remember to update everything (RGB variables, PixMaps, Color Windows, etc.).

One of the new user interfaces on the Macintosh is the Popup Menu. This menu is similar to the normal Menu Bar based menu, but can appear anywhere on a window. This feature is not exclusive to the Macintosh //. The latest System/Finder has been patched so that any Macintosh with the Enhanced Roms or later (Mac 512KE, MacPlus, Mac SE, Mac //) have the feature. While the PopUp Menu call (PopupMenuSelect) is not documented in the current release of Inside Macintosh V from APDA, the Rom call is implemented in the current release of MPW from APDA.

The Code

After explaining the new calls, the actual program is fairly simple. It displays a Life game in color. Depending on how old the cell is (0 generations or dead, 1 generation, 2 generation, etc.), a different RGB color is used to display it. Cells 7 generations or older use the same color (usually they have stabilized). The RGB values are converted into RGB Pix Patterns. Try different color combos. Having a black background and lighter cells create a striking image. Play with the number of Pixels the video card is using and see the effect.

The program was written in Lightspeed Pascal, and MPW 2.0 and used the new Build feature of MPW to automatically compile itself. Of course the program can be compiled/linked by hand. The source includes the Pascal file, Resource file and Make file for the MPW version, and the main, globals unit and color stuff unit for the LS Pascal version. The LSP version is shown here and is virtually identical to the MPW version, except that it is broken into units to keep the main segment under 32K, and the resources are given in RMaker format. If you are typing it into MPW, note the include file names required in the comment at the top of the program. It should be easily ported to either Turbo Pascal or TML Pascal as well. Both the MPW and LSP versions are included on the source code disk for this issue if you don’t want to type it in. Be sure to use a version of your compiler which supports the new Inside Macintosh Volume 5 traps and equates. For LS Pascal, this is version 1.1, which was released last month at the Boston Expo.

The Life algorithm used here is a brute force method, there are plenty of more elegant (translate faster) ones around. This program was designed to be readable first, speedy second. If it was not for the faster speed of the Mac //, this program would almost be to slow.

Next Issue Palatte Manipulation and Animation!

```PROGRAM ColorLife;

{A small Mac // Color Application written by Steve Sheets. It plays the
original Life game. It also demonstrates some of the new commands of
Color Quickdraw. LS Pascal version.}

{Memtypes, Quickdraw, OSIntf, ToolIntf, PackIntf, PickerIntf;}

USES
ROM85, ColorQuickDraw, ColorMenuMgr, ColorWindowMgr, PickerIntf, myColorGlobals,
ColorStuff;

{Called if Color Quickdraw does not exists to explain why the program
can not be run.}

PROCEDURE DoSorry;
VAR
n : integer;
BEGIN
END;

{Standard Init calls to set Macintosh up (regardless if it is a Mac //
or not).}

PROCEDURE DoInit;
BEGIN
InitGraf(@thePort);
InitFonts;
FlushEvents(everyEvent, 0);
InitWindows;
TEInit;
InitDialogs(NIL);
InitCursor;
END;

{Does the setup for this program (only if there is Color Quickdraw).
Makes the Menus, clears the Dishes, Sets the Colors, make the Color Patterns
and sets them, Make the windows, and inits in other variable that need
to be set.}

PROCEDURE DoSetup;
VAR
tempS : str255;
tempR : rect;
n : INTEGER;
BEGIN

ClearDish(OldDish);
ClearDish(CurDish);

SetMyColors(0, -1, -1, -1);
SetMyColors(1, 0, -1, -1);
SetMyColors(2, 0, -1, 0);
SetMyColors(3, -1, -1, 0);
SetMyColors(4, -1, 0, 0);
SetMyColors(5, -1, 0, -1);
SetMyColors(6, 0, 0, -1);
SetMyColors(7, 0, 0, 0);
CWhite.red := -1;
CWhite.green := -1;
CWhite.blue := -1;

FOR n := 0 TO MaxAge DO
BEGIN
myPixMap[n] := NewPixPat;
MakeRGBPat(myPixMap[n], myColors[n])
END;

colorWindow := GetNewCWindow(colorID, NIL, POINTER(-1));
SetPortColors(colorWindow);

GetIndString(tempS, strID, 2);
CenterRect(HQuick, VQuick, tempR);
quickWindow := NewCWindow(NIL, tempR, tempS, FALSE, noGrowDocProc, POINTER(-1),
TRUE, 0);
SetPortColors(quickWindow);

GetIndString(tempS, strID, 1);
CenterRect(HLife, VLife, tempR);
lifeWindow := NewCWindow(NIL, tempR, tempS, TRUE, noGrowDocProc, POINTER(-1),
TRUE, 0);
SetPortColors(lifeWindow);

SetRect(bigRect, -32000, -32000, 32000, 32000);
doneFlag := FALSE;
END;

{Standard Main Progrom Loop, loops til done flag is marked.  Handles
drags, go Aways, keys, and menu the same way.  Updates and mouse downs
in windows, call the specific routine to handle those events.}

PROCEDURE MainLoop;
VAR
myWindow : WindowPtr;
myChar : CHAR;
myEvent : EventRecord;
myPort : grafptr;
BEGIN
REPEAT
IF GetNextEvent(everyEvent, myEvent) THEN
CASE myEvent.what OF
mouseDown :
CASE FindWindow(myEvent.where, myWindow) OF
inSysWindow :
SystemClick(myEvent, myWindow);
inDrag :
DragWindow(myWindow, myEvent.where, bigRect);
inGoAway :
IF TrackGoAway(myWindow, myEvent.where) THEN
HideWindow(myWindow);
inContent :
IF myWindow <> frontWindow THEN
SelectWindow(myWindow)
ELSE IF myWindow = lifeWindow THEN
DoLifeClick(myEvent.where)
ELSE IF myWindow = colorWindow THEN
DoColorClick(myEvent.where);
OTHERWISE
BEGIN
END;
END;

keyDown, autoKey :
BEGIN
IF BitAnd(myEvent.modifiers, cmdKey) <> 0 THEN
END;

updateEvt :
BEGIN
GetPort(myPort);
myWindow := WindowPtr(myEvent.message);
BeginUpdate(myWindow);
SetPort(myWindow);
IF myWindow = lifeWindow THEN
DoLifeDraw
ELSE IF myWindow = colorWindow THEN
DoColorDraw
ELSE IF myWindow = aboutWindow THEN
ELSE IF myWindow = quickWindow THEN
DoQuickDraw;
EndUpdate(WindowPtr(myEvent.message));
SetPort(myPort);
END;

OTHERWISE
END;
UNTIL doneFlag;
END;

{Main Program.  First calls DoInit to do the standard inits.  Then checks
if Color Quickdraw Exists.  If so, does Setup and Mainloop.  If not,
call DoSorry to explain.}
BEGIN
DoInit;
IF ColorQDExists THEN
BEGIN
DoSetup;
MainLoop;
END
ELSE
DoSorry;
END.

UNIT ColorStuff;

INTERFACE

USES
ROM85, ColorQuickDraw, ColorMenuMgr, ColorWindowMgr, PickerIntf, myColorGlobals;

PROCEDURE ClearDish (VAR J : Dish);
PROCEDURE SetMyColors (i, r, g, b : integer);
PROCEDURE SetPortColors (W : windowptr);
PROCEDURE CenterRect (h, v : INTEGER;
VAR R : rect);
PROCEDURE DoCommand (mResult : LONGINT);
PROCEDURE DoColorClick (Where : point);
PROCEDURE DoLifeClick (Where : point);
PROCEDURE DoLifeDraw;
PROCEDURE DoColorDraw;
PROCEDURE DoQuickDraw;
FUNCTION ColorQDExists : boolean;

IMPLEMENTATION

{Given Integer value (corrsponding to a string stored in STR# resource),
draw that string, centered on point h,v in c color.}

PROCEDURE DrawCenter (N, h, v, c : integer);
VAR
S : str255;
BEGIN
RGBForeColor(myColors[c]);
GetIndString(S, strID, N);
MoveTo(h - (StringWidth(S) DIV 2), v);
DrawString(S);
END;

{Given a Dish, Clear all the cells (set to 0).}

PROCEDURE ClearDish; {(var J : Dish);}
VAR
h, v : integer;
BEGIN
FOR h := 0 TO HEdgeMax DO
FOR v := 0 TO VEdgeMax DO
J[h, v] := 0;
END;

{Given a Color Window Port, set the Pen and Background color Patterns
(stored in myPicMap in position 0 & 1, respectively).}

PROCEDURE SetPortColors;{(W : windowptr);}
BEGIN
SetPort(W);
BackPixPat(myPixMap[0]);
PenPixPat(myPixMap[1]);
END;

{Given a Color Window Port, calls SetPortColors to set Pen and Background
color Patterns and forces on update by invalidating a big Rectangle.}

PROCEDURE ForceUpdate (W : windowptr);
BEGIN
SetPortColors(W);
InvalRect(bigRect);
END;

{Given a Color Window Port, saves the current Port, calls UpdateOne and
then restore the current port.}

PROCEDURE UpdateOne (W : windowptr);
VAR
tempPort : grafptr;
BEGIN
GetPort(tempPort);
ForceUpdate(W);
SetPort(tempPort);
END;

{Saves the current Port, calls ForceUpdate for all the windows to update
them all and then restore the current port.}

PROCEDURE UpdateAll;
VAR
tempPort : grafptr;
BEGIN
GetPort(tempPort);
ForceUpdate(colorWindow);
ForceUpdate(quickWindow);
ForceUpdate(lifeWindow);
SetPort(tempPort);
END;

{Given a Color Window Port, if it is not in front (or invisible), Shows
it and Select it (make it the top window).}

PROCEDURE ShowIt (W : windowptr);
BEGIN
IF FrontWindow <> W THEN
BEGIN
ShowWindow(W);
SelectWindow(W);
END;
END;

{Loads a new Dish and Dish Colors from disk.  If successful, remakes
the Dishes Color Patterns and updates all the windows (using UpdateAll).}

VAR
tempType : SFTypeList;
tempP : point;
Ref, n : INTEGER;
tempE : OSErr;
tempLong : longint;
BEGIN
tempP.v := 40;
tempP.h := 40;
tempType[0] := FileType;
SFGetFile(tempP, ‘’, NIL, 1, tempType, NIL, tempReply);
Ref := 0;
BEGIN
IF tempE = noErr THEN
BEGIN
tempLong := SIZEOF(Dish);
IF tempE = noErr THEN
BEGIN
tempLong := SIZEOF(DishColor);
IF tempE = noErr THEN
BEGIN
tempE := FSClose(Ref);
Ref := 0;
FOR n := 0 TO MaxAge DO
MakeRGBPat(myPixMap[n], myColors[n]);
UpdateAll;
END;
END;
END;
IF Ref <> 0 THEN
tempE := FSClose(Ref);
END;
END;

{Save the current Dish and Dish Colors to disk.}

PROCEDURE DoSave;
VAR
tempP : point;
Ref : INTEGER;
tempE : OSErr;
tempLong : longint;
BEGIN
tempP.v := 40;
tempP.h := 40;
BEGIN
IF (tempE = noErr) OR (tempE = fnfErr) THEN
BEGIN
IF tempE = noErr THEN
BEGIN
IF tempE = noErr THEN
BEGIN
tempLong := SIZEOF(Dish);
tempE := FSWrite(Ref, tempLong, @CurDish);
IF tempE = noErr THEN
BEGIN
tempLong := SIZEOF(DishColor);
tempE := FSWrite(Ref, tempLong, @myColors);
IF tempE = noErr THEN
BEGIN
tempE := FSClose(Ref);
Ref := 0;
END;
END;
END;
END;
END;
IF Ref <> 0 THEN
tempE := FSClose(Ref);
END;
END;

{Given a cells horiztonal and vertical position (in cell position, not
screen position), return the screen position Rectangle that contains
that cell.}

PROCEDURE MakeRect (h, v : integer;
VAR R : rect);
VAR
n : integer;
BEGIN
n := (h * Big);
R.right := n;
R.left := n - big;
n := (v * Big);
R.bottom := n;
R.top := n - big;
END;

{Given a cells horiztonal and vertical position (in cell position, not
screen position) and the correct screen rectangle holding that cell,}
draws the Cell in the correct pattern.  It will only erase the cell if
this call was not done by an update event (update events erase everything).}

PROCEDURE DrawRect (h, v : INTEGER;
R : rect;
UpEvent : BOOLEAN);
BEGIN
IF CurDish[h, v] = 1 THEN
PaintRect(R)
ELSE IF (CurDish[h, v] > 1) AND (CurDish[h, v] <= MaxAge) THEN
FillCRect(R, myPixMap[CurDish[h, v]])
ELSE IF NOT UpEvent THEN
EraseRect(R);
END;

{Given a point in the window where a mouse down occured (global coordinates),
flips the cell at that point (erasing one that there} or making on in
an empty space).  Then loops for every cell the} mouse pass over (drawing
or erasing cells) until the button is released.}

PROCEDURE DoLifeClick;{(Where : point);}
VAR
h, v : integer;
tempPort : grafptr;
tempR : rect;
empty : BOOLEAN;

PROCEDURE CalcWhere;
BEGIN
h := (Where.h DIV big) + 1;
v := (Where.v DIV big) + 1;
END;

PROCEDURE DoDraw;
BEGIN
MakeRect(h, v, tempR);
DrawRect(h, v, tempR, FALSE);
END;

PROCEDURE DoClick;
BEGIN
IF (h > 0) AND (h <= HEdge) AND (v > 0) AND (v <= VEdge) THEN
BEGIN
IF Empty THEN
BEGIN
IF CurDish[h, v] = 0 THEN
BEGIN
CurDish[h, v] := 1;
DoDraw;
END;
END
ELSE
BEGIN
IF CurDish[h, v] <> 0 THEN
BEGIN
CurDish[h, v] := 0;
DoDraw;
END;
END;
END;
END;

BEGIN
GetPort(tempPort);
SetPort(lifeWindow);
GlobalToLocal(Where);
CalcWhere;
empty := (CurDish[h, v] = 0);
DoClick;
REPEAT
GetMouse(where);
CalcWhere;
DoClick;
UNTIL NOT Button;

SetPort(tempPort);
END;

{Given a mouse down point in the Color Window, display a pop up menu
listing the RGB colors.  If a color is selected, call the Color Picker
(GetColor) to select a new color.  If a color is picked, recalculate
the Color Pattern (for that color) and Update all the windows (using
UpdateAll).}

PROCEDURE DoColorClick;{(Where : point);}
VAR
tempLong : LONGINT;
M, I : INTEGER;
tempP : point;
tempS : str255;
ColorIt : RGBColor;
BEGIN
M := HiWord(tempLong);
I := LoWord(tempLong) - 1;
IF (M = popupID) AND (0 <= I) AND (I <= MaxAge) THEN
BEGIN
tempP.v := 0;
tempP.h := 0;
GetIndString(tempS, strID, 3);
ColorIt := myColors[I];
IF GetColor(tempP, tempS, ColorIt, ColorIt) THEN
BEGIN
myColors[i] := ColorIt;
MakeRGBPat(myPixMap[i], myColors[i]);
UpdateAll;
END;
END;
END;

{Draws all the cells in the Life window.}

PROCEDURE DoLifeDraw;
VAR
h, v : integer;
tempR : rect;
BEGIN
EraseRect(bigRect);
FOR h := 1 TO HEdge DO
FOR v := 1 TO VEdge DO
BEGIN
MakeRect(h, v, tempR);
DrawRect(h, v, tempR, TRUE);
END;
END;

{Draws all the colors in the Color window.}

PROCEDURE DoColorDraw;
VAR
tempR : rect;
n, start, finish : INTEGER;
BEGIN
SetRect(tempR, 0, 0, colorSize, colorSize);
EraseRect(tempR);
FOR n := 1 TO MaxAge DO
BEGIN
start := (360 * (n - 1)) DIV MaxAge;
finish := (360 * n) DIV MaxAge;
IF n = 1 THEN
PaintArc(tempR, start, finish - start)
ELSE
FillCArc(tempR, start, finish - start, myPixMap[n]);
END;
END;

type of desgin).}

VAR
h, v, n : INTEGER;
BEGIN
EraseRect(bigRect);

TextSize(24);
DrawCenter(4, HAbout DIV 2, 120, 7);
DrawCenter(5, HAbout DIV 2, 150, 6);
DrawCenter(6, HAbout DIV 2, 180, 5);

FOR n := 1 TO StepSize - 1 DO
BEGIN
h := n * HStep;
v := n * VStep;

PenPixPat(myPixMap[4]);
MoveTo(0, v);

PenPixPat(myPixMap[3]);
MoveTo(h, 0);

PenPixPat(myPixMap[2]);

PenPixPat(myPixMap[1]);
LineTo(0, v);
END;
END;

{Draws the Quickdraw Window that displays the major Color Quickdraw commands
(similar to the Lisa version).}

PROCEDURE DoQuickDraw;
VAR
tempRect : Rect;
tempPat : Pattern;
tempPoly : PolyHandle;
tempRgn : RgnHandle;
BEGIN
{Erase Background}
EraseRect(bigRect);
{Draws Squares}
PenPixPat(myPixMap[1]);
MoveTo(0, 150);
LineTo(450, 150);
MoveTo(150, 0);
LineTo(150, 300);
MoveTo(300, 0);
LineTo(300, 300);
{Draws Titles}
DrawCenter(7, 75, 20, 2);
DrawCenter(8, 225, 20, 3);
DrawCenter(9, 375, 20, 4);
DrawCenter(10, 75, 170, 5);
DrawCenter(11, 225, 170, 6);
DrawCenter(12, 375, 170, 7);
{Draw Rectangles}
SetRect(tempRect, 15, 25, 95, 105);
RGBForeColor(myColors[1]);
RGBBackColor(CWhite);
PenPat(Black);
FrameRect(tempRect);

OffSetRect(tempRect, QOff, QOff);
RGBForeColor(myColors[2]);
GetIndPattern(tempPat, 0, 31);
PenPat(tempPat);
PaintRect(tempRect);

OffSetRect(tempRect, QOff, QOff);
PenPixPat(myPixMap[3]);
PaintRect(tempRect);

OffSetRect(tempRect, QOff, QOff);
RGBForeColor(myColors[4]);
RGBBackColor(myColors[5]);
GetIndPattern(tempPat, 0, 32);
PenPat(tempPat);
PaintRect(tempRect);

OffSetRect(tempRect, QOff, QOff);
FillCRect(tempRect, myPixMap[6]);
{Draw Ovals}
SetRect(tempRect, 165, 25, 245, 105);
RGBForeColor(myColors[7]);
RGBBackColor(CWhite);
PenPat(Black);
FrameOval(tempRect);

OffSetRect(tempRect, QOff, QOff);
RGBForeColor(myColors[1]);
GetIndPattern(tempPat, 0, 33);
PenPat(tempPat);
PaintOval(tempRect);

OffSetRect(tempRect, QOff, QOff);
PenPixPat(myPixMap[2]);
PaintOval(tempRect);

OffSetRect(tempRect, QOff, QOff);
RGBForeColor(myColors[3]);
RGBBackColor(myColors[4]);
GetIndPattern(tempPat, 0, 34);
PenPat(tempPat);
PaintOval(tempRect);

OffSetRect(tempRect, QOff, QOff);
FillCOval(tempRect, myPixMap[5]);

{Draw Round Rectangles}
SetRect(tempRect, 315, 25, 395, 105);
RGBForeColor(myColors[6]);
RGBBackColor(CWhite);
PenPat(Black);
FrameRoundRect(tempRect, 20, 20);

OffSetRect(tempRect, QOff, QOff);
RGBForeColor(myColors[7]);
GetIndPattern(tempPat, 0, 35);
PenPat(tempPat);
PaintRoundRect(tempRect, 20, 20);

OffSetRect(tempRect, QOff, QOff);
PenPixPat(myPixMap[1]);
PaintRoundRect(tempRect, 20, 20);

OffSetRect(tempRect, QOff, QOff);
RGBForeColor(myColors[2]);
RGBBackColor(myColors[3]);
GetIndPattern(tempPat, 0, 36);
PenPat(tempPat);
PaintRoundRect(tempRect, 20, 20);

OffSetRect(tempRect, QOff, QOff);
FillCRoundRect(tempRect, 20, 20, myPixMap[4]);

{Draw Polygons}
tempPoly := OpenPoly;
SetRect(tempRect, 15, 175, 95, 255);
MoveTo(95, 175);
LineTo(65, 215);
LineTo(95, 255);
LineTo(15, 255);
LineTo(15, 215);
LineTo(55, 175);
LineTo(95, 175);
ClosePoly;

RGBForeColor(myColors[5]);
RGBBackColor(CWhite);
PenPat(Black);
FramePoly(tempPoly);

OffSetPoly(tempPoly, QOff, QOff);
RGBForeColor(myColors[6]);
GetIndPattern(tempPat, 0, 37);
PenPat(tempPat);
PaintPoly(tempPoly);

OffSetPoly(tempPoly, QOff, QOff);
PenPixPat(myPixMap[7]);
PaintPoly(tempPoly);

OffSetPoly(tempPoly, QOff, QOff);
RGBForeColor(myColors[1]);
RGBBackColor(myColors[2]);
GetIndPattern(tempPat, 0, 38);
PenPat(tempPat);
PaintPoly(tempPoly);

OffSetPoly(tempPoly, QOff, QOff);
FillCPoly(tempPoly, myPixMap[3]);

KillPoly(tempPoly);

{Draw Arcs}
SetRect(tempRect, 165, 175, 265, 275);
RGBForeColor(myColors[4]);
RGBBackColor(CWhite);
PenPat(Black);
FrameArc(tempRect, 198, 72);

RGBForeColor(myColors[5]);
GetIndPattern(tempPat, 0, 15);
PenPat(tempPat);
PaintArc(tempRect, 126, 72);

PenPixPat(myPixMap[6]);
PaintArc(tempRect, 270, 72);

RGBForeColor(myColors[7]);
RGBBackColor(myColors[1]);
GetIndPattern(tempPat, 0, 16);
PenPat(tempPat);
PaintArc(tempRect, 342, 72);

OffSetRect(tempRect, 20, 0);
FillCArc(tempRect, 54, 72, myPixMap[2]);

{Draw Regions}
tempRgn := NewRgn;
OpenRgn;
SetRect(tempRect, 315, 175, 395, 255);
FrameRoundRect(tempRect, 20, 20);
InsetRect(tempRect, 10, 10);
FrameOval(tempRect);
CloseRgn(tempRgn);

RGBForeColor(myColors[3]);
RGBBackColor(CWhite);
PenPat(Black);
FrameRgn(tempRgn);

OffSetRgn(tempRgn, QOff, QOff);
RGBForeColor(myColors[4]);
GetIndPattern(tempPat, 0, 17);
PenPat(tempPat);
PaintRgn(tempRgn);

OffSetRgn(tempRgn, QOff, QOff);
PenPixPat(myPixMap[5]);
PaintRgn(tempRgn);

OffSetRgn(tempRgn, QOff, QOff);
RGBForeColor(myColors[6]);
RGBBackColor(myColors[7]);
GetIndPattern(tempPat, 0, 18);
PenPat(tempPat);
PaintRgn(tempRgn);

OffSetRgn(tempRgn, QOff, QOff);
FillCRgn(tempRgn, myPixMap[1]);

DisposeRgn(tempRgn);
END;

{Steps through a generation of growth/death of the Dish.  Stores the
old Graf port, then sets the Life window as the new one.  Copies the
current dish into the old dish, then checks each cells one by one. Calculates
the number of cells around it, and decides deaths, lifes and births.
If the new value does not match the old value} (ie. change), redraw
that window.}

PROCEDURE DoStep;
VAR
n, h, v, c : INTEGER;
tempPort : grafptr;
tempR : rect;
BEGIN
GetPort(tempPort);
SetPort(lifeWindow);
BlockMove(@CurDish, @OldDish, SIZEOF(Dish));

FOR h := 1 TO HEdge DO
FOR v := 1 TO VEdge DO
BEGIN
n := 0;
IF OldDish[h - 1, v - 1] <> 0 THEN
n := n + 1;
IF OldDish[h, v - 1] <> 0 THEN
n := n + 1;
IF OldDish[h + 1, v - 1] <> 0 THEN
n := n + 1;
IF OldDish[h - 1, v] <> 0 THEN
n := n + 1;
IF OldDish[h + 1, v] <> 0 THEN
n := n + 1;
IF OldDish[h - 1, v + 1] <> 0 THEN
n := n + 1;
IF OldDish[h, v + 1] <> 0 THEN
n := n + 1;
IF OldDish[h + 1, v + 1] <> 0 THEN
n := n + 1;
c := OldDish[h, v];
IF (c = 0) AND (n = 3) THEN
CurDish[h, v] := 1
ELSE IF (c <> 0) AND ((n = 3) OR (n = 2)) THEN
BEGIN
IF c < MaxAge THEN
CurDish[h, v] := c + 1;
END
ELSE
CurDish[h, v] := 0;

IF CurDish[h, v] <> c THEN
BEGIN
MakeRect(h, v, tempR);
DrawRect(h, v, tempR, FALSE);
END;
END;

SetPort(tempPort);
END;

{Standard Menu Command command procedures.  Takes card of Desk Accessories,
3 other windows).}
PROCEDURE DoCommand;{(mResult : LONGINT);}
VAR
theItem : INTEGER;
name : Str255;
temp : INTEGER;
tempBool : BOOLEAN;
tempEvent : EventRecord;
BEGIN
theItem := LoWord(mResult);

appleID :
IF (theItem = 1) THEN
ELSE
BEGIN
temp := OpenDeskAcc(name);
END;
fileID :
CASE theItem OF
1 :
BEGIN
ClearDish(CurDish);
UpdateOne(lifeWindow);
END;
2 :
3 :
DoSave;
5 :
doneFlag := TRUE;
OTHERWISE
END;
editID :
tempBool := SystemEdit(theItem - 1);
actionID :
CASE theItem OF
1 :
DoStep;
2 :
FOR temp := 1 TO 10 DO
DoStep;
3 :
DoStep;
OTHERWISE
END;
showID :
CASE theItem OF
1 :
ShowIt(lifeWindow);
2 :
ShowIt(colorWindow);
3 :
ShowIt(quickWindow);
OTHERWISE
END;
OTHERWISE
END;
END;

{Checks if Color Quickdraw Exists on this machine.}

FUNCTION ColorQDExists; { : boolean;}
CONST
ROM85Loc = \$28E;
VAR
WordPtr : ^Integer;
ROM85Value : integer;
BEGIN
WordPtr := pointer(ROM85Loc);
ROM85Value := WordPtr^;
ColorQDExists := (BitAnd(ROM85Value, TwoHighMask) = 0);
END;

{Given a h and v size, calculates a Rectangle that is centered on the
screen.}

PROCEDURE CenterRect; {(h, v : INTEGER; var R : rect);}
VAR
Hoff, Voff : INTEGER;
BEGIN
Hoff := (ScreenBits.bounds.right - h) DIV 2;
VOff := (ScreenBits.bounds.bottom - v) DIV 2;
IF VOff < 35 THEN
VOff := 35;
SetRect(R, Hoff, VOff, Hoff + h, Voff + v);
END;

{Given RGB values (3 integers) and pos (which , places the values in
the Dish Color in the correct position.}
PROCEDURE SetMyColors;{(i, r, g, b : integer);}
BEGIN
myColors[i].red := r;
myColors[i].green := g;
myColors[i].blue := b;
END;
END.

UNIT myColorGlobals;

INTERFACE

USES
ColorQuickDraw;
{Constants: various Resource IDs, File Type and Creator Signitures, Maximum
recorded Age of Cell, Horizontal/Vertical size of Dish (containing cells),
Size of cells, Size of other windows and other size information.}
CONST
appleID = 256;
fileID = 257;
editID = 258;
actionID = 259;
showID = 260;
popupID = 261;

strID = 256;
sorryID = 256;
colorID = 257;

FileType = ‘life’;
CreatorType = ‘life’;

MaxAge = 7;
VEdge = 50;
VEdgeMax = 51;
HEdge = 70;
HEdgeMax = 71;
Big = 8;
VLife = 400; {Big * VEdge}
HLife = 560; {Big * HEdge}

VQuick = 300;
HQuick = 450;
QOff = 10;

colorSize = 300;
StepSize = 30;
HStep = 20;
VStep = 10;
HAbout = 600; {StepSize * HStep}
VAbout = 300; {StepSize * VStep}

{Types: Age range, Dish info (containing all cells & age), Dish Colors
(containing RGB colors), Dish Color Patterns (RGB info converted to a
Color Pattern).}
TYPE
Age = 0..MaxAge;
Dish = PACKED ARRAY[0..HEdgeMax, 0..VEdgeMax] OF Age;
DishColor = PACKED ARRAY[0..MaxAge] OF RGBColor;
DishPixMap = PACKED ARRAY[0..MaxAge] OF PixPatHandle;

large Rect (used for drag & updates), Done Flag and Current & 0ld Dishes.}
VAR
lifeWindow, aboutWindow, colorWindow, quickWindow : WindowPtr;
myColors : DishColor;
myPixMap : DishPixMap;
bigRect : Rect;
doneFlag : BOOLEAN;
OldDish, CurDish : Dish;
CWhite : RGBColor;

IMPLEMENTATION

END.

* Life.R
* Resources for Color Life
* © 1987 by Steve Sheets for MacTutor
*
*
Life.RSRC
????????

Type LIFE = STR
,0
Color Life Application - Version 1.0 by Steve Sheets

Type FREF
,128 (0)
APPL 0
,129 (0)
LIFE 1

Type BNDL
,128 (0)
LIFE 0
ICN#
0 128 1 129
FREF
0 128 1 129

TYPE STR#
,256
12
Life
Quickdraw
Select Color:
Color Life by Steve Sheets
Demo of Color Quickdraw
on the Mac //
Rectangles
Ovals
RoundRectangles:
Arcs
Polygons
Regions

TYPE WIND
,256
40 20 340 620
Invisible Goaway
4
0

TYPE WIND
,257
Colors
40 170 340 470
Invisible Goaway
4
0

*   ,ID (attributes)
*   menu title (an Apple symbol is \ 14 in hex)
*   menu items, ( means it’s initially disabled.
*   (-  means a disabled line of dashes.
*   A trailing /Q, etc. means a command-key.
,256
\14
(-

,257
File
New
Save
(-
Quit/Q

,258
Edit
Undo/Z
(-
Cut/X
Copy/C
Paste/V
Clear

,259
Action
Step/S
Ten Steps/T
Loop til Button/L

,260
Show
Life
Colors
Quickdraw

,261
x
Select Background Color [Age 0]
Select Foreground Color [Age 1]
Select Age 2 Color
Select Age 3 Color
Select Age 4 Color
Select Age 5 Color
Select Age 6 Color
Select Age 7 [&older] Color

* ------ Dialogs --------
* Program Messages Dialog box...
type DLOG
,257
Program Messages
100 100 200 400
Visible NoGoAway
1
0
257

type DITL
,257
3
BtnItem Enabled
65 230 95 285
OK

StatText Disabled
15 60 85 222
^0\0D^1\0D^2\0D^3

IconItem Disabled
10 10 42 42
1

type ALRT
,256
40 106 140 406
256
4444

* A Dialog or Alert Item List
*   ,ID (attributes)
*   number of items in list
*   type of item
*   top left bottom right
*   message
Type DITL
,256
2

button
60 120 80 180
OK

staticText Disabled
20 10 40 290
Sorry, this program only runs on a Mac //

* misc resources

* An icon list for the icon
*   ,ID (attributes)
*   Data is Hex data (.H)
*   the icon data: 32 lines of 8 hex chars each
*   the icon mask: 32 lines of 8 hex chars each
Type ICN# = GNRL
,128 (0)
.H
0000 0000 EE00 0EEE EE00 0EEE EE00 0EEE
0000 0000 E000 000E E000 000E E000 000E
0000 0000 000E 00E0 000E 00E0 000E 00E0
0000 0000 00EE 0000 00EE 0000 00EE 0000
0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0EE0 0000 0EE0 0000 0EE0 0000
0000 0000 E00E 00EE E00E 00EE E00E 00EE
0000 0000 0EE0 00EE 0EE0 00EE 0EE0 00EE
* [2]
0000 0000 EEEE EEEE EEEE EEEE EEEE EEEE
0000 0000 EEEE EEEE EEEE EEEE EEEE EEEE
0000 0000 EEEE EEEE EEEE EEEE EEEE EEEE
0000 0000 EEEE EEEE EEEE EEEE EEEE EEEE
0000 0000 EEEE EEEE EEEE EEEE EEEE EEEE
0000 0000 EEEE EEEE EEEE EEEE EEEE EEEE
0000 0000 EEEE EEEE EEEE EEEE EEEE EEEE
0000 0000 EEEE EEEE EEEE EEEE EEEE EEEE

* An icon list for the icon
*   ,ID (attributes)
*   Data is Hex data (.H)
*   the icon data: 32 lines of 8 hex chars each
*   the icon mask: 32 lines of 8 hex chars each
Type ICN# = GNRL
,129 (0)
.H
1FFF FC00 1000 0600 1000 0500 1000 0480
1000 0440 1360 3420 1360 37F0 1000 0010
1300 00D0 1300 00D0 1000 0010 1001 8610
1001 8610 1000 0010 100D 8010 100D 8010
1000 0010 1000 0010 1000 0010 1000 0010
106C 0010 106C 0010 1000 0010 1301 86D0
1301 86D0 1000 0010 106C 06D0 106C 06D0
1000 0010 1000 0010 1000 0010 1FFF FFF0
* [2]
1FFF FC00 1FFF FE00 1FFF FF00 1FFF FF80
1FFF FFC0 1FFF FFE0 1FFF FFF0 1FFF FFF0
1FFF FFF0 1FFF FFF0 1FFF FFF0 1FFF FFF0
1FFF FFF0 1FFF FFF0 1FFF FFF0 1FFF FFF0
1FFF FFF0 1FFF FFF0 1FFF FFF0 1FFF FFF0
1FFF FFF0 1FFF FFF0 1FFF FFF0 1FFF FFF0
1FFF FFF0 1FFF FFF0 1FFF FFF0 1FFF FFF0
1FFF FFF0 1FFF FFF0 1FFF FFF0 1FFF FFF0
```

Community Search:
MacTech Search:

calibre 4.17.0 - Complete e-book library...
MacPilot 11.1.4 - \$15.96
MacPilot gives you the power of UNIX and the simplicity of Macintosh, which means a phenomenal amount of untapped power in your hands! Use MacPilot to unlock over 1,200 features, and access them all... Read more
Transmission 3.00 - Popular BitTorrent c...
Transmission is a fast, easy, and free multi-platform BitTorrent client. Transmission sets initial preferences so things "just work", while advanced features like watch directories, bad peer blocking... Read more
Doom 3 1.3.1 - First-person shooter acti...
A massive demonic invasion has overwhelmed the Union Aerospace Corporation's (UAC) Mars Research Facility, leaving only chaos and horror in its wake. As one of only a few survivors, you must fight... Read more
Box Sync 4.0.8004 - Online synchronizati...
Box Sync gives you a hard-drive in the Cloud for online storage. Note: You must first sign up to use Box. What if the files you need are on your laptop -- but you're on the road with your iPhone? No... Read more
LibreOffice 6.4.4.2 - Free, open-source...
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
Day One 4.14 - Maintain a daily journal.
Day One is an easy, great-looking way to use a journal / diary / text-logging application. Day One is well designed and extremely focused to encourage you to write more through quick Menu Bar entry,... Read more
Doxie 2.12.2 - Scan, share, and store do...
Introducing Doxie, the new, modern paper scanner that's so simple, it'll revolutionize the way you think about sharing and storing docs and photos forever. Doxie is ultra-portable, fully automatic,... Read more
Tinderbox 8.7.0 - Store and organize you...
Tinderbox is a personal content management assistant. It stores your notes, ideas, and plans. It can help you organize and understand them. And Tinderbox helps you share ideas through Web journals... Read more
Viber 13.0.0 - Send messages and make fr...
Viber lets you send free messages and make free calls to other Viber users, on any device and network, in any country! Viber syncs your contacts, messages and call history with your mobile device, so... Read more

Latest Forum Discussions

SINoALICE, Yoko Taro and Pokelabo's...
Yoko Taro and developer Pokelabo's SINoALICE has now opened for pre-registration over on the App Store. It's already amassed 1.5 million Android pre-registrations, and it's currently slated to launch on July 1st. [Read more] | Read more »
Masketeers: Idle Has Fallen is the latest endeavour from Appxplore, the folks behind Crab War, Thor: War of Tapnarok and Light A Way. It's an idle RPG that's currently available for Android in Early Access and will head to iOS at a later date. [... | Read more »
Evil Hunter Tycoon celebrates 2 million...
Evil Hunter Tycoon has proved to be quite the hit since launching back in March, with its most recent milestone being 2 million downloads. To celebrate the achievement, developer Super Planet has released a new updated called Darkness' Front Yard... | Read more »
Peak's Edge is an intriguing roguel...
Peak's Edge is an upcoming roguelike puzzle game from developer Kenny Sun that's heading for both iOS and Android on June 4th as a free-to-play title. It will see players rolling a pyramid shape through a variety of different levels. [Read more] | Read more »
Clash Royale: The Road to Legendary Aren...
Supercell recently celebrated its 10th anniversary and their best title, Clash Royale, is as good as it's ever been. Even for lapsed players, returning to the game is as easy as can be. If you want to join us in picking the game back up, we've put... | Read more »
The Magic Gladiator class arrives in MU...
The Magic Gladiator class is now available in MU Origin 2 following the most recent patch. It also marks the start of Abyss Season 11 and the introduction of Couple Skills and Couple Dungeons. [Read more] | Read more »
The 5 Best Racing Games
With KartRider Rush+ making a splash this past week, we figured it was high time we updated our list of the best mobile racing games out there. From realistic racing sims to futuristic arcade racers (and even racing management games!), check out... | Read more »
KartRider Rush+ Guide - Tips for new rac...
KartRider Rush+ continues to be a surprisingly refreshing and fun kart racer that's entirely free-to-play. The main reason for this is just how high its skill ceiling is. Check out the video above if you're curious to know what top level play looks... | Read more »
KartRider Rush+ might be good, actually?
It's hard to find good racing games on mobile. Most of them are free-to-play, and free-to-play racers generally suck. Even Nintendo couldn't put together a competent Mario Kart game, opting instead for a weird score chaser that resembles--but feels... | Read more »
LifeAfter, NetEase's popular surviv...
A new map will be making its way into NetEase's popular survival game LifeAfter. The map is set to arrive on May 28th and will introduce a volcano that's teetering on the verge of eruption, bringing a host of added challenges to the game. [Read... | Read more »

Price Scanner via MacPrices.net

Memorial Day Weekend Sale: Take \$300 off thes...
Apple resellers are offering \$300 discounts on select 16″ MacBook Pros as part of their Memorial Day Weekend 2020 sales. Prices start at \$2099: – 16″ 2.6GHz 6-Core Space Gray MacBook Pro: \$2099 at... Read more
Best Memorial Day Weekend 2020 Apple AirPods...
Apple resellers are offering discounts ranging up to \$50 off MSRP on AirPods as part of their Memorial Day Weekend 2020 sales. These are the best deals today on various AirPods models. See our... Read more
Memorial Day Weekend Sale: 10″ Apple iPads fo...
Amazon is offering new 10.2″ iPads for \$80-\$100 off Apple’s MSRP as part of their Memorial Day Weekend 2020 sale, with prices starting at only \$249. These are the same iPads sold by Apple in their... Read more
Memorial Day Weekend Sale: 2020 Apple iPhone...
Sprint is offering Apple’s new 2020 64GB iPhone SE for \$0 per month for 18 months as part of their Memorial Day Weekend 2020 sale. New line of service and trade-in required. Offer is valid from 5/22/... Read more
Amazon’s popular \$100 Apple Watch Series 5 di...
Amazon has Apple Watch Series 5 GPS + Cellular models on sale for up to \$100 off Apple’s MSRP today. Shipping is free. These are the same Apple Watch models sold by Apple in their retail and online... Read more
2020 13″ 4-Core MacBook Air on sale for \$949,...
Apple reseller Adorama has the new 2020 13″ 1.1GHz 4-Core Space Gray MacBook Air on sale today for \$949 shipped. Their price is \$50 off Apple’s MSRP, and it’s the lowest price currently available for... Read more
Apple Retail Chief Announces Staggered Reopen...
NEWS: 05.20.20 – In the midst of a global pandemic, after its retail outlets were shuttered temporarily in mid-March as a mitigation measure enacted by Apple during the peak of the spread of COVID-19... Read more
Apple’s Pro Display XDR models in stock today...
Abt Electronics has Apple’s new 32″ Pro Display XDR models in stock and on sale today for up to \$305 off MSRP. Shipping is free. Their prices are currently the lowest available for these new models... Read more
Apple restocks refurbished iPhone 8 for \$339
Apple has restocked Apple Certified Refurbished 64GB iPhone 8 models for only \$339. Apple dropped their price on this phone last month by \$160, from \$499 to \$339. Each refurbished iPhone comes with a... Read more
New at AT&T: 50% off iPhone 11 for new cu...
AT&T is offering a 50% off the 64GB iPhone 11 for new customers who switch to AT&T and open a new line of service. Discount applied over a 30 month period The fine print: “iPhone 11 64GB for... Read more

Jobs Board

Cub Foods - *Apple* Valley - Now Hiring Par...
Cub Foods - Apple Valley - Now Hiring Part Time! United States of America, Minnesota, Apple Valley New Retail Operations Post Date 5 days ago Requisition # Read more
Senior Data Engineer - *Apple* - Theorem, L...
Job Summary Apple is seeking an experienced, detail-minded data engineeringconsultant to join our worldwide business development and strategy team. If you are Read more
Cub Foods - *Apple* Valley - Now Hiring Par...
Cub Foods - Apple Valley - Now Hiring Part Time! United States of America, Minnesota, Apple Valley New Retail Operations Post Date 4 days ago Requisition # Read more
Medical Screener - *Apple* Hill - Evenings...
Medical Screener - Apple Hill - Evenings Tracking Code D-MS-AH-E Job Description Medical Screener - Temporary We Are Hiring: WellSpan Health has a temporary Medical Read more
*Apple* Systems Administrator - Solidus Tech...
Solidus is searching for an Apple Systems Engineer. The engineer will be responsible for desktop and server infrastructure. This includes support for operating Read more