home *** CD-ROM | disk | FTP | other *** search
- *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.
-