Writing Programs with NCURSES

by Eric S. Raymond and Zeyd M. Ben-Halim

Contents


Introduction

This document is an introduction to programming with curses. It is not an exhaustive reference for the curses Application Programming Interface (API); that role is filled by the curses manual pages. Rather, it is intended to help C programmers ease into using the package.

This document is aimed at C applications programmers not yet specifically familiar with ncurses. If you are already an experienced curses programmer, you should nevertheless read the sections on Mouse Interfacing, Debugging, and Hints, Tips, and Tricks. These will bring you up to speed on the special features and quirks of the ncurses implementation. If you are not so experienced, keep reading.

The curses package is a subroutine library for terminal-independent screen-painting and input-event handling which presents a high level screen model to the programmer, hiding differences between terminal types and doing automatic optimization of output to change one screenfull of text into another. Curses uses terminfo, which is a database format that can describe the capabilities of thousands of different terminals.

The curses API may seem something of an archaism on UNIX desktops increasingly dominated by X, Motif, and Tcl/Tk. Nevertheless, UNIX still supports tty lines and X supports xterm(1); the curses API has the advantage of (a) back-portability to character-cell terminals, and (b) simplicity. For an application that does not require bit-mapped graphics and multiple fonts, an interface implementation using curses will typically be a great deal simpler and less expensive than one using an X toolkit.

A Brief History of Curses

Historically, the first ancestor of curses was the routines written to provide screen-handling for the game rogue; these used the already- existing termcap database facility for describing terminal capabilities. These routines were abstracted into a documented library and first released with the early BSD UNIX versions.

System III UNIX from Bell Labs featured a rewritten and much-improved curses library. It introduced the terminfo format. Terminfo is based on Berkeley's termcap database, but contains a number of improvements and extensions. Parameterized capabilities strings were introduced, making it possible to describe multiple video attributes, and colors and to handle far more unusual terminals than possible with termcap. In the later AT&T System V releases, curses evolved to use more facilities and offer more capabilities, going far beyond BSD curses in power and flexibility.

Scope of This Document

This document describes ncurses, a freeware implementation of the System V curses API with some clearly marked extensions. It includes the following System V curses features:

Also, this package makes use of the insert and delete line and character features of terminals so equipped, and determines how to optimally use these features with no help from the programmer. It allows arbitrary combinations of video attributes to be displayed, even on terminals that leave ``magic cookies'' on the screen to mark changes in attributes.

The ncurses package can also capture and use event reports from a mouse in some environments (notably, xterm under the X window system). This document includes tips for using the mouse. The ncurses package was originated by Pavel Curtis. The primary maintainer of the package is Zeyd Ben-Halim <zmbenhal@netcom.com>. Eric S. Raymond <esr@snark.thyrsus.com> wrote many of the new features in versions after 1.8.1 and wrote most of this introduction.

This document also describes the extension library, similarly modeled on the SVr4 panels facility. This library allows you to associate backing store with each of a stack or deck of overlapping windows, and provides operations for moving windows around in the stack that change their visibility in the natural way (handling window overlaps).

Finally, this document describes in detail the menus and forms extension libraries, also cloned from System V, which support easy construction and sequences of menus and fill-in forms.

Terminology

In this document, the following terminology is used with reasonable consistency:
window
A data structure describing a sub-rectangle of the screen (possibly the entire screen). You can write to a window as though it were a miniature screen, scrolling independently of other windows on the physical screen.

screens
A subset of windows which are as large as the terminal screen, i.e., they start at the upper left hand corner and encompass the lower right hand corner. One of these, stdscr, is automatically provided for the programmer.

terminal screen
The package's idea of what the terminal display currently looks like, i.e., what the user sees now. This is a special screen.

The Curses Library

An Overview of Curses

Compiling Programs using Curses

In order to use the library, it is necessary to have certain types and variables defined. Therefore, the programmer must have a line:
	  #include <curses.h>
at the top of the program source. The screen package uses the Standard I/O library, so <curses.h> includes <stdio.h>. <curses.h> also includes <termios.h>, <termio.h>, or <sgtty.h> depending on your system. It is redundant (but harmless) for the programmer to do these includes, too. In linking with curses you need to have -lncurses in your LDFLAGS or on the command line. There is no need for any other libraries.

Updating the Screen

In order to update the screen optimally, it is necessary for the routines to know what the screen currently looks like and what the programmer wants it to look like next. For this purpose, a data type (structure) named WINDOW is defined which describes a window image to the routines, including its starting position on the screen (the (y, x) coordinates of the upper left hand corner) and its size. One of these (called curscr, for current screen) is a screen image of what the terminal currently looks like. Another screen (called stdscr, for standard screen) is provided by default to make changes on.

A window is a purely internal representation. It is used to build and store a potential image of a portion of the terminal. It doesn't bear any necessary relation to what is really on the terminal screen; it's more like a scratchpad or write buffer.

To make the section of physical screen corresponding to a window reflect the contents of the window structure, the routine refresh() (or wrefresh() if the window is not stdscr) is called.

A given physical screen section may be within the scope of any number of overlapping windows. Also, changes can be made to windows in any order, without regard to motion efficiency. Then, at will, the programmer can effectively say ``make it look like this,'' and let the package implementation determine the most efficient way to repaint the screen.

Standard Windows and Function Naming Conventions

As hinted above, the routines can use several windows, but two are automatically given: curscr, which knows what the terminal looks like, and stdscr, which is what the programmer wants the terminal to look like next. The user should never actually access curscr directly. Changes should be made to through the API, and then the routine refresh() (or wrefresh()) called.

Many functions are defined to use stdscr as a default screen. For example, to add a character to stdscr, one calls addch() with the desired character as argument. To write to a different window. use the routine waddch() (for `w'indow-specific addch()) is provided. This convention of prepending function names with a `w' when they are to be applied to specific windows is consistent. The only routines which do not follow it are those for which a window must always be specified.

In order to move the current (y, x) coordinates from one point to another, the routines move() and wmove() are provided. However, it is often desirable to first move and then perform some I/O operation. In order to avoid clumsiness, most I/O routines can be preceded by the prefix 'mv' and the desired (y, x) coordinates prepended to the arguments to the function. For example, the calls

	  move(y, x);
	  addch(ch);
can be replaced by
	  mvaddch(y, x, ch);
and
	  wmove(win, y, x);
	  waddch(win, ch);
can be replaced by
	  mvwaddch(win, y, x, ch);
Note that the window description pointer (win) comes before the added (y, x) coordinates. If a function requires a window pointer, it is always the first parameter passed.

Variables

The curses library sets some variables describing the terminal capabilities.
      type   name      description
      ------------------------------------------------------------------
      int    LINES     number of lines on the terminal
      int    COLS      number of columns on the terminal
The curses.h also introduces some #define constants and types of general usefulness:
bool
boolean type, actually a `char' (e.g., bool doneit;)
TRUE
boolean `true' flag (1).
FALSE
boolean `false' flag (0).
ERR
error flag returned by routines on a fail (-1).
OK
error flag returned by routines when things go right.

Using the Library

Now we describe how to actually use the screen package. In it, we assume all updating, reading, etc. is applied to stdscr. These instructions will work on any window, providing you change the function names and parameters as mentioned above.

Here is a sample program to motivate the discussion:

#include <curses.h>
#include <signal.h>

static void finish(int sig);

main(int argc, char *argv[])
{
    /* initialize your non-curses data structures here */

    (void) signal(SIGINT, finish);      /* arrange interrupts to terminate */

    (void) initscr();      /* initialize the curses library */
    keypad(stdscr, TRUE);  /* enable keyboard mapping */
    (void) nonl();         /* tell curses not to do NL->CR/NL on output */
    (void) cbreak();       /* take input chars one at a time, no wait for \n */
    (void) noecho();       /* don't echo input */

    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 (;;)
    {
        int c = getch();     /* refresh, accept single keystroke of input */

        /* 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

In order to use the screen package, the routines must know about terminal characteristics, and the space for curscr and stdscr must be allocated. These function initscr() does both these things. Since it must allocate space for the windows, it can overflow memory when attempting to do so. On the rare occasions this happens, initscr() will terminate the program with an error message. initscr() must always be called before any of the routines which affect windows are used. If it is not, the program will core dump as soon as either curscr or stdscr are referenced. However, it is usually best to wait to call it until after you are sure you will need it, like after checking for startup errors. Terminal status changing routines like nl() and cbreak() should be called after initscr().

Once the screen windows have been allocated, you can set them up for your program. If you want to, say, allow a screen to scroll, use scrollok(). If you want the cursor to be left in place after the last change, use leaveok(). If this isn't done, refresh() will move the cursor to the window's current (y, x) coordinates after updating it.

You can create new windows of your own using the functions newwin(), derwin(), and subwin(). The routine delwin() will allow you to get rid of old windows. All the options described above can be applied to any window.

Output

Now that we have set things up, we will want to actually update the terminal. The basic functions used to change what will go on a window are addch() and move(). addch() adds a character at the current (y, x) coordinates. move() changes 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 above, you can combine the two into mvaddch() to do both things at once.

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

After you have put on the window what you want there, when you want the portion of the terminal covered by the window to be made to look like it, you must call refresh(). In order to optimize finding changes, refresh() assumes that any part of the window not changed since the last refresh() of that window has not been changed on the terminal, i.e., that you have not refreshed a portion of the terminal with an overlapping window. If this is not the case, the routine touchwin() is provided to make it look like the entire window has been changed, thus making refresh() check the whole subsection of the terminal for changes.

If you call wrefresh() with curscr as its argument, it will make the screen look like curscr thinks it looks like. This is useful for implementing a command which would redraw the screen in case it get messed up.

Input

The complementary function to addch() is getch() which, if echo is set, will call addch() to echo the character. Since the screen package needs to know what is on the terminal at all times, if characters are to be echoed, the tty must be in raw or cbreak mode. Since initially the terminal has echoing enabled and is in ordinary ``cooked'' mode, one or the other has to changed before calling getch(); otherwise, the program's output will be unpredictable.

When you need to accept line-oriented input in a window, the functions wgetstr() and friends are available. There is even a wscanw() function that can do scanf()(3)-style multi-field parsing on window input. 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 which are specially defined by ncurses. These are #define values set up in the curses.h header; see there for a complete list (look for the prefix ACS_).

The most useful of the ACS defines are the forms-drawing characters. You can use these 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 (though ugly) 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 logical-or 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 is to set the current-highlight value. This is logical-or'ed 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 actually thinks in terms of color pairs, 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 non-conflicting values could have been used as the first arguments of the init_pair() values.

Once you've 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.

Mouse Interfacing

The ncurses library also provides a mouse interface. Note: his facility is original to ncurses, it is not part of either the XSI Curses standard, nor of System V Release 4, nor BSD curses. Thus, we recommend that you wrap mouse-related code in an #ifdef using the feature macro NCURSES_MOUSE_VERSION so it will not be compiled and linked on non-ncurses systems.

Presently, mouse event reporting works only under xterm. In the future, ncurses will detect the presence of \fBgpm\fR(1), Alessandro Rubini's freeware mouse server for Linux systems, and accept mouse reports through it.

The mouse interface is very simple. To activate it, you use the function mousemask(), passing it as first argument a bit-mask that specifies what kinds of events you want your program to be able to see. It will return the bit-mask of events that actually become visible, which may differ from the argument if the mouse device is not capable of reporting some of the event types you specify.

Once the mouse is active, your application's command loop should watch for a return value of KEY_MOUSE from wgetch(). When you see this, a mouse event report has been queued. To pick it off the queue, use the function getmouse() (you must do this before the next wgetch(), otherwise another mouse event might come in and make the first one inaccessible).

Each call to getmouse() fills a structure (the address of which you'll pass it) with mouse event data. The event data includes zero-origin, screen-relative character-cell coordinates of the mouse pointer. It also includes an event mask. Bits in this mask will be set, corresponding to the event type being reported.

The mouse structure contains two additional fields which may be significant in the future as ncurses interfaces to new kinds of pointing device. In addition to x and y coordinates, there is a slot for a z coordinate; this might be useful with touchscreens that can return a pressure or duration parameter. There is also a device ID field, which could be used to distinguish between multiple pointing devices.

The class of visible events may be changed at any time via getmouse(). Events that can be reported include presses, releases, single-, double- and triple-clicks (you can set the maximum button-down time for clicks). If you don't make clicks visible, they will be reported as press-release pairs. In some environments, the event mask may include bits reporting the state of shift, alt, and ctrl keys on the keyboard during the event.

A function to check whether a mouse event fell within a given window is also supplied. You can use this to see whether a given window should consider a mouse event relevant to it.

Because mouse event reporting will not be available in all environments, it would be unwise to build ncurses applications that require the use of a mouse. Rather, you should use the mouse as a shortcut for point-and-shoot commands your application would normally accept from the keyboard. Two of the test games in the ncurses distribution (bs and knight) contain code that illustrates how this can be done.

See the manual page curs_mouse(3X) for full details of the mouse-interface functions.

Finishing Up

In order to clean up after the ncurses routines, the routine endwin() is provided. It restores tty modes to what they were when initscr() was first called, and moves the cursor down to the lower-left corner. Thus, anytime after the call to initscr, endwin() should be called before exiting.

Function Descriptions

We describe the detailed behavior of some important curses functions here, as a supplement to the manual page descriptions.

Initialization and Wrapup

initscr()
The first function called should almost always be initscr(). This will determine the terminal type and initialize curses data structures. initscr() also arranges that the first call to refresh() will clear the screen. If an error occurs a message is written to standard error and the program exits. Otherwise it returns a pointer to stdscr. A few functions may be called before initscr (slk_init(), filter(), ripofflines(), use_env(), and, if you are using multiple terminals, newterm().)

endwin()
Your program should always call endwin() before exiting or shelling out of the program. This function will restore tty modes, move the cursor to the lower left corner of the screen, reset the terminal into the proper non-visual mode. Calling refresh() or doupdate() after a temporary escape from the program will restore the ncurses screen from before the escape.

newterm(type, ofp, ifp)
A program which outputs to more than one terminal should use newterm() instead of initscr(). newterm() should be called once for each terminal. It returns a variable of type SCREEN * which should be saved as a reference to that terminal. The arguments are the type of the terminal (a string) and FILE pointers for the output and input of the terminal. If type is NULL then the environment variable $TERM is used. endwin() should called once at wrapup time for each terminal opened using this function.

set_term(new)
This function is used to switch to a different terminal previously opened by newterm(). The screen reference for the new terminal is passed as the parameter. The previous terminal is returned by the function. All other calls affect only the current terminal.

delscreen(sp)
The inverse of newterm(); deallocates the data structures associated with a given SCREEN reference.

Causing Output to the Terminal

refresh() and wrefresh(win)
These functions must be called to actually get any output on the terminal, as other routines merely manipulate data structures. wrefresh() copies the named window to the physi- cal terminal screen, taking into account what is already there in order to do optimizations. refresh() does a refresh of stdscr(). Unless leaveok() has been enabled, the physical cursor of the terminal is left at the location of the window's cursor.

doupdate() and wnoutrefresh(win)
These two functions allow multiple updates with more efficiency than wrefresh. To use them, it is important to understand how curses works. In addition to all the window structures, curses keeps two data structures representing the terminal screen: a physical screen, describing what is actually on the screen, and a virtual screen, describing what the programmer wants to have on the screen. wrefresh works by first copying the named window to the virtual screen (wnoutrefresh()), and then calling the routine to update the screen (doupdate()). If the programmer wishes to output several windows at once, a series of calls to wrefresh will result in alternating calls to wnoutrefresh() and doupdate(), causing several bursts of output to the screen. By calling wnoutrefresh() for each window, it is then possible to call doupdate() once, resulting in only one burst of output, with probably fewer total characters transmitted.

Low-Level Capability Access

setupterm(term, filenum, errret) This routine is called to initialize a terminal's description, without setting up the curses screen structures or changing the tty-driver mode bits. term is the character string representing the name of the terminal being used. filenum is the UNIX file descriptor of the terminal to be used for output. errret is a pointer to an integer, in which a success or failure indication is returned. The values returned can be 1 (all is well), 0 (no such terminal), or -1 (some problem locating the terminfo database).

The value of term can be given as NULL, which will cause the value of TERM in the environment to be used. The errret pointer can also be given as NULL, meaning no error code is wanted. If errret is defaulted, and something goes wrong, setupterm() will print an appropriate error message and exit, rather than returning. Thus, a simple program can call setupterm(0, 1, 0) and not worry about initialization errors.

After the call to setupterm(), the global variable cur_term is set to point to the current structure of terminal capabilities. By calling setupterm() for each terminal, and saving and restoring cur_term, it is possible for a program to use two or more terminals at once. Setupterm() also stores the names section of the terminal description in the global character array ttytype[]. Subsequent calls to setupterm() will overwrite this array, so you'll have to save it yourself if need be.

Debugging

_tracef()
NOTE: THIS FUNCTION IS NOT PART OF THE STANDARD CURSES API! This function can be used to output your own debugging information. It is only available only if you link with -lncurses_g. It can be used the same way as printf(), only it outputs a newline after the end of arguments. The output goes to a file called trace in the current directory.
Trace logs can be difficult to interpret due to the sheer volume of data dumped in them. There is a script called tracemunch included with the ncurses distribution that can alleviate this problem somewhat; it compacts long sequences of similar operations into more succinct single-line pseudo-operations. These pseudo-ops can be distinguished by the fact that they are named in capital letters.

Hints, Tips, and Tricks

The ncurses manual pages are a complete reference for this library. In the remainder of this document, we discuss various useful methods that may not be obvious from the manual page descriptions.

Some Notes of Caution

If you find yourself thinking you need to use noraw() or nocbreak(), think again and move carefully. It's probably better design to use getstr() or one of its relatives to simulate cooked mode. The noraw() and nocbreak() functions try to restore cooked mode, but they may end up clobbering some control bits set before you started your application. Also, they have always been poorly documented, and are likely to hurt your application's usability with other curses libraries.

Bear in mind that refresh() is a synonym for wrefresh(stdscr), and don't try to mix use of stdscr with use of windows declared by newwin(); a refresh() call will blow them off the screen. The right way to handle this is to use subwin(), or not touch stdscr at all and tile your screen with declared windows which you then wnoutrefresh() somewhere in your program event loop, with a single doupdate() call to trigger actual repainting.

You are much less likely to run into problems if you design your screen layouts to use tiled rather than overlapping windows. Historically, curses support for overlapping windows has been weak, fragile, and poorly documented. The ncurses library is not yet an exception to this rule.

There is a freeware panels library included in the ncurses distribution that does a pretty good job of strengthening the overlapping-windows facilities.

Try to avoid using the global variables LINES and COLS. Use getmaxyx() on the stdscr context instead. Reason: your code may be ported to run in an environment with window resizes, in which case several screens could be open with different sizes.

Temporarily Leaving ncurses Mode

Sometimes you will want to write a program that spends most of its time in screen mode, but occasionally returns to ordinary `cooked' mode. A common reason for this is to support shell-out. This behavior is simple to arrange in ncurses.

To leave ncurses mode, call endwin() as you would if you were intending to terminate the program. This will take the screen back to cooked mode; you can do your shell-out. When you want to return to ncurses mode, simply call refresh() or doupdate(). This will repaint the screen.

There is a boolean function, isendwin(), which code can use to test whether ncurses screen mode is active. It returns TRUE in the interval between an endwin() call and the following refresh(), FALSE otherwise.

Here is some sample code for shellout:

    addstr("Shelling out...");
    def_prog_mode();           /* save current tty modes */
    endwin();                  /* restore original tty modes */
    system("sh");              /* run shell */
    addstr("returned.\n");     /* prepare return message */
    refresh();                 /* restore save modes, repaint screen */

Using ncurses Under xterm

A resize operation in X sends SIGWINCH to the application running under xterm. The ncurses library does not catch this signal, because it cannot in general know how you want the screen re-painted. You will have to write the SIGWINCH handler yourself.

The easiest way to code your SIGWINCH handler is to have it do an endwin, followed by an initscr and a screen repaint you code yourself. The initscr will pick up the new screen size from the xterm's environment.

Handling Multiple Terminal Screens

The initscr() function actually calls a function named newterm() to do most of its work. If you are writing a program that opens multiple terminals, use newterm() directly.

For each call, you will have to specify a terminal type and a pair of file pointers; each call will return a screen reference, and stdscr will be set to the last one allocated. You will switch between screens with the set_term call. Note that you will also have to call def_shell_mode and def_prog_mode on each tty yourself.

Testing for Terminal Capabilities

Sometimes you may want to write programs that test for the presence of various capabilities before deciding whether to go into ncurses mode. An easy way to do this is to call setupterm(), then use the functions tigetflag(), tigetnum(), and tigetstr() to do your testing.

A particularly useful case of this often comes up when you want to test whether a given terminal type should be treated as `smart' (cursor-addressable) or `stupid'. The right way to test this is to see if the return value of tigetstr("cup") is non-NULL. Alternatively, you can include the term.h file and test the value of the macro cursor_address.

Tuning for Speed

Use the addchstr() family of functions for fast screen-painting of text when you know the text doesn't contain any control characters. Try to make attribute changes infrequent on your screens. Don't use the immedok() option!

Special Features of ncurses

When running on PC-clones, ncurses has enhanced support for the IBM high-half and ROM characters. The A_ALTCHARSET highlight, enables display of both high-half ACS graphics and the PC ROM graphics 0-31 that are normally interpreted as control characters.

Background Erase -- Compatibility with Old Versions

If you have been using a very old versions of ncurses (1.8.7 or older) you may be surprised by the behavior of the erase functions. In older versions, erased areas of a window were filled with a blank modified by the window's current attribute (as set by wattrset(), wattron(), wattroff() and friends).

In newer versions, this is not so. Instead, the attribute of erased blanks is normal unless and until it is modified by the functions bkgdset() or wbkgdset().

This change in behavior conforms ncurses to System V Release 4 and the XSI Curses standard.

XSI Curses Conformance

The ncurses library is intended to be base-level conformant with the XSI Curses standard from X/Open. May extended-level features (in fact, almost all features not directly concerned with wide characters and internationalization) are also supported.

One effect of XSI conformance is the change in behavior described under "Background Erase -- Compatibility with Old Versions".

Also, ncurses meets the XSI requirement that every macro entry point have a corresponding function which may be linked (and will be prototype-checked) if the macro definition is disabled with #undef.

The Panels Library

The ncurses library by itself provides good support for screen displays in which the windows are tiled (non-overlapping). In the more general case that windows may overlap, you have to use a series of wnoutrefresh() calls followed by a doupdate(), and be careful about the order you do the window refreshes in. It has to be bottom-upwards, otherwise parts of windows that should be obscured will show through.

When your interface design is such that windows may dive deeper into the visibility stack or pop to the top at runtime, the resulting book-keeping can be tedious and difficult to get right. Hence the panels library.

The panel library first appeared in AT&T System V. The version documented here is the freeware panel code distributed with ncurses.

Compiling With the Panels Library

Your panels-using modules must import the panels library declarations with
	  #include <panel.h>
and must be linked explicitly with the panels library using an -lpanel argument. Note that they must also link the ncurses library with -lncurses. Most modern linkers are two-pass and will accept either order, but it is still good practice to put -lpanel first and -lncurses second.

Overview of Panels

A panel object is a window that is implicitly treated as part of a deck including all other panel objects. The deck has an implicit bottom-to-top visibility order. The panels library includes an update function (analogous to refresh()) that displays all panels in the deck in the proper order to resolve overlaps. The standard window, stdscr, is considered below all panels.

Details on the panels functions are available in the man pages. We'll just hit the highlights here.

You create a panel from a window by calling new_panel() on a window pointer. It then becomes the top of the deck. The panel's window is available as the value of panel_window() called with the panel pointer as argument.

You can delete a panel (removing it from the deck) with del_panel. This will not deallocate the associated window; you have to do that yourself. You can replace a panel's window with a different window by calling replace_window. The new window may be of different size; the panel code will re-compute all overlaps. This operation doesn't change the panel's position in the deck.

To move a panel's window, use move_panel(). The mvwin() function on the panel's window isn't sufficient because it doesn't update the panels library's representation of where the windows are. This operation leaves the panel's depth, contents, and size unchanged.

Two functions (top_panel(), bottom_panel()) are provided for rearranging the deck. The first pops its argument window to the top of the deck; the second sends it to the bottom. Either operation leaves the panel's screen location, contents, and size unchanged.

The function update_panels() does all the wnoutrefresh() calls needed to prepare for doupdate() (which you must call yourself, afterwards).

Panels, Input, and the Standard Screen

You shouldn't mix wnoutrefresh() or wrefresh() operations with panels code; this will work only if the argument window is either in the top panel or un-obscured by any other panels.

The stsdcr window is a special case. It is considered below all panels. Because changes to panels may obscure parts of stdscr, though, you should call update_panels() before doupdate() even when you only change stdscr.

Note that wgetch automatically calls wrefresh. Therefore, before requesting input from a panel window, you need to be sure that the panel is totally un-obscured.

There is presently no way to display changes to one obscured panel without repainting all panels.

Hiding Panels

It's possible to remove a panel from the deck temporarily; use hide_panel for this. You can un-hide a panel with show_panel(). The predicate function panel_hidden tests whether or not a panel is hidden.

The panel_update code ignores hidden panels. You cannot do top_panel() or bottom_panel on a hidden panel(). Other panels operations are applicable.

Miscellaneous Other Facilities

It's possible to navigate the deck using the functions panel_above() and panel_below. Handed a panel pointer, they return the panel above or below that panel. Handed NULL, they return the bottom-most or top-most panel.

Every panel has an associated user pointer, not used by the panel code, to which you can attach application data. See the man page documentation of set_panel_userptr() and panel_userptr for details.

The Menu Library

A menu is a screen display that assists the user to choose some subset of a given set of items. The menu library is a curses extension that supports easy programming of menu hierarchies with a uniform but flexible interface.

The menu library first appeared in AT&T System V. The version documented here is the freeware menu code distributed with ncurses.

Compiling With the menu Library

Your menu-using modules must import the menu library declarations with
	  #include <menu.h>
and must be linked explicitly with the menus library using an -lmenu argument. Note that they must also link the ncurses library with -lncurses. Most modern linkers are two-pass and will accept either order, but it is still good practice to put -lmenu first and -lncurses second.

Overview of Menus

The menus created by this library consist of collections of items including a name string part and a description string part. To make menus, you create groups of these items and connect them with menu frame objects.

The menu can then by posted, that is written to an associated window. Actually, each menu has two associated windows; a containing window in which the programmer can scribble titles or borders, and a subwindow in which the menu items proper are displayed. If this subwindow is too small to display all the items, it will be a scrollable viewport on the collection of items.

A menu may also be unposted (that is, undisplayed), and finally freed to make the storage associated with it and its items available for re-use.

The general flow of control of a menu program looks like this:

  1. Initialize curses.
  2. Create the menu items, using new_item().
  3. Create the menu using new_menu().
  4. Post the menu using menu_post().
  5. Refresh the screen.
  6. Process user requests via an input loop.
  7. Unpost the menu using menu_unpost().
  8. Free the menu, using free_menu().
  9. Free the items using free_item().
  10. Terminate curses.

Selecting items

Menus may be multi-valued or (the default) single-valued (see the manual page mitem_opts(3x) to see how to change the default). Both types always have a current item.

From a single-valued menu you can read the selected value simply by looking at the current item. From a multi-valued menu, you get the selected set by looping through the items applying the item_value() predicate function. Your menu-processing code can use the function set_item_value() to flag the items in the select set.

Menu items can be made un-selectable using set_item_opts() or item_opts_off() with the O_SELECTABLE argument. This is the only option so far defined for menus, but it is good practice to code as though other option bits might be on.

Menu Display

The menu library calculates a minimum display size for your window, based on the following variables:

The function set_menu_format() allows you to set the maximum size of the viewport or menu page that will be used to display menu items. You can retrieve any format associated with a menu with menu_format(). The default format is rows=16, columns=1.

The actual menu page may be smaller than the format size. This depends on the item number and size and whether O_ROWMAJOR is on. This option (on by default) causes menu items to be displayed in a `raster-scan' pattern, so that if more than one item will fit horizontally the first couple of items are side-by-side in the top row. The alternative is column-major display, which tries to put the first several items in the first column.

As mentioned above, a menu format not large enough to allow all items to fit on-screen will result in a menu display that is vertically scrollable.

You can scroll it with requests to the menu driver, which will be described in the section on menu input handling.

Each menu has a mark string used to visually tag selected items; see the menu_mark(3x) manual page for details. The mark string length also influences the menu page size.

The function scale_menu() returns the minimum display size that the menu code computes from all these factors. There are other menu display attributes including a select attribute, an attribute for selectable items, an attribute for unselectable items, and a pad character used to separate item name text from description text. These have reasonable defaults which the library allows you to change (see the menu_attribs(3x)manual page.

Menu Windows

Each menu has, as mentioned previously, a pair of associated windows. Both these windows are painted when the menu is posted and erased when the menu is unposted.

The outer or frame window is not otherwise touched by the menu routines. It exists so the programmer can associate a title, a border, or perhaps help text with the menu and have it properly refreshed or erased at post/unpost time. The inner window or subwindow is where the current menu page is displayed.

By default, both windows are stdscr. You can set them with the functions in menu_win(3x).

When you call menu_post(), you write the menu to its subwindow. When you call menu_unpost(), you erase the subwindow, However, neither of these actually modifies the screen. To do that, call wrefresh() or some equivalent.

Processing Menu Input

The main loop of your menu-processing code should call menu_driver() repeatedly. The first argument of this routine is a menu pointer; the second is a menu command code. You should write an input-fetching routine that maps input characters to menu command codes, and pass its output to menu_driver(). The menu command codes are fully documented in menu_driver(3x).

The simplest group of command codes is REQ_NEXT_ITEM, REQ_PREV_ITEM, REQ_FIRST_ITEM, REQ_LAST_ITEM, REQ_UP_ITEM, REQ_DOWN_ITEM, REQ_LEFT_ITEM, REQ_RIGHT_ITEM. These change the currently selected item. These requests may cause scrolling of the menu page if it only partially displayed.

There are explicit requests for scrolling which also change the current item (because the select location does not change, but the item there does). These are REQ_SCR_DLINE, REQ_SCR_ULINE, REQ_SCR_DPAGE, and REQ_SCR_UPAGE.

The REQ_TOGGLE_ITEM selects or deselects the current item. It is for use in multi-valued menus; if you use it with O_ONEVALUE on, you'll get an error return (E_REQUEST_DENIED).

Each menu has an associated pattern buffer. The menu_driver() logic tries to accumulate printable ASCII characters passed in in that buffer; when it matches a prefix of an item name, that item (or the next matching item) is selected. If appending a character yields no new match, that character is deleted from the pattern buffer, and menu_driver() returns E_NO_MATCH.

Some requests change the pattern buffer directly: REQ_CLEAR_PATTERN, REQ_BACK_PATTERN, REQ_NEXT_MATCH, REQ_PREV_MATCH. The latter two are useful when pattern buffer input matches more than one item in a multi-valued menu.

Each successful scroll or item navigation request clears the pattern buffer. It is also possible to set the pattern buffer explicitly with set_menu_pattern().

Finally, menu driver requests above the constant MAX_COMMAND are considered application-specific commands. The menu_driver() code ignores them and returns E_UNKNOWN_COMMAND.

Miscellaneous Other Features

Various menu options can affect the processing and visual appearance and input processing of menus. See menu_opts(3x) for details.

It is possible to change the current item from application code; this is useful if you want to write your own navigation requests. It is also possible to explicitly set the top row of the menu display. See mitem_current(3x). If your application needs to change the menu subwindow cursor for any reason, pos_menu_cursor() will restore it to the correct location for continuing menu driver processing.

It is possible to set hooks to be called at menu initialization and wrapup time, and whenever the selected item changes. See menu_hook(3x).

Each item, and each menu, has an associated user pointer on which you can hang application data. See mitem_userptr(3x) and menu_userptr(3x).