2.8 Using Escape



There are a few additional considerations when using these routines inside your program. The include file <cbase\escape.h> defines the functions that can be sent to escape and might be received from getkey. You should include this file with any program that calls these functions.

As mentioned before, you should call the ttyinit function to check that a valid terminal type has been defined. Ttyinit also performs internal initialization the first time it is called. You should put the following code in your program's initialization code. (Ttyinit never returns a status code of less than zero on MS-DOS systems. Therefore the first part of this code does not need to be included unless you will be porting your program to a UNIX system).

if ((status = ttyinit()) < 0) {

fprintf (stderr, "TERM not set\n");

exit (1);

}

else if (status == 0) {

fprintf (stderr, "TERM %s not defined\n",

getenv ("TERM"));

exit (1);

}

Escape is very easy to use. The first parameter to escape names the function to perform. Escape finds the corresponding escape sequence in the terminal definition and sends that escape sequence to standard output. Since escape calls the standard I/O library to write to standard output, calls to escape can be intermixed with other calls to the standard I/O library. For example, the following will put a title in the middle of the terminal screen.

#include <cbase/escape.h>

/* ... */

/* clear terminal screen */

escape (CLEARALL);

/* move to the middle of the screen */

escape (MOVCUR, 12, 30);

printf ("Example using escape");

/* move cursor to upper left */

escape (HOME);

There is an efficiency consideration to be made with escape. The standard I/O library normally buffers output produced by its routines. If, however, the library routines find that the output is being sent to a terminal, the output is not be buffered. Since escape writes to a terminal, standard output would normally be unbuffered, causing a separate write system call for each character sent to the terminal screen. This can significantly degrade the speed of displaying characters on the terminal.

A solution to this is to make the standard output file buffered. This is automatically done by the ttyinit function the first time it is called. Thus, all subsequent output to standard output is buffered.

Buffered standard output puts an additional responsibility on the programmer. Each time your program reads from the terminal keyboard, (say with getchar), the buffered contents of the standard output buffer must first be sent to the terminal. This is done in the following manner:

int c;

/* ... */

/* flush any buffered output */

fflush (stdout);

/* get a key from the keyboard */

c = getchar ();

If this is not done, then prompts to the operator may not be displayed before an answer is expected. Actually, getkey does flush buffered output before reading from the keyboard, so the call to fflush is not necessary before the call to getkey. However, if you read characters with a standard I/O routine other than getkey, the call to fflush is required.

The escape function returns an integer value to indicate whether the requested function was performed or not. If no escape sequence is defined for the function specified, escape returns a negative value. If the function is performed, escape returns a positive value. The following code fragment illustrates how it might be used.

if (escape (DELLINE) < 0) {

escape (CLEAREOS);

for (i=curline; i<=lastline; i++) {

escape (MOVCUR, i, 1);

fputs (text[curline+1], stdout);

}

}

The code attempts to delete the current terminal line, but if the terminal cannot perform the DELLINE function, the bottom of the screen is erased, and the lines are redrawn leaving out the current line. This code is works on many different terminals, and it takes advantage of the DELLINE function if it is available. Note that this code does assume that the CLEAR TO END OF SCREEN and MOVE CURSOR functions are always available.

You could also use the escout function to see if the terminal can perform the DELLINE function. Escout is especially useful when you want to see if a function exists without actually performing it if it does exist.

There are commonly two ways that terminals implement visual attributes.

In terminals like the Wyse 50, visual attributes stick to the screen. When you send a visual attribute command to the terminal it puts a marker at the current cursor location that indicates that the following characters on the screen have the specified attribute until the next marker (or the end of the screen). Many terminals in this class, including the Wyse 50, require a character position on the screen for the attribute marker.

The other class of terminals, like the DEC VT100, have visual attributes that stick to the cursor. When you send a visual attribute command to the terminal, all successive characters written to the terminal have the specified attribute until you send a different visual attribute command.

The escape functions that start a field (WHITE, BLACK, UNDERLINE, and BLINK) always show the start of the field at the current cursor location. On terminals like the Wyse 50 that take a position on the screen for the visual attribute, escape places the visual attribute character in the location preceding the current cursor location. Therefore, you should not start fields in column 1, and you should not have characters displayed immediately before the start of a field.

In a similar manner, the escape functions that end a field (WHITE, BLACK, UNDERLINE, and BLINK) may take a space immediately after the current cursor location, so you should not end a field in the last column, and you should not have characters displayed immediately after the start of a field.

The following code shows a way of displaying a field that works on both classes of terminals

/* Put ending attribute at end of field to

* prevent flashing on terminals whose

* attributes stick to the screen.

* This only needs to be done the first

* time the field is displayed.

*/

escape (MOVCUR, row, col+length);

escape (ENDWHITE);

/* Put starting attribute at beginning

* of field

*/

escape (MOVCUR, row, col);

escape (WHITE);

/* Save fill character for WHITE fields */

fillchar = termparm ()->tp_white;

/* Display the character data in the field.

* Change blanks to the fillchar. Truncate

* the data if longer than the field.

*/

while (*data && length > 0) {

if (*data == ' ')

putchar (fillchar);

else

putchar (*data);

data++;

}

/* Pad rest of field with the fillchar. */

while (length > 0)

putchar (fillchar);

/* Put ending field attribute at end

* of field

*/

escape (ENDWHITE);