Reminder DA
Volume Number: 4
Issue Number: 3
Column Tag: Forth Forum
Time Manager Reminder DA 
By Jörg Langowski, MacTutor Editorial Board, Grenoble, France
Jörg Langowski is a bio-chemical engineer for a french concern and is a founding
and current board member of MacTutor. He can be reached at EMBL, c/o I.L.L. 156x,
Grenoble, Cedex, France F-38042. We encourage European authors to contact Jörg
directly about submissions to MacTutor.
“Using the Time Manager”
One thing I always wanted to have on my Mac is some utility that reminds me of
an appointment, no matter what I’m doing on the machine at any given time. On our
VAX, for example, there is this great program called Reminder which sits in the
background and will beep at you when your next appointment arrives.
In the new system (128K ROMs and up) there exists a set of routines called the
Time Manager. I thought it would be a good idea to show you their usage in developing a
Reminder utility similar to the one on the VAX. In the process, I’ll show you how to
work with modeless dialogs from a DA, how to call memory manager dependent traps
from a time manager task, and some more.
The Time Manager
The Time manager is used to execute a task at a predetermined time. Its routines
are documented in IM Vol. 4. All time manager tasks are described by entries in the
time mamager queue, where each element has the following format:
header TMtask 14 allot
0 CONSTANT qLink
4 CONSTANT qType
6 CONSTANT tmTask
10 CONSTANT tmCount
That means, the queue entry looks just like a regular queue element, with two
4-byte fields, tmTask and tmCount, relevant to our task. tmTask contains a pointer to
the routine to be executed, and tmCount is used by the system to count down the elapsed
time. When it has reached zero, the task at tmTask will be executed.
Three routines are provided by the time manager: InsTime, called with a pointer
to the queue element in A0, will insert this queue element into the time manager queue.
PrimeTime, with the queue element pointer in A0 and a 32-bit count in D0, will
schedule the routine specified in that queue element to be executed after count
milliseconds have elapsed. RmvTime, with a pointer to the queue element in A0, will
remove the queue element. The Mach2 interface to these routines is given at the
beginning of listing 1.
Fig. 1 Our Reminder DA lets Mac Widows post alerts for late night programmers!
Programming the Reminder utility
The strategy to follow seems very simple: we create a desk accessory that allows
us to enter a message text and a delay time, that desk accessory will then setup a queue
element with a pointer to a routine that displays an alert box containing that string.
Thinking about it, programming such a utility is not quite so simple. First of all,
the routine passed to the time manager for execution cannot be part of the desk
accessory. We’d like to enter appointments that are due in several hours or days (32
bits of milliseconds, unsigned, give a maximum possible delay of 49.7 days), and we’ll
most probably have left the application where the desk accessory was started by then.
Under MultiFinder this doesn’t matter, but under Finder, the desk accessory would
have been closed, leaving the task pointer point to anything but the desired code.
My first thought was to use a ‘stick-around’ desk accessory that would
automatically restart itself after an involuntary close, like I described earlier. But it
is actually much easier to put a copy of the routine to be executed into a small block of
system heap, leaving it independent of the DA. Then, one has to take caution that the
routine, once executed, will remove its own queue entry and dispose of its own
memory.
The second problem is that a time manager task may be executed at any time,
interrupting whatever else is being done at that moment. For the same reason as in the
case of VBL tasks, we may not call any routines from the time manager task that can
move things around in the heap. If we did, there would be a chance that the interrupt
occurs right in the middle of a memory manager operation, or while a handle hangs
around dereferenced somewhere.
The time manager task must therefore be rather simple. I use the same trick as I
described for the ‘stick-around’ DA, patching SystemTask from the scheduled routine.
The next time SystemTask is called (which should happen rather soon in any benign
application) the patch routine will be executed, which then can do the more involved
stuff such as drawing an alert box.
Now look at the first part of the example: the routine that is executed by the time
manager is contained between the markers mytask and mytask.end. The first bytes of
this block contain the queue element. The name string ‘Reminder’ follows for debugging
purposes. Some local variable space is reserved, where the original SystemTask trap
address, the alert ID and the alert message can be kept. The SystemTask patch routine
alertMe follows. It will display a note alert with the message in parameter ^0, unlock
the alert, remove the time manager queue element and dispose of the memory allocated
for itself.
Fig.2 After time has elapsed, an alert pops up!
alertMe is patched into SystemTask by the following routine, wakeMe, which is
the one first executed by the time manager. A pointer to wakeMe will be contained in
the tmTask field of the queue element at the beginning of the block. Since we do not
know yet where this routine will be located, we’ll have to install the pointer after
moving the code into the system heap.
As you see, the code between mytask and mytask.end is completely self-contained
(as long as the ALRT resource can be found). We may therefore move any number of
these little code blocks into the system heap and install their queue elements using the
time manager; each will be executed at its scheduled time and display its little message.
Appointment ‘objects’, so to say.
The desk accessory
The DA is used to install the tasks. It is a very simple DA which just draws up a
modeless dialog box with two editable text items. The dialog window will have the
driver reference number in its windowKind field, so that the system will handle mouse
down events in the close box and drag region. Only when the event is passed through to
the DA, windowKind will be temporarily set to 2 (dialog window), so that
IsDialogEvent and DialogSelect work correctly.
When the OK button in the dialog box has been clicked, the dialog handler will be
invoked. This routine converts the string in item 4 to a number, the delay in seconds,
and gets the message to be displayed after this delay (item 3). These parameters are
passed to install.wakeup, together with the alert ID. install.wakeup gets the wakeup
routine described above from the resource file and installs a copy of it in the system
heap. It saves a pointer to the wakeup routine in the tmTask field of the queue element,
stores the alert ID and the message string at their appropriate positions and then
schedules the routine for execution by calling InsTime and PrimeTime.
Things missing
The desk accessory described here is already quite useful ‘as is’. However, one
could include some improvements that I leave as an exercise for you (or me, for that
matter):
- Input of the appointment time in standard date format.
- Keeping a list of appointments that can be displayed and edited.
- Saving this list to a file every time it is changed; this file is then checked on
system startup and the pending appointments rescheduled automatically.
I wish you good luck with those experiments; now, some mail that I recently
received.
Feedback Dept.
“Dear Jörg,
I read your recent column in MacTutor with great interest and would like to
obtain the addresses for Serge Rostan and also for Winsoft. [Serge Rostan,
“TechnoPro”, rue Faraday, F-78180 Montigny le Bretonneux, France, phone (33) 1
30 45 26 62, and Winsoft, 34, boulevard de l’Esplanade, F-38000 Grenoble, France,
phone (33) 76 87 56 01 - JL].
We are in the process of completing a book on fonts for the Macintosh for
European and many other non-Roman languages. It will also show specimens of over
2000 bit-mapped and several hundred PostScript fonts.
If you can suggest any font vendors that do not advertise in US Macintosh
magazines, it would be very helpful [not off the top of my head, but I’ll look around -
JL].
As you may know, Apple refuses to sell European keyboards in the US, even to
certified developers. Users here must cultivate acquaintances overseas and buy them
indirectly. Do you know whether Apple tries to prevent the export of such keyboards
from Europe? We would like to be able to list a commercial source for such keyboards
in our book, but do not want to cause difficulties for anyone there. If they are readily
available for export, we would appreciate the name of a recommended source.
[You address a problem that I’ve encountered myself. There seems no way for
Macintosh developers in France to get other than French keyboards through the
developer program. However, there is at least one Apple dealer in Grenoble who sells
the US keyboard on request; we have several international research institutions here,
so there’s a market. I’ll send you addresses of some Apple dealers in Germany and
France that you might be able to order from.
I don’t think there are any export restrictions imposed by Apple on non-US
keyboards into the US (unless you re-sell them to Colonel Kh...). The problem most
probably is the bureaucracy that inevitably builds up in a large corporation, even one
like Apple that for a while tried to maintain some ‘non-conformistic hacker’ kind of
image. Which it is trying to forget at a rapid rate, I presume. Someone, it seems, must
have set a guideline that in country X only systems localized for X will be sold, with
few exceptions. The only way I get the most recent US system updates is through US
sources, too. Funniest of all: The MacII system that I finally got a week ago (yes, I love
it), has English documentation, US system disks (but 4.1, not 4.2, which is not
officially released at the time I write this), but no way can I get a US keyboard.
Probably the people at Apple France just wanted to be nice and ship me as many US
parts as possible, but then they had only French keyboards. What a nuisance to have all
the numbers on the top row...]
Lastly, Apple also tries to limit the availability of any Script Manager code or
fonts it has developed to the country for which it was developed! This seems strange
given that the Macintosh is touted as a Multinational machine. Apple has made Kanji and
Arabic available through APDA, but feels that they are sufficient for the testing
purposes intended. They have also developed a Chinese, a Hebrew and a Greek. The
Chinese is distributed from Hong Kong and the Hebrew and Greek are distributed from
Paris. I have written to the Paris office to no avail after a conversation with Mark
Davis at Apple, the creator of the Script Manager. Do you know of a way for someone in
the US to get these fonts from Paris? Actually, I am more interested in finding out the
arrangement of characters in the coding table than I am in the font, but the exact status
of ‘dead’ keys might be hard to discern accurately without actually having the font in
hand.
Best regards,
Tim Ryan, SourceNet, Santa Barbara, CA “
[You address a very timely issue, that’s why I have your letter printed
immediately without having the answers on hand yet. I’ll forward your mail with my
comments to Apple France. I’m actually quite optimistic that you can get the Script
Manager versions that you want. JL]
One comment to the letter from Peter Adamson, MT V4#1, p.11: The Pascal
equivalent to the Mach2 PAUSE is actually WaitNextEvent; much like PAUSE under
Mach2, WaitNextEvent will transfer control to the next MultiFinder task under
certain circumstances. If you have a very long event loop, you may try to intersperse
WaitNextEvents with event masks of zero, so that they’ll always return a null event.
That should transfer control to the next task under MultiFinder. Only the crucial
WaitNextEvent - the one with the BIG case statement behind it - would be called with a
non-zero event mask. This is all untested, so don’t blame me if it doesn’t work.
See you next month.
Listing 1: Appointment reminder using the time manager
\ ***** Time manager example - a ‘reminder’ utility
\ J. Langowski December 87
\ Strategy: write a driver that sets up a dialog which allows
\ to enter a time & message to display after that time. After
\ the appointment has been entered, the driver sets up a
\ time manager call for that appointment.
\ The time manager routine installs a SystemTask trap
\ patch which at the next occasion will draw an alert box
\ containing the message to be displayed.
\ Note that we have to use the patch rather than calling
\ the alert routine directly from our time manager task,
\ since we can’t be sure we’re not in the middle of a
\ memory manager operation when it is called.
only forth also assembler also mac
CODE InsTime ( tmTaskPtr | -- )
MOVE.L (A6)+,A0
_InsTime
RTS
END-CODE MACH
CODE PrimeTime ( tmTaskPtr count | -- )
MOVE.L (A6)+,D0
MOVE.L (A6)+,A0
_PrimeTime
RTS
END-CODE MACH
CODE RmvTime ( tmTaskPtr | -- )
MOVE.L (A6)+,A0
_RmvTime
RTS
END-CODE MACH
4ascii MENU constant “menu
4ascii PROC constant “proc
\ *** compiler support words for external definitions ***
: :xdef
create -4 allot
$4EFA w, ( JMP )
0 w, ( entry point to be filled later )
0 , ( length of routine to be filled later )
here 6 - 76543
;
: ;xdef { branch marker entry | -- }
marker 76543 <> abort” xdef mismatch”
entry branch - branch w!
here branch - 2+ branch 2+ !
;
: xlen 4 + @ ; ( get length word of external definition )
( *** driver header block *** )
0 CONSTANT drvrFlags
2 CONSTANT drvrdelay
4 CONSTANT drvrEMask
6 CONSTANT drvrMenu
8 CONSTANT drvrOpen
10 CONSTANT drvrPrime
12 CONSTANT drvrCtl
14 CONSTANT drvrStatus
16 CONSTANT drvrClose
18 CONSTANT drvrname
50 CONSTANT DAlength
\ *** compiler support words for DA and driver definitions