home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
GEMini Atari
/
GEMini_Atari_CD-ROM_Walnut_Creek_December_1993.iso
/
files
/
program
/
progem
/
gem11.asc
< prev
next >
Wrap
Text File
|
1987-10-10
|
21KB
|
398 lines
*PROFESSIONAL GEM*
by Tim Oren
Column #11 - GEM Hooks and Hacks
An Insider's AES Tricks
Welcome to the eleventh episode of ST PRO GEM, which is
devoted to exploring some of the little documented, but powerful,
features of GEM. Like the authors of most complex systems, the
GEM programmers left behind a set of "hooks", powerful features
which would aid them in enhancing the system later. I am going to
lay out a number of these methods which have served me well in
making creative use of the AES. You will find that most of them
concern the object and form libraries, since I was most involved
in those parts of GEM. There are probably many more useful tricks
waiting to be found in other parts of GEM, so if you happen onto
one, please let me know in the Feedback! Before you begin,be sure
to pick up the download for this column: GMCL11.C in DL3 of PCS-
57.
POWERFUL OBJECTS
The first four tricks all involve augmenting standard AES
objects. This is a powerful technique for two reasons. First,
you can take advantage of the regular AES object and form
libraries to draw and handle most of your objects, so that your
program need only process the exceptions. Second, you can use the
RCS to copy the special objects into multiple dialogs or
resources. These four tricks are Extended Object Types, User-
defined Objects, TOUCHEXIT, and INDIRECT. Let's look at each of
them in turn.
EXTENDED OBJECT TYPES
If you look at the AES Object Library documentation, you will
notice that the values for the OB_TYPE field in an object are all
32 or less. This means that a number of bits are unused in this
field. In fact, the AES completely ignores the top byte of the
OB_TYPE field. In addition, the RCS ignores the top byte, but it
also preserves its value when an object is read, written, or
copied.
This gives you one byte per object to use as you see fit.
Since the processing of an object or dialog is (so far) in the
hands of the AES, the best uses of Extended Types are in flagging
methods for initializing an object or handling its value when the
dialog completes.
For example, you might have several dialogs containing
editable numeric fields. The Extended Type of each numeric field
could be set to the index of the corresponding value in an array.
Then your application's dialog initialization code could scan the
object tree for such objects, pick up the appropriate value
from the array and convert it to ASCII, storing the result in the
resource's string area. When the dialog was finished, another
pass could be made to reconvert the ASCII to binary and store away
the results in the array. (Note that the map_tree() utility from
column #5 will scan an entire resource tree.)
Another application is to assign uniform codes to exit
buttons in dialogs. If you give every "OK" button an Extended
Type of one, and every "Cancel" button an Extended Type of two,
then you needn't worry about naming every exit object. Just
examine the Extended Type of the object returned by form_do, and
proceed accordingly.
The catch, of course, is that you have to find a way to enter
the Extended Type code in the first place. Version 1.0 of the
RCS, as shipped with the Atari developer's kit, makes no provision
for this. So you have your choice of two methods for creating the
first object with each Extended Type code.
First, you can dump out a C source of a resource, insert the
new type code by hand, and regenerate the resource with STCREATE.
Alternately, you could carefully modify the binary resource using
SID. You will probably want to reread the AES object manual, ST
PRO GEM #4 and #5, and use the C source as a guide when doing so.
In both cases, you should make things easy on yourself by creating
a one dialog resource with only a single object other than the
root. Version 2.0 of the RCS will let you directly enter an
Extended Type, but it has not yet been released for the ST by DRI.
Once you have created a prototype extended object by either
method, you can use the RCS to propogate it. The safest way is to
use the MERGE option to add the modified tree to the resource you
are building. Then copy the prototype object via the clipboard to
your dialog(s), deleting the extra tree when you are done. If you
are using several different extended objects, you can use MERGE
and clipboard copies to get them all into one tree which will then
become your own object library.
The second way of using RCS is easier, but more dangerous.
If you want to try the following technique, BACK UP YOUR RCS DISK
FIRST! Put simply, the RCS does not care what is in its dialog
partbox. It will make copies of anything that it finds there! This
gives you the option of using the RCS on ITS OWN RESOURCE in order
to add your customized objects to the partbox.
To do this, open RCS.RSC from the RCS. Since there is no DEF
file, you will get a collection of question mark icons. Use the
NAME option to make TREE5 into a DIALOG. Open it, and you will
see the dialog partbox.
Now you can use the MERGE technique described above to insert
your customized objects. Then SAVE the modified resource, and
rerun the RCS. Your new objects should now appear in the partbox.
If you added several, you may have to stretch the partbox to see
them all. You can now make copies of the new objects just like
any other part. (Note: DO NOT modify the alert or menu partboxes,
you will probably crash the RCS.)
USER-DEFINED OBJECTS
The one type of object which was not discussed in the earlier
articles on AES objects was G_USERDEF, the programmer defined
object. This is the hook for creating objects with other
appearances beyond those provided by the standard AES. By the
way, you should note that the G_PROGDEF and APPLBLK mnemonics used
in the AES documents are incorrect; the actual names as used
defined OBDEFS.H are G_USERDEF and USERBLK.
The RCS does not support the creation of G_USERDEF objects,
since it has no idea how they will be drawn by your program.
Instead, you must insert a dummy object into your resource where
you want the G_USERDEF to appear, and patch it after your
application performs its resource load.
You must replace the object's existing OB_TYPE with
G_USERDEF, though you may still use the upper byte as an Extended
Type. You must also change the OB_SPEC field to be a 32-bit
pointer to a USERBLK structure. An USERBLK is simply two LONG
(32-bit) fields. The first is the address of the drawing code
associated with the user defined object. The second is an
arbitrary 32-bit value assigned to the object by your application.
You can designate objects for conversion to G_USERDEFs in the
normal fashion by assigning them names which are referenced one by
one in your initialization code. You can also combine two tricks
by using the Extended Type field as a designator for objects to be
converted to G_USERDEF. Each tree can then be scanned for objects
to be converted. There is a short code segment in the download
which demonstrates this technique.
My usual convention is to define new drawing objects as
variants of existing objects, using the Extended Type field to
designate the particular variation. Thus an Extended Type of one
might designate a G_BUTTON with rounded corners, while a value of
two could flag a G_STRING of boldface text. When using this
technique, the RCS can be used to build a rough facsimile of the
dialog by inserting the basic object type as placeholders. The
existing OB_SPEC information can then be copied to the second
position in the USERBLK when the object is initialized.
One final note before moving on: There is no reason that the
USERBLK cannot be extended beyond two fields. You might want to
add extra words to store more information related to drawing the
object, such as its original type.
The AES will call your drawing code whenever the G_USERDEF
needs to be drawn. This occurs when you make an objc_draw call
for its tree, or when an objc_change occurs for that object. If
your user-defined object is in a menu drop-drop, then your drawing
code will be called any time the user exposes that menu.
Before getting into the details of the AES to application
calling sequence, some warnings are in order. First, remember
that your drawing code will execute in the AES' context, using its
stack. Therefore, be careful not to overuse the stack with deep
recursion, long parameter lists, or large dynamic arrays. Second,
the AES is NOT re-entrant, so you may not make ANY calls to it
from within a G_USERDEF procedure. You may, of course, call the
VDI. Finally, realize that drawing code associated with a menu
object may be called by the AES at any time. Exercise great care
in sharing data space between such code and the rest of the
application!
When your drawing code is called by the AES, the stack is set
up as if a normal procedure call had occured. There will be one
parameter on the stack: a 32-bit pointer to a PARMBLK structure.
This structure lies in the AES' data space, so do not write beyond
its end!
The PARMBLK contains 15 words. The first two are the long
address of the object tree being drawn, and the third word is the
number of the G_USERDEF object. You may need these values if the
same drawing code is used for more than one object at a time.
Words four and five contain the previous and current OB_STATE
values of the object. If these values are different, your drawing
code is being called in response to an objc_change request.
Otherwise, the active AES call is an objc_draw.
Words six through nine contain the object's rectangle on the
screen. Remember that you cannot call objc_offset within the
drawing code, so you will need these values! The next four words
contain the clipping rectangle specified in the active objc_change
or objc_draw call. You should set the VDI clip rectangle to this
value before doing any output to the screen.
The last two words in the PARMBLK contain a copy of the extra
32-bit parameter from the object's USERBLK. If you have followed
the method of copying an OB_SPEC into this location, these words
will be your pointer to a string, or BITBLK, or whatever.
When your drawing routine is done, it should return a zero
value to the AES. This is a "magic" value; anything else will
stop the drawing operation.
The download contains a sample drawing routine which defines
one extended drawing object, a rounded rectangle button. You can
use this procedure as a starting point for your own User Defined
objects.
PUT ANYTHING YOU WANT ON THE DESKTOP!
In ST PRO GEM #2, I described the use of the WF_NEWDESK
wind_set call to substitute your own object tree for the normal
green desktop background. If the tree you supply contains User
Defined objects, you can draw whatever you want on the desktop!
Some of the things you might try are free hand drawings imported
in metafile format from EasyDraw, or whole screen bit images
generated by Degas. If you do the latter, you will have to store
the entire image off screen and blit parts of it to the display as
requested.
In any case, remember that your desktop drawing code can be
called any time that a window is moved, so exercise the same care
as with a menu drawer. Also, be aware that making the WF_NEWDESK
call does not force an immediate redraw of the desktop. To do
that, do a form_dial(3) call for the entire desktop rectangle.
THE TOUCHEXIT FLAG
The TOUCHEXIT attribute is an alternative to the more usual
EXIT. When the TOUCHEXIT bit is set in an object's OB_FLAG word,
the form_do routine will exit immediately when the mouse button is
pressed with the cursor over the object. Your code can
immediately take control of the mouse and display, without waiting
for the release of the button. This method is used for generating
effects such as slider bars within otherwise normal dialogs.
The easiest way to code a TOUCHEXIT handler is to place a
loop around the form_do call. If the object number returned is
TOUCHEXIT, then the animation procedure is called, followed by a
resumption of the form_do (WITHOUT recalling form_dial or
objc_draw!). If the object returned is a normal EXIT, the dialog
is complete and control flows to the cleanup code.
There is one idiosyncrasy of TOUCHEXIT which should be noted.
When the AES "notices" that the mouse button has been pressed over
a TOUCHEXIT, it immediately retests the button state. If it has
already been released, it waits to see if a double click is
performed. If so, the object number returned by form_do will have
its high bit set. If you don't care about double clicks, your
code should mask off this flag. However, you may want to use the
double click to denote some enhanced action. For example, the GEM
file selector uses a double click on one of the file name objects
to indicate a selection plus immediate exit.
THE INDIRECT FLAG
If the INDIRECT bit is set in an object's OB_STATE word, the
AES interprets the 32-bit OB_SPEC field as a pointer to the memory
location in which the ACTUAL OB_SPEC is to be found. Like User
Defined objects, this capability is not supported by the RCS, so
you have to set up the INDIRECT bit and alter the OB_SPEC at run
time.
The value of INDIRECT is that you can use it to associate an
AES object with other data or code. The general technique is to
set up a table with a spare 32-bit location at its beginning.
Then, when initializing the application's resource, you move the
true OB_SPEC into this location, set the INDIRECT flag, and
replace the OB_SPEC field with a pointer to the table. The object
behaves normally during drawing and form handling. However, if you
receive its number from form_do or objc_find, you have an
immediate pointer to the associated table, without having to go
through a lookup procedure.
This technique works well in programs like the GEM Desktop.
Each G_ICON object is set up with INDIRECT. Its OB_SPEC goes to
the beginning of a data area defining the associated file. The
blank location at the beginning of file table is filled up with
the former OB_SPEC, which points to a ICONBLK.
You can also combine INDIRECT with TOUCHEXIT when creating
objects that must change when they are clicked by a user. For
instance, a color selection box might be linked to a table
containing the various OB_SPEC values through which the program
will cycle. Each time the user clicked on the box, the TOUCHEXIT
routine would advance the table pointer, copy the next value into
the dummy OB_SPEC location at the front of the table, and redraw
the object in its new appearance.
A programmer who wanted to follow a truly object-oriented
"Smalltalkish" approach could use the INDIRECT method to bind AES
drawing object to tables of associated procedures or "methods".
For instance, one procedure could be flagged for use when the user
clicked on the object, one when the object was dragged, one for
double-click, and so on. If the table structure was capable of
indicating that the true method was stored in another table, a
rudimentary form of class inheritance could be obtained.
INSTANT CO-ROUTINES
We turn to the AES event and message system for this trick.
While some languages like Modula 2 provide a method for
implementing co-routines, there is no such capability in C.
However, we can effectively fake it by using the AES event
library.
As already seen in an earlier column, an application can
write a message to its own event queue using the appl_write call.
Usually, this is a redraw message, but there is nothing to prevent
you from using this facility to send messages from one routine in
your program to another. To set up co-routines using this method,
they would be coded as separate procedures called from the
application's main event loop.
When one of the co-routines wanted to call the other, it
would post a message containing the request and any associated
parameters into the application's queue and then return. The main
loop would find the message and make the appropriate call to the
second co-routine. It it was necessary to then re-enter the first
co-routine at the calling point, the original message could
contain an imbedded reply message to be sent back when the request
was complete. A simple switch structure could then be used to
resume at the appropriate point.
There are two potential problems in using this method. The
first is the limited capacity of the application event queue. The
queue contains eight entries. While the AES economizes this
space by merging redraws and multiple events, it cannot merge
messages. Because of this limit, you must be extremely careful
when one message received has the potential to generate two or
more messages sent. Unless this situation is carefully managed,
you can get a sort of "cancer" which will overflow the queue and
probably crash your application.
The second danger involves race conditions. Message sent by
the application are posted to the end of the queue. If other
events have occurred, such as mouse clicks or keyboard presses,
they will be seen and processed ahead of the application generated
message. This implies that you cannot use this method if the
program must complete its action before a new user generated event
can be processed.
THAT'S ALL FOR NOW
Hopefully these hints will keep you profitably occupied for a
while. ST PRO GEM number twelve will return to the topic of user
interfaces. Reaction to the first article on this subject was
mostly positive, but a lot of folks wanted to see real code as
well. In response to your feedback, there will also be code for
implemented your own "mouse sensitive" objects which highlight
when the cursor touches them. This will be presented as part of
an alternate form manager.
UPDATE: ATARI ST
I have recently gotten more information on some topics
mentioned in earlier articles. These notes will bring you up to
date:
A number of developers reported that they were unable to get
the self-redraw technique described in ST PRO GEM #2 to work.
This is usually due to a bug in the appl_init binding in Alcyon C.
The value returned from the function, which would normally be
assigned to gl_apid, is coming back as garbage. To work around
the problem, declare EXTERN WORD gl_apid; in your program and DO
NOT assign the value from appl_init. The binding WILL make the
assignment. A tip of the hat to Russ Wetmore for this report.
The last column mentioned that turning off the clip
rectangle while drawing graphics text will speed things up. It
turns out that the VDI will also run at the non-clipped speed if
the ENTIRE string to be written is within the current clip
rectangle. To compound the problem, there is a one-off bug in the
detection algorithm for the right edge. That is, the clip
rectangle has to be one pixel BEYOND the right edge of the text
for the fast write to work.
The Feedback in ST PRO GEM #10 mentioned that there are known
bugs in the Alcyon C floating point library. In fact, this
library has been replaced with a new, debugged version in recent
shipments of the Toolkit. If you need to use floating point and
have run into this bug, you should be able to get an update from
Atari. Also, check the Atari Developer's SIG (PCS-57) for a
possible download.
In addition, it turns out there is an undocumented feature in
Alcyon C which allows you to imbed assembly code in-line. Try
using:
asm(".....");
where the dots are replaced with an assembly instruction. You get
one instruction per asm(), one asm() per line. Thanks to Leonard
Tramiel for both of the above tidbits.