Using GEM from the C Language

By Steve Pedler

 

Issue 29

Sep/Oct 87

Next Article >>

<< Prev Article

 

 

Second in our programming series

In the first article in this series, we looked at some of the graphics
capabilities of GEM, culminating in a simple demonstration. In this article, we will examine some more GEM functions and add a couple of demos to the first version of the program.

HITCHES, GLITCHES AND PROBLEMS.

There were a few teething troubles with the first article which I will deal with first. I compiled the first program with Metacomco's Lattice C compiler version 3.03, with which it compiled and linked without problems. I subsequently upgraded to version 3.04 of the compiler, and on recompiling the program two warnings were output. These may have occurred on other systems, so I include fixes for them here. The first warning came from the compiler, in that a function return value mismatch warning occurred three times. This happens because Lattice 3.04 complies very closely with the proposed ANSI standard for C, in which functions not returning a value to the calling function should be declared as type void. Three functions — main(), init_gem() and finish_gem() — return no value, and the way to fix this warning is either to declare these functions as void, or — at least with Lattice C — to use a compiler option to disable this warning message in this particular context. The problem doesn't affect the running of this particular program, it is just a warning of a syntax error that might cause problems elsewhere.

The second warning was returned by the linker, to the effect that the GEM VDI global arrays contrl[ ], intin[ ], ptsin[ ], intout[ ] and ptsout[ ] had been defined more than once. Again, this does not prevent the program from operating, since the linker uses the first values it finds and ignores any others. The reason is presumably that the compiler automatically defines these arrays so the programmer doesn't have to do it. This may be specific to Lattice, but if you are like me and don't like seeing warnings from the compiler or linker here's how to fix the problem:

i) delete the definitions of the five GEM VDI arrays
ii) replace them with the following arrays: WORD work_in[ 11 ], work_out[ 57 ]
iii) replace all references to intin[] with work_in[ ], and all references to intout[ ] with work_out[ ]

Other minor problems were that the XBIOS function Setpallete() was misspelled as Setpalette() (not in the listing, just in the text of the article) and that the format of the string for alert boxes was shown incorrectly in the text. The brackets surrounding the icon, text and buttons should be square not round brackets, and the character used to separate the lines of text and the buttons is the vertical line character, not a colon. This character is obtained by pressing Shift and the key adjacent to the left Shift key on the ST keyboard. It does print out as two short vertical lines one above the other on a dot matrix printer, so the typesetter's confusion is understandable! Again, these errors did not affect the actual listing.

Table 1. The attribute functions.

Attribute group Functions Feature(s)
 changed
Line vsl_type line style
  vsl_width line thickness
  vsl_color line colour
  vsl_ends line end style
     
Fill vsf_interior fill style
  vsf_style pattern fill type
  vsf_color fill color
  vsf_perimeter fill border visibility
     
Polymarker vsm_type marker type
  vsm_height marker height
  vsm_colour marker colour
     
Text vst_height character height
  vst_rotation text baseline rotation
  vst_color text colour
  vst_effects text special effects
  vst_alignment text alignment
     
Miscellaneous vs_color change a colour
  vswr_mode change writing mode

THE PROGRAM LISTINGS

Listing 1 accompanying this article contains some minor changes and additions which should be made to the previous version (0.2) of the program.

  Listings 1 & 2 source code

There are some additional #defines and declarations, a few extra lines for main() to enable the new demos to be called, and I have tidied up the finish_gem() function slightly to provide a cleaner exit back to the desktop. Make the necessary changes, then add Listing 2 to the modified program. Once successfully compiled, run the program. Demo 1 will appear as before, but this time holding down the right mouse button will progress to demo 2. In this demo, holding down either button will run demo 3 and holding down both simultaneously will exit to the desktop. In demo 3, there is a change, since the program waits not for a mouse button click but for a keypress. Pressing any key returns to demo 1, unless you press the space bar which will terminate the program.

GRAPHICS FUNCTIONS AND ATTRIBUTES

Before we look at the two new demos to be added to the program, it seems an opportune moment to examine the question of graphic attributes. As we have seen, GEM possesses a large number of graphics functions. Each of these have a number of features — or attributes — which may be set as desired, including such things as colour, fill styles, line type and so on. Not all graphics functions are affected by all the various attributes and in fact each graphic output function is usually associated with a particular group of attributes. There are four such groups —line, fill, text and polymarker — plus one or two attributes affecting all graphic output. These four groups are listed in Table 1. This is not a complete list, but it contains the vast majority of attribute functions most users will need. Clearly, changing one attribute does not affect another, so that (for example) changing the text colour will not alter the line or fill colours. From now on, when a graphic function is said to be associated with a particular group of attributes it should be easy to decide how to change a particular feature.

DEMONSTRATION 2

The second demonstration to be included in our developing program is another example of simple animation, carried out this time by rapidly drawing and redrawing circles. The aim is to give the illusion of a chain of circles moving across the screen.

The demo starts by first setting the background colour to black. The screen is cleared, the demo's title printed at the bottom of the screen and three of the fill attributes are set. We looked at vsf_interior() last time, but note that a fill style of zero denotes no fill - that is, just the outline of the object will be drawn. When GEM draws an object which is associated with the fill attributes, it encloses the object with a line drawn in the current fill colour. You would not be able to distinguish this line if the object is drawn in solid colour, but it might be visible if a patterned fill style were used. For this reason, the enclosing line can be disabled with the function vsf_perimeter(). The format of this function is :

vsf_perimeter( device handle, perimeter flag )

where the perimeter flag can have the value 0 (outline disabled) or 1 (outline enabled). Here we ensure that the outline is enabled, because if it were not the combination of no fill and no visible outline would result in invisible circles!

After initialising some variables, the program draws 10 circles starting at position 160 (x-coordinate), 100 (y-coordinate). The centre of the circle is changed each time through the loop by adding the incremental values x_incr and y_incr.

There are in fact two ways of drawing circles in GEM. The first is to use v_circle() as shown here, which is called by:

v_circle( device handle, circle centre x-coordinate, circle centre y-coordinate, circle radius )

The parameters passed to the function are self-explanatory, and are the same as in v_pieslice() which we looked at last time. The alternative method is to use v_arc() as follows:

v_arc( device handle, circle centre x-coordinate, circle centre y-coordinate, circle radius, start angle, stop angle )

These parameters are identical to those in v_pieslice(). The major difference between the two functions, other than the obvious one that v_circle() draws complete circles whereas v_arc() can draw portions of a circle (i.e. arcs), is that v_circle() uses fill attributes but v_arc() uses line attributes. If you want circles filled with a colour or pattern you can use v_circle() but if you want circles drawn with thick lines or different line types (such as dotted lines) use v_arc(). You might care to modify this program to use v_arc() rather than v_circle(). To draw a complete circle using v_arc() set the start angle to 0 and the stop angle to 3600 (angles are given in tenths of degrees). You can change the line colour with vsLcolor(), which is called in identical fashion to vsf_color(), and the line width with vsLwidth() which has this format:

vsLwidth( device handle, line width )

where width is the line thickness in pixels. The width should be an odd number (default thickness is 1) and if an even number is given the nearest lower odd number is used. The line type can be changed with vsLtype() as follows:

vsLtype( device handle, line type )

where line type can have one of these values:

value

line type result

1 solid line
2 long dashed line
3 dotted line
4 dash-dot line
5 dashed line
6 dash-dot-dot line
7 user defined line style

Experiment with these values and see what effects are produced.

To return to the demo, the program next enters a 'while' loop in which another circle is drawn at the head of the chain while the last one is erased. This is done by setting the circle colour to zero (background) and overwriting the last circle. At the end of each pass through the loop, the state of the mouse buttons is checked and providing a button has not been pressed the loop continues. The function used to detect a button press is vq_mouse() (just to make a change from graf_mkstate() which we used last time). Call this function as follows:

vq_mouse( device handle, mouse button, mouse x-coordinate, mouse y-coordinate)

where the mouse button can have the same values as we found with graf_mkstate(). We are not interested in the position of the mouse here, so the coordinates are just placed in the dummy variable. Note that pointers to the three variables, not the variable names themselves, are passed to the function. (This must also be done for graf_mkstate().) Finally, before returning the mouse button state to the main() function, the mouse button is checked again until the user releases it. This ensures that the button is not still being pressed on exit from the function, which might have untoward effects.

DEMONSTRATION 3

This simple demo shows how text can be printed to the screen using GEM. Rather than go through the demo in detail – it's very simple – I will discuss the functions used in the program and leave you to decide how the program works.

GEM has two functions for outputting text to a graphics screen. The simpler is v_gtext(), which is used as follows:

v_gtext( device handle, text x-coordinate, text y- coordinate, text string )

How the x and y coordinates are used is affected by one of the text attribute functions, vst_alignment(), which we will look at in a moment. The text string can either be a literal string enclosed in double quotes (the compiler automatically adds a terminating null byte as required by C convention) or a pointer to the string. Both methods are used here, the second method being used to print the Atari logo on the screen. The logo comes in two halves with the character values OE and OF (hex) respectively, and these values are placed into the array str[] at the beginning of the program.

The function vst_alignment() alters the way GEM uses the supplied coordinates. It is called as follows:

vst_alignment( device handle, horizontal alignment, vertical alignment, selected horizontal alignment, selected vertical alignment )

where the last two parameters are supplied as the addresses of (i.e. pointers to) two variables into which GEM will place the selected alignments. In this example, the dummy variable is used for convenience. The horizontal alignment can have one of three values:

value

 text alignment

0 left justified – x coordinate is that of the leftmost end of the string
1 centred – x coordinate is that of the centre of the string
2 right justified – x coordinate is that of the rightmost end of the string

By default the text is set to left justified. The vertical alignment can have the following values:

value

 text alignment

0 y coordinate is that of the text baseline
1 half line
2 ascent line
3 bottom line
4 descent line
5  topline

I am unsure of the effect of values other than zero, which is the default value. Readers may care to try them out and see what results are produced.

The second method of text printing is to use v_justified(). This is used as follows:

v_justified( device handle, x-coordinate, y-coordinate, text string, string length, word space flag, character space flag)

The first four parameters are the same as for v_gtext(). The string length is the distance on screen, expressed in pixels, into which the text string is to fit. Text can therefore be compressed or stretched by giving a length different to that of the string itself. To do this, GEM will either have to alter spaces between characters or between words, or both. You can specify which of these methods is used by setting the two flags in the function. If the flag is set to zero, space alteration is disabled; if set to one, it is enabled.

The other attribute functions used here are straightforward. Text colour is selected with vst_color(), and character height (in pixels) with vst_height(). There are minimum and maximum limits on character size, and these are returned in the four parameters following the requested height. We aren't really interested in these here, so they are placed in a dummy variable. Text special effects can be selected using vst_effects() which is called as follows:

vst_effects( device handle, effects )

where different values of the variable effects have these results:

value

effect

0 normal text
1 bold text
2 light text
4 italic text
8 underlined text
16 outlined text
32 shadowed text

These values can be combined, so that (for example) a value of 28 would produce underlined italic outlined text.

Most of these effects are shown in the demo, although the effect of shadowed text is not very apparent.

The final effect shown is that of changing the text baseline vector using vst_rotation(). This is called as follows:

vst_rotation( device handle, baseline rotation )

where the baseline rotation is expressed in tenths of degrees. Normally, this is set to zero, producing horizontal text, but it can supposedly produce text printed at an angle. Unfortunately, I found that in low resolution at least only angles of 0, 90, 180, or 270 degrees were accepted, and any other value resulted in the nearest of these angles being selected. This may simply be a limitation of low resolution and it may be that other angles can be used in higher resolutions.

Some of the text on the screen is shown enclosed in a box with rounded corners. The box is drawn with the following function:

v_rbox( device handle, points array )

where the array contains the x and y coordinates of the top left corner of the box in its first two elements, and the coordinates of the diagonally opposite corner in the second two elements. This function uses line attributes.

Finally, just for a change the demo waits for a keypress before exiting rather than a mouse button click. For this purpose we could have used a standard C library function, but instead I have used the GEM function evnt_keybd(). This waits for the user to press a key and returns a 16-bit integer (not a char) containing the value of the key pressed. The value returned is therefore not an ASCII value but a GEM VDI keycode; lists of these can be found in most GEM reference books. The great advantage of using this function rather than one from the standard library is that it will detect the use of the function keys as well as standard ASCII keycodes.

COMING NEXT

I think that about wraps it up this time. In the next part of this series we will look at some of the remaining GEM graphic functions, and after that it will be time to examine those features which really distinguish GEM such as windows, menus, dialogs and so on.

top