"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.