From:	CSBVAX::CSBVAX::MRGATE::"SMTP::CRVAX.SRI.COM::RELAY-INFO-VAX" 24-JAN-1989 18:47
To:	MRGATE::"ARISIA::EVERHART"
Subj:	Using the SMG$ routines from VAX C


Received: From KL.SRI.COM by CRVAX.SRI.COM with TCP; Wed,  4 JAN 89 19:01:25 PDT
Received: from gold.bacs.indiana.edu by KL.SRI.COM with TCP; Wed, 4 Jan 89 18:52:51 PST
Date: 4 Jan 89 21:45:00 EST
From: "IVAX::IJAH400" <ijah400%ivax.decnet@gold.bacs.indiana.edu>
Subject: Using the SMG$ routines from VAX C
To: "info-vax" <info-vax@kl.sri.com>

[]

There have been a couple of inquiries lately about how to call the SMG$
routines from C.  Attached at the end of this posting is a C example, in
VMS_SHARE format, analogous to the one (in FORTRAN, the 3rd example after
the description of SMG$SELECT_FROM_MENU) on page SMG-340 of the V5.0 VMS
RTL Screen Management manual.  It requires SMG for VMS V5.0, but may also
prove useful as an example of how to call the older routines (for example,
SMG$CREATE_VIRTUAL_PASTEBOARD).

The things you have to watch out for the most are:

o	Arguments required to be passed by reference (pass &arg_value).

o	Routines that return values in quantities of storage less than a
	longword (like the selected index returned from SMG$SELECT_FROM_MENU;
	it is declared as a short int as the routine only returns a 16 bit
	value).

o	Passing the addresses of structures.  Make sure that is what you
	are really passing, and not the structures itself by value (a
	violation of the VAX calling standard for structures bigger than a
	longword, which is an allowed exception for VAX C only).

o	In C you omit arguments by passing 0 by value.

Also, if you are writing routines in C to be called from other languages,
you should avoid modifying the passed arguments (a violation of the VAX
calling standard which is an allowed exception for VAX C only), as they
may not be on the stack.

The "choices" argument SMG$CREATE_MENU wants is just the address of an
array descriptor.  A static array of character strings just means all the
strings are fixed-length (blank padded) and contiguous in memory.  The
definition (well, at least part of it) of the array descriptor is in
SYS$LIBRARY:DESCRIP.H.  The SMG routine seems to require the multiplier
and bounds information in the descriptor.  If you don't have the flags on
for them in the descriptor, it returns an illegal argument error.  I'm
not sure if this is a bug or not (the calling standard describes the
multiplier and bounds blocks as optional, which would seem reasonable for
an array with one dimension).

The #define for CHOICE_SIZE is the size of each individual choice (here
20, as in the above-mentioned FORTRAN example).  In the initialization of
the "choices" array, you must make sure that the initialization strings
are padded out to CHOICE_SIZE with blanks, otherwise you will get nulls
in the strings (which you don't want).  Choices can be added by inserting
them at the end of the list (inserting them in the middle would cause
the "choice numbers" to change).  If CHOICE_SIZE is increased, you must
remember to make sure all the choices are padded to the correct length.
They can be padded longer for convenience, although I have not done it
here (see "Initialization of arrays" sect. 6.7.1 of the VAX C manual).
The "choice numbers" run from 1 (for "One") to 20 (for "Exit this menu")
as in the FORTRAN example.  For those who prefer zero-based choice numbers
(i.e. zero to the number of choices minus one) one must change the
array descriptor; the address of the zeroth element becomes &choices
rather than &choices-CHOICE_SIZE, the lower bound becomes 0 rather than
1, and the upper bound becomes sizeof(choices)/CHOICE_SIZE-1 rather
than sizeof(choices)/CHOICE_SIZE.

It's a bit ugly, but it works.  Perhaps the ugliness is the reason why
there are no C examples in the manual.  It's a pity as the choice of
language used is not always up to the programmer, for various reasons...

     - Jim Harvey, IUPUI Computing Services (Indiana University)
     Internet:	IJAH400%IVAX.DECNET@GOLD.BACS.INDIANA.EDU
     BITNET:	IJAH400@INDYVAX

P.S.:  Here is the example program in VMS_SHARE format:
...................... Cut between dotted lines and save. .....................
$!.............................................................................
$! VAX/VMS archive file created by VMS_SHARE V06.00 26-May-1988.
$!
$! VMS_SHARE was written by James Gray (Gray:OSBUSouth@Xerox.COM) from
$! VMS_SHAR by Michael Bednarek (U3369429@ucsvc.dn.mu.oz.au).
$!
$! To unpack, simply save, concatinate all parts into one file and
$! execute (@) that file.
$!
$! This archive was created by user IJAH400
$! on 23-DEC-1988 21:55:49.34.
$!
$! It contains the following 1 file:
$!        HCMENU.C
$!
$!==============================================================================
$ SET SYMBOL/SCOPE=( NOLOCAL, NOGLOBAL )
$ VERSION = F$GETSYI( "VERSION" )
$ IF VERSION .GES "V4.4" THEN GOTO VERSION_OK
$ WRITE SYS$OUTPUT "You are running VMS ''VERSION'; ", -
    "VMS_SHARE V06.00 26-May-1988 requires VMS V4.4 or higher."
$ EXIT 44
$VERSION_OK:
$ GOTO START
$
$UNPACK_FILE:
$ WRITE SYS$OUTPUT "Creating ''FILE_IS'"
$ DEFINE/USER_MODE SYS$OUTPUT NL:
$ EDIT/TPU/COMMAND=SYS$INPUT/NODISPLAY/OUTPUT='FILE_IS'/NOSECTION -
    VMS_SHARE_DUMMY.DUMMY
b_part := CREATE_BUFFER( "{Part}", GET_INFO( COMMAND_LINE, "file_name" ) )
; s_file_spec := GET_INFO( COMMAND_LINE, "output_file" ); SET( OUTPUT_FILE
, b_part, s_file_spec ); b_errors := CREATE_BUFFER( "{Errors}" ); i_errors
:= 0; pat_beg_1 := ANCHOR & "-+-+-+ Beginning"; pat_beg_2 := LINE_BEGIN
& "+-+-+-+ Beginning"; pat_end := ANCHOR & "+-+-+-+-+ End"; POSITION
( BEGINNING_OF( b_part ) ); i_append_line := 0; LOOP EXITIF MARK( NONE
) = END_OF( b_part ); s_x := ERASE_CHARACTER( 1 ); IF s_x = "+" THEN r_skip
:= SEARCH( pat_beg_1, FORWARD, EXACT ); IF r_skip <> 0 THEN s_x := ""
; MOVE_HORIZONTAL( -CURRENT_OFFSET ); ERASE_LINE; ENDIF; ENDIF
; IF s_x = "-" THEN r_skip := SEARCH( pat_end, FORWARD, EXACT ); IF r_skip <
> 0 THEN s_x := ""; MOVE_HORIZONTAL( -CURRENT_OFFSET ); m_skip := MARK( NONE )
; r_skip := SEARCH( pat_beg_2, FORWARD, EXACT ); IF r_skip <> 0 THEN POSITION
( END_OF( r_skip ) ); MOVE_HORIZONTAL( -CURRENT_OFFSET ); MOVE_VERTICAL( 1 )
; MOVE_HORIZONTAL( -1 ); ELSE POSITION( END_OF( b_part ) ); ENDIF; ERASE
( CREATE_RANGE( m_skip, MARK( NONE ), NONE ) ); ENDIF; ENDIF
; IF s_x = "V" THEN s_x := ""; IF i_append_line <> 0 THEN APPEND_LINE
; MOVE_HORIZONTAL( -CURRENT_OFFSET ); ENDIF; i_append_line := 1; MOVE_VERTICAL
( 1 ); ENDIF; IF s_x = "X" THEN s_x := ""; IF i_append_line <
> 0 THEN APPEND_LINE; MOVE_HORIZONTAL( -CURRENT_OFFSET ); ENDIF
; i_append_line := 0; MOVE_VERTICAL( 1 ); ENDIF; IF s_x <> "" THEN i_errors
:= i_errors + 1; s_text := CURRENT_LINE; POSITION( b_errors ); COPY_TEXT
( "The following line could not be unpacked properly:" ); SPLIT_LINE
; COPY_TEXT( s_x ); COPY_TEXT( s_text ); POSITION( b_part ); MOVE_VERTICAL( 1
); ENDIF; ENDLOOP; POSITION( BEGINNING_OF( b_part ) ); LOOP r_x := SEARCH( "`"
, FORWARD, EXACT ); EXITIF r_x = 0; POSITION( r_x ); ERASE_CHARACTER( 1 )
; IF CURRENT_CHARACTER = "`" THEN MOVE_HORIZONTAL( 1 ); ELSE COPY_TEXT( ASCII
( INT( ERASE_CHARACTER( 3 ) ) ) ); ENDIF; ENDLOOP; IF i_errors = 0 THEN SET
( NO_WRITE, b_errors, ON ); ELSE POSITION( BEGINNING_OF( b_errors ) )
; COPY_TEXT( FAO( "The following !UL errors were detectedwhile unpacking !AS"
, i_errors, s_file_spec ) ); SPLIT_LINE; SET( OUTPUT_FILE, b_errors
, "SYS$COMMAND" ); ENDIF; EXIT;
$ DELETE VMS_SHARE_DUMMY.DUMMY;*
$ CHECKSUM 'FILE_IS
$ WRITE SYS$OUTPUT " CHECKSUM ", -
  F$ELEMENT( CHECKSUM_IS .EQ. CHECKSUM$CHECKSUM, ",", "failed!,passed." )
$ RETURN
$
$START:
$ FILE_IS = "HCMENU.C"
$ CHECKSUM_IS = 2102379175
$ COPY SYS$INPUT VMS_SHARE_DUMMY.DUMMY
X/*
X**++
X**`009This VAX C program illustrates the use of SMG$CREATE_MENU,
X**`009SMG$SELECT_FROM_MENU, and SMG$DELETE_MENU. to create an
X**`009application that lets a user make multiple selections from
X**`009a horizontal menu.
X**
X**`009This C example is similar to the FORTRAN example in the VMS
X**`009RTL Screen Management (SMG$) Manual, p SMG-340 (Digital Equipment
X**`009Corporation order number AA-LA77A-TE, April 1988).
X**
X**`009NOTE:  Requires SMG for VMS V5.0.
X**
X**`009Author: James A. Harvey, IUPUI Computing Services (Indiana University)
X**--
X*/
X
X#include descrip
X#include smgdef
X
X#define failure(arg) (~(arg) & 1)
X
X/* Define a symbol for the size of each individual choice */
X#define CHOICE_SIZE (20)
X
Xstatic readonly char choices[][CHOICE_SIZE] = {
X    "One                 ",
X    "Two                 ",
X    "Three               ",
X    "This is Four        ",
X    "Five                ",
X    "Six                 ",
X    "Seven               ",
X    "Eight               ",
X    "Nine                ",
X    "I like ten          ",
X    "Eleven              ",
X    "Twelve              ",
X    "Thirteen            ",
X    "Fourteen            ",
X    "Fifteen             ",
X    "Sixteen             ",
X    "Seventeen           ",
X    "Eighteen            ",
X    "Nineteen            ",
X    "Exit this menu.     "
X};
X
X/*
X**   DESCRIP.H defines an array descriptor, but we (picky SMG) needs the
X**   multiplier and bounds block too, so define all that c--p...
X*/
Xstruct bounds_block {
X    char *dsc$a_a0;`009`009/* Address of element whose subscripts are 0 */
X    long dsc$l_m;`009`009/* Addressing coefficient */
X};
X
Xstruct multipliers_block {
X    long dsc$l_l;`009`009/* Lower bound */
X    long dsc$l_u;`009`009/* Upper bound */
X};
X
Xstruct array_of_charstring_dsc {
X    struct dsc$descriptor_a dsc_no_extra_blocks;
X    struct multipliers_block m_block;
X    struct bounds_block b_block;
X};
X
Xstatic readonly struct array_of_charstring_dsc a = {
X    /* The part of the descriptor defined in DESCRIP.H: */
X    {`009CHOICE_SIZE,`009`009/* Length of data item in bytes */
X`009DSC$K_DTYPE_T,`009`009/* Data type code (fixed text) */
X`009DSC$K_CLASS_A,`009`009/* Descriptor class code (array) */
X`009&choices,`009`009/* Address of first byte of data */
X`0090,`009`009`009/* Scale multiplier (not used) */
X`0090,`009`009`009/* Number of digits in int. rep. (not used) */
X`009{ `009`009`009/* Flags: */
X`009    0,`009    `009`009/* (unused, must be zero) */
X`009    0,`009`009`009/* Array cannot be redimensioned */
X`009    1,`009`009`009/* Column-major (but irrelevant for 1 dim) */
X`009    1,`009`009`009/* SMG$ requires a bounds block */
X`009    1,`009`009`009/* SMG$ requires a multiplier block too */
X`009},
X`0091,`009`009`009/* Number of dimensions */
X`009sizeof(choices)`009`009/* Total size of array in bytes */
X    },
X    /* The infernal multiplier block: */
X    {
X`009&choices-CHOICE_SIZE,`009/* Address of zeroth element (like FORTRAN) */
X`009CHOICE_SIZE`009`009/* Addressing coefficient (multiplier) */
X    },
X    /* The infernal bounds block: */
X    {
X`0091,`009`009`009`009/* Lower bound (like FORTRAN) */
X`009sizeof(choices)/CHOICE_SIZE`009/* Upper bound */
X    }
X};
X
X
Xmain ()
X{
X    unsigned long s, p_id, k_id, d_id, d_id2;
X    unsigned short int n;
X    char choice[20];
X    struct dsc$descriptor_s c = { 20, DSC$K_DTYPE_T, DSC$K_CLASS_S, choice };
X
X    s = SMG$CREATE_PASTEBOARD(&p_id);
X    if (failure(s))
X`009LIB$SIGNAL(s);
X    s = SMG$CREATE_VIRTUAL_KEYBOARD(&k_id);
X    if (failure(s))
X`009LIB$SIGNAL(s);
X    s = SMG$CREATE_VIRTUAL_DISPLAY(&6, &50, &d_id2, &SMG$M_BORDER);
X    if (failure(s))
X`009LIB$SIGNAL(s);
X    s = SMG$CREATE_VIRTUAL_DISPLAY(&6, &50, &d_id, &SMG$M_BORDER);
X    if (failure(s))
X`009LIB$SIGNAL(s);
X
X    s = SMG$PASTE_VIRTUAL_DISPLAY(&d_id2, &p_id, &2, &2);
X    if (failure(s))
X`009LIB$SIGNAL(s);
X    s = SMG$PASTE_VIRTUAL_DISPLAY(&d_id, &p_id, &10, &2);
X    if (failure(s))
X`009LIB$SIGNAL(s);
X
X    s = SMG$CREATE_MENU(&d_id, &a, &SMG$K_HORIZONTAL, 0, &2, &SMG$M_REVERSE);
X    if (failure(s))
X`009LIB$SIGNAL(s);
X
X    do {
X`009s = SMG$SELECT_FROM_MENU(&k_id, &d_id, &n, &6, 0, 0, 0, 0, &c,
X`009`009`009`009&SMG$M_BOLD, &0);
X`009if (failure(s))
X`009    LIB$SIGNAL(s);
X`009s = SMG$PUT_LINE(&d_id2, &c);
X`009if (failure(s))
X`009    LIB$SIGNAL(s);
X    } while (n != 20);
X
X    s = SMG$DELETE_MENU(&d_id);
X    if (failure(s))
X`009LIB$SIGNAL(s);
X}
$ GOSUB UNPACK_FILE
$ EXIT


