Skip to main content

 "Fools rush in and get the best seats."
                                       Arthur Bloch, Murphy's Law


                GEM PROGRAMMING - AN IDIOTS GUIDE
            FOR THE ASSEMBLER PROGRAMMER (AND OTHERS)
                             PART II
                   by Ciaran 'Ringpull' Wills

 Welcome  to the second instalment of my GEM programming  series.
In the first part we covered what GEM is,  how to call it from an
assembler program and we looked at how to create alert boxes.  If
you  missed that then you may want to get your hands on  ST  NEWS
Volume 8 Issue 1.
 I have decided to start making this series a little wider in its
scope  as most GEM programmers use C or "GfA Basic"  for  writing
their programs.   For this reason I will provide some details  of
how to use the GEM calls in languages other than assembler.  This
will  usually  take the form of an example showing how  the  call
would  be made from C.   For other languages the call  should  be
similar  but you would be best to check with your  documentation.
I  can  not guarantee the accuracy of any examples I give  as  my
copy of "GfA Basic" 3.5 only came with limited documentation  (it
was a freebie) and my own GEM documentation is not very complete.
The  C examples are using version 1.4 of Ian Lepore's PD  GEMFAST
library.
 All you assembler programmers can now wake up again as we  shall
now continue by having a look at GEM objects.

GEM Objects

 What is a GEM object?   Good question.  The answer is that a GEM
object can be almost anything you want it to be.  GEM objects are
all the boxes,  buttons,  texts etc.  that make up dialogue boxes
and menus.  Although menus and dialogue boxes are the most common
applications  of GEM objects there are many others such as  icons
in windows and on the backdrop (i.e.  the desktop or "1st  Word")
or placing a toolbox in a window.
 Now  down to the technical bits.   GEM objects exist in  a  tree
structure  with  each dialogue box or menu bar being  a  separate
tree.  This tree has a root object (in a dialogue box this is the
big  box  which contains all the buttons etc.).   All  the  other
objects  are children of the root object and the root  object  is
the  parent of all the others.   You may find it helpful at  this
point  to  go  out  into the garden and look  at  some  trees  or
alternatively think of a family tree.   An example at this  point
should also help clear things up:

A Dialogue box tree

                   The box itself
               /          |        \
      OK button     Cancel button  An invisible box
                                      /        \
                                  Button A   Button B

 As you can see,  most of your objects (buttons, text etc.) go on
the first level of the tree.   Note the invisible box though, you
may wonder what the point of a box which you can't see is, so now
we  can show one of the ways in which the tree structure  can  be
put  to  use.   Although the box is invisible it can  still  have
children, in this case two buttons.  The reason these buttons are
not on the first level like the others is that they are a special
kind of button known as a radio button.
 Radio  buttons  are the ones which only allow you  to  have  one
selected at once (i.e. when one is selected all the others in the
box are deselected).   By having several boxes it is possible  to
have  several  sets of radio buttons.   Also note  that  the  box
doesn't have to be invisible.

 Now  that  you hopefully understand the tree structure  I  shall
briefly outline the 13 types of object available:

Number    Type        Description

20        gbox        A  box.   It can have a border and  a  fill
                      pattern & colour.
21        g_text      A text string.
22        g_boxtext   A text string in a box.
23        g_image     A single bit-plane (one colour) image.
24        g_progdef   A programmer-defined object.
25        g_ibox      An invisible box.
26        g_button    A  button.    A  bit  of  text  in  a   box
                      which can be selected.
27        g_boxchar   A  button  but  instead  of  text  only   a
                      single character is allowed.
28        g_string    Like   g_text  except  only   the   default
                      font may be used.
29        g_ftext     An editable text string.
30        g_fboxtext  Same as above but in a box.
31        g_icon      An icon.   Like g_image but the image  also
                      has  a  mask  so that it can  be  shown  in
                      inverse when selected.
32        g_title     A  special  type of string  used  for  menu
                      bars.

 For  the moment we can ignore g_progdef and g_title as they  are
not normally used anyway (OK, so g_title is used in menus, but we
don't need to bother about that).

 OK,  I know what a GEM object is and what a tree of objects  is,
but  how do I make one?   There are two ways of making  a  object
tree:  You can include all the object definitions in your code by
hand  writing  the  data structures or you  can  use  a  resource
construction program
.   I must at this point advise very strongly
against  taking  the  first option as it is a  lot  of  work  and
resource construction programs are readily available.
 Different resource construction programs have different ways  of
working so I can't tell you how to use yours here,  but once  you
have created your objects you should be able to save them out  as
an  .RSC file.   You should also be able to create a header  file
which  is used to define the names for the objects  you  created.
This  lets you refer to the objects by name in your  code  rather
than by their ID numbers.   Often this header file is in the form
of  a  .H file for use with a C compiler but you should  able  to
easily  change  the  '#define' statements into  'EQU's  for  your
assembler.

 OK, I have my objects created, but how do I use them?

 There are a number of AES calls which can be used to display and
manipulate objects.   These calls have names starting with 'obj_'
or  'objc_' depending on who you believe.   I personally use  the
shorter  as it saves on typing.   The following 'obj_' calls  are
available:

obj_add       Allows you to add an object to a tree
obj_delete    Deletes an object from a tree
obj_draw      The  important  call,   this  allows  you  to  draw
              objects   or  object  trees  on  the   screen.    A
              clipping rectangle can also be used.
obj_find      Finds  the  ID of the object  currently  under  the
              mouse
obj_offset    An   objects   coordinates  are   stored   relative
              to   the  objects  parent.    This   call   returns
              the   absolute  position  on  the  screen  of   the
              object.
obj_order     Allows you to reorder objects in a tree
obj_edit      This   call  allows  the  user  to  interact   with
              an editable text object.
obj_change    Allows you to change an objects state

 Although  the  AES  provides  these calls it  may  at  times  be
necessary  to change an objects attributes by accessing the  data
in  the object tree directly.   In many instances this is  easier
and  quicker  than  using the AES calls.   To do this  is  it  is
necessary  to  know how the data in the object  tree  is  stored.
Each  object  is  described  by  a 12  word  (24  byte)  list  of
attributes.  This list is as follows:

Word    Description

0       ID of next object
1       ID of first child of this object
2       ID of last child of this object
3       Object type
4       Object flags (See below)
5       Object status (See below)
6-7     Address of object data structure (See below)
8       Object's x coordinate (relative to parent)
9       Object's y coordinate (relative to parent)
10      Width of object
11      Height of object

 These 12 word lists are stored in the tree in order according to
the objects ID numbers.   Once you have  the address of the start
of  the tree you can find the address of a particular  object  by
multiplying its ID by 24 and adding it to the tree's address.

 Word 4 contains the object flags.   The lower 9 bits of the word
are allocated to the following meanings:

Bit     Meaning if bit is set

0       The object is Selectable
1       The object is the default object exit button  (Selectable
        by pressing return)
2       As  above,  this  object  will exit  a  dialogue  box  if
        selected
3       The object is editable (i.e. editable text)
4       The object is a radio button
5       This bit is set in the last object in the tree
6       TouchExit.   This is similar to exit but the dialogue box
        exits as soon as the mouse button is pressed.   On normal
        exit  buttons the dialogue box exits when the  button  is
        released.
7       Hides  the tree.   When the tree is drawn this branch  is
        hidden
8       Indirect.  Means this object points to another value

 Word 5 contains the object's status flags.   The lower 6 bits of
the word carry the following meanings:

Bit     Meaning if bit is set

0       Object is currently selected
1       Object is crossed out
2       Object has a check mark beside it.  Mostly used in menus
3       Object  is  disabled.   Text appears in gray  and  object
        cannot be selected
4       Object is outlined
5       Object has a shadow

 Words  6 and 7 hold the address of another data structure  which
is  used to hold special information for objects which  use  text
(i.e.  text strings and editable text fields).  This structure is
called 'tedinfo' and has the following form:

Word    Description

0-1     te_ptext - The pointer to the text string
2-3     te_ptmplt  -  Pointer to the template used  for  editable
        text
4-5     te_pvalid  -  Pointer to validation string  for  editable
        text
6       te_font - Font side ( 3: normal  5: small)
7       te_resvd1 - Reserved word, left blank
8       te_just - Justification of text ( 0:  left  1:  right  2:
        centre)
9       te_colour - Text, border and fill colour and fill pattern
10      te_resvd2 - Another blank reserved word
11      te_thickness - Thickness of the border
12      te_txtlen - Length of text string + 1
13      te_tmplen - Length of te_ptmplt string + 1

 After  all  these  lists I will try to  explain  how  all  these
attributes,  trees,  lists are used in a dialogue box by using  a
simple example program.

* An example GEM program showing use of a simple dialogue box
* The box is displayed and selections can be made
* When the box is exited the program quits
* By Ciaran Wills 29/8/93

* These equates are generated by the resource editor
* They let us use names to refer to the objects
BOX             EQU 0           ; Index of the dialogue box tree
OK              EQU 2           ; The OK button
CANCEL          EQU 3           ; The Cancel button
TIME            EQU 4           ; The editable time field
DATE            EQU 5           ; The editable date field
BUTTON1         EQU 7           ; The first radio button
BUTTON2         EQU 8           ; The second radio button
* The non-selectable objects such as the box and test string
* are not listed here as they will not be used or changed

* The GEM Header
                movea.l sp,a5           ; GEM header
                lea     stack,sp
                movea.l 4(a5),a5
                move.l  12(a5),d0
                add.l   20(a5),d0
                add.l   28(a5),d0
                add.l   #256,d0
                move.l  d0,-(sp)
                move.l  a5,-(sp)
                move.w  d0,-(sp)
                move.w  #$4A,-(sp)
                trap    #1
                lea     12(sp),sp

* Now the program
                bsr     initGEM         ; Do initialisation stuff
                moveq   #1,d0           ; Display greeting box
                bsr     alert
                bsr     handle_box      ; Do the dialogue box
                bra     quit            ; And quit

* Initialisation stuff
initGEM:        lea     aespb(pc),a0    ; Address is used often
                move.l  #appl_init,(a0) ; First call, of course,
                bsr     aes             ; is an appl_init call

                move.l  #rscfile,addrin ; Address of RSC filename
                move.l  #rsc_load,(a0)  ; Get AES to load
                bsr     aes             ; the RSC file
                tst.w   intout          ; Was there an error?
                bne.s   rsc_ok          ; If not zero then OK
* If intout=0 then the file was not loaded and we cannot
* continue.  We tell the user and quit
                moveq   #0,d0           ; Alert box number 0
                bsr     alert           ; Call alert box handler
                bra     quit            ; And quit

* If the file was loaded we arrive here
rsc_ok:         clr.w   intin           ; We want tree address
                move.w  #BOX,intin+2    ; The dialogue box tree
                move.l  #rsc_gaddr,(a0)
                bsr     aes
                move.l  addrout(pc),boxaddr ; Store box address

* The last thing our initialisation routine does is change
* the mouse pointer into an arrow
                clr.w   intin           ; Pointer #0 (Arrow)
                move.l  #graf_mouse,(a0)
                bsr     aes
                rts                     ; Initialisation finished

* This routine handles the dialogue box
* It displays the box, gives control to the AES to let the
* user use the box and then tidies up
handle_box:     movea.l boxaddr(pc),a0  ; Get object tree address
                ori.w   #1,BUTTON1*24+10(a0) ; Set but.1 selected

                lea     aespb(pc),a1
                lea     intin(pc),a2    ; For easy reference

* First we use form_centre to centre the box on the screen
                move.l  a0,addrin       ; Address of tree
                move.l  #form_center,(a1) ; Center box on screen
                bsr     aes
                movem.l intout+2(pc),d0-d1 ; Four .w rect. coords

* form_dial reserves the screen area under the box
                clr.w   (a2)            ; Reserve screen area
                movem.l d0-d1,10(a2)    ; The coords to reserve
                move.l  #form_dial,(a1)
                bsr     aes

* obj_draw draws the box in the specified place
                clr.w   (a2)            ; Parents index
                move.w  #8,2(a2)        ; Draw all children
                movem.l d0-d1,4(a2)     ; Clipping rectangle
                move.l  a0,addrin       ; Address of tree to draw
                move.l  #obj_draw,(a1)
                bsr     aes

* form_do handles the users interaction with the box
                move.w  #TIME,(a2)      ; First editable field
                move.l  a0,addrin       ; Dial. box tree address
                move.l  #form_do,(a1)   ; This call does all!
                bsr     aes
                move.w  intout(pc),d2   ; Index of exiting button

* Use form_dial to replace corrupted screen area
                move.w  #3,(a2)         ; Replace screen are
                movem.l d0-d1,10(a2)    ; Box coords
                move.l  #form_dial,(a1) ; form_dial again
                bsr     aes

* Unselect the button that was used to exit the box
                mulu    #24,d2          ; Find obj. data in tree
                bclr    #0,11(a0,d2.w)  ; Unselect object
                rts                     ; The box is done

* This routine tidies up and quits
quit:           move.l  #appl_exit,aespb
                bsr     aes
                clr.w   -(sp)
                trap    #1              ; Quit properly

* The Alert box handling routine
alert:          lea     alerttab(pc),a0 ; Address of addresses(?)
                mulu    #6,d0           ; 6 bytes per alert box
                adda.l  d0,a0
                move.w  (a0)+,intin     ; The default exit button
                move.l  (a0),addrin     ; Alert string address
                move.l  #form_alert,aespb
                bsr     aes
                move.w  intout(pc),d0   ; Button used to exit box
                rts

* The AES subroutine
aes:            movem.l d0-a6,-(sp)
                lea     aespb(pc),a0
                move.l  a0,d1
                move.l  #$C8,d0
                trap    #2
                movem.l (sp)+,d0-a6
                rts

                DATA
* Call data for the AES calls
appl_init:      DC.W 10,0,1,0,0
appl_exit:      DC.W 19,0,1,0,0
obj_draw:       DC.W 42,6,1,1,0
form_do:        DC.W 50,1,2,1,0
form_dial:      DC.W 51,9,1,0,0
form_alert:     DC.W 52,1,1,1,0
form_center:    DC.W 54,0,5,1,0
graf_mouse:     DC.W 78,1,1,1,0
rsc_load:       DC.W 110,0,1,1,0
rsc_gaddr:      DC.W 112,2,1,0,1

aespb:          DC.L
 contrl,global,intin,intout,addrin,addrout

* The alert box data table. 6 bytes per box:
* First word: Number of default exit button
* 2-3rd word: Address of string for alert box
alerttab:       DC.W 1
                DC.L rsc_error
                DC.W 1
                DC.L hello

* The strings for the alert boxes
rsc_error:      DC.B '[1][Cannot find resource file!][ Quit! ]',0
hello:          DC.B '[0][ This is an example program to |'
                DC.B '  demonstrate dialogue boxes! |'
                DC.B '    Written by Ringpull! ][ Continue ]',0

rscfile:        DC.B 'gem2.rsc',0 ; Filename of resource file
                EVEN

                BSS
boxaddr:        DS.L 1

contrl:         DS.W 16
global:         DS.W 16
intin:          DS.W 128
intout:         DS.W 128
addrin:         DS.W 128
addrout:        DS.W 128

                DS.B 1024       ; A small stack
stack:
                END

 This program is not much more complicated than the last one,  it
just uses a few new AES calls.   It starts off,  as all  programs
should,  with  the  GEM  header.   I have  then  placed  all  the
initialisation calls in one subroutine called initGEM.  Note that
I  have placed the address of the parameter block (aespb)  in  an
address register.   This means that every time I to set up an AES
call  I only have to use the address in the  register,  saving  4
bytes  of  code  each  time and  improving  speed  (although  not
noticeably, it is good practice).
 The  initialisation routine makes the obligatory appl_init  call
and then uses the rsc_load call to load the resource file.   This
call  needs one parameter:  The address of the string  containing
the  filename (terminated with a zero byte,  of course) which  is
placed  in  the fist 4 bytes of the  addrin  array.   The  string
should contain the filename only,  with no pathnames.  This means
the file is loaded from the current directory,  which is fine  as
long as the resource file is kept with the program file.   If you
put  a  filename like 'a:\gem2.rsc' in your program  and  someone
copies it onto their hard-drive it won't work.
 Once the resource file is loaded you need to find the  addresses
of the object trees to use them.   The rsc_gaddr call can be used
to  find the addresses of the objects and data fields within  the
resource  file.   In this case we want the address of  an  object
tree which is type zero.   Then we specify the index of the  tree
but  since there is only one tree in the file this is  zero  (See
the equates at the top of the program).   The address is returned
in addrout and stored for later use.
 The  graf_mouse call is then called to return the mouse  to  its
arrow  shape  or else it will stay in the 'busy bee'  shape  used
while loading the program.
 The  alert  box  number one is then displayed  using  the  alert
subroutine.

 Now we come to the main part of the program;  using the dialogue
box.   First of all bit zero of BUTTON1's status field is set  so
that it appears selected when the box is displayed.   This  could
have  been  done  with  the  resource  editor  but  I  wanted  to
demonstrate it here.
 The  form_center  call  takes in the address  of  the  tree  and
returns the centred coordinates for the box, saving you having to
find the size of the screen and work it out yourself.  The coords
are returned as 4 words of x coord,  y coord,  width and  height.
Since  all the other calls use this format for coordinates the  4
words  can simply be 'movem'ed into two registered and  'movemed'
out when needed.
 The form_dial call used next has two uses: Preserving the screen
under  a dialogue box and drawing the infamous 'expanding  boxes'
as  seen  on the desktop when you open a window.   We  are  using
function zero (reserve screen area).  The coords of the box go in
8  bytes  on  from here as there is space for  a  second  set  of
coordinates for the expanding boxes.
 Obj_draw  is  the call which actually displays the  box  on  the
screen.   It needs the index of the first object to draw (Usually
zero  for the parent),  the number of levels of child objects  to
draw (The maximum is eight),  a clipping rectangle (In this  case
the coords of the box) and the address of the tree.
 Now  we can call form_do to handle all the  interaction  between
user and dialogue box.   It requires the address of the tree  and
the  index  of the first editable object (In this case  the  TIME
object).   If there are no editable objects then intin should  be
zero.   form_do returns the index of the button used to exit  the
box in intout.
 The screen is replaced using form_dial function 3 which replaces
the area saved earlier and sends a redraw message to any  windows
on the screen.
 Since the exiting button was selected to exit the dialogue  box,
it is still selected.  It is now necessary to unselect it or else
next time we display the box either the OK or Cancel buttons will
already be selected!
 The quit routine requires no explanation.

 The  only  other part of this program which requires  any  other
explanation is the 'alert' subroutine.   This saves you having to
set up a form_alert call every time you want an alert  box.   The
details of all the alert boxes used in the program are stored  in
the alerttab table and it is a simple case of placing the  number
of the box to use in d0.

 In case you need help connecting the formats of the object  data
given  before  the program to the program  itself,  here  is  the
output  from the resource construction kit showing the  structure
of the objects in the resource file I created:

TEDINFO ted0[] = {
    "1200",
    "Time:__:__",
    "9999",
    3,6,0,$1180,0,255,4,10,
    "290893",
    "Date:__/__/__",
    "999999",
    3,6,0,$1180,0,255,6,13      };

OBJECT box[] = {
    -1,1,6, G_BOX,$0,$10,$21100L,0,0,43,12,
    2,-1,-1,G_STRING,$0,$0, "This is a text string" ,11,1,21,1,
    3,-1,-1,G_BUTTON,$7,$0, "OK" ,7,10,8,1,
    4,-1,-1,G_BUTTON,$5,$0, "Cancel" ,28,10,8,1,
    5,-1,-1,G_FTEXT,$8,$0,(char *)&ted0[0],4,4,11,1,
    6,-1,-1,G_FTEXT,$8,$0,(char *)&ted0[1],4,6,13,1,
    0,7,8, G_BOX,$0,$30,$ff1100L,22,3,18,5,
    8,-1,-1,G_BUTTON,$11,$30, " Button 1 " ,4,1,10,1,
    6,-1,-1,G_BUTTON,$31,$30, " Button 2 " ,4,3,10,1      };

 Although in the format of a C file,  it should be easy enough to
follow;  each  line  of  the  box[]  structure  is  the  24  byte
description of an object.
 One  point  to note is that each tedinfo  structure  contains  3
lines of text.   The middle one is the template (te_ptmplt)  with
the underlines being where you can enter text.   The top line  is
the text entered in the underlines and the last is the validation
string  (te_pvalid)  which  specifies  which  characters  can  be
entered.   The '9's mean that only numbers can be entered.  Other
possibilities are:

'A'     Upper case letters and spaces
'a'     Upper and lower case numbers and spaces
'N'     Upper case letters, numbers and spaces
'n'     All numbers, letters and spaces
'F'     Valid filename characters
'p'     Valid filename characters and '\'
'P'     Valid filename characters, '\' and ':'
'X'     Anything and everything

 That's it for this instalment.   I hope you are managing to keep
up  but if not you can always take your time,  after all ST  NEWS
doesn't come out that frequently!
 Next  time  we  will  be exploring menus  and  further  uses  of
dialogue  boxes  and we will actually write a program  that  does
something!   You would be well advised to get yourself a resource
editor as you will definitely need one!   A couple are  available
in  the  public  domain including MKRSC which  I  used  for  this
program and another which I cannot remember the name of (which is
probably  the  seriously excellent "Interface"  by  Shift,  ED.).
Also  there is WERKS from HiSoft which I have heard is  good  but
can't afford at the moment!

 See you next time! 

Disclaimer
The text of the articles is identical to the originals like they appeared in old ST NEWS issues. Please take into consideration that the author(s) was (were) a lot younger and less responsible back then. So bad jokes, bad English, youthful arrogance, insults, bravura, over-crediting and tastelessness should be taken with at least a grain of salt. Any contact and/or payment information, as well as deadlines/release dates of any kind should be regarded as outdated. Due to the fact that these pages are not actually contained in an Atari executable here, references to scroll texts, featured demo screens and hidden articles may also be irrelevant.