Hey You
Volume Number: 5
Issue Number: 5
Column Tag: Advanced Mac'ing
The Notification Manager 
By Steven Sheets, Contributing Editor, Haufman Estates, IL
Note: Source code files accompanying article are located on MacTech CD-ROM orsource code disks.
Hey You: The Mac Notification Manager
More and more, code is being written for the Macintosh that runs in a
multitasking environment. At first, an application only had to share space with Desk
Accessories. Then came MultiFinder, and multiple applications started running at the
same time. Along the way, Macintosh programmers started creating various low level
routines to do tricks with the machine (INITs that load routines into system memory,
vertical retrace routines, time manager ‘wakeup’ routines, redirection of normal
traps, etc.). At any given time, half a dozen or more code segments may be running in
addition to a single ‘normal’ application.
These code segments have to work in a very restrictive environment. Some are
not allowed access to global data. Many can not make Toolbox calls that move or purge
memory. Others can not even have control of the CPU for longer than a tick (60th
second). Even a normal application cannot place an alert where the user can see it if it
is running in the background under MultiFinder. How can these code segments inform
the user of some event or action with these limitations?
Enter one of newest Toolbox managers from Apple; the Notification Manager. The
Notification Manager allows the code to post a notification request that, in some
method, notifies the user of something. A Notification Request can consist of an Alert
Notification (displaying a text message), an Audible Notification (playing a ‘snd’
sound), an Icon Notification (displaying a small icon, or ‘SICN’, in the upper left
corner on top of the Apple icon) or some combination.
Using the Notification Manager, a terminal program running in the background
can inform the user when a download is complete. No matter what is running at the
time, when the request is processed, the Notification Alert will appear on top of all
windows. Using the Manager, an Appletalk Mail program could inform the user of new
mail with a flashing mail box icon.
The Notification Manager handles the processing of all requests (playing the
sound, clicking on the alert, flashing the icon, etc.) asynchronously. The code segment
would use two new Toolbox traps to inform the manager of a request. Neither of these
two calls process the request, they simply add or detach records from the Notification
Manager list. Because of this, they neither move nor purge memory, and can be safely
called at any time, even inside of a low level interrupt procedure.
The Notification Manager was implemented in the System Software 6.0. The new
Toolbox traps will not function on systems prior to 6.0. Portions of the System
Software 6.0 already uses the Notification Manager. By flashing a small Clock icon,
The Alarm Clock use the manager to signal that the alarm has gone off. The Print
Monitor also uses the Notification Manager to signal printing information.
The Notification Manager
The Notification Manager maintains a queue (list) of requests that need to be
processed. Whenever SystemTask or WaitNextEvent is called, the Notification Manager
checks the queue for an unprocessed request. First the Notification Request, if there is
one, is processed, then the response routine, if there is one, is called. The response
routine can be the default routine (simply removes the request record from the queue)
or the code segment can define it’s own response routine to do whatever it wants.
The Notification Request is a standard Macintosh Queue record expanded for the
Notification Manager. The record has the following format:
{1}
NMRec = record
qLink: QElemPtr;
qType: INTEGER;
nmFlags: INTEGER;
nmPrivate: LONGINT;
nmReserved: INTEGER;
nmMark: INTEGER;
nmSIcon: Handle;
nmSound: Handle;
nmStr: StringPtr;
nmResp: ProcPtr;
nmRefCon: LONGINT;
end;
The qLink pointer points to the next request in the linked list and should not be
changed by the code segment. Neither should the nmPrivate or nmReserved elements
be changed by the code; the manager handles them. The qType (type of queue) of a
Notification Queue is the value 8, the integer value of ORD(nmType). The qType
element needs to be set to 8 before the request is added to the queue.
The nmStr pointer points to the text message that would be displayed by the
Notification Manager in an alert. If the pointer is set to nil, no alert will be displayed.
The nmSound is a handle to a sound record (ie. ‘snd ‘ resource). The Sound Manager
will play it before the alert is displayed. If nmSound is set to -1, the “System Error”
sound is used. If it is set to 0, no sounds are played. The nmSIcon is the handle to the
small icon (32 bytes, 16 by 16 pixel bitmap) that the Notification Manager will flash
in the upper left corner of the screen on top of the Apple icon. Usually this small icon
is stored as a resource of type ‘SICN’. A nil value for nmSIcon signifies no flashing
icon. The nmMark element indicates who generated the notification event. An
application should set nmMark to 1. When under MultiFinder, a diamond mark will be
placed in the Apple menu next to the application’s name. If the request was generated
by a Desk Accessory, nmMark should be set to the refNum of the DA. This will place a
diamond mark next to the DA’s name. All other types of code segments (Drivers,
patches, etc.) should set this value to 0.
The nmResp is the pointer to the procedure called after the request is processed.
If nmResp is set to 0, no procedure is called. If it is set to -1, a special response
routine is called that removes that request from the queue. This response routine does
not dispose of any memory (the string, the sound, the icon, or the request itself). If
the code segment wants to define it’s own response routine, nmResp is set to the
pointer of that routine. In most cases (main exception is Icon Alerts), the response
procedure should remove the request from the queue immediately. The response
procedure has the following format:
PROCEDURE MyResponse (nmReqPtr : QElemPtr) ;
The response routine will be called by the Notification Manager during a
SystemTask or WaitNextEvent call. Thus it is safe to move or purge memory. It is also
safe to do any I/O (file, resource, serial, Appletalk). If the routine is suppose to
change some global variables of an application, it must first make sure that the A5 is
correct. For example, when under MultiFinder, the notification may occur when some
other application’s A5 is set. The nmRefCon may come in handy to store the value of
A5. The nmRefCon element can be used by the code segment for what ever purpose the
code segment wants. A handle to a data structure, a pointer to a Boolean flag, or the
correct A5 value could be stored there.
Apple states that the response routine should not do “user interface” type of
drawing. Instead the routine should set a flag so that the application knows when it
should do the drawing. To change a Boolean flag, it is simpler to stuff a pointer to the
flag into nmRefCon than it is to change A5 back and forth.
There are only two routines that exercise the Notification Manager, NMInstall
and NMRemove. NMInstall is passed the pointer to the notification request that is to be
installed into the queue. It returns noErr (0) if successful. NMRemove is passed a
pointer to a notification request already installed in the queue. If it is successful in
removing that request, it returns noErr also. Unless a request wants an Icon to be
flashed for awhile, the NMRemove routine should be called after a notification request
has been processed. Both calls are register based. The following are the Inline Glue
routines needed to use the call from Pascal and the Assembler information:
{2}
FUNCTION NMInstall (nmReqPtr : QElemPtr) : OSErr;
inline $205F, $A05E, $3E80;
_NMInstall ($A05E)
On entry: A0 pointer to the NMRec
On exit: D0 result code
FUNCTION NMRemove (nmReqPtr : QElemPtr) : OSErr;
inline $205F, $A05F, $3E80;
__ NMRemove ( $A05F )
On entry: A0 pointer to the NMRec
On exit: D0 result code
The Sample Program: HeyYou
HeyYou uses the Notification Manager to show 3 different effects. The NotifStr
routine demonstrates the simplest effect, displaying a text message to the user. The
routine uses two global variables, a string to hold the text, and a NMRec to hold the
notification request. The request nmResp is set to -1 so that the Notification Manager
will automatically remove this request from the queue after it has been done. Since the
data is stored in global variables, no other clean up has to be done.
The NotifSnd routine demonstrates a slightly more involved request. It plays a
sound (‘snd ‘ resource) and displays a text message. No global variables are used in
this example. Instead memory is allocated to hold the request and the test string. The
nmResp is set to a pointer to the MySndResponse so that when the request is done, the
MySndResponse routine can be called. MySndResponse can remove the request from
the queue, release the sound from memory, dispose of the memory holding the string,
and finally dispose of the memory holding the request itself.
The method used in NotifSnd works well for code segments that have no access to
global memory space such as Desk Accessories, INITs, etc. This method also has the
additional advantage of allowing more than one request to be added at the same time.
This could not be done with NotifStr since each request shared the same global
variables. As long as MySndResponse is locked in memory somewhere, the method used
in NotifSnd will work.
The last example, NotifIcon, demonstrates the use of icons by the Notification
Manager. It displays a text message, blinks a small icon in the upper left corner of the
screen and marks HeyYou as the application that caused this request. Usually the code
segment wants the icon to flash in the corner until the user has done something in
response. For example, the Alarm Clock does not remove it’s icon until the alarm is
turned off. To keep the Icon in use until the appropriate time, the notification request
can not be removed from the queue or the flashing effect would be lost. Thus no
response routine is specified. Instead the application removes the request (and
flashing icon) when the application quits, when it resumes after a switch under
MultiFinder, or when the user selects the “Remove Icon” menu item. In HeyYou, one
example sets a timer going so that NotifIcon is processed 60 seconds after the menu
item is selected. Since HeyYou and the timer work in the background under
MultiFinder, this will demonstrate how the Notification Manager works when some
other application is running. Notice the mark that appears next to the program name
HeyYou in the Apple menu.
Beyond...
Here are some last ideas for Notification Manager routines/utilities that can be
written:
1) An example that places a Notification Request in low System memory, and starts a