JamPaint
Volume Number: 5
Issue Number: 7
Column Tag: Pascal Procedures
Build a Network Painting Program 
By Edgar Circenis, Rod Magnuson, Lincoln, NE
Note: Source code files accompanying article are located on MacTech CD-ROM orsource code disks.
[Edgar Circenis: Has programmed the Mac since 1984. Has been employed at
Nordic Software and written software for both the Apple // and Macintosh. Graduate of
the University of Nebraska--Lincoln with a B.S. C.S. Rod Magnuson: Has had 2 years
programming the Macintosh. Founded MindVision software with Steve Kiene in early
1988. Co-author of MaxWrite, the first color word processor and MindVision’s first
offering.]
JamPaint: A Networked Paint Program
JamPaint’s Beginnings
Back in 1987, an AppleTalk network was installed in our workplace. We decided
to write a simple program to allow communications between different nodes of this
network. Our program, Transceive, used the NBP and LAP protocols to send text
messages between nodes.
When we got the program working and realized how easy it was to use AppleTalk,
we took Transceive one step further by rewriting it so that a user on one Mac could
draw one the screen of several other Macs. To do this, we created a message format
consisting of a message type identifier and data needed for the given type. We
implemented two different types of messages: Click and Drag. A click message consisted
of the Click message identifier and a Point. The drag message took a similar form. A
Click message was sent when a user initially clicked on the screen. Then, Drag
messages were sent every time that the mouse location changed, until the mouse button
was released.
Transceive was rewritten to operate in either master or slave mode. In master
mode, the click and drag messages were generated and broadcast over the network. In
slave mode, the messages were interpreted and the results were drawn on the screen.
We relied entirely on the LAP protocol for our transmissions. This caused a
problem because LAP has no facility for buffering incoming messages. Therefore, we
experimented with using a VBLTask to periodically check the message buffer and put
messages into our own buffer. This was a bad idea because the LAP protocol moves
memory and VBLTasks cannot cause memory moves. Our program crashed periodically
because of this. To solve our problem, we had to resort to polling the message buffer.
This caused us to lose messages on occasion, but it was better than periodic crashes.
Finally, we were able to have up to eight (a practical limit) Macs hooked up and
drawing on each other’s screens simultaneously. This was great fun for the staff at
work, so we thought about the feasibility of extending Transceive into more of a paint
program.
Transceive sat idle for months while we thought about what could and couldn’t be
done easily over a network. We wanted to keep message sizes small and had to have a
system where lost messages wouldn’t cause problems or crashes (with polling,
messages will get lost). We thought about using the ATP protocol to avoid message loss,
but decided that response time would be too slow for a real-time paint program.
Eventually, we decided that LAP was still the best choice. Then, we decided to write
JamPaint.
JamPaint is a simple, first come, first served Networked Paint program. To
control users, we have an array of environment records. Each slot in this array is
filled as unidentified users announce themselves (by sending an initial message) on the
network. Again, we decided to limit the number of users to eight to reduce traffic on
the network. JamPaint was written during a long weekend, but fixing bugs and adding
new tools has kept us busy for 3-4 months. Initially, we had decided to make JamPaint
a color paint program for the Mac II in addition to being networked. We wrote color
code, but never spent much time trying to get offscreen pixmaps to work correctly.
Hence, our screen updating was a mess on a Mac II. As soon as we get that code
working, we will re-release JamPaint for color use on the Mac II.
The Way it was Programmed
JamPaint was written in MPW Pascal with little regard for proper programming
technique. As a result, JamPaint variables are for the most part global variables. By
using many globals, we eliminated the creation and initialization of large stack frames.
Most of our procedure parameters are VAR parameters (to eliminate excessive stack
frame initialization). By adopting this style, we were able to decrease memory usage
and increase execution speed of our program.
JamPaint implements eight paint tools: paint brush, letters tool, eraser,
rectangle, filled rectangle, oval, filled oval, and a spray-can tool. Our palette includes
eight patterns which can be edited by double-clicking on them. JamPaint also includes
a unique pen sizing tool which allows horizontal and vertical pen size to be set
independently. We implemented the Pattern and Constraint keys found in most paint
programs and tried to emulate the standard paint program user interface.
Once again, we used the type-and-data format for sending our messages across
AppleTalk. To make JamPaint a fully networked paint program, we needed to create
many new message types. For instance, occurrences such as selecting a new tool had to
be transmitted over the network so that each node would know how to handle subsequent
drag messages. A problem with this approach is that a tool selection message may be
lost and drag message may be mis-interpreted. Fortunately, with only eight nodes,
this doesn’t happen often.
With JamPaint, we eliminated the master/slave modes used in Transceive and put
all nodes in a combined master/slave mode. What this means is that every message
incoming from a previously identified node will be executed. Also, every action
performed locally will be broadcast.
To handle the transmission and execution of command messages, we implemented
a simple command queue. Every incoming message is put into this queue, and
periodically, the first message in the queue is executed. To save ourselves the trouble
of duplicating this mechanism for local actions, and to simplify the testing of the
incoming message routines, outgoing messages are broadcast and then put into the
incoming message queue for execution locally. This way, all drawing gets handled by
the same set of routines. Our approach not only cut down on code size, but simplified
debugging and streamlined our message handling system.
How Things Get Drawn
JamPaint uses an offscreen bitmap to perform its updating and redraw. All
drawing commands are duplicated -- once for the screen, and once for an offscreen
bitmap. When an update is required, CopyBits is used to transfer the image back onto
the screen. This approach was made necessary when we decided that our window
wouldn’t always be the front window (because of DA’s, dialogs, etc.).
The Users on the Network
JamPaint treats each user as a paint environment. When a user first starts
using JamPaint, a message corresponding to their first action is broadcast over the
network. Any nodes that still have room in their environment tables (remember, only
eight entries) will notice that this message comes from an unidentified node. They will
then request that the sending node transmit its environment. The same happens in the
other direction. The new node will not recognize the already operating nodes and will
fill its own environment tables the same way.
The following data structure is used to keep track of a user environment:
{1}
UserTableRec = record
id:integer; { AppleTalk ID number }
time:longint; { time elapsed since last message sent }
theTool:integer; { current pain tool }
x,y:integer; { last pen coordinate }
theMode:boolean; { line or dot mode }
thePat:pattern; { penPat }
theClr:RGBColor; { color for Mac II }
hSize,vSize:integer; { pen size }
theFnum:integer; { font number }
theFsize:integer; { font size }
theFstyle:style; { font style }
splatRad:integer; { splatter radius for airbrush }
splatSpeed:integer; { splatter speed for airbrush }
end;
id : The AppleTalk node id of the user. This is needed to send our messages through the
LAP protocol calls.
time : The TickCount value of the last time a message from the user was executed. This
is used to eliminate “dead” users from the network.
theTool : is the current tool for the user.
x, y : The current pen position of the user.
theMode : The current mode of the user. JamPaint supports two drawing modes: line
and dot. In line mode, drag packets connect the current x,y position with a line to the
previous position. In Dot mode, no connection is made.
splatRad : The airbrush radius; double clicking on the spray-can tool will bring up a
dialog that allows the airbrush radius to be resized.
splatSpeed : The airbrush speed, or how many particles get sprayed each time a drag
message is received; this can also be changed by double-clicking the spray-can tool.
Packet Types
The following is the variant data structure used for the different message types in
JamPaint:
{2}
MSGType=(paintMode,alpha,settool,eraseall,setpat,setcolor,setpen,
setfont, setpos,drag,rectpck,setSplat,rqinfo,sendinfo);
LAPMsg = record
size :packed array [0..1] of byte;
theType :MSGType;
id :byte;
case MSGType of
paintMode:(mode:boolean);
alpha:(ch:char);{ type char in current font,size,style ,location
}
settool:(tool:integer); { select a tool }
setpat:(pat:pattern); { select a pattern }
setcolor:(clr:RGBColor); { select a color }
setpen:(px,py:integer); { set pen size to px,py }
setfont:(fnum:integer; { select font characteristics }
fsize:integer;
fstyl:style);
setpos:(mx,my:integer); { set current position to mx,my }
drag:(cx,cy:integer);{ mouse dragged from mx,my to cx,cy }
setSplat:(sRad:integer; { resize splatter tool }
sSpeed:integer);
rectpck:(rct:rect; { rectangle for TRect..TFOval }
optDown:boolean);
sendinfo:(info:UserTableRec);{ user info }
end;
All paint functions are executed by transmitting one of these message data
structures to the users on the network.
Implementing Tools That Require Waiting
Implementing the oval and rectangle drawing tools was more difficult than
implementing the other tools. This is due to the fact that these objects can be enlarged
and shrunken before actually being placed into a document. With messages coming in
from other machines, we had to address the problem of what to do while the user was
sizing an object. Also, we had to worry about how resizing was to be done on a remote
machine.
We decided that it would be a waste of code and time to resize objects on a remote
screen. After all, the finished object was what we were after. This left only the
problem of how to handle resizing locally while executing incoming messages. Our
solution was to do all resizing on the screen only. We would leave the offscreen bitmap
alone until the object was complete. All incoming messages get executed on the
offscreen bitmap and updated to the screen. Then, when the sized object is complete, a
message is transmitted to draw it both locally and elsewhere. This way, we reduce
AppleTalk traffic, eliminate the problem of missed messages, and avoid over-writing
of previous drawing.
Drag Packets
Whenever a user is holding down the mouse, drag messages are transmitted
(unless the rectangle or oval tools are selected). Drag messages contain only the new
location of the mouse. When a Drag message is received, it is sent to one of the drawing
routines depending on the current tool in the environment record for the user in
question. This local interpretation of commands was chosen because it results in
smaller AppleTalk packets, leading to better throughput.
JamPaint’s Future
Is there a future for JamPaint? We think so. Anyone who has played (yes, it’s
more like playing than painting) with JamPaint has to admit that it was fun. We plan
to enhance JamPaint by making it into a real paint program. Planned enhancements
include:
• Color
• Separate palette and tool windows with more patterns and tools.
• A full page (or possibly oversize) drawing surface.
• Selection tools.
• Special effects (fades, contrast enhancement, smoothing, etc.).
• Paint channels and privacy mode.
• Object-oriented drawing.
• Enhance compatibility with existing paint programs.
• Memos.
We would like to think that JamPaint is a step into the future. JamPaint is a new
kind of tool which not only allows many people to see the same piece of work, but to
also modify it. We are trying to bring real connectivity to the Mac by creating
multi-user software.
We ask that you do not change JamPaint and distribute it. It has been copy
righted. To maintain some sort of order, and minimize confusion, MindVision Software
will gladly accept suggestions for improvements to this program. If you would like to
add something to JamPaint, let us know what you’re doing and maybe we can work
together. Our About Window has room for plenty of names; just don’t expect to become
rich.
After Thought
We suggest that you run JamPaint on System 4.1 or 4.2 because JamPaint is
very slow running under System Software 6.0. We do not know why this is. We
haven’t had time to look into it yet.
We have also noted that Apple has implemented something similar to JamPaint in
HyperCard with their XCMD set that allows access to NBP and LAP AppleTalk protocols
from script.
Listing: MakeFile.p
##### JamPaint MakeFile #####
Pascal = Pascal
POptions = -s
.p.o á.p
Pascal -k “{PLibraries}” {default}.p
Rez “{RIncludes}”Types.r JamPaint.r -o JamPaint -c JPNT
SetFile -a B -d . JamPaint
JamPaint.code áJamPaint.p.o
Link JamPaint.p.o ∂
“{Libraries}”Interface.o ∂
“{PLibraries}”Paslib.o ∂
“{Libraries}”Runtime.o ∂
-o JamPaint.code
Continued in next frame