123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915 |
- <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 3.0//EN">
- <!--
- $Id: hackguide.html,v 1.27 2005/12/24 15:37:13 tom Exp $
- ****************************************************************************
- * Copyright (c) 1998-2003,2005 Free Software Foundation, Inc. *
- * *
- * Permission is hereby granted, free of charge, to any person obtaining a *
- * copy of this software and associated documentation files (the *
- * "Software"), to deal in the Software without restriction, including *
- * without limitation the rights to use, copy, modify, merge, publish, *
- * distribute, distribute with modifications, sublicense, and/or sell *
- * copies of the Software, and to permit persons to whom the Software is *
- * furnished to do so, subject to the following conditions: *
- * *
- * The above copyright notice and this permission notice shall be included *
- * in all copies or substantial portions of the Software. *
- * *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS *
- * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
- * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
- * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
- * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR *
- * THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
- * *
- * Except as contained in this notice, the name(s) of the above copyright *
- * holders shall not be used in advertising or otherwise to promote the *
- * sale, use or other dealings in this Software without prior written *
- * authorization. *
- ****************************************************************************
- -->
- <HTML>
- <HEAD>
- <TITLE>A Hacker's Guide to Ncurses Internals</TITLE>
- <link rev="made" href="mailto:bugs-ncurses@gnu.org">
- <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
- <!--
- This document is self-contained, *except* that there is one relative link to
- the ncurses-intro.html document, expected to be in the same directory with
- this one.
- -->
- </HEAD>
- <BODY>
- <H1>A Hacker's Guide to NCURSES</H1>
- <H1>Contents</H1>
- <UL>
- <LI><A HREF="#abstract">Abstract</A>
- <LI><A HREF="#objective">Objective of the Package</A>
- <UL>
- <LI><A HREF="#whysvr4">Why System V Curses?</A>
- <LI><A HREF="#extensions">How to Design Extensions</A>
- </UL>
- <LI><A HREF="#portability">Portability and Configuration</A>
- <LI><A HREF="#documentation">Documentation Conventions</A>
- <LI><A HREF="#bugtrack">How to Report Bugs</A>
- <LI><A HREF="#ncurslib">A Tour of the Ncurses Library</A>
- <UL>
- <LI><A HREF="#loverview">Library Overview</A>
- <LI><A HREF="#engine">The Engine Room</A>
- <LI><A HREF="#input">Keyboard Input</A>
- <LI><A HREF="#mouse">Mouse Events</A>
- <LI><A HREF="#output">Output and Screen Updating</A>
- </UL>
- <LI><A HREF="#fmnote">The Forms and Menu Libraries</A>
- <LI><A HREF="#tic">A Tour of the Terminfo Compiler</A>
- <UL>
- <LI><A HREF="#nonuse">Translation of Non-<STRONG>use</STRONG> Capabilities</A>
- <LI><A HREF="#uses">Use Capability Resolution</A>
- <LI><A HREF="#translation">Source-Form Translation</A>
- </UL>
- <LI><A HREF="#utils">Other Utilities</A>
- <LI><A HREF="#style">Style Tips for Developers</A>
- <LI><A HREF="#port">Porting Hints</A>
- </UL>
- <H1><A NAME="abstract">Abstract</A></H1>
- This document is a hacker's tour of the <STRONG>ncurses</STRONG> 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.
- <H1><A NAME="objective">Objective of the Package</A></H1>
- The objective of the <STRONG>ncurses</STRONG> package is to provide a free software API for
- character-cell terminals and terminal emulators with the following
- characteristics:
- <UL>
- <LI>Source-compatible with historical curses implementations (including
- the original BSD curses and System V curses.
- <LI>Conformant with the XSI Curses standard issued as part of XPG4 by
- X/Open.
- <LI>High-quality -- stable and reliable code, wide portability, good
- packaging, superior documentation.
- <LI>Featureful -- should eliminate as much of the drudgery of C interface
- programming as possible, freeing programmers to think at a higher
- level of design.
- </UL>
- 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.
- <H2><A NAME="whysvr4">Why System V Curses?</A></H2>
- We used System V curses as a model, reverse-engineering their API, in
- order to fulfill the first two objectives. <P>
- 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. <P>
- 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.
- <H2><A NAME="extensions">How to Design Extensions</A></H2>
- The third objective (standards conformance) requires that it be easy to
- condition source code using <STRONG>ncurses</STRONG> so that the absence of nonstandard
- extensions does not break the code. <P>
- 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 <STRONG>ncurses</STRONG> extension. <P>
- For example, there is a macro <CODE>NCURSES_MOUSE_VERSION</CODE> which XSI Curses
- does not define, but which is defined in the <STRONG>ncurses</STRONG> library header.
- You can use this to condition the calls to the mouse API calls.
- <H1><A NAME="portability">Portability and Configuration</A></H1>
- Code written for <STRONG>ncurses</STRONG> may assume an ANSI-standard C compiler and
- POSIX-compatible OS interface. It may also assume the presence of a
- System-V-compatible <EM>select(2)</EM> call. <P>
- We encourage (but do not require) developers to make the code friendly
- to less-capable UNIX environments wherever possible. <P>
- We encourage developers to support OS-specific optimizations and methods
- not available under POSIX/ANSI, provided only that:
- <UL>
- <LI>All such code is properly conditioned so the build process does not
- attempt to compile it under a plain ANSI/POSIX environment.
- <LI>Adding such implementation methods does not introduce incompatibilities
- in the <STRONG>ncurses</STRONG> API between platforms.
- </UL>
- We use GNU <CODE>autoconf(1)</CODE> 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.
- <H1><A NAME="documentation">Documentation Conventions</A></H1>
- There are three kinds of documentation associated with this package. Each
- has a different preferred format:
- <UL>
- <LI>Package-internal files (README, INSTALL, TO-DO etc.)
- <LI>Manual pages.
- <LI>Everything else (i.e., narrative documentation).
- </UL>
- Our conventions are simple:
- <OL>
- <LI><STRONG>Maintain package-internal files in plain text.</STRONG>
- The expected viewer for them <EM>more(1)</EM> or an editor window; there's
- no point in elaborate mark-up.
- <LI><STRONG>Mark up manual pages in the man macros.</STRONG> These have to be viewable
- through traditional <EM>man(1)</EM> programs.
- <LI><STRONG>Write everything else in HTML.</STRONG>
- </OL>
- When in doubt, HTMLize a master and use <EM>lynx(1)</EM> to generate
- plain ASCII (as we do for the announcement document). <P>
- The reason for choosing HTML is that it's (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 don't 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.
- <H1><A NAME="bugtrack">How to Report Bugs</A></H1>
- The <A NAME="bugreport">reporting address for bugs</A> is
- <A HREF="mailto:bug-ncurses@gnu.org">bug-ncurses@gnu.org</A>.
- This is a majordomo list; to join, write
- to <CODE>bug-ncurses-request@gnu.org</CODE> with a message containing the line:
- <PRE>
- subscribe <name>@<host.domain>
- </PRE>
- The <CODE>ncurses</CODE> code is maintained by a small group of
- volunteers. While we try our best to fix bugs promptly, we simply
- don't 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 <CODE>ncurses</CODE>, there are some steps you can take
- before contacting us that will help get the bug fixed quickly. <P>
- In order to use our bug-fixing time efficiently, we put people who
- show us they've taken these steps at the head of our queue. This
- means that if you don't, you'll probably end up at the tail end and
- have to wait a while.
- <OL>
- <LI>Develop a recipe to reproduce the bug.
- <p>
- 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.)
- <LI>Try to reproduce the bug on a different terminal type. <P>
- 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're using a traditional
- asynchronous terminal or PC-based terminal emulator, rather than xterm
- or a UNIX console entry. <P>
- It's therefore extremely helpful if you can tell us whether or not your
- problem reproduces on other terminal types. Usually you'll have both
- a console type and xterm available; please tell us whether or not your
- bug reproduces on both. <P>
- 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've seen
- are either triggered or masked by these.
- <LI>Generate and examine a trace file for the broken behavior. <P>
- Recompile your program with the debugging versions of the libraries.
- Insert a <CODE>trace()</CODE> call with the argument set to <CODE>TRACE_UPDATE</CODE>.
- (See <A HREF="ncurses-intro.html#debugging">"Writing Programs with
- NCURSES"</A> for details on trace levels.)
- Reproduce your bug, then look at the trace file to see what the library
- was actually doing. <P>
- 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. <P>
- If the virtual-screen dumps look correct but the bug persists, it's
- 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. <P>
- Often you'll find terminfo problems at this stage by noticing that the
- escape sequences put out for various capabilities are wrong. If not,
- you're likely to learn enough to be able to characterize any bug in
- the screen-update logic quite exactly.
- <LI>Report details and symptoms, not just interpretations. <P>
- If you do the preceding two steps, it is very likely that you'll 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 can't characterize and fix yourself. <P>
- If you're still stuck, at least you'll 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. <P>
- If your bug produces a bad update, include a trace file. Try to make
- the trace at the <EM>least</EM> voluminous level that pins down the
- bug. Logs that have been through tracemunch are OK, it doesn't throw
- away any information (actually they're better than un-munched ones because
- they're easier to read). <P>
- If your bug produces a core-dump, please include a symbolic stack trace
- generated by gdb(1) or your local equivalent. <P>
- Tell us about every terminal on which you've reproduced the bug -- and
- every terminal on which you can't. Ideally, sent us terminfo sources
- for all of these (yours might differ from ours). <P>
- Include your ncurses version and your OS/machine type, of course! You can
- find your ncurses version in the <CODE>curses.h</CODE> file.
- </OL>
- 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. <P>
- The most important of these is <CODE>mvcur</CODE>, 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've got
- a bad capability identified, you can disable it and test again. The
- program is command-driven and has on-line help. <P>
- If you think the vertical-scroll optimization is broken, or just want to
- understand how it works better, build <CODE>hashmap</CODE> and read the
- header comments of <CODE>hardscroll.c</CODE> and <CODE>hashmap.c</CODE>; then try
- it out. You can also test the hardware-scrolling optimization separately
- with <CODE>hardscroll</CODE>. <P>
- <H1><A NAME="ncurslib">A Tour of the Ncurses Library</A></H1>
- <H2><A NAME="loverview">Library Overview</A></H2>
- 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
- <blockquote>
- <CODE>
- 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
- </CODE>
- </blockquote>
- are all in this category. They are very
- unlikely to need change, barring bugs or some fundamental
- reorganization in the underlying data structures. <P>
- These files are used only for debugging support:
- <blockquote>
- <code>
- lib_trace.c
- lib_traceatr.c
- lib_tracebits.c
- lib_tracechr.c
- lib_tracedmp.c
- lib_tracemse.c
- trace_buf.c
- </code>
- </blockquote>
- It is rather unlikely you will ever need to change these, unless
- you want to introduce a new debug trace level for some reason.<P>
- There is another group of files that do direct I/O via <EM>tputs()</EM>,
- computations on the terminal capabilities, or queries to the OS
- environment, but nevertheless have only fairly low complexity. These
- include:
- <blockquote>
- <code>
- 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.
- </code>
- </blockquote>
- They are likely to need revision only if
- ncurses is being ported to an environment without an underlying
- terminfo capability representation. <P>
- These files
- have serious hooks into
- the tty driver and signal facilities:
- <blockquote>
- <code>
- lib_kernel.c
- lib_baudrate.c
- lib_raw.c
- lib_tstp.c
- lib_twait.c
- </code>
- </blockquote>
- 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 <CODE>lib_print.c</CODE> uses sleep(2) and also
- falls in this category.<P>
- Almost all of the real work is done in the files
- <blockquote>
- <code>
- 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
- </code>
- </blockquote>
- Most of the algorithmic complexity in the
- library lives in these files.
- If there is a real bug in <STRONG>ncurses</STRONG> itself, it's probably here.
- We'll tour some of these files in detail
- below (see <A HREF="#engine">The Engine Room</A>). <P>
- Finally, there is a group of files that is actually most of the
- terminfo compiler. The reason this code lives in the <STRONG>ncurses</STRONG>
- library is to support fallback to /etc/termcap. These files include
- <blockquote>
- <code>
- 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
- </code>
- </blockquote>
- We'll discuss these in the compiler tour.
- <H2><A NAME="engine">The Engine Room</A></H2>
- <H3><A NAME="input">Keyboard Input</A></H3>
- All <CODE>ncurses</CODE> input funnels through the function
- <CODE>wgetch()</CODE>, defined in <CODE>lib_getch.c</CODE>. 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. <P>
- 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 <CODE>ungetch()</CODE>. <P>
- The <CODE>wgetch()</CODE> 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. <P>
- Hackers bruised by previous encounters with variant <CODE>select(2)</CODE>
- calls may find the code in <CODE>lib_twait.c</CODE> interesting. It deals
- with the problem that some BSD selects don't return a reliable
- time-left value. The function <CODE>timed_wait()</CODE> effectively
- simulates a System V select.
- <H3><A NAME="mouse">Mouse Events</A></H3>
- If the mouse interface is active, <CODE>wgetch()</CODE> polls for mouse
- events each call, before it goes to the keyboard for input. It is
- up to <CODE>lib_mouse.c</CODE> how the polling is accomplished; it may vary
- for different devices. <P>
- Under xterm, however, mouse event notifications come in via the keyboard
- input stream. They are recognized by having the <STRONG>kmous</STRONG> 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. <P>
- This kluge only works because <STRONG>kmous</STRONG> isn't actually used by any
- historic terminal type or curses implementation we know of. Best
- guess is it's a relic of some forgotten experiment in-house at Bell
- Labs that didn't 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. <P>
- Here are some more details about mouse event handling: <P>
- The <CODE>lib_mouse()</CODE>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. <P>
- 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) <CODE>_nc_mouse_event()</CODE> detects a series of incoming
- mouse reports and queues them, or (b) code in <CODE>lib_getch.c</CODE> detects the
- <STRONG>kmous</STRONG> prefix in the keyboard input stream and calls _nc_mouse_inline
- to queue up a series of adjacent mouse reports. <P>
- In either case, <CODE>_nc_mouse_parse()</CODE> 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).
- <H3><A NAME="output">Output and Screen Updating</A></H3>
- With the single exception of character echoes during a <CODE>wgetnstr()</CODE>
- call (which simulates cooked-mode line editing in an ncurses window),
- the library normally does all its output at refresh time. <P>
- The main job is to go from the current state of the screen (as represented
- in the <CODE>curscr</CODE> window structure) to the desired new state (as
- represented in the <CODE>newscr</CODE> window structure), while doing as
- little I/O as possible. <P>
- The brains of this operation are the modules <CODE>hashmap.c</CODE>,
- <CODE>hardscroll.c</CODE> and <CODE>lib_doupdate.c</CODE>; the latter two use
- <CODE>lib_mvcur.c</CODE>. Essentially, what happens looks like this: <P>
- The <CODE>hashmap.c</CODE> 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. <P>
- The <CODE>hardscroll.c</CODE> module computes an optimum set of scroll,
- insertion, and deletion operations to make the indices match. It calls
- <CODE>_nc_mvcur_scrolln()</CODE> in <CODE>lib_mvcur.c</CODE> to do those motions. <P>
- Then <CODE>lib_doupdate.c</CODE> goes to work. Its job is to do line-by-line
- transformations of <CODE>curscr</CODE> lines to <CODE>newscr</CODE> lines. Its main
- tool is the routine <CODE>mvcur()</CODE> in <CODE>lib_mvcur.c</CODE>. This routine
- does cursor-movement optimization, attempting to get from given screen
- location A to given location B in the fewest output characters possible. <P>
- If you want to work on screen optimizations, you should use the fact
- that (in the trace-enabled version of the library) enabling the
- <CODE>TRACE_TIMES</CODE> 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. <P>
- 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
- <CODE>_nc_optimize_enable</CODE>. See the file <CODE>include/curses.h.in</CODE>
- for mask values, near the end.
- <H1><A NAME="fmnote">The Forms and Menu Libraries</A></H1>
- 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. <P>
- The configuration code prefers the POSIX regex facility, modeled on
- System V's, but will settle for BSD regexps if the former isn't available. <P>
- 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 <CODE>ncurses</CODE>.
- <H1><A NAME="tic">A Tour of the Terminfo Compiler</A></H1>
- The <STRONG>ncurses</STRONG> implementation of <STRONG>tic</STRONG> 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. <P>
- The implementation therefore starts with a table-driven, dual-mode
- lexical analyzer (in <CODE>comp_scan.c</CODE>). 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".
- <H2><A NAME="nonuse">Translation of Non-<STRONG>use</STRONG> Capabilities</A></H2>
- Translation of most things besides <STRONG>use</STRONG> 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. <P>
- 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 <CODE>include/Caps</CODE>; 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). <P>
- Thus, adding a new capability is usually pretty trivial, just a matter
- of adding one line to the <CODE>include/Caps</CODE> file. We'll have more
- to say about this in the section on <A HREF="#translation">Source-Form
- Translation</A>.
- <H2><A NAME="uses">Use Capability Resolution</A></H2>
- The background problem that makes <STRONG>tic</STRONG> tricky isn't the capability
- translation itself, it's the resolution of <STRONG>use</STRONG> capabilities. Older
- versions would not handle forward <STRONG>use</STRONG> 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. <P>
- This won't do for <STRONG>ncurses</STRONG>. The problem is that that the whole
- compilation process has to be embeddable in the <STRONG>ncurses</STRONG> library
- so that it can be called by the startup code to translate termcap
- entries on the fly. The embedded version can't go promiscuously writing
- everything it translates out to disk -- for one thing, it will typically
- be running with non-root permissions. <P>
- So our <STRONG>tic</STRONG> is designed to parse an entire terminfo file into a
- doubly-linked circular list of entry structures in-core, and then do
- <STRONG>use</STRONG> 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're written out easy to do. <P>
- And this is exactly how the embedded version works. But the stand-alone
- user-accessible version of <STRONG>tic</STRONG> partly reverts to the historical
- strategy; it writes to disk (not keeping in core) any entry with no
- <STRONG>use</STRONG> references. <P>
- 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 <STRONG>three hours</STRONG>, rather than the twenty seconds
- or less typical on the author's development box. <P>
- So. The executable <STRONG>tic</STRONG> passes the entry-parser a hook that
- <EM>immediately</EM> 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's OK;
- the resolution code will fetch it off disk when it can't find it in
- core. <P>
- Name collisions will still be detected, just not as cleanly. The
- <CODE>write_entry()</CODE> code complains before overwriting an entry that
- postdates the time of <STRONG>tic</STRONG>'s first call to
- <CODE>write_entry()</CODE>, Thus it will complain about overwriting
- entries newly made during the <STRONG>tic</STRONG> run, but not about
- overwriting ones that predate it.
- <H2><A NAME="translation">Source-Form Translation</A></H2>
- Another use of <STRONG>tic</STRONG> 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 <STRONG>captoinfo(1)</STRONG>
- manual page. <P>
- The translation output code (<CODE>dump_entry()</CODE> in
- <CODE>ncurses/dump_entry.c</CODE>) is shared with the <STRONG>infocmp(1)</STRONG>
- utility. It takes the same internal representation used to generate
- the binary form and dumps it to standard output in a specified
- format. <P>
- The <CODE>include/Caps</CODE> file has a header comment describing ways you
- can specify source translations for nonstandard capabilities just by
- altering the master table. It's possible to set up capability aliasing
- or tell the compiler to plain ignore a given capability without writing
- any C code at all. <P>
- For circumstances where you need to do algorithmic translation, there
- are functions in <CODE>parse_entry.c</CODE> called after the parse of each
- entry that are specifically intended to encapsulate such
- translations. This, for example, is where the AIX <STRONG>box1</STRONG> capability
- get translated to an <STRONG>acsc</STRONG> string.
- <H1><A NAME="utils">Other Utilities</A></H1>
- The <STRONG>infocmp</STRONG> utility is just a wrapper around the same
- entry-dumping code used by <STRONG>tic</STRONG> for source translation. Perhaps
- the one interesting aspect of the code is the use of a predicate
- function passed in to <CODE>dump_entry()</CODE> to control which
- capabilities are dumped. This is necessary in order to handle both
- the ordinary De-compilation case and entry difference reporting. <P>
- The <STRONG>tput</STRONG> and <STRONG>clear</STRONG> utilities just do an entry load
- followed by a <CODE>tputs()</CODE> of a selected capability.
- <H1><A NAME="style">Style Tips for Developers</A></H1>
- See the TO-DO file in the top-level directory of the source distribution
- for additions that would be particularly useful. <P>
- The prefix <CODE>_nc_</CODE> 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. <P>
- Look for the string <CODE>FIXME</CODE> in source files to tag minor bugs
- and potential problems that could use fixing. <P>
- Don't try to auto-detect OS features in the main body of the C code.
- That's the job of the configuration system. <P>
- To hold down complexity, do make your code data-driven. Especially,
- if you can drive logic from a table filtered out of
- <CODE>include/Caps</CODE>, do it. If you find you need to augment the
- data in that file in order to generate the proper table, that's still
- preferable to ad-hoc code -- that's why the fifth field (flags) is
- there. <P>
- Have fun!
- <H1><A NAME="port">Porting Hints</A></H1>
- The following notes are intended to be a first step towards DOS and Macintosh
- ports of the ncurses libraries. <P>
- The following library modules are `pure curses'; they operate only on
- the curses internal structures, do all output through other curses
- calls (not including <CODE>tputs()</CODE> and <CODE>putp()</CODE>) 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.
- <blockquote>
- <code>
- 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
- </code>
- </blockquote>
- <P>
- This module is pure curses, but calls outstr():
- <blockquote>
- <code>
- lib_getstr.c
- </code>
- </blockquote>
- <P>
- These modules are pure curses, except that they use <CODE>tputs()</CODE>
- and <CODE>putp()</CODE>:
- <blockquote>
- <code>
- lib_beep.c
- lib_color.c
- lib_endwin.c
- lib_options.c
- lib_slk.c
- lib_vidattr.c
- </code>
- </blockquote>
- <P>
- This modules assist in POSIX emulation on non-POSIX systems:
- <DL>
- <DT> sigaction.c
- <DD> signal calls
- </DL>
- The following source files will not be needed for a
- single-terminal-type port.
- <blockquote>
- <code>
- 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
- </code>
- </blockquote>
- <P>
- The following modules will use open()/read()/write()/close()/lseek() on files,
- but no other OS calls.
- <DL>
- <DT>lib_screen.c
- <DD>used to read/write screen dumps
- <DT>lib_trace.c
- <DD>used to write trace data to the logfile
- </DL>
- Modules that would have to be modified for a port start here: <P>
- The following modules are `pure curses' but contain assumptions inappropriate
- for a memory-mapped port.
- <dl>
- <dt>lib_longname.c<dd>assumes there may be multiple terminals
- <dt>lib_acs.c<dd>assumes acs_map as a double indirection
- <dt>lib_mvcur.c<dd>assumes cursor moves have variable cost
- <dt>lib_termcap.c<dd>assumes there may be multiple terminals
- <dt>lib_ti.c<dd>assumes there may be multiple terminals
- </dl>
- The following modules use UNIX-specific calls:
- <dl>
- <dt>lib_doupdate.c<dd>input checking
- <dt>lib_getch.c<dd>read()
- <dt>lib_initscr.c<dd>getenv()
- <dt>lib_newterm.c
- <dt>lib_baudrate.c
- <dt>lib_kernel.c<dd>various tty-manipulation and system calls
- <dt>lib_raw.c<dd>various tty-manipulation calls
- <dt>lib_setup.c<dd>various tty-manipulation calls
- <dt>lib_restart.c<dd>various tty-manipulation calls
- <dt>lib_tstp.c<dd>signal-manipulation calls
- <dt>lib_twait.c<dd>gettimeofday(), select().
- </dl>
- <HR>
- <ADDRESS>Eric S. Raymond <esr@snark.thyrsus.com></ADDRESS>
- (Note: This is <EM>not</EM> the <A HREF="#bugtrack">bug address</A>!)
- </BODY>
- </HTML>
|