From 80cd5ab568e26f185cd2dad3a7766514cf4a6443 Mon Sep 17 00:00:00 2001 From: Baptiste Daroussin Date: Thu, 20 Jun 2024 15:33:23 +0200 Subject: [PATCH] ncurses: readd 2 html files in the doc (fix MK_HTML=yes) Reported by: Michael Butler (cherry picked from commit 8d9900a313593adeeaae295b4aea982cb14cb8a5) --- contrib/ncurses/doc/html/hackguide.html | 962 ++++++ contrib/ncurses/doc/html/ncurses-intro.html | 3390 +++++++++++++++++++ 2 files changed, 4352 insertions(+) create mode 100644 contrib/ncurses/doc/html/hackguide.html create mode 100644 contrib/ncurses/doc/html/ncurses-intro.html diff --git a/contrib/ncurses/doc/html/hackguide.html b/contrib/ncurses/doc/html/hackguide.html new file mode 100644 index 00000000000..2b9445538dc --- /dev/null +++ b/contrib/ncurses/doc/html/hackguide.html @@ -0,0 +1,962 @@ + + + + + + A Hacker's Guide to Ncurses Internals + + + + +

A Hacker's Guide to NCURSES

+ +

A Hacker's Guide to NCURSES

+ + + +

Abstract

+ +

This document is a hacker's tour of the + ncurses library and utilities. It discusses + design philosophy, implementation methods, and the conventions + used for coding and documentation. It is recommended reading for + anyone who is interested in porting, extending or improving the + package.

+ +

Objective of the + Package

+ +

The objective of the ncurses package is to + provide a free software API for character-cell terminals and + terminal emulators with the following characteristics:

+ + + +

These objectives are in priority order. So, for example, + source compatibility with older version must trump featurefulness + — we cannot add features if it means breaking the portion + of the API corresponding to historical curses versions.

+ +

Why System V Curses?

+ +

We used System V curses as a model, reverse-engineering their + API, in order to fulfill the first two objectives.

+ +

System V curses implementations can support BSD curses + programs with just a recompilation, so by capturing the System V + API we also capture BSD's.

+ +

More importantly for the future, the XSI Curses standard + issued by X/Open is explicitly and closely modeled on System V. + So conformance with System V took us most of the way to + base-level XSI conformance.

+ +

How to Design + Extensions

+ +

The third objective (standards conformance) requires that it + be easy to condition source code using ncurses + so that the absence of nonstandard extensions does not break the + code.

+ +

Accordingly, we have a policy of associating with each + nonstandard extension a feature macro, so that ncurses client + code can use this macro to condition in or out the code that + requires the ncurses extension.

+ +

For example, there is a macro + NCURSES_MOUSE_VERSION which XSI Curses does not + define, but which is defined in the ncurses + library header. You can use this to condition the calls to the + mouse API calls.

+ +

Portability and + Configuration

+ +

Code written for ncurses may assume an + ANSI-standard C compiler and POSIX-compatible OS interface. It + may also assume the presence of a System-V-compatible + select(2) call.

+ +

We encourage (but do not require) developers to make the code + friendly to less-capable UNIX environments wherever possible.

+ +

We encourage developers to support OS-specific optimizations + and methods not available under POSIX/ANSI, provided only + that:

+ + + +

We use GNU autoconf(1) as a tool to deal with + portability issues. The right way to leverage an OS-specific + feature is to modify the autoconf specification files + (configure.in and aclocal.m4) to set up a new feature macro, + which you then use to condition your code.

+ +

Documentation + Conventions

+ +

There are three kinds of documentation associated with this + package. Each has a different preferred format:

+ + + +

Our conventions are simple:

+ +
    +
  1. Maintain package-internal files in plain + text. The expected viewer for them is more(1) or + an editor window; there is no point in elaborate mark-up.
  2. + +
  3. Mark up manual pages in the man macros. + These have to be viewable through traditional man(1) + programs.
  4. + +
  5. Write everything else in HTML. +
  6. +
+ +

When in doubt, HTMLize a master and use lynx(1) to + generate plain ASCII (as we do for the announcement + document).

+ +

The reason for choosing HTML is that it is (a) well-adapted + for on-line browsing through viewers that are everywhere; (b) + more easily readable as plain text than most other mark-ups, if + you do not have a viewer; and (c) carries enough information that + you can generate a nice-looking printed version from it. Also, of + course, it make exporting things like the announcement document + to WWW pretty trivial.

+ +

How to Report Bugs

+ +

The reporting address for + bugs is bug-ncurses@gnu.org. This is a + majordomo list; to join, write to + bug-ncurses-request@gnu.org with a message + containing the line:

+ +
+             subscribe <name>@<host.domain>
+
+

The ncurses code is maintained by a small group + of volunteers. While we try our best to fix bugs promptly, we + simply do not have a lot of hours to spend on elementary + hand-holding. We rely on intelligent cooperation from our users. + If you think you have found a bug in ncurses, there + are some steps you can take before contacting us that will help + get the bug fixed quickly.

+ +

In order to use our bug-fixing time efficiently, we put people + who show us they have taken these steps at the head of our queue. + This means that if you do not, you will probably end up at the + tail end and have to wait a while.

+ +
    +
  1. Develop a recipe to reproduce the bug. +

    Bugs we can reproduce are likely to be fixed very quickly, + often within days. The most effective single thing you can do + to get a quick fix is develop a way we can duplicate the bad + behavior — ideally, by giving us source for a small, + portable test program that breaks the library. (Even better + is a keystroke recipe using one of the test programs provided + with the distribution.)

    +
  2. + +
  3. Try to reproduce the bug on a different terminal type. +

    In our experience, most of the behaviors people report as + library bugs are actually due to subtle problems in terminal + descriptions. This is especially likely to be true if you are + using a traditional asynchronous terminal or PC-based + terminal emulator, rather than xterm or a UNIX console + entry.

    + +

    It is therefore extremely helpful if you can tell us + whether or not your problem reproduces on other terminal + types. Usually you will have both a console type and xterm + available; please tell us whether or not your bug reproduces + on both.

    + +

    If you have xterm available, it is also good to collect + xterm reports for different window sizes. This is especially + true if you normally use an unusual xterm window size — + a surprising number of the bugs we have seen are either + triggered or masked by these.

    +
  4. + +
  5. Generate and examine a trace file for the broken behavior. +

    Recompile your program with the debugging versions of the + libraries. Insert a trace() call with the + argument set to TRACE_UPDATE. (See "Writing Programs with + NCURSES" for details on trace levels.) Reproduce your + bug, then look at the trace file to see what the library was + actually doing.

    + +

    Another frequent cause of apparent bugs is application + coding errors that cause the wrong things to be put on the + virtual screen. Looking at the virtual-screen dumps in the + trace file will tell you immediately if this is happening, + and save you from the possible embarrassment of being told + that the bug is in your code and is your problem rather than + ours.

    + +

    If the virtual-screen dumps look correct but the bug + persists, it is possible to crank up the trace level to give + more and more information about the library's update actions + and the control sequences it issues to perform them. The test + directory of the distribution contains a tool for digesting + these logs to make them less tedious to wade through.

    + +

    Often you will find terminfo problems at this stage by + noticing that the escape sequences put out for various + capabilities are wrong. If not, you are likely to learn + enough to be able to characterize any bug in the + screen-update logic quite exactly.

    +
  6. + +
  7. Report details and symptoms, not just interpretations. +

    If you do the preceding two steps, it is very likely that + you will discover the nature of the problem yourself and be + able to send us a fix. This will create happy feelings all + around and earn you good karma for the first time you run + into a bug you really cannot characterize and fix + yourself.

    + +

    If you are still stuck, at least you will know what to + tell us. Remember, we need details. If you guess about what + is safe to leave out, you are too likely to be wrong.

    + +

    If your bug produces a bad update, include a trace file. + Try to make the trace at the least voluminous level + that pins down the bug. Logs that have been through + tracemunch are OK, it does not throw away any information + (actually they are better than un-munched ones because they + are easier to read).

    + +

    If your bug produces a core-dump, please include a + symbolic stack trace generated by gdb(1) or your local + equivalent.

    + +

    Tell us about every terminal on which you have reproduced + the bug — and every terminal on which you cannot. + Ideally, send us terminfo sources for all of these (yours + might differ from ours).

    + +

    Include your ncurses version and your OS/machine type, of + course! You can find your ncurses version in the + curses.h file.

    +
  8. +
+ +

If your problem smells like a logic error or in cursor + movement or scrolling or a bad capability, there are a couple of + tiny test frames for the library algorithms in the progs + directory that may help you isolate it. These are not part of the + normal build, but do have their own make productions.

+ +

The most important of these is mvcur, a test + frame for the cursor-movement optimization code. With this + program, you can see directly what control sequences will be + emitted for any given cursor movement or scroll/insert/delete + operations. If you think you have got a bad capability + identified, you can disable it and test again. The program is + command-driven and has on-line help.

+ +

If you think the vertical-scroll optimization is broken, or + just want to understand how it works better, build + hashmap and read the header comments of + hardscroll.c and hashmap.c; then try it + out. You can also test the hardware-scrolling optimization + separately with hardscroll.

+ +

A Tour of the Ncurses + Library

+ +

Library Overview

+ +

Most of the library is superstructure — fairly trivial + convenience interfaces to a small set of basic functions and data + structures used to manipulate the virtual screen (in particular, + none of this code does any I/O except through calls to more + fundamental modules described below). The files

+ +
+ lib_addch.c lib_bkgd.c lib_box.c lib_chgat.c lib_clear.c + lib_clearok.c lib_clrbot.c lib_clreol.c lib_colorset.c + lib_data.c lib_delch.c lib_delwin.c lib_echo.c lib_erase.c + lib_gen.c lib_getstr.c lib_hline.c lib_immedok.c lib_inchstr.c + lib_insch.c lib_insdel.c lib_insstr.c lib_instr.c + lib_isendwin.c lib_keyname.c lib_leaveok.c lib_move.c + lib_mvwin.c lib_overlay.c lib_pad.c lib_printw.c lib_redrawln.c + lib_scanw.c lib_screen.c lib_scroll.c lib_scrollok.c + lib_scrreg.c lib_set_term.c lib_slk.c lib_slkatr_set.c + lib_slkatrof.c lib_slkatron.c lib_slkatrset.c lib_slkattr.c + lib_slkclear.c lib_slkcolor.c lib_slkinit.c lib_slklab.c + lib_slkrefr.c lib_slkset.c lib_slktouch.c lib_touch.c + lib_unctrl.c lib_vline.c lib_wattroff.c lib_wattron.c + lib_window.c +
+ +

are all in this category. They are very unlikely to need + change, barring bugs or some fundamental reorganization in the + underlying data structures.

+ +

These files are used only for debugging support:

+ +
+ lib_trace.c lib_traceatr.c lib_tracebits.c lib_tracechr.c + lib_tracedmp.c lib_tracemse.c trace_buf.c +
+ +

It is rather unlikely you will ever need to change these, + unless you want to introduce a new debug trace level for some + reason.

+ +

There is another group of files that do direct I/O via + tputs(), computations on the terminal capabilities, or + queries to the OS environment, but nevertheless have only fairly + low complexity. These include:

+ +
+ lib_acs.c lib_beep.c lib_color.c lib_endwin.c + lib_initscr.c lib_longname.c lib_newterm.c lib_options.c + lib_termcap.c lib_ti.c lib_tparm.c lib_tputs.c lib_vidattr.c + read_entry.c. +
+ +

They are likely to need revision only if ncurses is being + ported to an environment without an underlying terminfo + capability representation.

+ +

These files have serious hooks into the tty driver and signal + facilities:

+ +
+ lib_kernel.c lib_baudrate.c lib_raw.c lib_tstp.c + lib_twait.c +
+ +

If you run into porting snafus moving the package to another + UNIX, the problem is likely to be in one of these files. The file + lib_print.c uses sleep(2) and also falls in this + category.

+ +

Almost all of the real work is done in the files

+ +
+ hardscroll.c hashmap.c lib_addch.c lib_doupdate.c + lib_getch.c lib_mouse.c lib_mvcur.c lib_refresh.c lib_setup.c + lib_vidattr.c +
+ +

Most of the algorithmic complexity in the library lives in + these files. If there is a real bug in ncurses + itself, it is probably here. We will tour some of these files in + detail below (see The Engine Room).

+ +

Finally, there is a group of files that is actually most of + the terminfo compiler. The reason this code lives in the + ncurses library is to support fallback to + /etc/termcap. These files include

+ +
+ alloc_entry.c captoinfo.c comp_captab.c comp_error.c + comp_hash.c comp_parse.c comp_scan.c parse_entry.c + read_termcap.c write_entry.c +
+ +

We will discuss these in the compiler tour.

+ +

The Engine Room

+ +

Keyboard Input

+ +

All ncurses input funnels through the function + wgetch(), defined in lib_getch.c. This + function is tricky; it has to poll for keyboard and mouse events + and do a running match of incoming input against the set of + defined special keys.

+ +

The central data structure in this module is a FIFO queue, + used to match multiple-character input sequences against + special-key capabilities; also to implement pushback via + ungetch().

+ +

The wgetch() code distinguishes between function + key sequences and the same sequences typed manually by doing a + timed wait after each input character that could lead a function + key sequence. If the entire sequence takes less than 1 second, it + is assumed to have been generated by a function key press.

+ +

Hackers bruised by previous encounters with variant + select(2) calls may find the code in + lib_twait.c interesting. It deals with the problem + that some BSD selects do not return a reliable time-left value. + The function timed_wait() effectively simulates a + System V select.

+ +

Mouse Events

+ +

If the mouse interface is active, wgetch() polls + for mouse events each call, before it goes to the keyboard for + input. It is up to lib_mouse.c how the polling is + accomplished; it may vary for different devices.

+ +

Under xterm, however, mouse event notifications come in via + the keyboard input stream. They are recognized by having the + kmous capability as a prefix. This is kind of + klugey, but trying to wire in recognition of a mouse key prefix + without going through the function-key machinery would be just + too painful, and this turns out to imply having the prefix + somewhere in the function-key capabilities at terminal-type + initialization.

+ +

This kluge only works because kmous is not + actually used by any historic terminal type or curses + implementation we know of. Best guess is it is a relic of some + forgotten experiment in-house at Bell Labs that did not leave any + traces in the publicly-distributed System V terminfo files. If + System V or XPG4 ever gets serious about using it again, this + kluge may have to change.

+ +

Here are some more details about mouse event handling:

+ +

The lib_mouse() code is logically split into a + lower level that accepts event reports in a device-dependent + format and an upper level that parses mouse gestures and filters + events. The mediating data structure is a circular queue of event + structures.

+ +

Functionally, the lower level's job is to pick up primitive + events and put them on the circular queue. This can happen in one + of two ways: either (a) _nc_mouse_event() detects a + series of incoming mouse reports and queues them, or (b) code in + lib_getch.c detects the kmous + prefix in the keyboard input stream and calls _nc_mouse_inline to + queue up a series of adjacent mouse reports.

+ +

In either case, _nc_mouse_parse() should be + called after the series is accepted to parse the digested mouse + reports (low-level events) into a gesture (a high-level or + composite event).

+ +

Output and Screen Updating

+ +

With the single exception of character echoes during a + wgetnstr() call (which simulates cooked-mode line + editing in an ncurses window), the library normally does all its + output at refresh time.

+ +

The main job is to go from the current state of the screen (as + represented in the curscr window structure) to the + desired new state (as represented in the newscr + window structure), while doing as little I/O as possible.

+ +

The brains of this operation are the modules + hashmap.c, hardscroll.c and + lib_doupdate.c; the latter two use + lib_mvcur.c. Essentially, what happens looks like + this:

+ + + +

If you want to work on screen optimizations, you should use + the fact that (in the trace-enabled version of the library) + enabling the TRACE_TIMES trace level causes a report + to be emitted after each screen update giving the elapsed time + and a count of characters emitted during the update. You can use + this to tell when an update optimization improves efficiency.

+ +

In the trace-enabled version of the library, it is also + possible to disable and re-enable various optimizations at + runtime by tweaking the variable + _nc_optimize_enable. See the file + include/curses.h.in for mask values, near the + end.

+ +

The Forms and Menu Libraries

+ +

The forms and menu libraries should work reliably in any + environment you can port ncurses to. The only portability issue + anywhere in them is what flavor of regular expressions the + built-in form field type TYPE_REGEXP will recognize.

+ +

The configuration code prefers the POSIX regex facility, + modeled on System V's, but will settle for BSD regexps if the + former is not available.

+ +

Historical note: the panels code was written primarily to + assist in porting u386mon 2.0 (comp.sources.misc v14i001-4) to + systems lacking panels support; u386mon 2.10 and beyond use it. + This version has been slightly cleaned up for + ncurses.

+ +

A Tour of the Terminfo Compiler

+ +

The ncurses implementation of + tic is rather complex internally; it has to do a + trying combination of missions. This starts with the fact that, + in addition to its normal duty of compiling terminfo sources into + loadable terminfo binaries, it has to be able to handle termcap + syntax and compile that too into terminfo entries.

+ +

The implementation therefore starts with a table-driven, + dual-mode lexical analyzer (in comp_scan.c). The + lexer chooses its mode (termcap or terminfo) based on the first + “,” or “:” it finds in each entry. The + lexer does all the work of recognizing capability names and + values; the grammar above it is trivial, just "parse entries till + you run out of file".

+ +

Translation of + Non-use Capabilities

+ +

Translation of most things besides use + capabilities is pretty straightforward. The lexical analyzer's + tokenizer hands each capability name to a hash function, which + drives a table lookup. The table entry yields an index which is + used to look up the token type in another table, and controls + interpretation of the value.

+ +

One possibly interesting aspect of the implementation is the + way the compiler tables are initialized. All the tables are + generated by various awk/sed/sh scripts from a master table + include/Caps; these scripts actually write C + initializers which are linked to the compiler. Furthermore, the + hash table is generated in the same way, so it doesn't have to be + generated at compiler startup time (another benefit of this + organization is that the hash table can be in shareable text + space).

+ +

Thus, adding a new capability is usually pretty trivial, just + a matter of adding one line to the include/Caps + file. We will have more to say about this in the section on + Source-Form Translation.

+ +

Use Capability Resolution

+ +

The background problem that makes tic tricky + is not the capability translation itself, it is the resolution of + use capabilities. Older versions would not + handle forward use references for this reason + (that is, a using terminal always had to follow its use target in + the source file). By doing this, they got away with a simple + implementation tactic; compile everything as it blows by, then + resolve uses from compiled entries.

+ +

This will not do for ncurses. The problem is + that that the whole compilation process has to be embeddable in + the ncurses library so that it can be called by + the startup code to translate termcap entries on the fly. The + embedded version cannot go promiscuously writing everything it + translates out to disk — for one thing, it will typically + be running with non-root permissions.

+ +

So our tic is designed to parse an entire + terminfo file into a doubly-linked circular list of entry + structures in-core, and then do use resolution + in-memory before writing everything out. This design has other + advantages: it makes forward and back use-references equally easy + (so we get the latter for free), and it makes checking for name + collisions before they are written out easy to do.

+ +

And this is exactly how the embedded version works. But the + stand-alone user-accessible version of tic + partly reverts to the historical strategy; it writes to disk (not + keeping in core) any entry with no use + references.

+ +

This is strictly a core-economy kluge, implemented because the + terminfo master file is large enough that some core-poor systems + swap like crazy when you compile it all in memory...there have + been reports of this process taking three hours, + rather than the twenty seconds or less typical on the author's + development box.

+ +

So. The executable tic passes the + entry-parser a hook that immediately writes out the + referenced entry if it has no use capabilities. The compiler main + loop refrains from adding the entry to the in-core list when this + hook fires. If some other entry later needs to reference an entry + that got written immediately, that is OK; the resolution code + will fetch it off disk when it cannot find it in core.

+ +

Name collisions will still be detected, just not as cleanly. + The write_entry() code complains before overwriting + an entry that postdates the time of tic's first + call to write_entry(), Thus it will complain about + overwriting entries newly made during the tic + run, but not about overwriting ones that predate it.

+ +

Source-Form + Translation

+ +

Another use of tic is to do source + translation between various termcap and terminfo formats. There + are more variants out there than you might think; the ones we + know about are described in the captoinfo(1) + manual page.

+ +

The translation output code (dump_entry() in + ncurses/dump_entry.c) is shared with the + infocmp(1) utility. It takes the same internal + representation used to generate the binary form and dumps it to + standard output in a specified format.

+ +

The include/Caps file has a header comment + describing ways you can specify source translations for + nonstandard capabilities just by altering the master table. It is + possible to set up capability aliasing or tell the compiler to + plain ignore a given capability without writing any C code at + all.

+ +

For circumstances where you need to do algorithmic + translation, there are functions in parse_entry.c + called after the parse of each entry that are specifically + intended to encapsulate such translations. This, for example, is + where the AIX box1 capability get translated to + an acsc string.

+ +

Other Utilities

+ +

The infocmp utility is just a wrapper around + the same entry-dumping code used by tic for + source translation. Perhaps the one interesting aspect of the + code is the use of a predicate function passed in to + dump_entry() to control which capabilities are + dumped. This is necessary in order to handle both the ordinary + De-compilation case and entry difference reporting.

+ +

The tput and clear utilities + just do an entry load followed by a tputs() of a + selected capability.

+ +

Style Tips for Developers

+ +

See the TO-DO file in the top-level directory of the source + distribution for additions that would be particularly useful.

+ +

The prefix _nc_ should be used on library public + functions that are not part of the curses API in order to prevent + pollution of the application namespace. If you have to add to or + modify the function prototypes in curses.h.in, read + ncurses/MKlib_gen.sh first so you can avoid breaking XSI + conformance. Please join the ncurses mailing list. See the + INSTALL file in the top level of the distribution for details on + the list.

+ +

Look for the string FIXME in source files to tag + minor bugs and potential problems that could use fixing.

+ +

Do not try to auto-detect OS features in the main body of the + C code. That is the job of the configuration system.

+ +

To hold down complexity, do make your code data-driven. + Especially, if you can drive logic from a table filtered out of + include/Caps, do it. If you find you need to augment + the data in that file in order to generate the proper table, that + is still preferable to ad-hoc code — that is why the fifth + field (flags) is there.

+ +

Have fun!

+ +

Porting Hints

+ +

The following notes are intended to be a first step towards + DOS and Macintosh ports of the ncurses libraries.

+ +

The following library modules are “pure curses”; + they operate only on the curses internal structures, do all + output through other curses calls (not including + tputs() and putp()) and do not call any + other UNIX routines such as signal(2) or the stdio library. Thus, + they should not need to be modified for single-terminal + ports.

+ +
+ lib_addch.c lib_addstr.c lib_bkgd.c lib_box.c lib_clear.c + lib_clrbot.c lib_clreol.c lib_delch.c lib_delwin.c lib_erase.c + lib_inchstr.c lib_insch.c lib_insdel.c lib_insstr.c + lib_keyname.c lib_move.c lib_mvwin.c lib_newwin.c lib_overlay.c + lib_pad.c lib_printw.c lib_refresh.c lib_scanw.c lib_scroll.c + lib_scrreg.c lib_set_term.c lib_touch.c lib_tparm.c lib_tputs.c + lib_unctrl.c lib_window.c panel.c +
+ +

This module is pure curses, but calls outstr():

+ +
+ lib_getstr.c +
+ +

These modules are pure curses, except that they use + tputs() and putp():

+ +
+ lib_beep.c lib_color.c lib_endwin.c lib_options.c + lib_slk.c lib_vidattr.c +
+ +

This modules assist in POSIX emulation on non-POSIX + systems:

+ +
+
sigaction.c
+ +
signal calls
+
+ +

The following source files will not be needed for a + single-terminal-type port.

+ +
+ alloc_entry.c captoinfo.c clear.c comp_captab.c + comp_error.c comp_hash.c comp_main.c comp_parse.c comp_scan.c + dump_entry.c infocmp.c parse_entry.c read_entry.c tput.c + write_entry.c +
+ +

The following modules will use + open()/read()/write()/close()/lseek() on files, but no other OS + calls.

+ +
+
lib_screen.c
+ +
used to read/write screen dumps
+ +
lib_trace.c
+ +
used to write trace data to the logfile
+
+ +

Modules that would have to be modified for a port start + here:

+ +

The following modules are “pure curses” but + contain assumptions inappropriate for a memory-mapped port.

+ +
+
lib_longname.c
+ +
assumes there may be multiple terminals
+ +
lib_acs.c
+ +
assumes acs_map as a double indirection
+ +
lib_mvcur.c
+ +
assumes cursor moves have variable cost
+ +
lib_termcap.c
+ +
assumes there may be multiple terminals
+ +
lib_ti.c
+ +
assumes there may be multiple terminals
+
+ +

The following modules use UNIX-specific calls:

+ +
+
lib_doupdate.c
+ +
input checking
+ +
lib_getch.c
+ +
read()
+ +
lib_initscr.c
+ +
getenv()
+ +
lib_newterm.c
+ +
lib_baudrate.c
+ +
lib_kernel.c
+ +
various tty-manipulation and system calls
+ +
lib_raw.c
+ +
various tty-manipulation calls
+ +
lib_setup.c
+ +
various tty-manipulation calls
+ +
lib_restart.c
+ +
various tty-manipulation calls
+ +
lib_tstp.c
+ +
signal-manipulation calls
+ +
lib_twait.c
+ +
gettimeofday(), select().
+
+ +
+ +
+ Eric S. Raymond <esr@snark.thyrsus.com> +
+ (Note: This is not the bug + address!) + + diff --git a/contrib/ncurses/doc/html/ncurses-intro.html b/contrib/ncurses/doc/html/ncurses-intro.html new file mode 100644 index 00000000000..194850246fa --- /dev/null +++ b/contrib/ncurses/doc/html/ncurses-intro.html @@ -0,0 +1,3390 @@ + + + + + + Writing Programs with NCURSES + + + + +

Writing Programs with NCURSES

+ +

Writing Programs with NCURSES

+ +
+ by Eric S. Raymond and Zeyd M. Ben-Halim
+ updates since release 1.9.9e by Thomas Dickey +
+ + + +
+ +

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, Compatibility with Older Versions, 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 screen full 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 + vi editor; these used the termcap + database facility (both released in 3BSD) for describing terminal + capabilities. These routines were abstracted into a documented + library and first released with the early BSD UNIX versions. All + of this work was done by students at the University of California + (Berkeley campus). The curses library was first published in + 4.0BSD, a year after 3BSD (i.e., late 1980).

+ +

After graduation, one of those students went to work at + AT&T Bell Labs, and made an improved termcap + library called terminfo (i.e., + “libterm”), and adapted the curses library to use + this. That was subsequently released in System V Release 2 (early + 1984). Thereafter, other developers added to the curses and + terminfo libraries. For instance, a student at Cornell University + wrote an improved terminfo library as well as a tool + (tic) to compile the terminal descriptions. As a + general rule, AT&T did not identify the developers in the + source-code or documentation; the tic and + infocmp programs are the exceptions.

+ +

System V Release 3 (System III UNIX) from Bell Labs featured a + rewritten and much-improved curses library, along + with the tic program (late 1986).

+ +

To recap, 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 free + 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 original maintainer of this 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. + Jürgen Pfeifer wrote all of the menu and forms code as well + as the Ada95 binding. + Ongoing work is being done by Thomas Dickey + (maintainer). Contact the current maintainers at bug-ncurses@gnu.org.

+ +

This document also describes the panels + 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 does not bear any necessary relation to what is really on the + terminal screen; it is 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 + window-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 failure (-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 <stdlib.h>
+#include <curses.h>
+#include <signal.h>
+
+static void finish(int sig);
+
+int
+main(int argc, char *argv[])
+{
+    int num = 0;
+
+    /* 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) echo();         /* echo input - in color */
+
+    if (has_colors())
+    {
+        start_color();
+
+        /*
+         * Simple color assignment, often all we need.  Color pair 0 cannot
+         * be redefined.  This example uses the same value for the color
+         * pair as for the foreground color, though of course that is not
+         * necessary:
+         */
+        init_pair(1, COLOR_RED,     COLOR_BLACK);
+        init_pair(2, COLOR_GREEN,   COLOR_BLACK);
+        init_pair(3, COLOR_YELLOW,  COLOR_BLACK);
+        init_pair(4, COLOR_BLUE,    COLOR_BLACK);
+        init_pair(5, COLOR_CYAN,    COLOR_BLACK);
+        init_pair(6, COLOR_MAGENTA, COLOR_BLACK);
+        init_pair(7, COLOR_WHITE,   COLOR_BLACK);
+    }
+
+    for (;;)
+    {
+        int c = getch();     /* refresh, accept single keystroke of input */
+        attrset(COLOR_PAIR(num % 8));
+        num++;
+
+        /* process the command keystroke */
+    }
+
+    finish(0);               /* we are 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 is not 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-ORed 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 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.

+ +

Mouse Interfacing

+ +

The ncurses library also provides a mouse + interface.

+ +
+ NOTE: this facility is specific to + ncurses, it is not part of either the XSI Curses + standard, nor of System V Release 4, nor BSD curses. System V + Release 4 curses contains code with similar interface + definitions, however it is not documented. Other than by + disassembling the library, we have no way to determine exactly + how that mouse code works. 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 in the following + environments:

+ + + +

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 will 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 + touch-screens 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 + mousemask(). 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 do not 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(), + ripoffline(), 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. (NOTE: a + SCREEN variable is not a screen in the sense we are + describing in this introduction, but a collection of parameters + used to assist in optimizing the display.) 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 physical 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 fewer total characters + transmitted (this also avoids a visually annoying flicker at + each update).
+
+ +

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 + will have to save it yourself if need be.

+
+
+ +

Debugging

+ +
+ NOTE: These functions are not part of the + standard curses API! +
+ +
+
trace() +
+ +
This function can be used to explicitly set a trace level. + If the trace level is nonzero, execution of your program will + generate a file called “trace” in the current + working directory containing a report on the library's actions. + Higher trace levels enable more detailed (and verbose) + reporting -- see comments attached to TRACE_ + defines in the curses.h file for details. (It is + also possible to set a trace level by assigning a trace level + value to the environment variable + NCURSES_TRACE).
+ +
_tracef() +
+ +
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 is 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). Do not 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 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 easiest way to handle + SIGWINCH is to do an endwin, followed + by an refresh and a screen repaint you code + yourself. The refresh will pick up the new screen + size from the xterm's environment.

+ +

That is the standard way, of course (it even works with some + vendor's curses implementations). Its drawback is that it clears + the screen to reinitialize the display, and does not resize + subwindows which must be shrunk. Ncurses provides an + extension which works better, the resizeterm + function. That function ensures that all windows are limited to + the new screen dimensions, and pads stdscr with + blanks if the screen is larger.

+ +

The ncurses library provides a SIGWINCH signal + handler, which pushes a KEY_RESIZE via the wgetch() + calls. When ncurses returns that code, it calls + resizeterm to update the size of the standard + screen's window, repainting that (filling with blanks or + truncating as needed). It also resizes other windows, but its + effect may be less satisfactory because it cannot know how you + want the screen re-painted. You will usually have to write + special-purpose code to handle KEY_RESIZE + yourself.

+ +

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 does not contain + any control characters. Try to make attribute changes infrequent + on your screens. Do not use the immedok() + option!

+ +

Special Features of + NCURSES

+ +

The wresize() function allows you to resize a + window in place. The associated resizeterm() + function simplifies the construction of SIGWINCH handlers, for resizing all windows.

+ +

The define_key() function allows you to define at + runtime function-key control sequences which are not in the + terminal description. The keyok() function allows + you to temporarily enable or disable interpretation of any + function-key control sequence.

+ +

The use_default_colors() function allows you to + construct applications which can use the terminal's default + foreground and background colors as an additional "default" + color. Several terminal emulators support this feature, which is + based on ISO 6429.

+ +

Ncurses supports up 16 colors, unlike SVr4 curses which + defines only 8. While most terminals which provide color allow + only 8 colors, about a quarter (including XFree86 xterm) support + 16 colors.

+ +

Compatibility with Older + Versions

+ +

Despite our best efforts, there are some differences between + ncurses and the (undocumented!) behavior of older + curses implementations. These arise from ambiguities or omissions + in the documentation of the API.

+ +

Refresh of Overlapping + Windows

+ +

If you define two windows A and B that overlap, and then + alternately scribble on and refresh them, the changes made to the + overlapping region under historic curses versions + were often not documented precisely.

+ +

To understand why this is a problem, remember that screen + updates are calculated between two representations of the + entire display. The documentation says that when you + refresh a window, it is first copied to the virtual screen, and + then changes are calculated to update the physical screen (and + applied to the terminal). But "copied to" is not very specific, + and subtle differences in how copying works can produce different + behaviors in the case where two overlapping windows are each + being refreshed at unpredictable intervals.

+ +

What happens to the overlapping region depends on what + wnoutrefresh() does with its argument -- what + portions of the argument window it copies to the virtual screen. + Some implementations do "change copy", copying down only + locations in the window that have changed (or been marked changed + with wtouchln() and friends). Some implementations + do "entire copy", copying all window locations to the + virtual screen whether or not they have changed.

+ +

The ncurses library itself has not always been + consistent on this score. Due to a bug, versions 1.8.7 to 1.9.8a + did entire copy. Versions 1.8.6 and older, and versions 1.9.9 and + newer, do change copy.

+ +

For most commercial curses implementations, it is not + documented and not known for sure (at least not to the + ncurses maintainers) whether they do change copy or + entire copy. We know that System V release 3 curses has logic in + it that looks like an attempt to do change copy, but the + surrounding logic and data representations are sufficiently + complex, and our knowledge sufficiently indirect, that it is hard + to know whether this is reliable. It is not clear what the SVr4 + documentation and XSI standard intend. The XSI Curses standard + barely mentions wnoutrefresh(); the SVr4 documents seem to be + describing entire-copy, but it is possible with some effort and + straining to read them the other way.

+ +

It might therefore be unwise to rely on either behavior in + programs that might have to be linked with other curses + implementations. Instead, you can do an explicit + touchwin() before the wnoutrefresh() + call to guarantee an entire-contents copy anywhere.

+ +

The really clean way to handle this is to use the panels + library. If, when you want a screen update, you do + update_panels(), it will do all the necessary + wnoutrefresh() calls for whatever panel stacking + order you have defined. Then you can do one + doupdate() and there will be a single burst + of physical I/O that will do all your updates.

+ +

Background Erase

+ +

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. Many + 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 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. Many + 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 will 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 + does not 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 is not + sufficient because it does not 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).

+ +

Typically, you will want to call update_panels() + and doupdate() just before accepting command input, + once in each cycle of interaction with the user. If you call + update_panels() after each and every panel write, + you will generate a lot of unnecessary refresh activity and + screen flicker.

+ +

Panels, Input, and the + Standard Screen

+ +

You should not mix wnoutrefresh() or + wrefresh() operations with panels code; this will + work only if the argument window is either in the top panel or + unobscured 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 + unobscured.

+ +

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

+ +

Hiding Panels

+ +

It is possible to remove a panel from the deck temporarily; + use hide_panel for this. Use + show_panel() to render it visible again. 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 is 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 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. Many + 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. + +
  3. Create the menu items, using new_item().
  4. + +
  5. Create the menu using new_menu().
  6. + +
  7. Post the menu using post_menu().
  8. + +
  9. Refresh the screen.
  10. + +
  11. Process user requests via an input loop.
  12. + +
  13. Unpost the menu using unpost_menu().
  14. + +
  15. Free the menu, using free_menu().
  16. + +
  17. Free the items using free_item().
  18. + +
  19. Terminate curses.
  20. +
+ +

Selecting items

+ +

Menus may be multi-valued or (the default) single-valued (see + the manual page menu_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 unselectable 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 post_menu(), you write the menu to + its subwindow. When you call unpost_menu(), 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 will 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).

+ +

The Forms Library

+ +

The form library is a curses extension that + supports easy programming of on-screen forms for data entry and + program control.

+ +

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

+ +

Compiling With the form + Library

+ +

Your form-using modules must import the form library + declarations with

+ +
+          #include <form.h>
+
+

and must be linked explicitly with the forms library using an + -lform argument. Note that they must also link the + ncurses library with -lncurses. Many + linkers are two-pass and will accept either order, but it is + still good practice to put -lform first and + -lncurses second.

+ +

Overview of Forms

+ +

A form is a collection of fields; each field may be either a + label (explanatory text) or a data-entry location. Long forms may + be segmented into pages; each entry to a new page clears the + screen.

+ +

To make forms, you create groups of fields and connect them + with form frame objects; the form library makes this relatively + simple.

+ +

Once defined, a form can be posted, that is written + to an associated window. Actually, each form has two associated + windows; a containing window in which the programmer can scribble + titles or borders, and a subwindow in which the form fields + proper are displayed.

+ +

As the form user fills out the posted form, navigation and + editing keys support movement between fields, editing keys + support modifying field, and plain text adds to or changes data + in a current field. The form library allows you (the forms + designer) to bind each navigation and editing key to any + keystroke accepted by curses Fields may have + validation conditions on them, so that they check input data for + type and value. The form library supplies a rich set of + pre-defined field types, and makes it relatively easy to define + new ones.

+ +

Once its transaction is completed (or aborted), a form may 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 form program looks like + this:

+ +
    +
  1. Initialize curses.
  2. + +
  3. Create the form fields, using + new_field().
  4. + +
  5. Create the form using new_form().
  6. + +
  7. Post the form using post_form().
  8. + +
  9. Refresh the screen.
  10. + +
  11. Process user requests via an input loop.
  12. + +
  13. Unpost the form using unpost_form().
  14. + +
  15. Free the form, using free_form().
  16. + +
  17. Free the fields using free_field().
  18. + +
  19. Terminate curses.
  20. +
+ +

Note that this looks much like a menu program; the form + library handles tasks which are in many ways similar, and its + interface was obviously designed to resemble that of the menu library wherever possible.

+ +

In forms programs, however, the “process user + requests” is somewhat more complicated than for menus. + Besides menu-like navigation operations, the menu driver loop has + to support field editing and data validation.

+ +

Creating and Freeing Fields + and Forms

+ +

The basic function for creating fields is + new_field():

+ +
+FIELD *new_field(int height, int width,   /* new field size */
+                 int top, int left,       /* upper left corner */
+                 int offscreen,           /* number of offscreen rows */
+                 int nbuf);               /* number of working buffers */
+
+

Menu items always occupy a single row, but forms fields may + have multiple rows. So new_field() requires you to + specify a width and height (the first two arguments, which mist + both be greater than zero).

+ +

You must also specify the location of the field's upper left + corner on the screen (the third and fourth arguments, which must + be zero or greater). Note that these coordinates are relative to + the form subwindow, which will coincide with stdscr + by default but need not be stdscr if you have done + an explicit set_form_win() call.

+ +

The fifth argument allows you to specify a number of + off-screen rows. If this is zero, the entire field will always be + displayed. If it is nonzero, the form will be scrollable, with + only one screen-full (initially the top part) displayed at any + given time. If you make a field dynamic and grow it so it will no + longer fit on the screen, the form will become scrollable even if + the offscreen argument was initially zero.

+ +

The forms library allocates one working buffer per field; the + size of each buffer is ((height + offscreen)*width + + 1, one character for each position in the field plus a NUL + terminator. The sixth argument is the number of additional data + buffers to allocate for the field; your application can use them + for its own purposes.

+ +
+FIELD *dup_field(FIELD *field,            /* field to copy */
+                 int top, int left);      /* location of new copy */
+
+

The function dup_field() duplicates an existing + field at a new location. Size and buffering information are + copied; some attribute flags and status bits are not (see the + form_field_new(3X) for details).

+ +
+FIELD *link_field(FIELD *field,           /* field to copy */
+                  int top, int left);     /* location of new copy */
+
+

The function link_field() also duplicates an + existing field at a new location. The difference from + dup_field() is that it arranges for the new field's + buffer to be shared with the old one.

+ +

Besides the obvious use in making a field editable from two + different form pages, linked fields give you a way to hack in + dynamic labels. If you declare several fields linked to an + original, and then make them inactive, changes from the original + will still be propagated to the linked fields.

+ +

As with duplicated fields, linked fields have attribute bits + separate from the original.

+ +

As you might guess, all these field-allocations return + NULL if the field allocation is not possible due to + an out-of-memory error or out-of-bounds arguments.

+ +

To connect fields to a form, use

+ +
+FORM *new_form(FIELD **fields);
+
+

This function expects to see a NULL-terminated array of field + pointers. Said fields are connected to a newly-allocated form + object; its address is returned (or else NULL if the allocation + fails).

+ +

Note that new_field() does not copy the + pointer array into private storage; if you modify the contents of + the pointer array during forms processing, all manner of bizarre + things might happen. Also note that any given field may only be + connected to one form.

+ +

The functions free_field() and + free_form are available to free field and form + objects. It is an error to attempt to free a field connected to a + form, but not vice-versa; thus, you will generally free your form + objects first.

+ +

Fetching and Changing + Field Attributes

+ +

Each form field has a number of location and size attributes + associated with it. There are other field attributes used to + control display and editing of the field. Some (for example, the + O_STATIC bit) involve sufficient complications to be + covered in sections of their own later on. We cover the functions + used to get and set several basic attributes here.

+ +

When a field is created, the attributes not specified by the + new_field function are copied from an invisible + system default field. In attribute-setting and -fetching + functions, the argument NULL is taken to mean this field. Changes + to it persist as defaults until your forms application + terminates.

+ +

Fetching Size and Location + Data

+ +

You can retrieve field sizes and locations through:

+ +
+int field_info(FIELD *field,              /* field from which to fetch */
+               int *height, *int width,   /* field size */
+               int *top, int *left,       /* upper left corner */
+               int *offscreen,            /* number of offscreen rows */
+               int *nbuf);                /* number of working buffers */
+
+

This function is a sort of inverse of + new_field(); instead of setting size and location + attributes of a new field, it fetches them from an existing + one.

+ +

Changing the Field + Location

+ +

It is possible to move a field's location on the screen:

+ +
+int move_field(FIELD *field,              /* field to alter */
+               int top, int left);        /* new upper-left corner */
+
+

You can, of course. query the current location through + field_info().

+ +

The Justification Attribute

+ +

One-line fields may be unjustified, justified right, justified + left, or centered. Here is how you manipulate this attribute:

+ +
+int set_field_just(FIELD *field,          /* field to alter */
+                   int justmode);         /* mode to set */
+
+int field_just(FIELD *field);             /* fetch mode of field */
+
+

The mode values accepted and returned by this functions are + preprocessor macros NO_JUSTIFICATION, + JUSTIFY_RIGHT, JUSTIFY_LEFT, or + JUSTIFY_CENTER.

+ +

Field Display + Attributes

+ +

For each field, you can set a foreground attribute for entered + characters, a background attribute for the entire field, and a + pad character for the unfilled portion of the field. You can also + control pagination of the form.

+ +

This group of four field attributes controls the visual + appearance of the field on the screen, without affecting in any + way the data in the field buffer.

+ +
+int set_field_fore(FIELD *field,          /* field to alter */
+                   chtype attr);          /* attribute to set */
+
+chtype field_fore(FIELD *field);          /* field to query */
+
+int set_field_back(FIELD *field,          /* field to alter */
+                   chtype attr);          /* attribute to set */
+
+chtype field_back(FIELD *field);          /* field to query */
+
+int set_field_pad(FIELD *field,           /* field to alter */
+                 int pad);                /* pad character to set */
+
+chtype field_pad(FIELD *field);
+
+int set_new_page(FIELD *field,            /* field to alter */
+                 int flag);               /* TRUE to force new page */
+
+chtype new_page(FIELD *field);            /* field to query */
+
+

The attributes set and returned by the first four functions + are normal curses(3x) display attribute values + (A_STANDOUT, A_BOLD, + A_REVERSE etc). The page bit of a field controls + whether it is displayed at the start of a new form screen.

+ +

Field Option Bits

+ +

There is also a large collection of field option bits you can + set to control various aspects of forms processing. You can + manipulate them with these functions:

+ +
+int set_field_opts(FIELD *field,          /* field to alter */
+                   int attr);             /* attribute to set */
+
+int field_opts_on(FIELD *field,           /* field to alter */
+                  int attr);              /* attributes to turn on */
+
+int field_opts_off(FIELD *field,          /* field to alter */
+                   int attr);             /* attributes to turn off */
+
+int field_opts(FIELD *field);             /* field to query */
+
+

By default, all options are on. Here are the available option + bits:

+ +
+
O_VISIBLE
+ +
Controls whether the field is visible on the screen. Can be + used during form processing to hide or pop up fields depending + on the value of parent fields.
+ +
O_ACTIVE
+ +
Controls whether the field is active during forms + processing (i.e. visited by form navigation keys). Can be used + to make labels or derived fields with buffer values alterable + by the forms application, not the user.
+ +
O_PUBLIC
+ +
Controls whether data is displayed during field entry. If + this option is turned off on a field, the library will accept + and edit data in that field, but it will not be displayed and + the visible field cursor will not move. You can turn off the + O_PUBLIC bit to define password fields.
+ +
O_EDIT
+ +
Controls whether the field's data can be modified. When + this option is off, all editing requests except + REQ_PREV_CHOICE and REQ_NEXT_CHOICE + will fail. Such read-only fields may be useful for help + messages.
+ +
O_WRAP
+ +
Controls word-wrapping in multi-line fields. Normally, when + any character of a (blank-separated) word reaches the end of + the current line, the entire word is wrapped to the next line + (assuming there is one). When this option is off, the word will + be split across the line break.
+ +
O_BLANK
+ +
Controls field blanking. When this option is on, entering a + character at the first field position erases the entire field + (except for the just-entered character).
+ +
O_AUTOSKIP
+ +
Controls automatic skip to next field when this one fills. + Normally, when the forms user tries to type more data into a + field than will fit, the editing location jumps to next field. + When this option is off, the user's cursor will hang at the end + of the field. This option is ignored in dynamic fields that + have not reached their size limit.
+ +
O_NULLOK
+ +
Controls whether validation is + applied to blank fields. Normally, it is not; the user can + leave a field blank without invoking the usual validation check + on exit. If this option is off on a field, exit from it will + invoke a validation check.
+ +
O_PASSOK
+ +
Controls whether validation occurs on every exit, or only + after the field is modified. Normally the latter is true. + Setting O_PASSOK may be useful if your field's validation + function may change during forms processing.
+ +
O_STATIC
+ +
Controls whether the field is fixed to its initial + dimensions. If you turn this off, the field becomes dynamic and will stretch to fit entered + data.
+
+ +

A field's options cannot be changed while the field is + currently selected. However, options may be changed on posted + fields that are not current.

+ +

The option values are bit-masks and can be composed with + logical-or in the obvious way.

+ +

Field Status

+ +

Every field has a status flag, which is set to FALSE when the + field is created and TRUE when the value in field buffer 0 + changes. This flag can be queried and set directly:

+ +
+int set_field_status(FIELD *field,      /* field to alter */
+                   int status);         /* mode to set */
+
+int field_status(FIELD *field);         /* fetch mode of field */
+
+

Setting this flag under program control can be useful if you + use the same form repeatedly, looking for modified fields each + time.

+ +

Calling field_status() on a field not currently + selected for input will return a correct value. Calling + field_status() on a field that is currently selected + for input may not necessarily give a correct field status value, + because entered data is not necessarily copied to buffer zero + before the exit validation check. To guarantee that the returned + status value reflects reality, call field_status() + either (1) in the field's exit validation check routine, (2) from + the field's or form's initialization or termination hooks, or (3) + just after a REQ_VALIDATION request has been + processed by the forms driver.

+ +

Field User Pointer

+ +

Each field structure contains one character pointer slot that + is not used by the forms library. It is intended to be used by + applications to store private per-field data. You can manipulate + it with:

+ +
+int set_field_userptr(FIELD *field,       /* field to alter */
+                   char *userptr);        /* mode to set */
+
+char *field_userptr(FIELD *field);        /* fetch mode of field */
+
(Properly, this user pointer field ought to have (void +*) type. The (char *) type is retained for +System V compatibility.) +

It is valid to set the user pointer of the default field (with + a set_field_userptr() call passed a NULL field + pointer.) When a new field is created, the default-field user + pointer is copied to initialize the new field's user pointer.

+ +

Variable-Sized Fields

+ +

Normally, a field is fixed at the size specified for it at + creation time. If, however, you turn off its O_STATIC bit, it + becomes dynamic and will automatically resize itself + to accommodate data as it is entered. If the field has extra + buffers associated with it, they will grow right along with the + main input buffer.

+ +

A one-line dynamic field will have a fixed height (1) but + variable width, scrolling horizontally to display data within the + field area as originally dimensioned and located. A multi-line + dynamic field will have a fixed width, but variable height + (number of rows), scrolling vertically to display data within the + field area as originally dimensioned and located.

+ +

Normally, a dynamic field is allowed to grow without limit. + But it is possible to set an upper limit on the size of a dynamic + field. You do it with this function:

+ +
+int set_max_field(FIELD *field,     /* field to alter (may not be NULL) */
+                   int max_size);   /* upper limit on field size */
+
+

If the field is one-line, max_size is taken to be + a column size limit; if it is multi-line, it is taken to be a + line size limit. To disable any limit, use an argument of zero. + The growth limit can be changed whether or not the O_STATIC bit + is on, but has no effect until it is.

+ +

The following properties of a field change when it becomes + dynamic:

+ + + +

Field Validation

+ +

By default, a field will accept any data that will fit in its + input buffer. However, it is possible to attach a validation type + to a field. If you do this, any attempt to leave the field while + it contains data that does not match the validation type will + fail. Some validation types also have a character-validity check + for each time a character is entered in the field.

+ +

A field's validation check (if any) is not called when + set_field_buffer() modifies the input buffer, nor + when that buffer is changed through a linked field.

+ +

The form library provides a rich set of + pre-defined validation types, and gives you the capability to + define custom ones of your own. You can examine and change field + validation attributes with the following functions:

+ +
+int set_field_type(FIELD *field,          /* field to alter */
+                   FIELDTYPE *ftype,      /* type to associate */
+                   ...);                  /* additional arguments*/
+
+FIELDTYPE *field_type(FIELD *field);      /* field to query */
+
+

The validation type of a field is considered an attribute of + the field. As with other field attributes, Also, doing + set_field_type() with a NULL field + default will change the system default for validation of + newly-created fields.

+ +

Here are the pre-defined validation types:

+ +

TYPE_ALPHA

+ +

This field type accepts alphabetic data; no blanks, no digits, + no special characters (this is checked at character-entry time). + It is set up with:

+ +
+int set_field_type(FIELD *field,          /* field to alter */
+                   TYPE_ALPHA,            /* type to associate */
+                   int width);            /* maximum width of field */
+
+

The width argument sets a minimum width of data. + Typically you will want to set this to the field width; if it is + greater than the field width, the validation check will always + fail. A minimum width of zero makes field completion + optional.

+ +

TYPE_ALNUM

+ +

This field type accepts alphabetic data and digits; no blanks, + no special characters (this is checked at character-entry time). + It is set up with:

+ +
+int set_field_type(FIELD *field,          /* field to alter */
+                   TYPE_ALNUM,            /* type to associate */
+                   int width);            /* maximum width of field */
+
+

The width argument sets a minimum width of data. + As with TYPE_ALPHA, typically you will want to set this to the + field width; if it is greater than the field width, the + validation check will always fail. A minimum width of zero makes + field completion optional.

+ +

TYPE_ENUM

+ +

This type allows you to restrict a field's values to be among + a specified set of string values (for example, the two-letter + postal codes for U.S. states). It is set up with:

+ +
+int set_field_type(FIELD *field,          /* field to alter */
+                   TYPE_ENUM,             /* type to associate */
+                   char **valuelist;      /* list of possible values */
+                   int checkcase;         /* case-sensitive? */
+                   int checkunique);      /* must specify uniquely? */
+
+

The valuelist parameter must point at a + NULL-terminated list of valid strings. The checkcase + argument, if true, makes comparison with the string + case-sensitive.

+ +

When the user exits a TYPE_ENUM field, the validation + procedure tries to complete the data in the buffer to a valid + entry. If a complete choice string has been entered, it is of + course valid. But it is also possible to enter a prefix of a + valid string and have it completed for you.

+ +

By default, if you enter such a prefix and it matches more + than one value in the string list, the prefix will be completed + to the first matching value. But the checkunique + argument, if true, requires prefix matches to be unique in order + to be valid.

+ +

The REQ_NEXT_CHOICE and + REQ_PREV_CHOICE input requests can be particularly + useful with these fields.

+ +

TYPE_INTEGER

+ +

This field type accepts an integer. It is set up as + follows:

+ +
+int set_field_type(FIELD *field,          /* field to alter */
+                   TYPE_INTEGER,          /* type to associate */
+                   int padding,           /* # places to zero-pad to */
+                   int vmin, int vmax);   /* valid range */
+
+

Valid characters consist of an optional leading minus and + digits. The range check is performed on exit. If the range + maximum is less than or equal to the minimum, the range is + ignored.

+ +

If the value passes its range check, it is padded with as many + leading zero digits as necessary to meet the padding + argument.

+ +

A TYPE_INTEGER value buffer can conveniently be + interpreted with the C library function atoi(3).

+ +

TYPE_NUMERIC

+ +

This field type accepts a decimal number. It is set up as + follows:

+ +
+int set_field_type(FIELD *field,              /* field to alter */
+                   TYPE_NUMERIC,              /* type to associate */
+                   int padding,               /* # places of precision */
+                   double vmin, double vmax); /* valid range */
+
+

Valid characters consist of an optional leading minus and + digits. possibly including a decimal point. If your system + supports locale's, the decimal point character used must be the + one defined by your locale. The range check is performed on exit. + If the range maximum is less than or equal to the minimum, the + range is ignored.

+ +

If the value passes its range check, it is padded with as many + trailing zero digits as necessary to meet the padding + argument.

+ +

A TYPE_NUMERIC value buffer can conveniently be + interpreted with the C library function atof(3).

+ +

TYPE_REGEXP

+ +

This field type accepts data matching a regular expression. It + is set up as follows:

+ +
+int set_field_type(FIELD *field,          /* field to alter */
+                   TYPE_REGEXP,           /* type to associate */
+                   char *regexp);         /* expression to match */
+
+

The syntax for regular expressions is that of + regcomp(3). The check for regular-expression match + is performed on exit.

+ +

Direct Field Buffer + Manipulation

+ +

The chief attribute of a field is its buffer contents. When a + form has been completed, your application usually needs to know + the state of each field buffer. You can find this out with:

+ +
+char *field_buffer(FIELD *field,          /* field to query */
+                   int bufindex);         /* number of buffer to query */
+
+

Normally, the state of the zero-numbered buffer for each field + is set by the user's editing actions on that field. It is + sometimes useful to be able to set the value of the zero-numbered + (or some other) buffer from your application:

+ +
+int set_field_buffer(FIELD *field,        /* field to alter */
+                   int bufindex,          /* number of buffer to alter */
+                   char *value);          /* string value to set */
+
+

If the field is not large enough and cannot be resized to a + sufficiently large size to contain the specified value, the value + will be truncated to fit.

+ +

Calling field_buffer() with a null field pointer + will raise an error. Calling field_buffer() on a + field not currently selected for input will return a correct + value. Calling field_buffer() on a field that is + currently selected for input may not necessarily give a correct + field buffer value, because entered data is not necessarily + copied to buffer zero before the exit validation check. To + guarantee that the returned buffer value reflects on-screen + reality, call field_buffer() either (1) in the + field's exit validation check routine, (2) from the field's or + form's initialization or termination hooks, or (3) just after a + REQ_VALIDATION request has been processed by the + forms driver.

+ +

Attributes of Forms

+ +

As with field attributes, form attributes inherit a default + from a system default form structure. These defaults can be + queried or set by of these functions using a form-pointer + argument of NULL.

+ +

The principal attribute of a form is its field list. You can + query and change this list with:

+ +
+int set_form_fields(FORM *form,           /* form to alter */
+                    FIELD **fields);      /* fields to connect */
+
+char *form_fields(FORM *form);            /* fetch fields of form */
+
+int field_count(FORM *form);              /* count connect fields */
+
+

The second argument of set_form_fields() may be a + NULL-terminated field pointer array like the one required by + new_form(). In that case, the old fields of the form + are disconnected but not freed (and eligible to be connected to + other forms), then the new fields are connected.

+ +

It may also be null, in which case the old fields are + disconnected (and not freed) but no new ones are connected.

+ +

The field_count() function simply counts the + number of fields connected to a given from. It returns -1 if the + form-pointer argument is NULL.

+ +

Control of Form Display

+ +

In the overview section, you saw that to display a form you + normally start by defining its size (and fields), posting it, and + refreshing the screen. There is an hidden step before posting, + which is the association of the form with a frame window + (actually, a pair of windows) within which it will be displayed. + By default, the forms library associates every form with the + full-screen window stdscr.

+ +

By making this step explicit, you can associate a form with a + declared frame window on your screen display. This can be useful + if you want to adapt the form display to different screen sizes, + dynamically tile forms on the screen, or use a form as part of an + interface layout managed by panels.

+ +

The two windows associated with each form have the same + functions as their analogues in the menu + library. Both these windows are painted when the form is + posted and erased when the form is unposted.

+ +

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

+ +

In order to declare your own frame window for a form, you will + need to know the size of the form's bounding rectangle. You can + get this information with:

+ +
+int scale_form(FORM *form,                /* form to query */
+               int *rows,                 /* form rows */
+               int *cols);                /* form cols */
+
+

The form dimensions are passed back in the locations pointed + to by the arguments. Once you have this information, you can use + it to declare of windows, then use one of these functions:

+ +
+int set_form_win(FORM *form,              /* form to alter */
+                 WINDOW *win);            /* frame window to connect */
+
+WINDOW *form_win(FORM *form);             /* fetch frame window of form */
+
+int set_form_sub(FORM *form,              /* form to alter */
+                 WINDOW *win);            /* form subwindow to connect */
+
+WINDOW *form_sub(FORM *form);             /* fetch form subwindow of form */
+
+

Note that curses operations, including refresh(), + on the form, should be done on the frame window, not the form + subwindow.

+ +

It is possible to check from your application whether all of a + scrollable field is actually displayed within the menu subwindow. + Use these functions:

+ +
+int data_ahead(FORM *form);               /* form to be queried */
+
+int data_behind(FORM *form);              /* form to be queried */
+
+

The function data_ahead() returns TRUE if (a) the + current field is one-line and has undisplayed data off to the + right, (b) the current field is multi-line and there is data + off-screen below it.

+ +

The function data_behind() returns TRUE if the + first (upper left hand) character position is off-screen (not + being displayed).

+ +

Finally, there is a function to restore the form window's + cursor to the value expected by the forms driver:

+ +
+int pos_form_cursor(FORM *)               /* form to be queried */
+
+

If your application changes the form window cursor, call this + function before handing control back to the forms driver in order + to re-synchronize it.

+ +

Input Processing in the Forms + Driver

+ +

The function form_driver() handles virtualized + input requests for form navigation, editing, and validation + requests, just as menu_driver does for menus (see + the section on menu input handling).

+ +
+int form_driver(FORM *form,               /* form to pass input to */
+                int request);             /* form request code */
+
+

Your input virtualization function needs to take input and + then convert it to either an alphanumeric character (which is + treated as data to be entered in the currently-selected field), + or a forms processing request.

+ +

The forms driver provides hooks (through input-validation and + field-termination functions) with which your application code can + check that the input taken by the driver matched what was + expected.

+ +

Page Navigation Requests

+ +

These requests cause page-level moves through the form, + triggering display of a new form screen.

+ +
+
REQ_NEXT_PAGE +
+ +
Move to the next form page.
+ +
REQ_PREV_PAGE +
+ +
Move to the previous form page.
+ +
REQ_FIRST_PAGE +
+ +
Move to the first form page.
+ +
REQ_LAST_PAGE +
+ +
Move to the last form page.
+
+ +

These requests treat the list as cyclic; that is, + REQ_NEXT_PAGE from the last page goes to the first, + and REQ_PREV_PAGE from the first page goes to the + last.

+ +

Inter-Field Navigation + Requests

+ +

These requests handle navigation between fields on the same + page.

+ +
+
REQ_NEXT_FIELD +
+ +
Move to next field.
+ +
REQ_PREV_FIELD +
+ +
Move to previous field.
+ +
REQ_FIRST_FIELD +
+ +
Move to the first field.
+ +
REQ_LAST_FIELD +
+ +
Move to the last field.
+ +
REQ_SNEXT_FIELD +
+ +
Move to sorted next field.
+ +
REQ_SPREV_FIELD +
+ +
Move to sorted previous field.
+ +
REQ_SFIRST_FIELD +
+ +
Move to the sorted first field.
+ +
REQ_SLAST_FIELD +
+ +
Move to the sorted last field.
+ +
REQ_LEFT_FIELD +
+ +
Move left to field.
+ +
REQ_RIGHT_FIELD +
+ +
Move right to field.
+ +
REQ_UP_FIELD +
+ +
Move up to field.
+ +
REQ_DOWN_FIELD +
+ +
Move down to field.
+
+ +

These requests treat the list of fields on a page as cyclic; + that is, REQ_NEXT_FIELD from the last field goes to + the first, and REQ_PREV_FIELD from the first field + goes to the last. The order of the fields for these (and the + REQ_FIRST_FIELD and REQ_LAST_FIELD + requests) is simply the order of the field pointers in the form + array (as set up by new_form() or + set_form_fields()

+ +

It is also possible to traverse the fields as if they had been + sorted in screen-position order, so the sequence goes + left-to-right and top-to-bottom. To do this, use the second group + of four sorted-movement requests.

+ +

Finally, it is possible to move between fields using visual + directions up, down, right, and left. To accomplish this, use the + third group of four requests. Note, however, that the position of + a form for purposes of these requests is its upper-left + corner.

+ +

For example, suppose you have a multi-line field B, and two + single-line fields A and C on the same line with B, with A to the + left of B and C to the right of B. A REQ_MOVE_RIGHT + from A will go to B only if A, B, and C all share the + same first line; otherwise it will skip over B to C.

+ +

Intra-Field Navigation + Requests

+ +

These requests drive movement of the edit cursor within the + currently selected field.

+ +
+
REQ_NEXT_CHAR +
+ +
Move to next character.
+ +
REQ_PREV_CHAR +
+ +
Move to previous character.
+ +
REQ_NEXT_LINE +
+ +
Move to next line.
+ +
REQ_PREV_LINE +
+ +
Move to previous line.
+ +
REQ_NEXT_WORD +
+ +
Move to next word.
+ +
REQ_PREV_WORD +
+ +
Move to previous word.
+ +
REQ_BEG_FIELD +
+ +
Move to beginning of field.
+ +
REQ_END_FIELD +
+ +
Move to end of field.
+ +
REQ_BEG_LINE +
+ +
Move to beginning of line.
+ +
REQ_END_LINE +
+ +
Move to end of line.
+ +
REQ_LEFT_CHAR +
+ +
Move left in field.
+ +
REQ_RIGHT_CHAR +
+ +
Move right in field.
+ +
REQ_UP_CHAR +
+ +
Move up in field.
+ +
REQ_DOWN_CHAR +
+ +
Move down in field.
+
+ +

Each word is separated from the previous and next + characters by whitespace. The commands to move to beginning and + end of line or field look for the first or last non-pad character + in their ranges.

+ +

Scrolling Requests

+ +

Fields that are dynamic and have grown and fields explicitly + created with offscreen rows are scrollable. One-line fields + scroll horizontally; multi-line fields scroll vertically. Most + scrolling is triggered by editing and intra-field movement (the + library scrolls the field to keep the cursor visible). It is + possible to explicitly request scrolling with the following + requests:

+ +
+
REQ_SCR_FLINE +
+ +
Scroll vertically forward a line.
+ +
REQ_SCR_BLINE +
+ +
Scroll vertically backward a line.
+ +
REQ_SCR_FPAGE +
+ +
Scroll vertically forward a page.
+ +
REQ_SCR_BPAGE +
+ +
Scroll vertically backward a page.
+ +
REQ_SCR_FHPAGE +
+ +
Scroll vertically forward half a page.
+ +
REQ_SCR_BHPAGE +
+ +
Scroll vertically backward half a page.
+ +
REQ_SCR_FCHAR +
+ +
Scroll horizontally forward a character.
+ +
REQ_SCR_BCHAR +
+ +
Scroll horizontally backward a character.
+ +
REQ_SCR_HFLINE +
+ +
Scroll horizontally one field width forward.
+ +
REQ_SCR_HBLINE +
+ +
Scroll horizontally one field width backward.
+ +
REQ_SCR_HFHALF +
+ +
Scroll horizontally one half field width forward.
+ +
REQ_SCR_HBHALF +
+ +
Scroll horizontally one half field width backward.
+
+ +

For scrolling purposes, a page of a field is the + height of its visible part.

+ +

Editing Requests

+ +

When you pass the forms driver an ASCII character, it is + treated as a request to add the character to the field's data + buffer. Whether this is an insertion or a replacement depends on + the field's edit mode (insertion is the default.

+ +

The following requests support editing the field and changing + the edit mode:

+ +
+
REQ_INS_MODE +
+ +
Set insertion mode.
+ +
REQ_OVL_MODE +
+ +
Set overlay mode.
+ +
REQ_NEW_LINE +
+ +
New line request (see below for explanation).
+ +
REQ_INS_CHAR +
+ +
Insert space at character location.
+ +
REQ_INS_LINE +
+ +
Insert blank line at character location.
+ +
REQ_DEL_CHAR +
+ +
Delete character at cursor.
+ +
REQ_DEL_PREV +
+ +
Delete previous word at cursor.
+ +
REQ_DEL_LINE +
+ +
Delete line at cursor.
+ +
REQ_DEL_WORD +
+ +
Delete word at cursor.
+ +
REQ_CLR_EOL +
+ +
Clear to end of line.
+ +
REQ_CLR_EOF +
+ +
Clear to end of field.
+ +
REQ_CLEAR_FIELD +
+ +
Clear entire field.
+
+ +

The behavior of the REQ_NEW_LINE and + REQ_DEL_PREV requests is complicated and partly + controlled by a pair of forms options. The special cases are + triggered when the cursor is at the beginning of a field, or on + the last line of the field.

+ +

First, we consider REQ_NEW_LINE:

+ +

The normal behavior of REQ_NEW_LINE in insert + mode is to break the current line at the position of the edit + cursor, inserting the portion of the current line after the + cursor as a new line following the current and moving the cursor + to the beginning of that new line (you may think of this as + inserting a newline in the field buffer).

+ +

The normal behavior of REQ_NEW_LINE in overlay + mode is to clear the current line from the position of the edit + cursor to end of line. The cursor is then moved to the beginning + of the next line.

+ +

However, REQ_NEW_LINE at the beginning of a + field, or on the last line of a field, instead does a + REQ_NEXT_FIELD. O_NL_OVERLOAD option is + off, this special action is disabled.

+ +

Now, let us consider REQ_DEL_PREV:

+ +

The normal behavior of REQ_DEL_PREV is to delete + the previous character. If insert mode is on, and the cursor is + at the start of a line, and the text on that line will fit on the + previous one, it instead appends the contents of the current line + to the previous one and deletes the current line (you may think + of this as deleting a newline from the field buffer).

+ +

However, REQ_DEL_PREV at the beginning of a field + is instead treated as a REQ_PREV_FIELD.

+ +

If the O_BS_OVERLOAD option is off, this special + action is disabled and the forms driver just returns + E_REQUEST_DENIED.

+ +

See Form Options for discussion of + how to set and clear the overload options.

+ +

Order Requests

+ +

If the type of your field is ordered, and has associated + functions for getting the next and previous values of the type + from a given value, there are requests that can fetch that value + into the field buffer:

+ +
+
REQ_NEXT_CHOICE +
+ +
Place the successor value of the current value in the + buffer.
+ +
REQ_PREV_CHOICE +
+ +
Place the predecessor value of the current value in the + buffer.
+
+ +

Of the built-in field types, only TYPE_ENUM has + built-in successor and predecessor functions. When you define a + field type of your own (see Custom Validation + Types), you can associate our own ordering functions.

+ +

Application Commands

+ +

Form requests are represented as integers above the + curses value greater than KEY_MAX and + less than or equal to the constant MAX_COMMAND. If + your input-virtualization routine returns a value above + MAX_COMMAND, the forms driver will ignore it.

+ +

Field Change Hooks

+ +

It is possible to set function hooks to be executed whenever + the current field or form changes. Here are the functions that + support this:

+ +
+typedef void    (*HOOK)();       /* pointer to function returning void */
+
+int set_form_init(FORM *form,    /* form to alter */
+                  HOOK hook);    /* initialization hook */
+
+HOOK form_init(FORM *form);      /* form to query */
+
+int set_form_term(FORM *form,    /* form to alter */
+                  HOOK hook);    /* termination hook */
+
+HOOK form_term(FORM *form);      /* form to query */
+
+int set_field_init(FORM *form,   /* form to alter */
+                  HOOK hook);    /* initialization hook */
+
+HOOK field_init(FORM *form);     /* form to query */
+
+int set_field_term(FORM *form,   /* form to alter */
+                  HOOK hook);    /* termination hook */
+
+HOOK field_term(FORM *form);     /* form to query */
+
+

These functions allow you to either set or query four + different hooks. In each of the set functions, the second + argument should be the address of a hook function. These + functions differ only in the timing of the hook call.

+ +
+
form_init
+ +
This hook is called when the form is posted; also, just + after each page change operation.
+ +
field_init
+ +
This hook is called when the form is posted; also, just + after each field change
+ +
field_term
+ +
This hook is called just after field validation; that is, + just before the field is altered. It is also called when the + form is unposted.
+ +
form_term
+ +
This hook is called when the form is unposted; also, just + before each page change operation.
+
+ +

Calls to these hooks may be triggered

+ +
    +
  1. When user editing requests are processed by the forms + driver
  2. + +
  3. When the current page is changed by + set_current_field() call
  4. + +
  5. When the current field is changed by a + set_form_page() call
  6. +
+ +

See Field Change Commands for + discussion of the latter two cases.

+ +

You can set a default hook for all fields by passing one of + the set functions a NULL first argument.

+ +

You can disable any of these hooks by (re)setting them to + NULL, the default value.

+ +

Field Change Commands

+ +

Normally, navigation through the form will be driven by the + user's input requests. But sometimes it is useful to be able to + move the focus for editing and viewing under control of your + application, or ask which field it currently is in. The following + functions help you accomplish this:

+ +
+int set_current_field(FORM *form,         /* form to alter */
+                      FIELD *field);      /* field to shift to */
+
+FIELD *current_field(FORM *form);         /* form to query */
+
+int field_index(FORM *form,               /* form to query */
+                FIELD *field);            /* field to get index of */
+
+

The function field_index() returns the index of + the given field in the given form's field array (the array passed + to new_form() or + set_form_fields()).

+ +

The initial current field of a form is the first active field + on the first page. The function set_form_fields() + resets this.

+ +

It is also possible to move around by pages.

+ +
+int set_form_page(FORM *form,             /* form to alter */
+                  int page);              /* page to go to (0-origin) */
+
+int form_page(FORM *form);                /* return form's current page */
+
+

The initial page of a newly-created form is 0. The function + set_form_fields() resets this.

+ +

Form Options

+ +

Like fields, forms may have control option bits. They can be + changed or queried with these functions:

+ +
+int set_form_opts(FORM *form,             /* form to alter */
+                  int attr);              /* attribute to set */
+
+int form_opts_on(FORM *form,              /* form to alter */
+                 int attr);               /* attributes to turn on */
+
+int form_opts_off(FORM *form,             /* form to alter */
+                  int attr);              /* attributes to turn off */
+
+int form_opts(FORM *form);                /* form to query */
+
+

By default, all options are on. Here are the available option + bits:

+ +
+
O_NL_OVERLOAD
+ +
Enable overloading of REQ_NEW_LINE as + described in Editing Requests. The value + of this option is ignored on dynamic fields that have not + reached their size limit; these have no last line, so the + circumstances for triggering a REQ_NEXT_FIELD + never arise.
+ +
O_BS_OVERLOAD
+ +
Enable overloading of REQ_DEL_PREV as + described in Editing Requests.
+
+ +

The option values are bit-masks and can be composed with + logical-or in the obvious way.

+ +

Custom Validation Types

+ +

The form library gives you the capability to + define custom validation types of your own. Further, the optional + additional arguments of set_field_type effectively + allow you to parameterize validation types. Most of the + complications in the validation-type interface have to do with + the handling of the additional arguments within custom validation + functions.

+ +

Union Types

+ +

The simplest way to create a custom data type is to compose it + from two preexisting ones:

+ +
+FIELD *link_fieldtype(FIELDTYPE *type1,
+                      FIELDTYPE *type2);
+
+

This function creates a field type that will accept any of the + values legal for either of its argument field types (which may be + either predefined or programmer-defined). If a + set_field_type() call later requires arguments, the + new composite type expects all arguments for the first type, than + all arguments for the second. Order functions (see Order Requests) associated with the component types + will work on the composite; what it does is check the validation + function for the first type, then for the second, to figure what + type the buffer contents should be treated as.

+ +

New Field Types

+ +

To create a field type from scratch, you need to specify one + or both of the following things:

+ + + +

Here is how you do that:

+ +
+typedef int     (*HOOK)();       /* pointer to function returning int */
+
+FIELDTYPE *new_fieldtype(HOOK f_validate, /* field validator */
+                         HOOK c_validate) /* character validator */
+
+int free_fieldtype(FIELDTYPE *ftype);     /* type to free */
+
+

At least one of the arguments of new_fieldtype() + must be non-NULL. The forms driver will automatically call the + new type's validation functions at appropriate points in + processing a field of the new type.

+ +

The function free_fieldtype() deallocates the + argument fieldtype, freeing all storage associated with it.

+ +

Normally, a field validator is called when the user attempts + to leave the field. Its first argument is a field pointer, from + which it can get to field buffer 0 and test it. If the function + returns TRUE, the operation succeeds; if it returns FALSE, the + edit cursor stays in the field.

+ +

A character validator gets the character passed in as a first + argument. It too should return TRUE if the character is valid, + FALSE otherwise.

+ +

Validation Function + Arguments

+ +

Your field- and character- validation functions will be passed + a second argument as well. This second argument is the address of + a structure (which we will call a pile) built from any + of the field-type-specific arguments passed to + set_field_type(). If no such arguments are defined + for the field type, this pile pointer argument will be NULL.

+ +

In order to arrange for such arguments to be passed to your + validation functions, you must associate a small set of + storage-management functions with the type. The forms driver will + use these to synthesize a pile from the trailing arguments of + each set_field_type() argument, and a pointer to the + pile will be passed to the validation functions.

+ +

Here is how you make the association:

+ +
+typedef char    *(*PTRHOOK)();    /* pointer to function returning (char *) */
+typedef void    (*VOIDHOOK)();    /* pointer to function returning void */
+
+int set_fieldtype_arg(FIELDTYPE *type,    /* type to alter */
+                      PTRHOOK make_str,   /* make structure from args */
+                      PTRHOOK copy_str,   /* make copy of structure */
+                      VOIDHOOK free_str); /* free structure storage */
+
+

Here is how the storage-management hooks are used:

+ +
+
make_str +
+ +
This function is called by set_field_type(). + It gets one argument, a va_list of the + type-specific arguments passed to + set_field_type(). It is expected to return a pile + pointer to a data structure that encapsulates those + arguments.
+ +
copy_str +
+ +
This function is called by form library functions that + allocate new field instances. It is expected to take a pile + pointer, copy the pile to allocated storage, and return the + address of the pile copy.
+ +
free_str +
+ +
This function is called by field- and type-deallocation + routines in the library. It takes a pile pointer argument, and + is expected to free the storage of that pile.
+
+ +

The make_str and copy_str functions + may return NULL to signal allocation failure. The library + routines will that call them will return error indication when + this happens. Thus, your validation functions should never see a + NULL file pointer and need not check specially for it.

+ +

Order Functions For + Custom Types

+ +

Some custom field types are simply ordered in the same + well-defined way that TYPE_ENUM is. For such types, + it is possible to define successor and predecessor functions to + support the REQ_NEXT_CHOICE and + REQ_PREV_CHOICE requests. Here is how:

+ +
+typedef int     (*INTHOOK)();     /* pointer to function returning int */
+
+int set_fieldtype_arg(FIELDTYPE *type,    /* type to alter */
+                      INTHOOK succ,       /* get successor value */
+                      INTHOOK pred);      /* get predecessor value */
+
+

The successor and predecessor arguments will each be passed + two arguments; a field pointer, and a pile pointer (as for the + validation functions). They are expected to use the function + field_buffer() to read the current value, and + set_field_buffer() on buffer 0 to set the next or + previous value. Either hook may return TRUE to indicate success + (a legal next or previous value was set) or FALSE to indicate + failure.

+ +

Avoiding Problems

+ +

The interface for defining custom types is complicated and + tricky. Rather than attempting to create a custom type entirely + from scratch, you should start by studying the library source + code for whichever of the pre-defined types seems to be closest + to what you want.

+ +

Use that code as a model, and evolve it towards what you + really want. You will avoid many problems and annoyances that + way. The code in the ncurses library has been + specifically exempted from the package copyright to support + this.

+ +

If your custom type defines order functions, have do something + intuitive with a blank field. A useful convention is to make the + successor of a blank field the types minimum value, and its + predecessor the maximum.

+ +