Vital Signs
Volume Number: 7
Issue Number: 7
Column Tag: C Forum
By Ted Johnson, President, T. Bear Software
Note: Source code files accompanying article are located on MacTech CD-ROM orsource code disks.
[Ted Johnson graduated from MIT with a BSEE in June of 1987 and works for
Hewlett-Packard now in Santa Clara]
MacTutor is a great magazine, but it can be a bit overwhelming for the beginning
Macintosh programmer. This article is an attempt to change that.
I did a fair amount of programming during my undergrad years at MIT in all
sorts of languages, including C, Pascal, Scheme, FORTRAN, assembly language, and
microcode. Most of it was on fairly large and robust multi-user systems, so I didn’t
have to worry about a bug in my program crashing the whole machine. Such is not the
case with the Macintosh.
I just began programming on the Mac last June, and quickly discovered that fatal
bugs in my program could be fatal to the Mac’s operating system as well. I found that
the best way to get something to work was to look at the source code for someone else’s
program who got it to work. I like to call this “leveraging off of the existing
technology”.
To make life easier for beginning Macintosh programmers, I have come up with a
THINK C™ desk accessory example, Vital Signs, which shows how to do just about
everything that you would ever want a DA to do. I hope that this desk accessory can
shorten the learning curve for beginning programmers, but also demonstrate some
useful techniques to the more seasoned programmers out there. I tried to follow all of
Inside Macintosh’s commandments, including checking all of the Toolbox routines’
return codes for errors. Feel free to cut and paste pieces of Vital Signs code into your
own DA. Don’t reinvent the wheel; leverage off of the existing technology!
What does Vital Signs do?
Vital Signs puts up a window with two bar graphs and an animated heart and
displays statistics about your Macintosh. As you can see in Figure 1, it is time for me
to clean some of the files off of my hard disk!
Figure 1. The Vital Signs display.
The heart beats (audibly!), and the various displays (time, RAM usage, etc.) are
updated automatically, to reflect the current state of your Mac. Since I use the
SysBeep() trap to generate the heartbeat sound, you can use the Control Panel DA to
adjust the volume.
It is kind of interesting to run Vital Signs when you are using different
programs. Some programs (did someone say Excel?) are real memory hogs, whereas
others are surprisingly frugal.
The RAM and default disk displays are updated every ten seconds; the time display
is updated every second. None of the other information is updated, because there is no
way it can change without closing Vital Signs.
The source code for Vital Signs will show you how to do the “standard” DA stuff,
i.e.:
-put up a window, and draw text and graphics in it
-compute owned resources
-put up a menu
-display an “About...” box (like mine, in Figure 2)
Figure 2. Our “About...” box.
-respond to update, activate, keydown, autokey, and all other events
In addition, Vital Signs shows you how to do some things I’ve haven’t yet seen in
MacTutor, i.e.:
-calculate and display the amount of disk space (used and total) on the default disk
drive
-find the amount of total RAM, and the amount of used RAM
-find the System and Finder version numbers
-use the GetDateTime(), IUDateString(), and IUTimeString() traps to find the
current date and time.
Hunting for version information
One of the first problems I had to solve was how to determine the version number
of the System and Finder system files from inside a program.
I had a feeling that there was a version number text string buried in an obscure
resource somewhere in the System and Finder system files, so I went hunting with
ResEdit. Sure enough, I found what I was looking for.
As a matter of fact, I found several resources with version information. The
‘MACS’, ‘FNDR’, and ‘STR ‘ resources which have ID = 0 all contain version
information.
Great! So I can just pick one of these resources and display its text string?
Unfortunately not. It turns out that Apple has been inconsistent over the various
System/Finder pairs (see Figures 3 and 4).
Figure 3. Resources in the System file which contain version information.
System/Finder 4.1/5.5 have a ‘FNDR’ and ‘STR ‘ resources (respectively) that
are the perfect length to be displayed in Vital Signs’ window. However the next release
(System/Finder 4.2/6.0) was incredibly verbose, so I had to abandon my idea. Oh
well.
Figure 4. Resources in the Finder file which contain version information.
I ended up just using the new SysEnvirons() trap to display the System file
version number. But at least now we know what resources to look at if we ever want to
identify the version number of a System or Finder system file without booting off of it.
General DA Information
In THINK C™, all DAs have a main() which takes three arguments:
/* 1 */
main(ioPB, theDCE, n)
cntrlParam *ioPB;
DCtlPtr theDCE;
int n;
Don’t be intimidated. Things aren’t as complicated as they appear. ioPB is our
DA’s I/O Parameter Block. It is used by the Mac operating system to tell our DA which
event it got, and which menu item was selected from our DA’s menu(s). It is also used
to tell our DA when it is time to perform some periodic action (e.g., update a clock
display).
theDCE is our DA’s device control entry. By setting certain flags in this
structure, we can ask the Mac operating system to notify our DA every x ticks (a tick is
1/60 of a second). This is useful for doing things like updating a clock display. theDCE
is where we ask to be “woken up” every x ticks. ioPB is where we receive the “wake
up” call. We also use theDCE to store our WindowPtr and our global data.
n is our DA’s entry point selector. Desk accessories have five different entry
points (open, prime, control, status, and close). The value of n is used to decide which
one of them was called.
To correspond to these five entry points there must be five subroutines. Two of
them (prime and status) are rarely used by DAs, but they must exist nonetheless.
What DAs Can’t Do
Desk accessories cannot access QuickDraw global variables. This means that you
can’t use the predefined Patterns (ltGray, dkGray, black, etc.), or the global variable
screenBits, among other things.
Hmm, if we can’t access screenBits (which contains information about the
physical size and resolution of the Mac’s monitor), how is Vital Signs able to display
the resolution of the screen?
The trick is to grab the portBits of the current port, and use its “bounds”
information. The code fragment which does this is shown below:
/* 2 */
GrafPtr oldPort;
BitMap ScreenInfoBitMap;
GetPort(&oldPort);
ScreenInfoBitMap = (*oldPort).portBits;
Now we can get the horizontal and vertical resolution from the values:
/* 3 */
ScreenInfoBitMap.bounds.right
and
ScreenInfoBitMap.bounds.bottom
Defining your own Patterns
I used a black Pattern to fill in the bar graph for the Vital Signs RAM and disk
space displays. I had to define my own because I couldn’t access QuickDraw’s predefined
black global variable. It is quite easy to define your own Patterns.
According to Inside Macintosh I-145, “A pattern is a 64-bit image, organized as
an 8-by-8-bit square, that’s used to define a repeating design (such as stripes) or
tone (such as gray). QuickDraw provides [five] predefined patterns in global
variables named white, black, gray, ltGray,and dkGray. Any other 64-bit variable or
constant can also be used as a pattern.”
The following code fragment shows how I created a black pattern:
/* 4 */
Ptr myBlackPattern;
/*First allocate some space.*/
myBlackPattern = NewPtr(sizeof(Pattern));
/*Now create the pattern.*/
StuffHex(myBlackPattern, "\pFFFFFFFFFFFFFFFF");
Creating a non-black pattern is a lot easier than it looks. Instead of playing
around with hex codes, just use ResEdit and create a ‘PAT ‘ resource in a scratch
resource file. Now you can edit your pattern graphically! To see what the16 digit hex
representation for this pattern is, simply close the ‘PAT ‘ resource and then do an
“Open General” on it. Voila!
You can also just look at the ‘PAT ‘ resources of the SuperPaint application.
Owned Resources Tutorial
A desk accessory can have non-code resources, just like an application. Your DA
certainly doesn’t have to use resources, but it often makes life easier if you do.
A DA’s resources are said to be “owned” by the DA. A resource is “owned” if it
has a negative resource ID number.
When you install a DA, the Font/DA Mover moves that DA’s ‘DRVR’ resource into
the System file. If the DA has any owned resources, those are moved into the System
file also. If the DA has resources but they aren’t owned, then they will be left behind
and the DA will soon die an ugly death.
A resource ID is a 16 bit integer. For owned resources, the top two bits of the
resource ID are always 1. Since the top bit is interpreted as a sign bit, this means the
resource ID is a negative number. The bottom 5 bits (the base ID) of the resource ID
number are constant.
In Vital Signs I #define’d the base id of my resouces to be the value decimal 1
(which is 00001 in binary). I could have chosen any value between 0 (0b00000) and
32 (0b11111), inclusive.
Desk accessories are “drivers”, which means that they have a DRVR number.
The DRVR number of a desk accessory is determined by the Font/DA Mover when you
install the DA into your System file. Thus, the same desk accessory on two different
Macs could have different DRVR numbers.
Every desk accessory that is created with LightspeedC™ gets a default DRVR ID of