Chapter 9


curses/terminfo

9.1 INTRODUCTION

Screen management programs are a common component of many commercial computer applications. These programs handle input and output at a video display terminal. A screen program might move a cursor, print a menu, divide a terminal screen into windows, or draw a display on the screen to help users enter and retrieve information from a data base.

This tutorial explains how to use the Terminal Information Utilities package, commonly called curses/terminfo, to write screen management programs on a UNIX system. This package includes a library of C routines, a data base, and a set of UNIX system support tools. To start users writing screen management programs as soon as possible, the tutorial does not attempt to cover every part of the package.

For instance, it covers only the most frequently used routines and then points users to curses(3X) and terminfo(4) in the SUPER-UX Programmer's Reference Manual for more information. Keep the manual close at hand; users can find it invaluable when they want to know more about one of these routines or about other routines not discussed here.

Because the routines are compiled C functions, users should be familiar with the C programming language before using curses/terminfo. Users should also be familiar with the UNIX system/C language standard I/O package (see stdio(3S)). With that knowledge and an appreciation for the UNIX philosophy of building on the work of others, users can design screen management programs for many purposes.

This chapter contains five sections.

Home


9.2 OVERVIEW

This section briefly describes curses, terminfo, and other components in the Terminal Information Utilities package.

9.2.1 curses Routines

curses(3X) is a library of routines that programmers use to create screen management programs on UNIX. The routines are C functions and macros; many of them resemble routines in the standard C library. For example, there is a printw( ) routine that behaves much like printf(3S) and a getch( ) routine that behaves like getc(3S). The automatic teller machine program at the bank might use printw( ) to print its menus and getch( ) to accept the requests for withdrawals (or deposits). A visual screen editor like the UNIX system screen editor vi(1) might also use these and other curses routines.

curses routines are usually located in /usr/lib/libcurses.a. To compile a program using these routines, users must use the cc(1) command and include -lcurses on the command line so that the link editor can locate and load them.

The name curses comes from the cursor optimization that this library of routines provides. Cursor optimization minimizes the amount of cursor movement time needed to update a screen. For example, if you designed a screen editor program with curses routines and edited the following sentence,

to read,

the program would output only the best in place of a great. The other characters are preserved. Because the amount of data transmitted (output) is minimized, cursor optimization is also referred to as output optimization.

Cursor optimization takes care of updating the screen in a manner appropriate for the terminal on which a curses program runs. This means that the curses library can do whatever is required to update many different terminal types. It searches the terminfo data base (described later) to find the correct description for a terminal.

Cursor optimization helps those who use the programs by saving time updating screens. It also reduces the load on the UNIX system communication lines when updating takes place. You do not need to worry about the myriad terminals on which the program might be running.

The following example shows a simple curses program. It uses some basic curses routines to move the cursor to the middle of the screen and print the character string BullsEye. Each routine is described in the following section on working with curses routines. For now, their names give an idea of what each does.

Home


9.2.2 terminfo

terminfo can be defined in two of the following ways.

Each terminal description in the data base is a separate, compiled file. Take the source code that terminfo(4) describes to create these files and use the tic(1M) command to compile them.

Compiled files are normally located in the /usr/share/lib/terminfo/? directory. This directory has single-character names, each of which is the first character in the name of a terminal. For example, an entry for the AT&T Teletype 5425 is normally located in the file /usr/share/lib/terminfo/a/att5425.

The following is a simple shell script that uses the terminfo data base.

Home


9.2.3 How curses and terminfo Work Together

A screen management program with curses routines refers to the terminfo data base at run-time to obtain the information it needs about the terminal being used what is called the current terminal from now on.

For example, suppose you are using an AT&T Teletype 5425 terminal to run the simple curses program shown earlier. To execute properly, the program needs to know how many lines and columns the terminal screen has to print the BullsEye in the middle of it. The description of the AT&T Teletype 5425 in the terminfo data base has this information. All the curses program needs to know before it goes looking for the information is the name of the terminal. Users tell the program the name by putting it in the environment variable $TERM when users log in or by setting and exporting $TERM in the .profile file (see profile(4)). Knowing $TERM, a curses program run on the current terminal can search the terminfo data base to find the correct terminal description.

For example, assume that the following example lines are in a .profile.

The first line names the terminal type, and the second line exports it. (See profile(4) in the SUPER-UX Programmer's Reference Manual.) The third line of the example tells UNIX to initialize the current terminal. That is, it makes sure that the terminal is set up according to its description in the terminfo data base. (The order of these lines is important. $TERM must be defined and exported first, so that when tput is called the proper initialization for the current terminal takes place.)

If users had these lines in the .profile and users ran a curses program, the program would get the information that it needs about the terminal from the /usr/share/lib/terminfo/a/att5425 file that provides a match for $TERM.

9.2.4 Other Components in the Terminal Information Utilities

Earlier, Terminal Information Utilities were referred to as curses/terminfo. The package, however, has other components, for instance, tic(1M). The following lists components discussed in this tutorial.

This chapter also refers to profile(4), scr_dump(4), term(4), and term(5). For more information about any of these components, see the SUPER-UX Programmer's Reference Manual and the SUPER-UX User's Reference Manual.

Home


9.3 WORKING WITH curses ROUTINES

This section describes basic curses routines for creating interactive screen management programs. It begins by describing the routines and other program components that every curses program needs to work properly. It tells users how to compile and run a curses program, and describes the most frequently used curses routines that do the following.

To illustrate the effect of these routines, simple example programs are included as the routines are introduced. The chapter also refers to a group of larger examples located in the section on curses program examples. These larger examples are more challenging; they sometimes make use of routines that are not discussed. Keep the curses(3X) manual page handy.

9.3.1 What Every curses Program Needs

All curses programs need to include the <curses.h> header file and call the initscr( ) and refresh( ) routines (or similar, related routines), and endwin( ).

9.3.1.1 THE <curses.h> HEADER FILE

The curses.h header file defines several global variables and data structures and defines several curses routines as macros.

Consider the defined variables and data structures. curses.h defines all the parameters used by curses routines. It also defines the integer variables LINES and COLS; when a curses program is run on a particular terminal, these variables are assigned the vertical and horizontal dimensions of the terminal screen, respectively, by the initscr( ) routine that is described later.

The header file defines the constants OK and ERR, too. Most curses routines have return values; the OK value is returned if a routine is properly completed, and the ERR value if some error occurs.

Home


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; a curses program uses the environment variables to determine the size of a screen.

Whenever the chapter refers to environment variables, use the $ to distinguish them from C declarations in the curses.h header file.

For more information about these variables, see the following sections on initscr( ), refresh( ), and endwin( ) routines, more about initscr( ), and lines and columns.

curses.h defines many curses routines as macros that call other macros or curses routines. For instance, the simple refresh( ) routine is a macro. The following line shows that when refresh is called, it is expanded to call the curses routine wrefresh( ).

The latter routine in turn calls two curses routines -- wnoutrefresh( ) and doupdate( ). Many other routines also group two or three routines together to achieve a particular result.

NOTE

Macro expansion in curses programs may cause problems with certain C features, such as the use of automatic incrementing variables.

curses.h automatically includes the stdio.h and termio.h tty driver interface file. Including either file again is harmless but unnecessary.

9.3.1.2 THE initscr( ), refresh( ), endwin( ) ROUTINES

These routines initialize a terminal screen to a curses state, update the contents of the screen, and restore the terminal to an out-of-curses state, respectively. Use the simple program introduced earlier to learn about each of these routines.

Home


A curses program usually starts by calling initscr( ); the program should call initscr( ) only once. Using the environment variable $TERM, as the section on how curses and terminfo work together describes, this routine determines what terminal is being used. 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, the following moves the cursor to the left of the middle of the screen.

Then the following line, writes 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 curses 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). The vertical coordinate is given first and the horizontal second, which is the opposite of the more 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. Before this, an internal representation of the screen called a window is updated. This is a very important concept, which is discusses later in Section 9.3.5 on more about refresh( ) and windows.

A curses program ends by calling endwin( ). This routine restores all terminal settings and positions the cursor at the lower left corner of the screen.

9.3.2 Compiling a curses Program

Compile programs that include curses routines as C language programs using the cc(1) command, which invokes the C compiler.

The routines are usually stored in the /usr/lib/libcurses.a library. To direct the link editor to search this library, use the -l option with the cc command.

The general command line for compiling a curses program follows.

file.c is the name of the source program;file is the executable object module.

Home


9.3.3 Running a curses Program

curses programs count on certain information being in a user's environment to run properly. Specifically, include the following lines in their files.

For an explanation of these lines, see Section 9.2.3 on how curses and terminfo work together. Users 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 a curses program does not run as expected, it can be debugged with the symbolic debugger, sdb(1), documented in the SUPER-UX Programmer's Reference Manual. When using sdb, keep a few points in mind. First, a curses 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 curses program is not aware.

A curses program outputs 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.

Setting break points on curses routines that are macros, such as refresh( ), does not work. You must use the routines defined for these macros instead; for example, users have to use wrefresh( ) instead of refresh( ). See the previous section on the curses.h header file for more information about macros.

9.3.4 More About initscr( ), LINES, and COLUMNS

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 data base, unless these values are overridden by the values of the environment $LINES and $COLUMNS.

9.3.5 More About refresh( ) and Windows

curses 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 in an editor. When users invoke vi(1) to edit a file, the changes they make to the file are reflected in the buffer. The changes become part of the permanent file only when users write or exit the file with the w or ZZ command, respectively. Similarly, when users invoke a screen program of curses routines, they change the contents of a window. The changes become part of the current terminal screen only when refresh( ) is called.

The curses.h header file supplies a default window named stdscr (standard screen) which is the size of the current terminal's screen. The header file defines stdscr to a WINDOW* type, a pointer to a C structure that users 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.

Home


When refresh( ) is called, it compares the two screen images and sends a stream of characters to the terminal that make the current screen look like stdscr. A curses 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. It optimizes output by printing as few characters as is possible. Figure 9-1 illustrates what happens when users execute the sample curses program that prints BullsEye at the center of a terminal screen. Notice that the terminal screen retains whatever garbage is on it until the first refresh( ) is called. This clears the screen and updates it with the current contents of stdscr.

Figure 9-1 Relationship Between stdscr and the Terminal Screen

Home


Users 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 do not affect the other window.

You can also subdivide a screen into many windows, refreshing each one of them as desired. When windows overlap, the contents of the current screen show the most recently refreshed window. It's also possible to create a window within a window; the smaller window is called a subwindow. Assume that users are designing an application that uses forms, for example, an expense voucher, as a user interface. Users could use subwindows to control access to certain fields on the form.

Some curses 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 they have a particularly large window or only need part of the window on the screen at any one time. For example, a pad might be used for an application with a spread sheet.

Figure 9-2 shows what a pad, subwindow, and other windows could look like in comparison to a terminal screen.

Figure 9-2 Multiple Windows and Pads Mapped to a Terminal Screen

The section on building windows and pads describes routines that users invoke to create and use windows and pads. To see a curses program with windows now, turn to the window program in the section on curses program examples later in this chapter.

Home


9.3.6 Getting Simple Output

The routines that curses provides for writing to stdscr are similar to those provided by the stdio(3S) library for writing to a file. They let users do the following.

Following are descriptions and examples of these routines.

NOTE

The curses library provides its own set of output and input functions. Other I/O routines or system calls, such as read(2) and write(2), should not be used in a curses program. They may cause undesirable results when the program is run.

Home


NAME

SYNOPSIS

DESCRIPTION

The output from the following example program appears as follows.

Home


NAME

SYNOPSIS

DESCRIPTION

Home


NAME

SYNOPSIS

DESCRIPTION

Home


NAME

SYNOPSIS

DESCRIPTION

Home


Home


NAME

SYNOPSIS

DESCRIPTION

Home


NAME

SYNOPSIS

DESCRIPTION

Home


9.3.7 Getting Simple Input

curses routines for reading from the current terminal are similar to those provided by the stdio(3S) library for reading from a file. They let you

The primary routine is getch( ), that processes a single input character and then returns that character. This routine is like the C library routine getchar(3S) except that it makes several terminal- or system-dependent options available that are not possible with getchar( ). For example, users can use getch( ) with the curses keypad( ) routine, that allows a curses program to interpret extra keys on a user's terminal, such as arrow keys, function keys, and other special keys that transmit escape sequences, and treat them as just another key. See the descriptions of getch( ) and keypad( ) on the curses(3X) manual page for more information about keypad( ).

The following pages describe and give examples of the basic routines for getting input in a screen program.

Home


NAME

SYNOPSIS

DESCRIPTION

Home


NAME

SYNOPSIS

DESCRIPTION

Home


NAME

SYNOPSIS

DESCRIPTION

Home


9.3.8 Controlling Output and Input

This section describes output attributes and input options.

9.3.8.1 OUTPUT ATTRIBUTES

When discussing addch( ), it was mentioned that it writes a single character of the chtype type to stdscr. chtype has two parts -- a part with information about the character itself and another part with information about a set of attributes associated with the character. The attributes allow a character to be printed in reverse video, bold, underlined, and so on.

stdscr always has a set of current attributes that it associates with each character as it is written. However, using the attrset( ) routine and related curses routines described later, users can change the current attributes. The following is a list of the attributes and what they mean.

To use these attributes, pass them as arguments to attrset( ) and related routines; they can also be ORed with the bitwise OR( | ) to addch( ).

NOTE

Not all terminals are capable of displaying all attributes. If a particular terminal cannot display a requested attribute, a curses program attempts to find a substitute attribute. If none is possible, the attribute is ignored.

Consider one of these attributes. To display a word in bold, users would use the following code.

Attributes can be turned on singly, such as attrset(A_BOLD) in the example, or in combination. To turn on blinking, bold text, users would use attrset(A_BLINK|A_BOLD). Individual attributes can be turned on and off with the curses routines attron( ) and attroff( ) without affecting other attributes. attrset(0) turns all attributes off.

Home


You might use A_STANDOUT to make text attract the attention of a user. The particular hardware attribute used for standout is the most visually pleasing attribute a terminal has. Standout is typically implemented as reverse video or bold. Many programs don't really need a specific attribute, such as bold or reverse video, but instead just need to highlight some text. For such applications, the A_STANDOUT attribute is recommended. Two convenient functions, standout( ) and standend( ) can be used to turn this attribute on and off. standend( ) turns off all attributes.

In addition to these attributes, two bit masks are called: A_CHARTEXT and A_ATTRIBUTES. These bit masks can be used with the curses function inch( ) and the C logical AND (&) operator to extract the character or attributes of a position on a terminal screen. See the discussion of inch( ) on the curses(3X) manual page.

Following are descriptions of attrset( ) and the other curses routines that users can use to manipulate attributes.

Home


NAME

SYNOPSIS

DESCRIPTION

Home


NAME

SYNOPSIS

DESCRIPTION

Home


NAME

SYNOPSIS

DESCRIPTION

Home


9.3.8.2 INPUT OPTIONS

The UNIX system does a considerable amount of processing on input before an application ever sees a character. For example, it does the following.

Because a curses program maintains total control over the screen, curses turns off echoing on the UNIX system and does echoing itself. At times, users may not want the UNIX system to process other characters in the standard way in an interactive screen management program. Some curses routines, noecho( ) and cbreak( ), for example, have been designed so that users can change the standard character processing. Using these routines in an application controls how input is interpreted.

Every curses program accepting input should set some input options. This is because when the program starts running, the terminal on which it runs may be in cbreak( ), raw( ), nocbreak( ), or noraw( ) mode. Although the curses program starts up in echo( ) mode, as Table 9-1 shows, none of the other modes are guaranteed.

The combination of noecho( ) and cbreak( ) is most common in interactive screen management programs. Suppose, for instance, that users do not want the characters sent to the application program to be echoed wherever the cursor currently happens to be; instead, they want them echoed at the bottom of the screen. The curses noecho( ) routine is designed for this purpose. However, when noecho( ) turns off echoing, normal erase and kill processing is still on. Using the routine cbreak( ) causes these characters to be uninterpreted.

Table 9-1 shows some of the major routines for controlling input.

Home


Table 9-1 Input Option Settings for curses Programs

Input OptionsInterpretedUninterpreted
Normal out of curses state interrupt, quit
stripping
Return to <NL>
echoing
erase, kill
EOF

Normal curses startup stateechoing (simulated)All else undefined.
cbreak( ) and echo( ) interrupt, quit
stripping
echoing
erase, kill
EOF
cbreak( ) and noecho( )interrupt, quit
stripping
echoing
erase, kill
EOF
nocbreak( ) and noecho( )break, quit
stripping
erase, kill
EOF
echoing
nocbreak( ) and echo( )See the following note
nl( )
Return
and <NL>

nonl( )
Return and <NL>
raw( ) (instead of cbreak( ))
break, quit
stripping

NOTE

Do not use the combination nocbreak( ) and noecho( ). If you use it in a program and also use getch( ), the program goes in and out of cbreak( ) mode to get each character. Depending on the state of the tty driver when each character is typed, the program may produce undesirable output.

In addition to the routines noted in Table 9-1, you can use the curses routines noraw( ), halfdelay( ), and nodelay( ) to control input. See the curses(3X) manual page for discussions of these routines.

The next few pages describe noecho( ), cbreak( ) and the related routines echo( ) and nocbreak( ) in more detail.

Home


NAME

SYNOPSIS

DESCRIPTION

Home


NAME

SYNOPSIS

DESCRIPTION

Home


9.3.9 Building Windows and Pads

In an earlier section in this chapter, more about refresh( ) and windows explained what windows and pads are and why users might want to use them. This section describes the curses routines users use to manipulate and create windows and pads.

9.3.9.1 OUTPUT AND INPUT

The routines that users use to send output to, and get input from, windows and pads are similar to those used with stdscr. The only difference is that users have to give the name of the window to receive the action. Generally, these functions have names formed by putting the letter w at the beginning of the name of a stdscr routine and adding the window name as the first parameter. For example, addch('c') would become waddch(mywin, 'c') if they wanted to write the character c to the window mywin. The following lists window (or w) versions of output routines discussed in the preceding section on getting simple output and input.

These routines differ from the versions that manipulate stdscr only in their names and the addition of a win argument. The routines whose names begin with mvw take the win argument before the y, x coordinates, which is contrary to what the names imply. See curses(3X) for more information about these routines or the versions of the input routines getch, getstr( ), and so on that users should use with windows.

All w routines can be used with pads except for wrefresh( ) and wnoutrefresh( ); use prefresh( ) and pnoutrefresh( ) with pads.

Home


9.3.9.2 THE wnoutrefresh( ) AND doupdate( ) ROUTINES

refresh( ) sends the output from stdscr to the terminal screen. It is a macro that expands to wrefresh(stdscr). See Sections 9.3.1 and 9.3.5.

The wrefresh( ) routine sends the contents of a window (stdscr or one that you create) to a screen; it calls the routines wnoutrefresh( ) and doupdate( ). Similarly, prefresh( ) sends the contents of a pad to a screen by calling pnoutrefresh( ) and doupdate( ).

Using wnoutrefresh( ) or pnoutrefresh( ) (this discussion is limited to the former routine for simplicity) and doupdate( ), users can update terminal screens with more efficiency than using wrefresh( ) by itself.

wrefresh( ) works by first calling wnoutrefresh( ) which copies the named window to a data structure referred to as the virtual screen.

The virtual screen contains what a program intends to display at a terminal. After calling wnoutrefresh( ), wrefresh( ) calls doupdate( ), which compares the virtual screen to the physical screen and does the actual update. To output several windows at once, call wrefresh( ) results in alternating calls to wnoutrefresh( ) and doupdate( ). This causes several bursts of output to a screen. However, by calling wnoutrefresh( ) for each window and then doupdate( ) only once, users can minimize the total number of characters transmitted and the processor time used. The following sample program uses only one doupdate( ).

A new window is declared at the beginning of a curses program. The following lines declare two windows named w1 and w2 with the newwin( ) routine. newwin( ) is discussed in more detail later.

Figure 9-3 illustrates the effect of wnoutrefresh( ) and doupdate( ) on these two windows, the virtual screen, and the physical screen.

Home


Figure 9-3 Relationship Between a Window and a Terminal Screen

Home


Figure 9-3 Relationship Between a Window and a Terminal Screen (cont'd)

Home


Figure 9-3 Relationship Between a Window and a Terminal Screen (cont'd)

Home


9.3.9.3 NEW WINDOWS

Descriptions of the newwin( ) and subwin( ) routines follow. Users create new windows with these routines. For information about creating new pads with newpad( ) and subpad( ), see the curses(3X) manual page.


NAME

SYNOPSIS

DESCRIPTION

Home


NAME

SYNOPSIS

DESCRIPTION

NOTE

Subwindows of subwindows are not currently supported.

Home


9.3.10 Using Advanced curses Features

With basic curses routines to output and input with windows, users can design screen management programs that meet the needs of many tasks. The curses library, however, has routines that let users do more in a program than handle I/O and multiple windows. This section briefly describes some of these routines and what they can help users do -- namely, draw simple graphics, use a terminal's soft labels, and work with more than one terminal in a single curses program.

Users should be comfortable using the routines previously discussed in this chapter and the other routines for I/O and window manipulation discussed on the curses(3X) manual page before trying to use advanced curses features.

9.3.10.1 ROUTINES FOR DRAWING LINES AND OTHER GRAPHICS

Many terminals have an alternate character set for drawing simple graphics (or glyphs or graphic symbols). Users can use this character set in curses programs. curses use the same names for glyphs as the VT100 line drawing character set.

To use the alternate character set in a curses program, users pass a set of variables whose names begin with ACS_ to the curses routine waddch( ) or a related routine. For example, ACS_ULCORNER is the variable for the upper left-corner glyph. If a terminal has a line drawing character for this glyph, ACS_ULCORNER's value is the terminal's character for the glyph OR'd( | ) with the A_ALTCHARSET bit-mask. If no line drawing character is available for that glyph, a standard ASCII character that approximates the glyph is stored in its place. For example, the default character for ACS_HLINE, a horizontal line, is a - (minus sign). When a close approximation is not available, a + (plus sign) is used. All the standard ACS_ names and their defaults are listed on the curses(3X) manual page.

Part of an example program that uses line drawing characters follows. The example uses the curses routine box( ) to draw a box around a menu on a screen. box( ) uses the line drawing characters by default or when | (the pipe) and - are chosen. (See curses(3X).) Up and down more indicators are drawn on the box border (using ACS_UARROW and ACS_DARROW) if the menu contained within the box continues above or below the screen.

Home


The down arrow, which is default, is not easily read from a screen containing many lowercase characters and users can change the down arrow (lowercase v) to an uppercase V.

For more information, see curses(3X) in the SUPER-UX Programmer's Reference Manual.

9.3.10.2 ROUTINES FOR USING SOFT LABELS

Another feature available on most terminals is a set of soft labels across the bottom of their screens. A terminal's soft labels are usually matched with a set of hard function keys on the keyboard. There are usually eight of these labels, each of which is usually eight characters wide and one or two lines high.

The curses library has routines that provide a uniform model of eight soft labels on the screen. If a terminal does not have soft labels, the bottom line of its screen is converted into a soft label area. It is not necessary for the keyboard to have hard function keys to match the soft labels for a curses program to make use of them.

Following is a brief discussion of most of the curses routines needed to use soft labels: slk_init( ), slk_set( ), slk_refresh( ) and slk_noutrefresh( ), slk_clear, and slk_restore.

When users use soft labels in a curses program, they have to call the slk_int( ) routine before initscr( ). This sets an internal flag for initscr( ) to look at that says to use the soft labels. If initscr( ) discovers that there are fewer than eight soft labels on the screen, that they are smaller than eight characters in size, or that there is no way to program them, then it removes a line from the bottom of stdscr to use for the soft labels. The size of stdscr and the LINES variable are reduced by 1 to reflect this change. A properly written program, one that is written to use the LINES and COLS variables, continues to run as if the line had never existed on the screen.

slk_init( ) takes a single argument. It determines how the labels are grouped on the screen should a line get removed from stdscr. The choices are between a 3-2-3 arrangement as appears on AT&T terminals, or a 4-4 arrangement as appears on Hewlett-Packard terminals. The curses routines adjust the width and placement of the labels to maintain the pattern. The widest label generated is eight characters.

The routine slk_set( ) takes three arguments, the label number (1-8), the string to go on the label (up to eight characters), and the justification within the label (0 = left-justified, 1 = centered, and 2 = right-justified).

The slk_noutrefresh( ) routine is comparable to wnoutrefresh( ) in that it copies the label information onto the internal screen image, but it does not cause the screen to be updated. Since a wrefresh( ) commonly follows, slk_noutrefresh( ) is the function that is most commonly used to output the labels.

Just as wrefresh( ) is equivalent to a wnoutrefresh( ) followed by a doupdate( ), so too the function

slk_refresh( ) is equivalent to a slk_noutrefresh( ) followed by a doupdate( ).

To prevent the soft labels from getting in the way of a shell escape, slk_clear( ) may be called before doing the endwin( ). This clears the soft labels off the screen and does a doupdate( ). The slk_restore( ) function may be used to restore them to the screen. See the curses(3X) manual page for more information about the routines for using soft labels.

Home


9.3.10.3 WORKING WITH MORE THAN ONE TERMINAL

A curses program can produce output on more than one terminal at the same time. This is useful for single process programs that access a common data base, such as multi-player games.

Writing programs that output to multiple terminals is a difficult business, and the curses library does not solve all the problems users might encounter. For instance, the programs not the library routines must determine the file name of each terminal line, and what kind of terminal is on each of those lines. The standard method, checking $TERM in the environment, does not work, because each process can only examine its own environment.

Another problem users might face is that of multiple programs reading from one line. This situation produces a race condition and should be avoided. However, a program trying to take over another terminal cannot just shut off whatever program is currently running on that line. (Usually, security reasons would also make this inappropriate. But, for some applications, such as an inter-terminal communication program, or a program that takes over unused terminal lines, it would be appropriate.) A typical solution to this problem requires each user logged in on a line to run a program that notifies a master program that the user is interested in joining the master program and tells it the notification programs process ID, the name of the tty line, and the type of terminal being used. Then the program goes to sleep until the master program finishes. When done, the master program wakes up the notification program and all programs exit.

A curses program handles multiple terminals by always having a current terminal. All function calls always affect the current terminal. The master program should set up each terminal, saving a reference to the terminals in its own variables. When it wishes to affect a terminal, it should set the current terminal as desired, and then call ordinary curses routines.

References to terminals in a curses program have the type SCREEN*. A new terminal is initialized by calling newterm(type, outfd, infd). newterm returns a screen reference to the terminal being set up. type is a character string, naming the kind of terminal being used. outfd is a stdio(3S) file pointer (FILE*) used for output to the terminal and infd a file pointer for input from the terminal.

This call replaces the normal call to initscr( ), which calls newterm (getenv(TERM), stdout, stdin).

To change the current terminal, call set_term(sp) where sp is the screen reference to be made current. set_term( ) returns a reference to the previous terminal.

It is important to realize that each terminal has its own set of windows and options. Each terminal must be initialized separately with newterm( ). Options such as cbreak( ) and noecho( ) must be set separately for each terminal. The endwin( ) and refresh( ) functions must be called separately for each terminal. The following example shows a typical scenario to output a message to several terminals.

Home


9.4 WORKING WITH terminfo ROUTINES

Some programs, such as primitives, need to use lower-level routines than those offered by the curses routines. For such programs, the terminfo routines are offered. They do not manage the terminal screen, but rather give users access to strings and capabilities that can be used to manipulate the terminal.

It is all right to use terminfo routines

NOTE

It is recommended that terminfo routines not be used except for the purposes noted, because curses routines take care of all the glitches present in physical terminals. When terminfo routines are used, glitches must be dealt with by the user. Also, these routines may change and be incompatible with previous releases.

9.4.1 What Every terminfo Program Needs

A terminfo program typically includes the header files and routines shown as follows.

The curses.h and term.h header files are required because they contain the definitions of the strings, numbers, and flags used by terminfo routines. setupterm( ) takes care of initialization. Passing this routine the values (char*)0, 1, and (int*)0 invokes reasonable defaults. If setupterm( ) can't figure out what kind of terminal users are on, it prints an error message and exits. reset_shell_mode( ) performs functions similar to endwin( ) and should be called before a terminfo program exits.

A global variable like clear_screen is defined by the setupterm( ) call. It can be output using the terminfo routines putp( ) or tputs( ), which gives a user more control. This string should not be directly output to the terminal using the printf(3S) C library routine, because it contains padding information. A program that directly outputs strings fails on terminals that require padding or that use the xon/xoff flow control protocol.

At the terminfo level, the higher-level routines like addch( ) and getch( ) are not available. It is up to users to output whatever is needed. For a list of capabilities and a description of what they do, see terminfo(4); see curses(3X) for a list of all the terminfo routines.

Home


9.4.2 Compiling and Running a terminfo Program

The general command line for compiling and the guidelines for running a program with terminfo routines are the same as those for compiling any other curses program. See the sections on compiling a curses program and running a curses program in this chapter for more information.

9.4.3 An Example terminfo Program

The example program, termhl, shows a simple use of terminfo routines. It is a version of the highlight program (see the section on curses program examples) that does not use the higher level curses routines. The termhl program can be used as a filter. It includes the strings to enter bold and underline mode and to turn off all attributes.

Home


Home


The following section discusses the tputs (cap, affcnt, outc) function in this program, which provides insight into the terminfo routines. tputs( ) applies padding information. Some terminals have the capability to delay output. Their terminal descriptions in the terminfo data base probably contain strings like $<20>, which means to pad for 20 milliseconds (see the following section on specifying capabilities in this chapter). tputs generates enough pad characters to delay for the appropriate time.

tputs( ) has three parameters. The first parameter is the string capability to be output. The second is the number of lines affected by the capability. (Some capabilities may require padding that depends on the number of lines affected. For example, insert_line may have to copy all lines below the current line, and may require time proportional to the number of lines copied. By convention, affcnt is 1 if no lines are affected. The value 1 is used, rather than 0, for safety, since affcnt is multiplied by the amount of time per item, and anything multiplied by 0 is 0.) The third parameter is a routine to be called with each character.

For many simple programs, affcnt is always 1 and outc always calls putchar. For these programs, the putp(cap) routine is a convenient abbreviation. The termhl program could be simplified by using putp( ).

Now to understand why curses-level routines should be used instead of terminfo-level routines. Whenever possible, note the special check for the underline_char capability in this sample program. Some terminals, rather than having a code to start underlining and a code to stop underlining, have a code to underline the current character. The termhl program keeps track of the current mode, and if the current character is supposed to be underlined, outputs underline_char, if necessary. Low level details such as this are precisely why the curses level is recommended over the terminfo level. curses takes care of terminals with different methods of underlining and other terminal functions. Programs at the terminfo level must handle such details themselves.

The termhl program was written to illustrate a typical use of the terminfo routines. It is more complex than it need be in order to illustrate some properties of terminfo programs. The routine vidattr (see curses(3X)) could have been used instead of directly outputting enter_bold_mode, enter_underline_mode, and exit_attribute_mode. In fact, the program would be more robust if it did, since there are several ways to change video attribute modes.

Home


9.5 WORKING WITH THE terminfo DATA BASE

The terminfo data base describes the many terminals with which curses programs, as well as some UNIX tools, like vi(1), can be used. Each terminal description is a compiled file containing the names that the terminal is known by and a group of comma-separated fields describing the actions and capabilities of the terminal. This section describes the terminfo data base, related support tools, and their relationship to the curses library.

9.5.1 Writing Terminal Descriptions

Descriptions of many popular terminals are already described in the terminfo data base. However, it is possible that users want to run a curses program on a terminal for which there is not currently a description. In that case, users have to build the description.

he general procedure for building a terminal description follows.

  1. Give the known names of the terminal.

  2. Learn about, list, and define the known capabilities.

  3. Compile the newly created description entry.

  4. Test the entry for correct operation.

  5. Go back to step 2, add more capabilities, and repeat as necessary.

Building a terminal description is sometimes easier. Small parts of the description are built and tested. These tests can expose deficiencies in the ability to describe the terminal. Also, modifying an existing description of a similar terminal can make the building task easier.

In the next few pages, follow each step required to build a terminal description for the fictitious terminal named myterm.

9.5.1.1 NAME THE TERMINAL

The name of a terminal is the first information given in a terminfo terminal description. This string of names, assuming there is more than one name, is separated by pipe symbols (|). The first name given should be the most common abbreviation for the terminal. The last name given should be a long name that fully identifies the terminal. The long name is usually the manufacturer's formal name for the terminal. All names between the first and last entries should be known synonyms for the terminal name. All names but the formal name should be typed in lowercase letters and contain no blanks. Naturally, the formal name is entered as closely as possible to the manufacturer's name.

Here is the name string from the description of the AT&T Teletype 5420 Buffered Display Terminal.

Notice that the first name is the most commonly used abbreviation and the last is the long name. Also notice the comma at the end of the name string.

The following is the name string for the fictitious terminal, myterm.

Terminal names should follow common naming conventions. These conventions start with a root name, like 5425 or myterm, for example. The root name should not contain odd characters, like hyphens, that may not be recognized as a synonym for the terminal name. Possible hardware modes or user preferences should be shown by adding a hyphen and a mode indicator at the end of the name. For example, wide mode (which is shown by a -w) version of our fictitious terminal would be described as myterm -w. term(5) describes mode indicators in greater detail.

Home


9.5.1.2 LEARNING ABOUT CAPABILITIES

After users complete the string of terminal names for the description, users have to learn about the terminal's capabilities so that users can properly describe them. To learn about the capabilities the terminal has, users should do the following.

Home


9.5.1.3 SPECIFYING CAPABILITIES

Once the capabilities of the terminal are known, they have to be describe in the terminal description. Describe them with a string of comma-separated fields that contain the abbreviated terminfo name and, in some cases, the terminal's value for each capability. For example, bel is the abbreviated name for the beeping or ringing capability. On most terminals, a Ctrl-G is the instruction that produces a beeping sound. Therefore, the beeping capability would be shown in the terminal description as bel=^G.

The list of capabilities may continue onto multiple lines as long as white space (tabs and spaces) begins every line but the first of the description. Comments can be included in the description by putting a pound sign (#) at the beginning of the line.

The terminfo(4) manual page has a complete list of the capabilities users can use in a terminal description. This list contains the name of the capability, the abbreviated name used in the data base, the two-letter code that corresponds to the old termcap data base name, and a short description of the capability. The abbreviated name that users provide in the data base descriptions is shown in the column titled Capname.

NOTE

For a curses program to run on any given terminal, its description in the terminfo data base must include, at least, the capabilities to move a cursor in all four directions and to clear the screen.

A terminal's character sequence (value) for a capability can be a keyed operation (like Ctrl-G), a numeric value, or a parameter string containing the sequence of operations required to achieve the particular capability. In a terminal description, certain characters are used after the capability name to show what type of character sequence is required. Explanations of these characters follow.

#
This shows a numeric value is to follow. This character follows a capability that needs a number as a value. For example, the number of columns is defined as cols#80.

=
This shows that the capability value is the character string that follows. This string instructs the terminal how to act and may actually be a sequence of commands. There are certain characters used in the instruction strings that have special meanings. These special characters follow.

Sometimes, it may be necessary to comment out a capability so that the terminal ignores this particular field. This is done by placing a period (.) in front of the abbreviated name for the capability. For example, if users would like to comment out the beeping capability, the description entry would appear as

With this background information about specifying capabilities, let's add the capability string to our description of myterm. Consider basic, screen-oriented, keyboard-entered, and parameter string capabilities.

Home


9.5.1.4 BASIC CAPABILITIES

Some capabilities common to most terminals are bells, columns, lines on the screen, and overstriking of characters, if necessary. Suppose the fictitious terminal has these and a few other capabilities, listed later. The list gives the abbreviated terminfo name for each capability in the parentheses following the capability description.

By combining the name string (see the preceding section on naming the terminal) and the capability descriptions, the following general terminfo data base entry is generated.

9.5.1.5 SCREEN-ORIENTED CAPABILITIES

Screen-oriented capabilities manipulate the contents of a screen. Our example terminal myterm has the following screen-oriented capabilities. Again, the abbreviated command associated with the given capability is shown in parentheses.

Home


9.5.1.6 KEYBOARD-ENTERED CAPABILITIES

Keyboard-entered capabilities are sequences generated when a key is typed on a terminal keyboard. Most terminals have, at least, a few special keys on their keyboard, such as arrow keys and the backspace key. Our example terminal has several of these keys whose sequences are, as follows:

Adding this new information to our data base entry for myterm produces the following.

Home


9.5.1.7 PARAMETER STRING CAPABILITIES

Parameter string capabilities are capabilities that can take parameters for example, those used to position a cursor on a screen or turn on a combination of video modes. To address a cursor, the cup capability is used and is passed two parameters: the row and column to address. String capabilities, such as cup and set attributes (sgr) capabilities, are passed arguments in a terminfo program by the tparm( ) routine.

The arguments to string capabilities are manipulated with special % sequences similar to those found in a printf(3S) statement. In addition, many of the features found on a simple stack-based RPN calculator are available. cup, as noted previously, takes two arguments: the row and column. sgr, takes nine arguments, one for each of the nine video attributes. See terminfo(4) for the list and order of the attributes and further examples of sgr.

The fancy terminal's cursor position sequence requires a row and column to be output as numbers separated by a semicolon, preceded by Esc-[ and followed with H. The coordinate numbers are 1-based rather than 0-based.

Integer arguments are pushed onto the stack with a %p sequence followed by the argument number, such as %p2 to push the second argument. A shorthand sequence to increment the first two arguments is %i. To output the top number on the stack as a decimal, a %d sequence is used, exactly as in printf.

The terminal's cup sequence is built up as follows.

cup =Meaning
\E[Output Esc-[
%iIncrement the two arguments
%p1Push the first argument (the row) onto the stack
%dOutput the row as a decimal
;Output a semicolon
%p2Push the second argument (the column) onto the stack
%dOutput the column as a decimal
HOutput the trailing letter

or

cup=\E[%i%pl%d;%p2%dH,

Adding this new information to our data base entry for myterm produces the following.

     myterm|mytm|mine|fancy|terminal|My FANCY Terminal,
                    am, bel=^G, cols#80, lines#30, xon,
                    cr=^M, cuul=^K, cudl=^J, cubl=^H, cufl=^L,
                    smso=\ED, rmso=\EZ, el=\EK$<3>, ind=0,
                    kbs=^H, kcuu1=\E[A, kcud1=\E[B, kcuf1=\E[C,
                    kcub1=\E[D, khome=\E[H,
                    cup=\E[%i%p1%d;%p2%dH,

See terminfo(4) for more information about parameter string capabilities.

Home


9.5.1.8 COMPILING THE DESCRIPTION

The terminfo data base entries are compiled using the tic compiler. This compiler translates terminfo data base entries from the source format into the compiled format.

The source file for the description is usually in a file suffixed with .ti. For example, the description of myterm would be in a source file named myterm.ti. The compiled description of myterm would usually be placed in /usr/share/lib/terminfo/m/myterm, since the first letter in the description entry is m. Links would also be made to synonyms of myterm, for example, to /f/fancy.

If the environment variable $TERMINFO were set to a directory and exported before the entry was compiled, the compiled entry would be placed in the $TERMINFO directory. All programs using the entry would then look in the new directory for the description file if $TERMINFO were set, before looking in the default /usr/share/lib/terminfo. The general format for the tic compiler is as follows:

tic [-v] [-c] file

The -v option causes the compiler to trace its actions and output information about its progress. The -c option causes a check for errors; it may be combined with the -v option. file shows what file is to be compiled. If users want to compile more than one file at the same time, users have to first use cat(1) to join them together. The following command line shows how to compile the terminfo source file for our fictitious terminal.

Refer to the tic(1M) manual page in the SUPER-UX System Administrator's Reference Manualfor more information about this compiler.

9.5.1.9 TESTING THE DESCRIPTION

Let's consider three ways to test a terminal description. First, it can be tested by setting the environment variable $TERMINFO to the path name of the directory containing the description. If programs run the same on the new terminal as they did on the older known terminals, then the new description is functional.

Second, users can test for correct insert line padding commenting xon in the description and then editing (using vi(1)) a large file (over 100 lines) at 9600 baud (if possible), and deleting about 15 lines from the middle of the screen. Type u (undo) several times quickly. If the terminal does not function, more padding is usually required. A similar test can be used for inserting a character.

Third, users can use the tput(1) command. This command outputs a string or an integer according to the type of capability being described. If the capability is a Boolean expression, then tput sets the exit code (0 for TRUE, 1 for FALSE) and produces no output. The general format for the tput command is as follows.

The type of terminal users are requesting information about is identified with the -Ttype option. Usually, this option is not necessary because the default terminal name is taken from the environment variable $TERM. The capname field is used to show which capability to output from the terminfo data base.

The following command line shows how to output the clear screen character sequence for the terminal being used.

The following command line shows how to output the number of columns for the terminal being used.

The tput(1) manual page found in the SUPER-UX User's Reference Manualcontains more information on the usage and possible messages associated with this command.

Home


9.5.2 Comparing or Printing terminfo Descriptions

Sometime users may want to compare two terminal descriptions or quickly look at a description without going to the terminfo source directory. The infocmp(1M) command was designed to help with both of these tasks. Compare two descriptions of the same terminal.

Example:

To print out the terminfo source for the 5420, enter the following.

9.5.2.1 CONVERTING termcap DESCRIPTIONS TO terminfo DESCRIPTIONS

NOTE

The terminfo data base is designed to take the place of the termcap data base. Because of the many programs and processes that have been written with and for the termcap data base, it is not feasible to do a complete cutover at one time. Any conversion from termcap to terminfo requires some experience with both data bases. All entries into the data bases should be handled with extreme caution. These files are important to the operation of the terminal.

The captoinfo(1M) command converts termcap descriptions to terminfo(4) descriptions. When a file is passed to captoinfo, it looks for termcap descriptions and writes the equivalent terminfo descriptions on the standard output. For example, the following converts the file /etc/termcap to terminfo source, preserving comments and other extraneous information within the file.

The command line looks up the current terminal in the termcap data base, as specified by the $TERM and $TERMCAP environment variables and converts it to terminfo.

If you must have both termcap and terminfo terminal descriptions, keep the terminfo description only and use infocmp -C to get the termcap descriptions.

If users have been using cursor optimization programs with the -ltermcap or -ltermlib option in the cc command line, those programs are still functional. However, these options should be replaced with the -lcurses option.

Home


9.6 curses PROGRAM EXAMPLES

The following examples demonstrate curses routines.

9.6.1 The Editor Program

This program illustrates how to use curses routines to write a screen editor. For simplicity, editor keeps the buffer in stdscr; obviously, a real screen editor would have a separate data structure for the buffer. This program has many other simplifications -- no provision is made for files of any length other than the size of the screen, for lines longer than the width of the screen, or for control characters in the file.

Several points about this program are worth making. First, it uses the move( ), mvaddstr( ), flash( ), wnoutrefresh( ) and clrtoeol( ) routines. These routines are all discussed in the section on working with curses routines.

Second, it also uses some curses routines that have not been discussed. For example, the function to write out a file uses the mvinch( ) routine, which returns a character in a window at a given position. The data structure used to write out a file does not keep track of the number of characters in a line or the number of lines in the file, so trailing blanks are eliminated when the file is written. The program also uses the insch( ), delch( ), insertln( ), and deleteln( ) routines. These functions insert and delete a character or line. See curses(3X) for more information about these routines.

Third, the editor command interpreter accepts special keys, as well as ASCII characters. On one hand, new users find an editor that handles special keys easier to learn. For example, it's easier for new users to use the arrow keys to move a cursor than it is to memorize that the letter h means left, j means down, k means up, and l means right. On the other hand, experienced users usually use ASCII characters to avoid moving their hands from the home row position to use special keys.

NOTE

Not all terminals have arrow keys. The curses programs work on more terminals if there is an ASCII character associated with each special key.

Fourth, the Ctrl-L command illustrates a feature most programs using curses routines should have. Often some program beyond the control of the routines writes something to the screen (for instance, a broadcast message) or some line noise affects the screen so much that the routines cannot keep track of it. A user invoking editor can type Ctrl-L, causing the screen to be cleared and redrawn with a call to wrefresh (curscr).

Finally, another important point is that the input command is terminated by Ctrl-D, not the Esc key. It is very tempting to use Esc as a command, since it is one of the few special keys available on every keyboard. (Return and Break are the only others.) However, using Esc as a separate key introduces an ambiguity. Most terminals use sequences of characters beginning with Esc, such as escape sequences, to control the terminal and have special keys that send escape sequences to the computer. If a computer receives an escape from a terminal, it cannot tell whether the user pressed the Esc key or whether a special key was pressed.

editor and other curses programs handle the ambiguity by setting a timer. If another character is received during this time, and if that character might be the beginning of a special key, the program reads more input until either a full special key is read, the time out is reached, or a character is received that could not have been generated by a special key. While this strategy works most of the time, it is not foolproof. It is possible for the user to press Esc and another key quickly, causing the curses program to think a special key has been pressed. Also, a pause occurs until the escape can be passed to the user program, resulting in a slower response to the Esc key.

Home


Many existing programs use escape as a fundamental command that cannot be changed without infuriating a large class of users. These programs cannot make use of special keys without dealing with this ambiguity and, at best, must resort to a time-out solution. When designing the curses programs, avoid the Esc key.

     /*     editor: a screen-oriented editor. The user
      *     interface is similar to a subset of vi.
      *     The buffer is kept in stdscr to simplify
      *     the program.
      */

     #include <stdio.h>
     #include <curses.h>

     #define CTRL(c) ((c) & 037)

     main(argc, argv)
     int argc;
     char **argv;
     {
          extern void perror( ), exit( );
               int i, n, l;
               int c;
               int line = 0;
               FILE *fd;

               if (argc != 2)
               {
                    fprintf(stderr, "Usage: %s file\n", argv[0]);
                    exit(1);
               }

               fd = fopen(argv[1], "r");
               if (fd == NULL)
               {
                    perror(argv[1]);
                    exit(2);
               }

               intscr( );
               cbreak( );
               nonl( );
               noecho( );
               idlok(stderr, TRUE);
               keypad(stdscr, TRUE);

               /* Read in the file */
               while ((c = getc(fd)) != EOF)
               {
                    if (c == '\n')
                              line++;
                    if (line > LINES - 2)
                              break;
                    addch(c);
               }

Home


               fclose(fd);

               move(0,0);
               refresh( );
               edit( );

               /* Write out the file */
               fd = fopen(argv[1], "w");
               for (1 = 0; 1 < LINES - 1; 1++)
               {
                    n = len(1);
                    for (i = 0, i < n; i++)
                         putc(mvinch(1, i) & A_CHARTEXT, fd);
                    putc('\n', fd);
               }
               fclose(fd);

               endwin( );
               exit(0);
     }

     len(lineno)
     int lineno;
     {
               int linelen = COLS - 1;

               while (linelen >= 0 && mvinch(lineno, linelen) == ' ')
                         linelen--;
               return linelen + 1;
     }

     /*     Global value of current cursor position */
     int row, col;

     edit( )
     {
               int c;
               for (;;)
     {
               move(row, col);
               refresh( );
               c = getch( );

               /* Editor commands */
               switch (c)
               {

               /* hjkl and arrow keys: move cursor
                * in direction indicated */
               case 'h':
               case KEY_LEFT:
                    if (col > 0)
                              col--;
                    else
                              flash( );
                    break;

Home


               case 'j':
               case KEY_DOWN:
                    if (row < LINES - 1)
                              row++;
                    else
                              flash( );
                    break;

               case 'k':
               case KEY_UP:
                    if (row > 0)
                              row--;
                    else
                              flash( );
                    break;

               case 'l':
               case KEY_RIGHT:
                    if (col < COLS - 1)
                              col++;
                    else
                              flash( );
                    break;
               /* i: enter input mode */
               case KEY_IC:
               case 'i':
                    input( );
                    break;

               /* x: delete current character */
               case KEY_DC:
               case 'x':
                    delch( );
                    break;

               /* o: open up a new line and enter input mode */
               case KEY_IL:
               case 'o':
                    move(++row, col = 0);
                    insertln( );
                    input( );
                    break;

               /* d: delete current line */
               case KEY_DL:
               case 'd':
                    deleteln( );
                    break;

Home


               /* ^L: redraw screen */
               case KEY_CLEAR:
               case CTRL('L'):
                    wrefresh(curscr);
                    break;

               /* w: write and quit */
               case 'w':
                    return;

               /* q: quit without writing */
               case 'q':
                    endwin( );
                    exit(2);
               default:
                    flash( );
                    break;
               }
          }
     }

     /*     Insert mode: accept characters and insert them.
      *     End with ^D or EIC
      */
     input( );
     {
          int c;

          standout( );
          mvaddstr(LINES - 1, COLS - 20, "INPUT MODE");
          standend( );
          move(row, col);
          refresh( );
          for (;;)
          {
               c = getch( );
               if (c == CTRL('D') || c == KEY_EIC)
                         break;
               insch(c);
               move(row, ++col);
               refresh( );
          }
          move(LINES - 1, COLS - 20);
          clrtoeol( );
          move(row, col);
          refresh( );
     }

Home


9.6.2 The Highlight Program

This program illustrates the attrset( ) routine. Highlight reads a text file and uses embedded escape sequences to control attributes. \U turns on underlining, \B turns on bold, and \N restores the default output attributes.

The first call to scrollok( ), a routine not previously discussed. See curses(3X). This routine allows the terminal to scroll if the file is longer than one screen. When an attempt is made to draw past the bottom of the screen, scrollok( ) automatically scrolls the terminal up a l