Using the curses library

Below is a sample program that uses curses. The program is followed by a description of how it works. In this example, all updating, reading, and so on is applied to stdscr. You can use these instructions on a window by supplying appropriate function names and parameters.

#include <curses.h>
#include <signal.h>
static void finish(int sig);
main(int argc, char *argv[])
{
/* initialize non-curses data structures here */
   /* arrange interrupts to terminate */
(void) signal(SIGINT, finish);
   /* initialize the curses library */
(void) initscr( );
   /* enable keyboard mapping */
keypad(stdscr, TRUE);
   /* tell curses not to do NL->CR/NL on output
	*/
(void) nonl( );
   /* take input chars one at a time, no wait for
	\n */
(void) cbreak( );
   /* don’t echo input */
(void) noecho( );
if (has_colors( ))
{
   start_color( );
   /*
	* Simple color assignment,
	* often all we need.
	*/
   init_pair(COLOR_BLACK, COLOR_BLACK,
	 COLOR_BLACK);
   init_pair(COLOR_GREEN, COLOR_GREEN,
	 COLOR_BLACK);
   init_pair(COLOR_RED, COLOR_RED, COLOR_BLACK);
   init_pair(COLOR_CYAN, COLOR_CYAN,
	 COLOR_BLACK);
   init_pair(COLOR_WHITE, COLOR_WHITE,
	 COLOR_BLACK);
   init_pair(COLOR_MAGENTA, COLOR_MAGENTA,
	 COLOR_BLACK);
   init_pair(COLOR_BLUE, COLOR_BLUE,
	 COLOR_BLACK);
   init_pair(COLOR_YELLOW, COLOR_YELLOW,
	 COLOR_BLACK);
}
for (;;)
{
/* refresh, accept single keystroke of input */
   int c = getch( );
   /* process the command keystroke */
}
finish(0); 		/* we’re done */
}
static void finish(int sig)
{
endwin( );
/* do your non-curses wrapup here */
exit(0);
}

Starting up

To use the curses library, the routines must know about terminal characteristics. In addition, the space for curscr and stdscr must be allocated. The function initscr() does both of these things. Because it must allocate space for the windows, it can overflow memory when attempting to do so. On the rare occasions when this happens, initscr() will terminate the program with an error message. The function must always be called before any of the routines that affect windows are used. If it is not, the program will perform a core dump as soon as either curscr or stdscr are referenced. It is usually best to wait to call initscr() until after you are sure you will need it; after checking for start-up errors, for example. Terminal status changing routines like nl() and cbreak() should be called after initscr().

After the screen windows have been allocated, you can set them up for your program. For example, if you want to allow a screen to scroll, use scrollok(). If you want the cursor to be left in place after the last change, use leaveok(); otherwise, refresh() will move the cursor to the current (y, x) coordinates of the window after updating it.

To create new windows of your own, you can use the functions newwin(), derwin(), and subwin(). You can also use the routine delwin() to remove old windows. You can apply to any window all of the options described previously.

Output

You can use the basic functions addch() and move() to change what will go on a window.

The addch() function adds a character at the current (y, x) coordinates. You can use the move() function to change the current (y, x) coordinates to whatever you want them to be. It returns ERR if you try to move off the window. As mentioned previously, you can combine the two into mvaddch() to do both things simultaneously.

The other output functions, such as addstr() and printw(), call addch() to add characters to the window.

After you have specified what you want on the window, you must call refresh() to display the changes on the terminal. In order to more efficiently, determine what has changed; refresh() assumes that any part of the window that has not changed since the last refresh() of that window has not been changed on the terminal. That is, it assumes that that you have not refreshed a portion of the terminal with an overlapping window. If this is not the case, you can use the routine touchwin() to indicate that the entire window might have been changed. This causes refresh() to look for changes in the whole subsection of the terminal for changes.

If you call wrefresh() with curscr as its argument, the screen appears as curscr would describe it. This is useful if you want to redraw the screen.

Input

The complementary function to addch() is getch(), which, if echo() is set, will call addch() to echo the character. Because the screen package must know what is on the terminal at all times, if characters are to be echoed, the tty must be in raw or cbreak mode. In addition, because the terminal initially has echoing enabled and is in ordinary "cooked" mode, one or the other must be changed before calling getch(). Otherwise, the output of the program will be unpredictable.

When you want to accept line-oriented input in a window, wgetstr() and similar functions are available. There is even a wscanw() function that can do multifield parsing on window input like scanf(). These pseudo line-oriented functions turn on echoing while they execute.

The example code above uses the call keypad(stdscr, TRUE) to enable support for function-key mapping. With this feature, the getch() code watches the input stream for character sequences that correspond to arrow and function keys. These sequences are returned as pseudo-character values. The #define values returned are listed in the <curses.h>. The mapping from sequences to #define values is determined by key_ capabilities in the terminal’s terminfo entry.

Using forms characters

The addch() function (and some others, including box() and border()) can accept some pseudo-character arguments that are specially defined by ncurses. These are #define values that are set up in the <curses.h> header; see the <curses.h> header for a complete list (look for the prefix ACS_).

The most useful of the ACS definitions are the forms-drawing characters. You can use them to draw boxes and simple graphs on the screen. If the terminal does not have such characters, <curses.h> will map them to a recognizable set of ASCII defaults.

Character attributes and color

The ncurses package supports screen highlights, including standout, reverse-video, underline, and blink. It also supports color, which is treated as another kind of highlight.

Highlights are encoded internally as high bits of the pseudo-character type (chtype) that <curses.h> uses to represent the contents of a screen cell. See the <curses.h> header file for a complete list of highlight mask values (look for the prefix A_).

There are two ways to make highlights. One is to use logical-or to combine the value of the highlights you want into the character argument of an addch() call or any other output call that takes a chtype argument.

The other way to make a highlight is to set the current-highlight value. This is combined using logical-or with any highlight you specify the first way. You do this with the functions attron(), attroff(), and attrset(); see the manual pages for details.

Color is a special kind of highlight. The package uses color pairs that are combinations of foreground and background colors. The sample code above sets up eight color pairs, all of the guaranteed-available colors on black. Note that each color pair is, in effect, given the name of its foreground color. Any other range of eight nonconflicting values could have been used as the first arguments of the init_pair() values.

Once you have done an init_pair() that creates color-pair N, you can use COLOR_PAIR(N) as a highlight that invokes that particular color combination. Note that COLOR_PAIR(N), for constant N, is itself a compile-time constant and can be used in initializers.

Finishing up

To clean up after the ncurses routines, you can use endwin(), which restores tty modes to their settings before initscr() was first called. The endwin() routine also moves the cursor down to the lower-left corner of the screen. Whenever you use initscr, it is important to use endwin() before exiting.