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 @@ + + + +
+ +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.
+ +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.
+ +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.
+ +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.
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.
There are three kinds of documentation associated with this + package. Each has a different preferred format:
+ +Our conventions are simple:
+ +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.
+ +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.
+ +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.)
+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.
+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.
+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.
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.
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.
+ +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.
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).
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:
The hashmap.c module tries to detect vertical
+ motion changes between the real and virtual screens. This
+ information is represented by the oldindex members in the
+ newscr structure. These are modified by vertical-motion and
+ clear operations, and both are re-initialized after each
+ update. To this change-journalling information, the hashmap
+ code adds deductions made using a modified Heckel algorithm
+ on hash values generated from the line contents.
The hardscroll.c module computes an optimum
+ set of scroll, insertion, and deletion operations to make the
+ indices match. It calls _nc_mvcur_scrolln() in
+ lib_mvcur.c to do those motions.
Then lib_doupdate.c goes to work. Its job is
+ to do line-by-line transformations of curscr
+ lines to newscr lines. Its main tool is the
+ routine mvcur() in lib_mvcur.c.
+ This routine does cursor-movement optimization, attempting to
+ get from given screen location A to given location B in the
+ fewest output characters possible.
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 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.
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 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.
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.
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.
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.
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!
+ +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:
+ +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.
+ +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.
+ +The following modules use UNIX-specific calls:
+ ++ by Eric S. Raymond and Zeyd M. Ben-Halim+ + + +
+ updates since release 1.9.9e by Thomas Dickey +
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.
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.
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.
+ +In this document, the following terminology is used with + reasonable consistency:
+ +stdscr, is automatically provided for the
+ programmer.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.
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.
+ +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.
+ +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
+ bool doneit;)TRUE
+ FALSE
+ ERR
+ OK
+ 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);
+}
+
+ 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.
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.
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.
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.
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.
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:
+ +gpm(1),
+ Alessandro Rubini's mouse server.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.
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.
We describe the detailed behavior of some important curses + functions here, as a supplement to the manual page + descriptions.
+ +initscr()
+ 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()
+ 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)
+ 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)
+ 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)
+ newterm(); deallocates the data
+ structures associated with a given SCREEN
+ reference.refresh() and wrefresh(win)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)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).setupterm(term, filenum, errret)
+ 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.
+ NOTE: These functions are not part of the + standard curses API! ++ +
trace()
+ 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()
+ 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.
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.
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.
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 */
+
+ 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.
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.
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.
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!
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.
+ +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.
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.
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.
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 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.
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.
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.
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.
+ +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.
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.
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.
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.
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:
+ +curses.new_item().new_menu().post_menu().unpost_menu().free_menu().free_item().curses.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.
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.
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.
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.
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 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.
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.
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:
+ +curses.new_field().new_form().post_form().unpost_form().free_form().free_field().curses.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.
+ +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.
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.
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.
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().
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.
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.
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:
+ +REQ_PREV_CHOICE and REQ_NEXT_CHOICE
+ will fail. Such read-only fields may be useful for help
+ messages.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.
+ +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.
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.
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:
+ +O_AUTOSKIP and
+ O_NL_OVERLOAD are ignored.dup_field() and link_field()
+ calls copy dynamic-buffer sizes. If the O_STATIC
+ option is set on one of a collection of links, buffer resizing
+ will occur only when the field is edited through that
+ link.field_info() will retrieve the
+ original static size of the field; use
+ dynamic_field_info() to get the actual dynamic
+ size.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:
+ +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.
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.
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.
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).
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).
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.
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.
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.
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.
+ +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.
+ +These requests cause page-level moves through the form, + triggering display of a new form screen.
+ +REQ_NEXT_PAGE
+ REQ_PREV_PAGE
+ REQ_FIRST_PAGE
+ REQ_LAST_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.
These requests handle navigation between fields on the same + page.
+ +REQ_NEXT_FIELD
+ REQ_PREV_FIELD
+ REQ_FIRST_FIELD
+ REQ_LAST_FIELD
+ REQ_SNEXT_FIELD
+ REQ_SPREV_FIELD
+ REQ_SFIRST_FIELD
+ REQ_SLAST_FIELD
+ REQ_LEFT_FIELD
+ REQ_RIGHT_FIELD
+ REQ_UP_FIELD
+ REQ_DOWN_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.
These requests drive movement of the edit cursor within the + currently selected field.
+ +REQ_NEXT_CHAR
+ REQ_PREV_CHAR
+ REQ_NEXT_LINE
+ REQ_PREV_LINE
+ REQ_NEXT_WORD
+ REQ_PREV_WORD
+ REQ_BEG_FIELD
+ REQ_END_FIELD
+ REQ_BEG_LINE
+ REQ_END_LINE
+ REQ_LEFT_CHAR
+ REQ_RIGHT_CHAR
+ REQ_UP_CHAR
+ REQ_DOWN_CHAR
+ 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.
+ +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
+ REQ_SCR_BLINE
+ REQ_SCR_FPAGE
+ REQ_SCR_BPAGE
+ REQ_SCR_FHPAGE
+ REQ_SCR_BHPAGE
+ REQ_SCR_FCHAR
+ REQ_SCR_BCHAR
+ REQ_SCR_HFLINE
+ REQ_SCR_HBLINE
+ REQ_SCR_HFHALF
+ REQ_SCR_HBHALF
+ For scrolling purposes, a page of a field is the + height of its visible part.
+ +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
+ REQ_OVL_MODE
+ REQ_NEW_LINE
+ REQ_INS_CHAR
+ REQ_INS_LINE
+ REQ_DEL_CHAR
+ REQ_DEL_PREV
+ REQ_DEL_LINE
+ REQ_DEL_WORD
+ REQ_CLR_EOL
+ REQ_CLR_EOF
+ REQ_CLEAR_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.
+ +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
+ REQ_PREV_CHOICE
+ 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.
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.
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.
+ +Calls to these hooks may be triggered
+ +set_current_field() callset_form_page() callSee 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.
+ +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.
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:
+ +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.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.
+ +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.
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.
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.
+ +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
+ 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
+ free_str
+ 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.
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.
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.
+ +