FKeys, Events
Volume Number: 3
Issue Number: 9
Column Tag: Forth Forum
FKeys & Events 
By Jörg Langowski, MacTutor Editorial Board, Grenoble, France
FKEYs and Events
The example we’ll deal with this time has some history. On a national bulletin
board here in France, Calvacom, which has a large section of its activity devoted to the
Mac, a proposal was made to write an FKEY that would paste one of a number of
character strings into the application currently under use. Thus one would have an
easy way to enter ‘boilerplate’ phrases or often used commands into text processors,
terminal emulators etc.
I thought this would make a nice example for the Forth column, e specially since
we hadn’t had an FKEY dealt with yet. In the way of writing this, I discovered several
things. First, the simple strategy that comes to mind - taking a string that was saved
away somewhere and posting key down events for one character after the other - works
only in certain cases, so one has to go a more complicated way (as you’ll see soon).
Second, somebody had of course in the meantime written the FKEY under question - in
some other language. Nevertheless, still a good Forth example.
Implementing an FKEY
Everyone of you has probably already used one or the other function key, that is,
command-shift-number combination. Some might even have written one, its
implementation being rather simple. The FKEY resource is a simple subroutine with
its entry point at the beginning of its code. It is called from the routine that handles the
keyboard. If that routine detects a command-shift-number combination, it will look
for the FKEY resource corresponding to the number, and if such a resource is present,
load and execute it.
No parameters are passed to the FKEY routine on the stack; we’ll simply set up a
local Forth stack on entry to the routine, using LINK, save all registers away, then call
the main body of the FKEY, and finally restore the registers and UNLINK A6. This is the
standard glue routine for writing kernel-independent Mach2 code which you might
already be familiar with. See listing 1.
[One remark with regard to the listing here. This program has been written in
Mach2.12 which has a different Toolbox call mechanism than earlier versions. In
particular, it expects D4 to point to a common stack low in memory which can be used
for all toolbox calls. This stack, and D4, are set up in the Mach2 system. Therefore, to
write kernel-independent code, one has to use the old CALL mechanism again. This word
is available under a new name: (CALL). If you have an older Mach2 version, simply
replace all occurrences of (CALL) with CALL, or redefine the word].
The main body of the FKEY will first look whether it has been called from a bona
fide application or whether the topmost window is a dialog or desk accessory. In the
latter two cases, the FKEY will do nothing but beep and return. Since the FKEY itself
might display a dialog (for editing the text strings), it should not be allowed to
redisplay its own dialog when it is already active. Checking whether any dialog is
already in place is one way of achieving this; you might think up some more
sophisticated ways and change the program accordingly.
If the routine has been called from within a good environment, it will wait for the
next key pressed. If the key is a number key, the appropriate message will be posted
(see later how this is done). If it is the ‘e’ key, a dialog will be displayed for editing
the text strings. In all other cases the routine will simply beep and return.
Posting Events
The first problem that we encounter here is how to post key down events that
correspond to the text string into the event queue. Very simple, you’ll say, do the
following:
: post.char ( char -- ) 3 swap call post.event drop ;
: post.string ( string -- )
count 0 DO dup i + c@ post.char LOOP drop ;
where 3 is the event code for a key down event, and the event message simply contains
the character code in the low byte, and no key code (=0). Leaving out the actual key
code has so far caused no problem in any case that I tested.
Well, the simple example works. In a way. If the string that is posted is longer
than 15 characters, Mach2 will simply beep because it cannot accept more keydown
events before switching tasks. This doesn’t have anything to do with the actual length of
the event queue or the maximum number of allowed events. It is the application that
can’t deal with so many ‘keystrokes’ coming in rapid succession. In addition, of course,
if we don’t check the event queue before posting an event, we take the risk of losing it
if the maximum number of allowed events (20 by default) has been reached.
This last number can be changed by editing the boot blocks. However, one can
think of a more elegant solution that makes the whole process completely independent
of the maximum length of the event queue. We allow the posting of a character only if
the GetNextEvent routine fetches a null event. In that case we can be sure that not too
much activity is going on and the key down event will be handled correctly.
But how do we handle this process? We cannot do some sort of waiting loop within
the FKEY, since GetNextEvent will only be called by the application after we have
returned. Therefore the FKEY must install a short background routine that monitors
the activity of GetNextEvent and posts key down events from the string to be
transmitted each time a null event is received. Here the JGNEFilter system global,
already used for several examples in MacTutor (V1#9, Bob Denny, and V2#6, JL),
comes in very handy. To post a message into the event queue, the post.string word as
defined in the listing saves the string and its length away into a defined place and then
changes the JGNEFilter vector to point to a custom routine that will post the
keystrokes.
The custom routine, GNE.glue, calls GNEIntfc through our standard glue code,
which you really know by now. GNEIntfc looks at A1 (which points to the event
record), and if a null event is about to be transmitted, it will take the next character
from the saved message string and post a key down event under the following
conditions:
1. A certain delay has expired since the last character was posted (2 ticks in my
example) and
2. The number of pending events is less than a predefined number (here 10).
When the end of the string has been reached, the filter routine resets the
JGNEFilter vector to its old value.
Using the method just described, one can transmit strings of arbitrary length to
applications. Which is what we wanted.
Editing the text strings
If an ‘e’ is pressed after the FKEY, we’ll display the editing dialog. The Rmaker
source for this dialog (ID=2000 for DLOG and DITL, you may want to change this) is in
listing 2. It simply consists of 10 editable text items, one OK button and some static
text.
The associated Forth word, edit.messages, does a number of things. It first tries
to open a resource file named ‘FKEY.messages’ and creates a new one if it doesn’t
succeed. By standard, this will be kept in the system folder; leave it there. You can
prepare any number of files containing standard messages by renaming them.
The routine then gets the dialog with ID 2000 (it is a shame that FKEYs cannot
‘own’ resources like DRVRs etc. can). If the dialog can’t be found, it beeps and returns;
otherwise, it displays the strings from the FKEY.messages file in the boxes for the
editable text items. They are 10 individual string-format resources type ‘bplt” with
ID=3 to 12 (corresponding to the dialog items 3 to 12). If the resources cannot be
found (i.e. the file has just been created and is empty), they are created and initialized
with whatever was in the EditText items of the dialog box. In my case, I used the texts
‘Message x’, but you might just leave the strings empty. If the bplt resources are
there, they replace the EditText items.
A ModalDialog is then called, allowing the strings to be edited. The only enabled
item is the OK button, which returns to the Forth routine. Then, the changed text
strings are written back to the bplt resources in the FKEY.messages file, the resource
file updated and the dialog disposed of.
Finally, post.message is the routine that is called when a number key is pushed
after the FKEY. It gets the bplt resource corresponding to that number and calls
post.string (described above) to transmit the string via the GetNextEvent filter
routine.
make.fkey creates a file ‘fkey.text’ that contains the FKEY resource and has the
correct type and creator to be opened from the FKEY installer (from Quick and Dirty
Utilities, Dreams of the Phoenix, Inc., P.O.Box 10273, Jacksonville FL 32247,
(904) 396-6952). This file is then packed together with the dialog resources into
one file. The FKEY may be installed with the installer; the DLOG 2000 and DITL 2000
must be copied with ResEdit.
Some last notes
As you’ve seen, yet another Mach2 version has been released, v2.12, making
v2.11 obsolete only two weeks after I received it. No BIG modifications, only minor
ones.
I received some comments from a reader, James Merkel, who mentioned that it
would be a good idea for Palo Alto Shipping to release the source for the multitasking
kernel as well, now that we have the source of the I/O task. The second comment was to
release bug fixes in MacTutor as well as on GEnie, for those not having access to the
GEnie Roundtable (including myself). Very good suggestion. PAS, do you listen?
Last, the bulletin board that I mentioned, Calvacom, is now accessible via Tymnet
from the US. If you’d like to see what’s going on over here or want to leave me some
mail, connect yourself to a Tymnet line and type ‘CalvaCom’ at the login prompt, then
‘nouveau’ when it prompts you for your access code. You can subscribe with your
credit card, as usual. Speaking some French helps, of course, but most anybody on the
board will understand English. Connect charges (overseas rates included) are of the
order of $25 per hour. I’d like to see your feedback in my mailbox!
{1}
Listing 1: Mach2 FKEY for text glossary
( *** Function Key example. JL June 1987 *** )
ONLY FORTH ALSO ASSEMBLER ALSO MAC
4ascii QD15 CONSTANT “qd15
4ascii bplt CONSTANT “bplt
4ascii DITL CONSTANT “ditl
4ascii DLOG CONSTANT “dlog