This chapter describes the low-level routines and other components that every ETI program needs to work properly. It tells you how to compile and run ETI applications using the low-level libraries and introduces important concepts (such as refreshing) that recur throughout this document.
All ETI programs need to include the header file <curses.h> and call the routines initscr(), refresh(), or similar routines, and endwin(). Some of the other header files, however, include <curses.h>
The header files <menu.h>, <form.h>, and <panel.h> define several global variables and data structures and defines several ETI routines as macros.
To begin, let's consider the variables and data structures defined. <curses.h>, among other things, defines the integer variables LINES and COLS; when an ETI program is run on a particular terminal, these variables are assigned the vertical and horizontal dimensions of the terminal screen, respectively, by the routine initscr() described below.
NOTE: LINES and COLS are external (global) variables that represent the size of a terminal screen. Two similar variables, $LINES and $COLUMNS, may be set in a user's shell environment; an ETI program uses the environment variables to determine the size of a screen. Whenever we refer to the environment variables in this chapter, we will use the $ to distinguish them from the C declarations in the <curses.h> header file.
For more information about these variables, see the sections "The Routines initscr(), refresh(), endwin()" and "More about initscr() and Lines and Columns."
The integer variables COLORS and COLOR_PAIRS are also defined in <curses.h>. These will be assigned, respectively, the maximum number of colors and color-pairs the terminal can support. These variables are initialized by the start_color() routine. (See "Color Manipulation" in Chapter 7.)
The header files define the integer constants OK, E_OK, ERR (E_OK is in <eti.h>), and others listed in the following chapters. ETI routines that return int values return these constants under the following conditions:
The other error values returned by the high-level functions are described in the appropriate chapters below.
Now let's consider the macro definitions. <curses.h> defines many ETI routines as macros that call other macros or ETI routines. For instance, the simple routine refresh() is a macro. The line
#define refresh() wrefresh(stdscr)
shows that when refresh is called, it is expanded to call the ETI routine wrefresh(). In turn, wrefresh() (although it is not a macro) calls the two ETI routines wnoutrefresh() and doupdate(). Many other routines also group two or three routines together to achieve a particular result.
CAUTION: Macro expansion in ETI programs may cause problems with certain sophisticated C features, such as the use of automatic incrementing variables.
One final point about <curses.h>: it automatically includes <stdio.h> and the <termio.h> tty driver interface file. Including either file again in a program is harmless but wasteful.
The routines initscr(), refresh(), and endwin() initialize a terminal screen to an ``in ETI state,''update the contents of the screen, and restore the terminal to an ``out of ETI state,'' respectively. Consider the simple program introduced earlier and reproduced in the following screen.
The Purposes of initscr(), refresh(), and endwin() in a Program
#include <curses.h> main() { initscr(); /* initialize terminal settings and <curses.h> data structures and variables */ move( LINES/2 - 1, COLS/2 - 4 ); addstr("Bulls"); refresh(); /* send output to (update) terminal screen */ addstr("Eye"); refresh(); /* send more output to terminal screen */ endwin(); /* restore all terminal settings */ }
An ETI program usually starts by calling initscr(); your program should call initscr() only once. This routine uses the environment variable $TERM to determine what terminal is being used. (See "The ETI/terminfo Connection" in Chapter 5 for details.) It then initializes all the declared data structures and other variables from <curses.h>. For example, initscr() would initialize LINES and COLS for the sample program on whatever terminal it was run. If the Teletype 5425 were used, this routine would initialize LINES to 24 and COLS to 80. Finally, this routine writes error messages to stderr and exits if errors occur.
During the execution of the program, output and input is handled by routines like move() and addstr() in the sample program. For example,
move (LINES/2 - 1, COLS/2 -4 );
says to move the cursor to the left of the middle of the screen. The line
addstr ("Bulls");
says to write the character string Bulls. For example, if the Teletype 5425 were used, these routines would position the cursor and write the character string at (11,36).
NOTE: All ETI routines that move the cursor move it from its home position in the upper left corner of a screen. The (LINES,COLS) coordinate at this position is (0,0) not (1,1). Notice that the vertical coordinate is given first and the horizontal second, which is the opposite of the common 'x,y' order of screen (or graph) coordinates. The -1 in the sample program takes the (0,0) position into account to place the cursor on the center line of the terminal screen.
Routines like move() and addstr() do not actually change a physical terminal screen when they are called. The screen is updated only when refresh() is called after one or more windows (internal representations of the screen) are updated. This is a very important concept, which we discuss in section "More about refresh() and Windows."
Finally, an ETI program ends by calling endwin(). This routine restores all terminal settings and positions the cursor at the lower left corner of the screen.
You compile programs that include ETI routines as C language programs. This means that you use the cc(1) command to invoke the C compiler. (See the Programmer's Guide: ANSI C and Programming Support Tools for details).
The routines are usually stored in the library /usr/ccs/lib/libX.a, where Xsignifies either curses, panel, menu, or form, depending on which library your program needs. To direct the link editor to search this library, you must use the -l option with the cc command.
The general command line for compiling an ETI program follows:
cc file.c [-lX] -lcurses-o file
where X is either panel, menu, or form; file.c is the name of the source program; and file is the executable object module. See the appropriate chapter below for more information.
Some users may have applications using the TAM library routines that originally ran on the UNIX PC. "TAM Transition Library," Appendix C of this document, explains how to compile and run these applications on any machine of the 3B2 computer family.
ETI programs count on certain information being in a user's environment to run properly. Specifically, users of a program should usually include the following three lines in their .profile files:
TERM=current terminal type export TERM tput init
For an explanation of these lines, turn again to the section "The ETI/terminfo Connection" in Chapter 5. Users of an ETI program could also define the environment variables $LINES, $COLUMNS, and $TERMINFO in their.profile files. However, unlike $TERM, these variables do not have to be defined.
If an ETI program does not run as expected, you might want to debug it with sdb(1) which is also documented in the Programmer's Guide: ANSI C and Programming Support Tools. When using sdb, you have to keep a few points in mind. First, an ETI program is interactive and always has knowledge of where the cursor is located. An interactive debugger like sdb, however, may cause changes to the contents of the screen of which the ETI program is not aware.
Second, an ETI program doesn't output to a window until refresh() or a similar routine is called. Because output from the program may be delayed, debugging the output for consistency may be difficult.
Third, setting break points on ETI routines that are macros, such as refresh(), does not work. You have to use the routines defined for these macros, instead; for example, you have to use wrefresh() instead of refresh(). See the above section, "The Header Files", for more information about macros.
After determining a terminal's screen dimensions, initscr() sets the variables LINES and COLS. These variables are set from the terminfo variables lines and columns. These, in turn, are set from the values in the terminfo database, unless these values are overridden by the values of the environment $LINES and $COLUMNS.
As mentioned above, ETI routines do not update a terminal until refresh() is called. Instead, they write to an internal representation of the screen called a window. When refresh() is called, all the accumulated output is sent from the window to the current terminal screen.
A window acts a lot like a buffer does when you use a UNIX system editor. When you invoke vi(1), for instance, to edit a file, the changes you make to the contents of the file are reflected in the buffer. The changes become part of the permanent file only when you use the w or ZZ command. Similarly, when you invoke a screen program made up of ETI routines, they change the contents of a window. The changes become part of the current terminal screen only when refresh() is called.
<curses.h> supplies a default window named stdscr(standard screen), which is the size of the current terminal's screen, for all programs using ETI routines. The header file defines stdscr to be of the type WINDOW*, a pointer to a C structure which you might think of as a two-dimensional array of characters representing a terminal screen. The program always keeps track of what is on the physical screen, as well as what is in stdscr. When refresh() is called, it compares the two screen images and sends a stream of characters to the terminal that make the physical screen look like stdscr. An ETI program considers many different ways to do this, taking into account the various capabilities of the terminal and similarities between what is on the screen and what is on the window (stdscr). It optimizes output by printing as few characters as is possible. The following figures illustrate what happens when you execute the sample ETI program that prints BullsEye at the center of a terminal screen. Notice in the figures that the terminal screen retains whatever garbage is on it until the first refresh() is called. This refresh() clears the screen and updates it with the current contents of stdscr.
You can create other windows and use them instead of stdscr. Windows are useful for maintaining several different screen images. For example, many data entry and retrieval applications use two windows: one to control input and output and one to print error messages that don't mess up the other window. It's possible to subdivide a screen into many windows, refreshing each one of them as desired. And it's possible to create a window within a window; the smaller window is called a subwindow. See the chapter "Windows" for more information.
Some ETI routines are designed to work with a special type of window called a pad. A pad is a window whose size is not restricted by the size of a screen or associated with a particular part of a screen. You can use a pad when you have a particularly large window or only need part of the window on the screen at any one time. For example, you might use a pad for an application with a spread sheet.
The following figure represents what a pad, a subwindow, and some other windows could look like in comparison to a physical screen.
The later chapter "Windows" describes the routines you use to create and use windows and pads. If you'd like to see an ETI program with windows now, turn to the "The window Program" program in Appendix D of this document.