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 columns 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 well 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 Gardners 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 its 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 pens settings. Old Quickdraw would draw the pen using the Pen Pattern; Color Quickdraw draws using the Pens 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 Pens 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 Quickdraws 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.
Fig. 4 The Color Picker Dialog in black and white
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 its 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.).
PopUp Menus
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 Menu to be used as a Popup menu must be created the same way any other Menu is created (with the NewMenu or GetMenu commands). The Menu must then be added to the Menu List with the InsertMenu command. However unlike normal menus, the beforeID parameter of the InsertMenu call must be set to -1. This places the Menu in the Menu List, but does not displays it. When a mouse down occurs at the correct spot in a window, the PopupMenuSelect Function is used. This function is passed the Popup Menu to be displayed, the vertical and horizontal position of where the popup menu should appear (in global coordinates) and the number of the menu item that initially should appear under the cursor. Depending on the position, this menu can be scrolling. The function return a long integer value, exactly like the MenuSelect or MenuKey function, that contains the Menu ID number and Item number in the High and Low words. After the call is made (and the menu disappears), the Menu should be removed from the menu list.
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.
Click in the Life window to create or erase the Life cells. Use the Action Menu to cycle through various numbers of generations. Use the Show Menu (and About Life Menu) to display the other windows. Click in the Color Window to use a Popup Menu to select a color to change and then use the Color Picker to create a new color. Finally use the Save and Load Menus to create and use Life Documents. Notice that the Save and Load commands saves the RGB colors also. The program has a long, but instructive drawing routines (specifically for the Quickdraw Window) that provide examples of how the old and new Drawing Operations (Rect, Oval, Arc, Polygon, etc.) work. It also includes a number of useful procedures including how to identify if color quickdraw is running. A PopUpMenu routine illustrates getting colors with the color picker as well. This should provide a wealth of useful information since Inside Mac volume 5 is pretty much useless due to its inaccurate information.
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 dont 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
n := Alert(sorryID, NIL);
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;
InitMenus;
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
appleMenu := GetMenu(appleID);
AddResMenu(appleMenu, DRVR);
InsertMenu(appleMenu, 0);
fileMenu := GetMenu(fileID);
InsertMenu(fileMenu, 0);
editMenu := GetMenu(editID);
InsertMenu(editMenu, 0);
actionMenu := GetMenu(actionID);
InsertMenu(actionMenu, 0);
showMenu := GetMenu(showID);
InsertMenu(showMenu, 0);
popupMenu := GetMenu(popupID);
InsertMenu(popupMenu, -1);
DrawMenuBar;
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;
aboutWindow := GetNewCWindow(aboutID, NIL, POINTER(-1));
SetPortColors(aboutWindow);
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
SystemTask;
IF GetNextEvent(everyEvent, myEvent) THEN
CASE myEvent.what OF
mouseDown :
CASE FindWindow(myEvent.where, myWindow) OF
inSysWindow :
SystemClick(myEvent, myWindow);
inMenuBar :
DoCommand(MenuSelect(myEvent.where));
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
myChar := CHR(BitAnd(myEvent.message, charCodeMask));
IF BitAnd(myEvent.modifiers, cmdKey) <> 0 THEN
DoCommand(MenuKey(myChar));
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
DoAboutDraw
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 DoAboutDraw;
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(aboutWindow);
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).}
PROCEDURE DoLoad;
VAR
tempReply : SFReply;
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;
IF tempReply.good THEN
BEGIN
tempE := FSOpen(tempReply.fname, tempReply.vrefnum, Ref);
IF tempE = noErr THEN
BEGIN
tempLong := SIZEOF(Dish);
tempE := FSRead(Ref, tempLong, @CurDish);
IF tempE = noErr THEN
BEGIN
tempLong := SIZEOF(DishColor);
tempE := FSRead(Ref, tempLong, @myColors);
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
tempReply : SFReply;
tempP : point;
Ref : INTEGER;
tempE : OSErr;
tempLong : longint;
BEGIN
tempP.v := 40;
tempP.h := 40;
SFPutFile(tempP, , , NIL, tempReply);
IF tempReply.good THEN
BEGIN
tempE := FSDelete(tempReply.fname, tempReply.vrefnum);
IF (tempE = noErr) OR (tempE = fnfErr) THEN
BEGIN
tempE := Create(tempReply.fname, tempReply.vrefnum, CreatorType, FileType);
IF tempE = noErr THEN
BEGIN
tempE := FSOpen(tempReply.fname, tempReply.vrefnum, Ref);
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
tempLong := PopUpMenuSelect(popupMenu, Where.v, Where.h, 1);
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;
{Draws the About Window (information about the program and a spirograph
type of desgin).}
PROCEDURE DoAboutDraw;
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);
LineTo(HAbout - h, 0);
PenPixPat(myPixMap[3]);
MoveTo(h, 0);
LineTo(HAbout, v);
PenPixPat(myPixMap[2]);
LineTo(HAbout, v);
LineTo(HAbout - h, VAbout);
PenPixPat(myPixMap[1]);
MoveTo(h, VAbout);
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,
About (show About Window), New (clear jar and update Life window), Load/Save
(call DoLoad/DoSave), Quit (mark Done Flag), Action Menus (Step, Step
10 Times or Step until a Mouse down) and Show Menus (show one of the
3 other windows).}
PROCEDURE DoCommand;{(mResult : LONGINT);}
VAR
theItem : INTEGER;
theMenu : INTEGER;
name : Str255;
temp : INTEGER;
tempBool : BOOLEAN;
tempEvent : EventRecord;
BEGIN
theItem := LoWord(mResult);
theMenu := HiWord(mResult);
CASE theMenu OF
appleID :
IF (theItem = 1) THEN
ShowIt(aboutWindow)
ELSE
BEGIN
GetItem(appleMenu, theItem, name);
temp := OpenDeskAcc(name);
END;
fileID :
CASE theItem OF
1 :
BEGIN
ClearDish(CurDish);
UpdateOne(lifeWindow);
END;
2 :
DoLoad;
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 :
WHILE NOT GetNextEvent(mDownMask, tempEvent) DO
DoStep;
OTHERWISE
END;
showID :
CASE theItem OF
1 :
ShowIt(lifeWindow);
2 :
ShowIt(colorWindow);
3 :
ShowIt(quickWindow);
OTHERWISE
END;
OTHERWISE
END;
HiliteMenu(0);
END;
{Checks if Color Quickdraw Exists on this machine.}
FUNCTION ColorQDExists; { : boolean;}
CONST
ROM85Loc = $28E;
TwoHighMask = $C000;
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;
aboutID = 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;
{Variables: Menus (including PopupMenu), Windows, RGB Colors, Color Patterns,
large Rect (used for drag & updates), Done Flag and Current & 0ld Dishes.}
VAR
appleMenu, fileMenu, editMenu, actionMenu, showMenu, popupMenu : MenuHandle;
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
About Life
40 20 340 620
Invisible Goaway
4
0
TYPE WIND
,257
Colors
40 170 340 470
Invisible Goaway
4
0
* Menus
* ,ID (attributes)
* menu title (an Apple symbol is \ 14 in hex)
* menu items, ( means its initially disabled.
* (- means a disabled line of dashes.
* A trailing /Q, etc. means a command-key.
Type MENU
,256
\14
About Life
(-
Type MENU
,257
File
New
Load
Save
(-
Quit/Q
Type MENU
,258
Edit
Undo/Z
(-
Cut/X
Copy/C
Paste/V
Clear
Type MENU
,259
Action
Step/S
Ten Steps/T
Loop til Button/L
Type MENU
,260
Show
Life
Colors
Quickdraw
Type MENU
,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
* ------ Alerts ------------
* Program error alerts...
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