
**************************************************************************
*
* GEMXTEND.DOC - Descriptions of extensions made to GEM bindings since
*                the original TOS 1.0 release.
*
*  09/02/91    - v1.7
*                Added new function vgd_detect() to detect GDOS.
*
*  07/18/91    - v1.6 (Has it really been a year since the last release?)
*                Added new VDI function vdicall(), which allows you to
*                "roll your own" binding to a VDI function that's not
*                directly supported in vdifast.a.  This ought to please
*                all the GDOS-using folks.  This is documented at the
*                very end of this doc file.
*
*  06/29/90    - v1.5
*                Added function v_gchar() to output a single text char.
*
*  05/26/90    - v1.4
*                Included documentation on the new calling standard,
*                and the bindings that currently support it:
*                   evnx_multi()
*                   frmx_center()
*                   winx_get()
*                   winx_calc()
*
*  09/06/89    - v1.3
*                This document is new with this release, and contains
*                revisions through TOS 1.4.
**************************************************************************

This document is divided into two sections.  Section 1 documents the 
changes and additions Atari has made to the GEM routines in TOS.  Section 2 
documents alternate calling standards to the existing functions.  These
alternate bindings provide more efficient ways to call AES functions.


**************************************************************************
*
* Section 1 - Changes Atari has made to GEM/TOS.
*
**************************************************************************

This section describes extensions Atari has made to TOS/GEM since the
original TOS 1.0 operating system.  When Atari adds new VDI/AES functions,
appropriate bindings will be created and documented here.  Note that some
of the functions listed here have been available since TOS 1.0, but Atari
neglected to document them.  The title bar for each function lists the
first TOS version that supported the function.  Other functions have 
backwards compatibility built into the GEMFAST binding so that they will 
work correctly on all TOS versions (these are noted in the title bars too).

*--------------------------------------------------------------------------
* About TOS 1.4...
*--------------------------------------------------------------------------

 The TOS 1.4 pre-release notes contain documentation for the following:

   form_error    form_alert    shel_write    shel_get    shel_put  
   fsel_exinput  wind_new

 Of these, the form_error/alert and shel_???? docs seem to be just a 
 clarification of the docs without any functional changes.
 
 The wind_new and fsel_exinput functions are new with TOS 1.4.

;-------------------------------------------------------------------------
; wind_new                    TOS 1.4
;-------------------------------------------------------------------------

  void wind_new();

  The 'wind_new' function is for doing a major cleanup after a GEM 
  application.  It closes & deletes all windows, flushes all the windows'
  buffers (of redraw msgs, I presume), clears the wind_update flag, and
  restores ownership of the mouse to the system (END_MCTRL I presume). The
  documentation is not clear on whether this function should be used by
  an application that wants to shut down everything quickly, or whether it
  is intended for a shell's use in cleaning up after an application exits.
  I tend to suspect the latter, and I think this function was developed 
  because shell writers all begged Atari to provide something that could
  clean up after an application the way the desktop does.
 
;-------------------------------------------------------------------------
; fsel_exinput                TOS 1.4 (binding compatible with 1.0 and up)
;-------------------------------------------------------------------------

  Full 1.4 emulation...

  status = fsel_exinput(in_path, in_sel, &exitbtn, prompt_text);
  
   (status and exitbtn are 16-bit ints, others are char *).
   
 This routine is functionally equivelent to fsel_input, except that it
 also allows you to specify a prompt string of up to 30 characters to be
 displayed along with the file selector.  While the function is new with
 TOS 1.4, the AESFAST bindings support it in all versions via a routine
 which checks the AES version number and simulates the actions of
 fsel_exinput by using fsel_input and objc_draw.  If running under
 TOS 1.4, the system will display your prompt text in place of the words
 'FILE SELECTOR' inside the fsel box.  If running under pre-TOS 1.4, the
 simulation routines place the prompt text in a box which appears 
 between the menu bar and the fsel box.

 Other TOS 1.4 changes to the fsel routines that this routine supports 
 via simulation when running on pre-TOS 1.4 systems...
 
  - The file selector now allows you to edit the pathname and hit RETURN
    without exiting the dialog.  If you edit the filename and hit <CR>,
    you will exit as if you clicked on OK.
  - If the initial pathname has a leading '\', it will be appended to the
    end of the current default drive and path, and the entire resulting
    string will be returned if the user exits via OK or <CR>.
  - The current default drive and path are preserved, and the contents
    of the current DTA are preserved.  Only the default path on the 
    default drive is saved with the simulation software. If the user
    changes devices during file selection, the default path on all devices
    may  be changed except for the device that was the default when 
    fsel_exinput was called.

  The executable code for the fsel_exinput binding is big -- about 800
  bytes.  Also, it uses about 350 bytes of stack space during the call.
  Still, having a prompted file selector that works correctly on all
  machines will lend a touch of class to your application (IMHO).

  Note that all of the above fsel ehancements which are supported by the
  simulation on pre-TOS 1.4 systems are supported ONLY if you call
  fsel_exinput; if you call fsel_input on a pre-1.4 system the default
  path et. al. will behave as they always have. (Hint: USE exinput).

;-------------------------------------------------------------------------
; fsel_smallexinput           TOS 1.4 (binding compatible with 1.0 and up)
;-------------------------------------------------------------------------

  Prompting emulation only...

  This function has calling parameters identical to fsel_exinput() (see
  above), but it's behavior (return values, etc) is identical to that of
  fsel_input() (the original).  This function will call the real 'exinput'
  routine if on TOS 1.4, but if on an earlier version it emulates only the
  prompting of 'exinput', it does not save the path or DTA, or handle <CR>
  correctly, or any of the other nice TOS 1.4 features.  On the other hand,
  it's only half as big as the full emulator for fsel_exinput(), so it's
  handy for accessories and other small-memory applications.  (It will add
  about 450 bytes to your program, as opposed to 800).        
  
  I'd like to recommend that you do not code calls to fsel_smallexinput()
  directly in your program.  Instead, just code fsel_exinput(), and at the
  top of your C source, code:
  
    #define fsel_exinput fsel_smallexinput

  and let the C compiler handle the rest for you.  This ought to keep your
  code compatible many years into the future...
 
;-------------------------------------------------------------------------
; fsel_14input              TOS 1.4 (binding compatible with 1.0 and up)
;-------------------------------------------------------------------------

  No emulation.

  This function has calling parameters identical to fsel_exinput() (see
  above).  However, it makes no attempt to emulate any of the 1.4 exinput
  features.  Basically, if running on TOS 1.4, fsel_exinput is called.  If
  running on a pre-1.4 system, the prompt string is thrown away, and the
  normal file selector is called.  This binding is very small compared to
  other (emulation mode) exinput bindings.
  
  Again, it is best to access this function through a #define statement:
  
    #define fsel_exinput fsel_14input

| This function was added with v1.4.

;-------------------------------------------------------------------------
; shel_get / shel_put         TOS 1.0
;-------------------------------------------------------------------------

  status = shel_get(char *bufadr, int buflen)
  status = shel_put(char *bufadr, int buflen)
  
          These functions read and write the desktop's internal buffer
          which holds a copy of the DESKTOP.INF file.  The buffer holds
          an exact image of the file, in plain ASCII text.  If 'status'
          is returned as zero, an error occurred.  The Atari docs say that
          the buffer should never exceed 1024 bytes, but I've been told
          that it *can* in fact exceed this length.

;-------------------------------------------------------------------------
; form_keybd                  TOS 1.0
;-------------------------------------------------------------------------

  keyret = form_keybd(tree, object, nxtobject, thechar, &nxtobject, &thechar);
  
     (All values are 16-bit words, except 'tree', which is OBJECT *).
     
     The form_keybd routine acts as a filter on character input.
     When it  recognizes a control  character,  it processes
     it and zeroes the keyboard word.  Other chararacters can be
     passed on to objc_edit to be inserted in the editable object.  
     If the routine returns a zero, a default object is selected (<CR>).
     (Hints:  If 'nxtobject' is not equal to 'object' after this call,
     form_keybd() has detected a TAB or ARROW key to move to the next
     edit field, so call objc_edit(..., EDEND) for the current field,
     as long as nxtobject is non-zero.  If 'thechar' comes back non-zero,
     pass it to objc_edit(..., EDCHAR).  If this doesn't make sense, get
     the Tim Oren articles and make your own interpretations.)

;-------------------------------------------------------------------------
; form_button                 TOS 1.0
;-------------------------------------------------------------------------

  btnret = form_button(tree, object, clicks, &nxtobject);
  
     (All values are 16-bit words, except 'tree', which is OBJECT *).
          
     I can't tell you as much about this one.  This routine handles an
     already-occurred mouse button event.  It handles changing the 
     selected object into reverse video.  It (presumably) handles radio
     buttons. 'object' is the index of the object the mouse is over, so
     I presume have have to call objc_find() first to get this. 'nxtobject'
     is the new selected object index.  If the routine returns a zero,
     a default or exit object was selected.


**************************************************************************
*
* Section 2 - An alternate calling standard, and supporting bindings...
*
**************************************************************************

BACKGROUND

The calling standard for AES functions defined and documented by DRI was
defined for ease of use in C programming, but it does not lend itself to
particularly efficient code generation.  For example, the wind_get() call
can be used to return many different types of values, so DRI defined the
function as taking pointers to 4 separate output fields.  However, one of 
most common functions for wind_get() is to return information about window
rectangles; the values are returned in typical GRECT order. 

In the old days, a call of this type was often coded as:

    wind_get(wi_handle, WF_CURRXYWH, &x, &y, &w, &h);
    
As GEM coding techniques have become more sophisticated, people have found
that it makes more sense to keep xywh values in GRECT structures instead of
in discrete variables.  This leads to the rather cumbersome construct:

    wind_get(wi_handle, WF_CURRXYWH, 
                &windrect.g_x, &windrect.g_y,
                &windrect.g_w, &windrect.g_h);
                
When ideally, one would prefer to code:

    wind_get(wi_handle, WF_CURRXYWH, &windrect);
    
This makes a huge difference in runtime performance.  When the binding for
the original wind_get() routine is called, it has no idea whether discrete
(possibly discontiguous) addresses are passed, so it must build a temporary
output array on the stack in which the AES can pass return values.  Upon 
return from the AES, the binding must copy each of the four return values
back to the caller's memory, using the 4 individual pointers specified.
Using the alternate calling standard, the binding can simply pass the AES
the GRECT pointer specified in the call, and the AES can place the returned
values directly into the caller's memory, avoiding intermediate arrays.
Also, the C compiler only has to stack one address when setting up the call.

STANDARDS

Because it is not possible to have one function name to handle both types
of calling standards, I have choosen to implement the alternate bindings
using similar names, with an 'x' to indicate it is one of the extended
bindings.  Thus, wind_get() would be called when discrete addresses are
passed, and winx_get() would be called when a single pointer is passed.

There will be a certain amount of schizophrenia in the alternate binding
standards.  Some functions (such as evnx_multi) will take all input and
output values in a single consolidated structure.  Others will pass 
discrete input values, but take the return values in a structure, or
vice versa.  I will try to use common sense when making such decisions;
what I want to avoid is the VDI mess.  With VDI calls, you have to load
*everything* into an array, even when it doesn't seem to make sense to 
do so.  I think VDI coding is very tedious for this reason, and I'm going
to try to avoid that with the alternate AES bindings.

As an aside:  I've heard rumours that Tim Oren defined an entire alternate
calling structure for the AES under the name GEMX.  Unfortunately, I can't
find out anything about it except that it 'may exist'.  Therefore, nothing
in my alternate calling standards are going to correspond to his, except
by accident.  If anybody has more info on GEMX, I'd love to learn about it.

IMPLEMENTATION

I had planned to write the entire set of alternate bindings and release
them as GemFast v2.0.  I've been planning that for months, and it isn't
working out.  Therefore, beginning with v1.4, I'm releasing things as I 
go.  As of 1.4, there are only a couple of these extended bindings 
available.

The C-source version of the AESFAST utilities (GEMFUTxx.ARC) contains 
versions of the extended AES routines (evnx_multi, et. al.), to provide
code compatibility.  The C versions of these functions do not provide the
speed of the assemblerized versions in AESFAST.A, but they also do not add
more than a few bytes to the size of your program.

X-STRUCTURES

Where custom data structures are needed by an alternate binding, the
structures will be documented with the binding(s) that use them.  All such
structures are defined in the GEMFAST.H header file.

;-------------------------------------------------------------------------
; evnx_multi    - Alternate for evnt_multi.
;-------------------------------------------------------------------------

    int evnx_multi(XMULTI *xm);
    
    This binding takes a single parm, a pointer to an XMULTI structure.  It 
    returns an integer, which is a bit mask of events which occurred (the
    same value returned by evnt_multi).  This mask is also returned in 
    the mwhich element of the XMULTI structure.
    
    All of the values normally passed to evnt_multi are included in the
    XMULTI structure.  All of the output values are returned in the XMULTI
    structure, including messages in the message buffer, which is the 1st
    eight words of the XMULTI structure.
    
    The XMULTI structure is defined as follows:
    
        typedef struct xmulti {         
            int     msgbuf[8];          /* Message buffer               */
            int     mflags;             /* Mask of events to wait for   */
            int     mbclicks,           /* Button clicks to wait for    */
                    mbmask,             /* Which buttons to wait for    */
                    mbstate;            /* Button state to wait for     */
            int     mm1flags;           /* M1 rect: wait for in/out flag*/
            GRECT   mm1rect;            /* M1 rect: xywh coordinates    */
            int     mm2flags;           /* M2 rect: wait for in/out flag*/
            GRECT   mm2rect;            /* M2 rect: xywh coordinates    */
            int     mtlocount,          /* Timer count low value        */
                    mthicount;          /* Timer count high value       */
            int     mwhich,             /* Mask of events that occurred */
                    mmox,               /* Mouse x at time of event     */
                    mmoy,               /* Mouse y at time of event     */
                    mmobutton,          /* Mouse buttons at event       */
                    mmokstate,          /* Keystate at event            */
                    mkreturn,           /* Key pressed at event         */
                    mbreturn;           /* Times buttons was clicked    */
        } XMULTI;                       

    It is most efficient to code the XMULTI structure as a local variable
    of the function that uses it, although it can be defined as a global
    or static variable.  A simple, typical multi-handler might look like:
    
    multi()
    {
        XMULTI  xm;
        int     events
        
        /* 
         * set up static portions of xm structure...
         */

        xm.mflags = MU_TIMER | MU_MESAG | MU_BUTTON;
        
        xm.tlocount = 1000;         /* 1 second     */
        xm.thicount = 0;            /* timer value  */

        xm.mbclicks = 1;            /* wait for single  */
        xm.mbmask   = 1;            /* click of left    */
        xm.mbstate  = 1;            /* button           */

        while (1) {

            events = evnx_multi(&xm);
            
            if (events & MU_BUTTON) {
                handle_button(xm.mox, xm.moy);
            }

            if (events & MU_TIMER) {
                handle_timeout();
            }

            if (events & MU_MESAG) {
                switch(xm.msgbuf[0]) {
                  case WM_REDRAW:
                    do_redraw(xm.msgbuf[3], &xm.msgbuf[4]);
                    break;
                  case MN_SELECTED:
                    do_menuitem(xm.msgbuf[3], xm.msgbuf[4]);
                    break;
                /* etc */
                }               /* end switch           */
            }                   /* end if (MU_MESAG)    */
        }                       /* end while(1)         */
    }                           /* end multi() function */
    
    As can be seen in the example, this can be much more efficient than
    the usual call to evnt_multi, because it is not necessary to stack
    50-some bytes of info on each loop iteration.  Also, the binding for
    envx_multi is *much* smaller and faster than the one for evnt_multi.
    
;-------------------------------------------------------------------------
; winx_get          - Alternate for wind_get
;-------------------------------------------------------------------------

    int winx_get(int whandle, int wfield, GRECT *outrect);
    
    This binding takes the same 2 input parms as wind_get; the window 
    handle, and a value indicating the window info to be returned.  For 
    output, it takes a pointer to a GRECT structure.  The return value is
    the same as for wind_get() (0 indicates error).
    
    Note that the pointer to the output doesn't *have* to be a GRECT 
    pointer, it is more specifically a pointer to 4 contiguous words of 
    storage which will hold the return values.  The only wind_get() 
    functions which return more than a single value all return GRECT type
    output, however.  
    
    If winx_get() is called with a wfield value that implies just one 
    return value (WF_TOP, for example), you must be aware that EIGHT BYTES
    OF MEMORY WILL BE MODIFIED at the location pointed to by the output
    pointer.  (All but the first 2 bytes will be garbage, of course).
    
    Usage example:
    
        GRECT currect;
        
            winx_get(wi_handle, WF_CURRXYWH, &currect);

;-------------------------------------------------------------------------
; winx_calc         - Alternate for wind_calc
;-------------------------------------------------------------------------

    int winx_calc(int type, kind, in_x, in_y, in_w, in_h, GRECT *outrect);

    This binding takes all the same input values as wind_calc, but returns
    the output in a GRECT structure.  Note that although the input xywh
    values are listed as discrete in the definition above, your compiler
    will accept the name of a GRECT as the third parameter, and will 
    stack the four values from the rectangle as discrete parms.
    
    The following example might be used to create a window around an
    object tree...
    
        int   wi_kind  = MOVER | FULLER | CLOSER;
        GRECT workrect;
        GRECT fullrect;

        frmx_center(tree, &workrect); 
        winx_calc(WC_BORDER, wi_kind, workrect, &fullrect);
        wi_handle = wind_create(wi_kind, fullrect);
        if (wi_handle >= 0)
            wind_open(wi_handle, fullrect);

;-------------------------------------------------------------------------
; frmx_center        - Alternate for form_center
;-------------------------------------------------------------------------

    int frmx_center(OBJECT *ptree, GRECT *outrect);

    This binding takes a pointer to an object tree (as form_center does),
    and a pointer to a GRECT where the output xywh values will be returned.
    The return value from the function is always 1, as for form_center.
    
    Example:
    
        GRECT treerect;
        
        frmx_center(ptree, &treerect);
        objc_draw(ptree, R_TREE, MAX_DEPTH, treerect);

;-------------------------------------------------------------------------
; v_gchar - Output a single character via a fast call to v_gtext
;-------------------------------------------------------------------------

    void v_gchar(int vdi_handle, int x, int y, char outchar);

    This function generates a fast call to v_gtext to output a single
    character.  It is about half the size of the regular v_gtext binding,
    and will execute **much** faster.  It is useful for things like 
    telecomm programs that must do character-at-a-time output to a window.
    
|   This function is new with v1.5.

;-------------------------------------------------------------------------
; vdicall - Issue a call to VDI with the specified control/intin/etc.
;-------------------------------------------------------------------------

    int vdicall(control, intin, ptsin, intout, ptsout);

    Each of the parameters is a pointer to an array of 16-bit ints.

    This function allows you to call VDI functions not directly supported
    by the vdifast bindings library.  Within your program, you set up 
    arrays containing the parameters for the function, and then pass
    pointers to those arrays to this routine.  It will combine the 
    pointers into a VDI parameter block, and issue the VDI trap.
    
    This routine always returns a 16-bit integer which is the contents
    of intout[0].  
    
    You must always supply a non-NULL pointer for control, and the values
    in control must contain everything VDI needs to process the call.
    (IE, opcode, sub-opcode if needed, intin count, ptsin count, and
    extend control info such as the FDB pointers for a blit, if needed).
    You never need to supply the intout or ptsout count values in the
    control array: VDI will fill those in (and will ignore anything you
    put there anyway.)
    
    The other array pointers may be NULL if they are not needed for a 
    given VDI call (ie, if there are no ptsin parms for the call, just
    code NULL for the ptsin pointer).  The intout array is handled a
    little strangely:  if you supply a NULL intout pointer, the vdicall()
    routine allocates a 256-byte intout array on the stack for you.  It
    will return the value from intout[0] as the function return value,
    and discard the rest of the intout array.  This is mainly to provide
    ease-of-use in coding custom bindings which only need a single-word
    return value (ie, success/failure).
    
    As an example, suppose the vro_cpyfm() binding was not supported.
    You could write a macro to implement it, as follows:
    
    #define vro_cpyfm(handle,op,xycoords,srcfdb,dstfdb)        \
            {                                                  \
             int control[11]      = {109, 4, 0, 1, 0, 0};      \
             int temp             = op;                        \  
            control[6]            = handle;                    \    
            *(long*)(&control[7]) = (long)srcfdb;              \
            *(long*)(&control[9]) = (long)dstfdb;              \
            vdicall(control, &temp, xycoords, NULL, NULL);     \
            }

    (NOTE: If you are using Sozobon C, the control and temp variables
    in this example would have to be declared as 'static' to avoid a 
    bug in the compiler (at least, as of v1.3) that causes bombs when
    you initialize an auto-class variable declared in a local block.)

    There are, of course, other ways to implement functions besides
    coding them as macros -- you can, for example, build your own 
    library of C functions which serve as bindings.  (In fact, I rather 
    hope someone will code up a set of GDOS bindings in this way, and
    that such bindings eventually find their way back to me.  Then I
    could re-do them in assembler and make them part of GemFast.  I
    just don't have any docs on GDOS.)
    
    As an assist to anyone undertaking their own bindings, the following
    is the code that implements this function:
    
    _vdicall:
          .cargs    #8,.control.l,.intin.l,.ptsin.l,.intout.l,.ptsout.l
          link      a6,#-256            ; nice big intout, 'just in case'.
          move.l    a3,-(sp)            ; save this, we use it for intout.
          move.l    .intout(a6),a3      ; get caller-supplied intout ptr.
          move.l    a3,d0               ; test it for NULLness, if it's
          bne.s     .no_local_intout    ; not NULL, continue, else set
          lea       -256(a6),a3         ; up a pointer to our local intout
          clr.w     (a3)                ; array, and blast local intout[0].
    .no_local_intout:
          move.l    .ptsout(a6),-(sp)   ; build vdipb on the stack...
          move.l    a3,-(sp)          
          move.l    .ptsin(a6),-(sp)
          move.l    .intin(a6),-(sp)
          move.l    .control(a6),-(sp)
          move.l    sp,d1               ; load pointer to vdipb
          moveq.l   #$73,d0             ; load vdi function code
          trap      #2                  ; do it.
          move.w    (a3),d0             ; return intout[0] in d0.
          move.l    (sp)+,a3
          unlk      a6
          rts

|   This function is new with v1.6.

;-------------------------------------------------------------------------
; vgd_detect - Detect presence of GDOS on the machine.
;-------------------------------------------------------------------------

    int vgd_detect()
    
    Returns zero if GDOS not present or non-zero if it is present.  Works
    by calling trap #2 with -2 in D0 register, then testing for -2 still
    in the register after the trap.

|   This functions is new with v1.7.

**************************************************************************
* END OF GEMXTEND DOC
**************************************************************************

