Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 1 | A Hacker's Guide to NCURSES |
| 2 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 3 | A Hacker's Guide to NCURSES |
| 4 | |
| 5 | Contents |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 6 | |
| 7 | * Abstract |
| 8 | * Objective of the Package |
| 9 | + Why System V Curses? |
| 10 | + How to Design Extensions |
| 11 | * Portability and Configuration |
| 12 | * Documentation Conventions |
| 13 | * How to Report Bugs |
| 14 | * A Tour of the Ncurses Library |
| 15 | + Library Overview |
| 16 | + The Engine Room |
| 17 | + Keyboard Input |
| 18 | + Mouse Events |
| 19 | + Output and Screen Updating |
| 20 | * The Forms and Menu Libraries |
| 21 | * A Tour of the Terminfo Compiler |
| 22 | + Translation of Non-use Capabilities |
| 23 | + Use Capability Resolution |
| 24 | + Source-Form Translation |
| 25 | * Other Utilities |
| 26 | * Style Tips for Developers |
| 27 | * Porting Hints |
| 28 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 29 | Abstract |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 30 | |
| 31 | This document is a hacker's tour of the ncurses library and utilities. |
| 32 | It discusses design philosophy, implementation methods, and the |
| 33 | conventions used for coding and documentation. It is recommended |
| 34 | reading for anyone who is interested in porting, extending or |
| 35 | improving the package. |
| 36 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 37 | Objective of the Package |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 38 | |
| 39 | The objective of the ncurses package is to provide a free software API |
| 40 | for character-cell terminals and terminal emulators with the following |
| 41 | characteristics: |
| 42 | * Source-compatible with historical curses implementations |
| 43 | (including the original BSD curses and System V curses. |
| 44 | * Conformant with the XSI Curses standard issued as part of XPG4 by |
| 45 | X/Open. |
| 46 | * High-quality -- stable and reliable code, wide portability, good |
| 47 | packaging, superior documentation. |
| 48 | * Featureful -- should eliminate as much of the drudgery of C |
| 49 | interface programming as possible, freeing programmers to think at |
| 50 | a higher level of design. |
| 51 | |
| 52 | These objectives are in priority order. So, for example, source |
| 53 | compatibility with older version must trump featurefulness -- we |
| 54 | cannot add features if it means breaking the portion of the API |
| 55 | corresponding to historical curses versions. |
| 56 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 57 | Why System V Curses? |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 58 | |
| 59 | We used System V curses as a model, reverse-engineering their API, in |
| 60 | order to fulfill the first two objectives. |
| 61 | |
| 62 | System V curses implementations can support BSD curses programs with |
| 63 | just a recompilation, so by capturing the System V API we also capture |
| 64 | BSD's. |
| 65 | |
| 66 | More importantly for the future, the XSI Curses standard issued by |
| 67 | X/Open is explicitly and closely modeled on System V. So conformance |
| 68 | with System V took us most of the way to base-level XSI conformance. |
| 69 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 70 | How to Design Extensions |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 71 | |
| 72 | The third objective (standards conformance) requires that it be easy |
| 73 | to condition source code using ncurses so that the absence of |
| 74 | nonstandard extensions does not break the code. |
| 75 | |
| 76 | Accordingly, we have a policy of associating with each nonstandard |
| 77 | extension a feature macro, so that ncurses client code can use this |
| 78 | macro to condition in or out the code that requires the ncurses |
| 79 | extension. |
| 80 | |
| 81 | For example, there is a macro NCURSES_MOUSE_VERSION which XSI Curses |
| 82 | does not define, but which is defined in the ncurses library header. |
| 83 | You can use this to condition the calls to the mouse API calls. |
| 84 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 85 | Portability and Configuration |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 86 | |
| 87 | Code written for ncurses may assume an ANSI-standard C compiler and |
| 88 | POSIX-compatible OS interface. It may also assume the presence of a |
| 89 | System-V-compatible select(2) call. |
| 90 | |
| 91 | We encourage (but do not require) developers to make the code friendly |
| 92 | to less-capable UNIX environments wherever possible. |
| 93 | |
| 94 | We encourage developers to support OS-specific optimizations and |
| 95 | methods not available under POSIX/ANSI, provided only that: |
| 96 | * All such code is properly conditioned so the build process does |
| 97 | not attempt to compile it under a plain ANSI/POSIX environment. |
| 98 | * Adding such implementation methods does not introduce |
| 99 | incompatibilities in the ncurses API between platforms. |
| 100 | |
| 101 | We use GNU autoconf(1) as a tool to deal with portability issues. The |
| 102 | right way to leverage an OS-specific feature is to modify the autoconf |
| 103 | specification files (configure.in and aclocal.m4) to set up a new |
| 104 | feature macro, which you then use to condition your code. |
| 105 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 106 | Documentation Conventions |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 107 | |
| 108 | There are three kinds of documentation associated with this package. |
| 109 | Each has a different preferred format: |
| 110 | * Package-internal files (README, INSTALL, TO-DO etc.) |
| 111 | * Manual pages. |
| 112 | * Everything else (i.e., narrative documentation). |
| 113 | |
| 114 | Our conventions are simple: |
| 115 | 1. Maintain package-internal files in plain text. The expected viewer |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 116 | for them is more(1) or an editor window; there is no point in |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 117 | elaborate mark-up. |
| 118 | 2. Mark up manual pages in the man macros. These have to be viewable |
| 119 | through traditional man(1) programs. |
| 120 | 3. Write everything else in HTML. |
| 121 | |
| 122 | When in doubt, HTMLize a master and use lynx(1) to generate plain |
| 123 | ASCII (as we do for the announcement document). |
| 124 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 125 | The reason for choosing HTML is that it is (a) well-adapted for |
| 126 | on-line browsing through viewers that are everywhere; (b) more easily |
| 127 | readable as plain text than most other mark-ups, if you do not have a |
| 128 | viewer; and (c) carries enough information that you can generate a |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 129 | nice-looking printed version from it. Also, of course, it make |
| 130 | exporting things like the announcement document to WWW pretty trivial. |
| 131 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 132 | How to Report Bugs |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 133 | |
| 134 | The reporting address for bugs is bug-ncurses@gnu.org. This is a |
| 135 | majordomo list; to join, write to bug-ncurses-request@gnu.org with a |
| 136 | message containing the line: |
| 137 | subscribe <name>@<host.domain> |
| 138 | |
| 139 | The ncurses code is maintained by a small group of volunteers. While |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 140 | we try our best to fix bugs promptly, we simply do not have a lot of |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 141 | hours to spend on elementary hand-holding. We rely on intelligent |
| 142 | cooperation from our users. If you think you have found a bug in |
| 143 | ncurses, there are some steps you can take before contacting us that |
| 144 | will help get the bug fixed quickly. |
| 145 | |
| 146 | In order to use our bug-fixing time efficiently, we put people who |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 147 | show us they have taken these steps at the head of our queue. This |
| 148 | means that if you do not, you will probably end up at the tail end and |
| 149 | have to wait a while. |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 150 | 1. Develop a recipe to reproduce the bug. |
| 151 | Bugs we can reproduce are likely to be fixed very quickly, often |
| 152 | within days. The most effective single thing you can do to get a |
| 153 | quick fix is develop a way we can duplicate the bad behavior -- |
| 154 | ideally, by giving us source for a small, portable test program |
| 155 | that breaks the library. (Even better is a keystroke recipe using |
| 156 | one of the test programs provided with the distribution.) |
| 157 | 2. Try to reproduce the bug on a different terminal type. |
| 158 | In our experience, most of the behaviors people report as library |
| 159 | bugs are actually due to subtle problems in terminal descriptions. |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 160 | This is especially likely to be true if you are using a |
| 161 | traditional asynchronous terminal or PC-based terminal emulator, |
| 162 | rather than xterm or a UNIX console entry. |
| 163 | It is therefore extremely helpful if you can tell us whether or |
| 164 | not your problem reproduces on other terminal types. Usually you |
| 165 | will have both a console type and xterm available; please tell us |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 166 | whether or not your bug reproduces on both. |
| 167 | If you have xterm available, it is also good to collect xterm |
| 168 | reports for different window sizes. This is especially true if you |
| 169 | normally use an unusual xterm window size -- a surprising number |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 170 | of the bugs we have seen are either triggered or masked by these. |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 171 | 3. Generate and examine a trace file for the broken behavior. |
| 172 | Recompile your program with the debugging versions of the |
| 173 | libraries. Insert a trace() call with the argument set to |
| 174 | TRACE_UPDATE. (See "Writing Programs with NCURSES" for details on |
| 175 | trace levels.) Reproduce your bug, then look at the trace file to |
| 176 | see what the library was actually doing. |
| 177 | Another frequent cause of apparent bugs is application coding |
| 178 | errors that cause the wrong things to be put on the virtual |
| 179 | screen. Looking at the virtual-screen dumps in the trace file will |
| 180 | tell you immediately if this is happening, and save you from the |
| 181 | possible embarrassment of being told that the bug is in your code |
| 182 | and is your problem rather than ours. |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 183 | If the virtual-screen dumps look correct but the bug persists, it |
| 184 | is possible to crank up the trace level to give more and more |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 185 | information about the library's update actions and the control |
| 186 | sequences it issues to perform them. The test directory of the |
| 187 | distribution contains a tool for digesting these logs to make them |
| 188 | less tedious to wade through. |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 189 | Often you will find terminfo problems at this stage by noticing |
| 190 | that the escape sequences put out for various capabilities are |
| 191 | wrong. If not, you are likely to learn enough to be able to |
| 192 | characterize any bug in the screen-update logic quite exactly. |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 193 | 4. Report details and symptoms, not just interpretations. |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 194 | If you do the preceding two steps, it is very likely that you will |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 195 | discover the nature of the problem yourself and be able to send us |
| 196 | a fix. This will create happy feelings all around and earn you |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 197 | good karma for the first time you run into a bug you really cannot |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 198 | characterize and fix yourself. |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 199 | If you are still stuck, at least you will know what to tell us. |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 200 | Remember, we need details. If you guess about what is safe to |
| 201 | leave out, you are too likely to be wrong. |
| 202 | If your bug produces a bad update, include a trace file. Try to |
| 203 | make the trace at the least voluminous level that pins down the |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 204 | bug. Logs that have been through tracemunch are OK, it does not |
| 205 | throw away any information (actually they are better than |
| 206 | un-munched ones because they are easier to read). |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 207 | If your bug produces a core-dump, please include a symbolic stack |
| 208 | trace generated by gdb(1) or your local equivalent. |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 209 | Tell us about every terminal on which you have reproduced the bug |
| 210 | -- and every terminal on which you cannot. Ideally, send us |
| 211 | terminfo sources for all of these (yours might differ from ours). |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 212 | Include your ncurses version and your OS/machine type, of course! |
| 213 | You can find your ncurses version in the curses.h file. |
| 214 | |
| 215 | If your problem smells like a logic error or in cursor movement or |
| 216 | scrolling or a bad capability, there are a couple of tiny test frames |
| 217 | for the library algorithms in the progs directory that may help you |
| 218 | isolate it. These are not part of the normal build, but do have their |
| 219 | own make productions. |
| 220 | |
| 221 | The most important of these is mvcur, a test frame for the |
| 222 | cursor-movement optimization code. With this program, you can see |
| 223 | directly what control sequences will be emitted for any given cursor |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 224 | movement or scroll/insert/delete operations. If you think you have got |
| 225 | a bad capability identified, you can disable it and test again. The |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 226 | program is command-driven and has on-line help. |
| 227 | |
| 228 | If you think the vertical-scroll optimization is broken, or just want |
| 229 | to understand how it works better, build hashmap and read the header |
| 230 | comments of hardscroll.c and hashmap.c; then try it out. You can also |
| 231 | test the hardware-scrolling optimization separately with hardscroll. |
| 232 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 233 | A Tour of the Ncurses Library |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 234 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 235 | Library Overview |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 236 | |
| 237 | Most of the library is superstructure -- fairly trivial convenience |
| 238 | interfaces to a small set of basic functions and data structures used |
| 239 | to manipulate the virtual screen (in particular, none of this code |
| 240 | does any I/O except through calls to more fundamental modules |
| 241 | described below). The files |
| 242 | |
| 243 | lib_addch.c lib_bkgd.c lib_box.c lib_chgat.c lib_clear.c |
| 244 | lib_clearok.c lib_clrbot.c lib_clreol.c lib_colorset.c lib_data.c |
| 245 | lib_delch.c lib_delwin.c lib_echo.c lib_erase.c lib_gen.c |
| 246 | lib_getstr.c lib_hline.c lib_immedok.c lib_inchstr.c lib_insch.c |
| 247 | lib_insdel.c lib_insstr.c lib_instr.c lib_isendwin.c lib_keyname.c |
| 248 | lib_leaveok.c lib_move.c lib_mvwin.c lib_overlay.c lib_pad.c |
| 249 | lib_printw.c lib_redrawln.c lib_scanw.c lib_screen.c lib_scroll.c |
| 250 | lib_scrollok.c lib_scrreg.c lib_set_term.c lib_slk.c |
| 251 | lib_slkatr_set.c lib_slkatrof.c lib_slkatron.c lib_slkatrset.c |
| 252 | lib_slkattr.c lib_slkclear.c lib_slkcolor.c lib_slkinit.c |
| 253 | lib_slklab.c lib_slkrefr.c lib_slkset.c lib_slktouch.c lib_touch.c |
| 254 | lib_unctrl.c lib_vline.c lib_wattroff.c lib_wattron.c lib_window.c |
| 255 | |
| 256 | are all in this category. They are very unlikely to need change, |
| 257 | barring bugs or some fundamental reorganization in the underlying data |
| 258 | structures. |
| 259 | |
| 260 | These files are used only for debugging support: |
| 261 | |
| 262 | lib_trace.c lib_traceatr.c lib_tracebits.c lib_tracechr.c |
| 263 | lib_tracedmp.c lib_tracemse.c trace_buf.c |
| 264 | |
| 265 | It is rather unlikely you will ever need to change these, unless you |
| 266 | want to introduce a new debug trace level for some reason. |
| 267 | |
| 268 | There is another group of files that do direct I/O via tputs(), |
| 269 | computations on the terminal capabilities, or queries to the OS |
| 270 | environment, but nevertheless have only fairly low complexity. These |
| 271 | include: |
| 272 | |
| 273 | lib_acs.c lib_beep.c lib_color.c lib_endwin.c lib_initscr.c |
| 274 | lib_longname.c lib_newterm.c lib_options.c lib_termcap.c lib_ti.c |
| 275 | lib_tparm.c lib_tputs.c lib_vidattr.c read_entry.c. |
| 276 | |
| 277 | They are likely to need revision only if ncurses is being ported to an |
| 278 | environment without an underlying terminfo capability representation. |
| 279 | |
| 280 | These files have serious hooks into the tty driver and signal |
| 281 | facilities: |
| 282 | |
| 283 | lib_kernel.c lib_baudrate.c lib_raw.c lib_tstp.c lib_twait.c |
| 284 | |
| 285 | If you run into porting snafus moving the package to another UNIX, the |
| 286 | problem is likely to be in one of these files. The file lib_print.c |
| 287 | uses sleep(2) and also falls in this category. |
| 288 | |
| 289 | Almost all of the real work is done in the files |
| 290 | |
| 291 | hardscroll.c hashmap.c lib_addch.c lib_doupdate.c lib_getch.c |
| 292 | lib_mouse.c lib_mvcur.c lib_refresh.c lib_setup.c lib_vidattr.c |
| 293 | |
| 294 | Most of the algorithmic complexity in the library lives in these |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 295 | files. If there is a real bug in ncurses itself, it is probably here. |
| 296 | We will tour some of these files in detail below (see The Engine |
| 297 | Room). |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 298 | |
| 299 | Finally, there is a group of files that is actually most of the |
| 300 | terminfo compiler. The reason this code lives in the ncurses library |
| 301 | is to support fallback to /etc/termcap. These files include |
| 302 | |
| 303 | alloc_entry.c captoinfo.c comp_captab.c comp_error.c comp_hash.c |
| 304 | comp_parse.c comp_scan.c parse_entry.c read_termcap.c write_entry.c |
| 305 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 306 | We will discuss these in the compiler tour. |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 307 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 308 | The Engine Room |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 309 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 310 | Keyboard Input |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 311 | |
| 312 | All ncurses input funnels through the function wgetch(), defined in |
| 313 | lib_getch.c. This function is tricky; it has to poll for keyboard and |
| 314 | mouse events and do a running match of incoming input against the set |
| 315 | of defined special keys. |
| 316 | |
| 317 | The central data structure in this module is a FIFO queue, used to |
| 318 | match multiple-character input sequences against special-key |
| 319 | capabilities; also to implement pushback via ungetch(). |
| 320 | |
| 321 | The wgetch() code distinguishes between function key sequences and the |
| 322 | same sequences typed manually by doing a timed wait after each input |
| 323 | character that could lead a function key sequence. If the entire |
| 324 | sequence takes less than 1 second, it is assumed to have been |
| 325 | generated by a function key press. |
| 326 | |
| 327 | Hackers bruised by previous encounters with variant select(2) calls |
| 328 | may find the code in lib_twait.c interesting. It deals with the |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 329 | problem that some BSD selects do not return a reliable time-left |
| 330 | value. The function timed_wait() effectively simulates a System V |
| 331 | select. |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 332 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 333 | Mouse Events |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 334 | |
| 335 | If the mouse interface is active, wgetch() polls for mouse events each |
| 336 | call, before it goes to the keyboard for input. It is up to |
| 337 | lib_mouse.c how the polling is accomplished; it may vary for different |
| 338 | devices. |
| 339 | |
| 340 | Under xterm, however, mouse event notifications come in via the |
| 341 | keyboard input stream. They are recognized by having the kmous |
| 342 | capability as a prefix. This is kind of klugey, but trying to wire in |
| 343 | recognition of a mouse key prefix without going through the |
| 344 | function-key machinery would be just too painful, and this turns out |
| 345 | to imply having the prefix somewhere in the function-key capabilities |
| 346 | at terminal-type initialization. |
| 347 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 348 | This kluge only works because kmous is not actually used by any |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 349 | historic terminal type or curses implementation we know of. Best guess |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 350 | is it is a relic of some forgotten experiment in-house at Bell Labs |
| 351 | that did not leave any traces in the publicly-distributed System V |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 352 | terminfo files. If System V or XPG4 ever gets serious about using it |
| 353 | again, this kluge may have to change. |
| 354 | |
| 355 | Here are some more details about mouse event handling: |
| 356 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 357 | The lib_mouse() code is logically split into a lower level that |
| 358 | accepts event reports in a device-dependent format and an upper level |
| 359 | that parses mouse gestures and filters events. The mediating data |
| 360 | structure is a circular queue of event structures. |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 361 | |
| 362 | Functionally, the lower level's job is to pick up primitive events and |
| 363 | put them on the circular queue. This can happen in one of two ways: |
| 364 | either (a) _nc_mouse_event() detects a series of incoming mouse |
| 365 | reports and queues them, or (b) code in lib_getch.c detects the kmous |
| 366 | prefix in the keyboard input stream and calls _nc_mouse_inline to |
| 367 | queue up a series of adjacent mouse reports. |
| 368 | |
| 369 | In either case, _nc_mouse_parse() should be called after the series is |
| 370 | accepted to parse the digested mouse reports (low-level events) into a |
| 371 | gesture (a high-level or composite event). |
| 372 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 373 | Output and Screen Updating |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 374 | |
| 375 | With the single exception of character echoes during a wgetnstr() call |
| 376 | (which simulates cooked-mode line editing in an ncurses window), the |
| 377 | library normally does all its output at refresh time. |
| 378 | |
| 379 | The main job is to go from the current state of the screen (as |
| 380 | represented in the curscr window structure) to the desired new state |
| 381 | (as represented in the newscr window structure), while doing as little |
| 382 | I/O as possible. |
| 383 | |
| 384 | The brains of this operation are the modules hashmap.c, hardscroll.c |
| 385 | and lib_doupdate.c; the latter two use lib_mvcur.c. Essentially, what |
| 386 | happens looks like this: |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 387 | * The hashmap.c module tries to detect vertical motion changes |
| 388 | between the real and virtual screens. This information is |
| 389 | represented by the oldindex members in the newscr structure. These |
| 390 | are modified by vertical-motion and clear operations, and both are |
| 391 | re-initialized after each update. To this change-journalling |
| 392 | information, the hashmap code adds deductions made using a |
| 393 | modified Heckel algorithm on hash values generated from the line |
| 394 | contents. |
| 395 | * The hardscroll.c module computes an optimum set of scroll, |
| 396 | insertion, and deletion operations to make the indices match. It |
| 397 | calls _nc_mvcur_scrolln() in lib_mvcur.c to do those motions. |
| 398 | * Then lib_doupdate.c goes to work. Its job is to do line-by-line |
| 399 | transformations of curscr lines to newscr lines. Its main tool is |
| 400 | the routine mvcur() in lib_mvcur.c. This routine does |
| 401 | cursor-movement optimization, attempting to get from given screen |
| 402 | location A to given location B in the fewest output characters |
| 403 | possible. |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 404 | |
| 405 | If you want to work on screen optimizations, you should use the fact |
| 406 | that (in the trace-enabled version of the library) enabling the |
| 407 | TRACE_TIMES trace level causes a report to be emitted after each |
| 408 | screen update giving the elapsed time and a count of characters |
| 409 | emitted during the update. You can use this to tell when an update |
| 410 | optimization improves efficiency. |
| 411 | |
| 412 | In the trace-enabled version of the library, it is also possible to |
| 413 | disable and re-enable various optimizations at runtime by tweaking the |
| 414 | variable _nc_optimize_enable. See the file include/curses.h.in for |
| 415 | mask values, near the end. |
| 416 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 417 | The Forms and Menu Libraries |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 418 | |
| 419 | The forms and menu libraries should work reliably in any environment |
| 420 | you can port ncurses to. The only portability issue anywhere in them |
| 421 | is what flavor of regular expressions the built-in form field type |
| 422 | TYPE_REGEXP will recognize. |
| 423 | |
| 424 | The configuration code prefers the POSIX regex facility, modeled on |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 425 | System V's, but will settle for BSD regexps if the former is not |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 426 | available. |
| 427 | |
| 428 | Historical note: the panels code was written primarily to assist in |
| 429 | porting u386mon 2.0 (comp.sources.misc v14i001-4) to systems lacking |
| 430 | panels support; u386mon 2.10 and beyond use it. This version has been |
| 431 | slightly cleaned up for ncurses. |
| 432 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 433 | A Tour of the Terminfo Compiler |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 434 | |
| 435 | The ncurses implementation of tic is rather complex internally; it has |
| 436 | to do a trying combination of missions. This starts with the fact |
| 437 | that, in addition to its normal duty of compiling terminfo sources |
| 438 | into loadable terminfo binaries, it has to be able to handle termcap |
| 439 | syntax and compile that too into terminfo entries. |
| 440 | |
| 441 | The implementation therefore starts with a table-driven, dual-mode |
| 442 | lexical analyzer (in comp_scan.c). The lexer chooses its mode (termcap |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 443 | or terminfo) based on the first "," or ":" it finds in each entry. The |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 444 | lexer does all the work of recognizing capability names and values; |
| 445 | the grammar above it is trivial, just "parse entries till you run out |
| 446 | of file". |
| 447 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 448 | Translation of Non-use Capabilities |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 449 | |
| 450 | Translation of most things besides use capabilities is pretty |
| 451 | straightforward. The lexical analyzer's tokenizer hands each |
| 452 | capability name to a hash function, which drives a table lookup. The |
| 453 | table entry yields an index which is used to look up the token type in |
| 454 | another table, and controls interpretation of the value. |
| 455 | |
| 456 | One possibly interesting aspect of the implementation is the way the |
| 457 | compiler tables are initialized. All the tables are generated by |
| 458 | various awk/sed/sh scripts from a master table include/Caps; these |
| 459 | scripts actually write C initializers which are linked to the |
| 460 | compiler. Furthermore, the hash table is generated in the same way, so |
| 461 | it doesn't have to be generated at compiler startup time (another |
| 462 | benefit of this organization is that the hash table can be in |
| 463 | shareable text space). |
| 464 | |
| 465 | Thus, adding a new capability is usually pretty trivial, just a matter |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 466 | of adding one line to the include/Caps file. We will have more to say |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 467 | about this in the section on Source-Form Translation. |
| 468 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 469 | Use Capability Resolution |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 470 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 471 | The background problem that makes tic tricky is not the capability |
| 472 | translation itself, it is the resolution of use capabilities. Older |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 473 | versions would not handle forward use references for this reason (that |
| 474 | is, a using terminal always had to follow its use target in the source |
| 475 | file). By doing this, they got away with a simple implementation |
| 476 | tactic; compile everything as it blows by, then resolve uses from |
| 477 | compiled entries. |
| 478 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 479 | This will not do for ncurses. The problem is that that the whole |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 480 | compilation process has to be embeddable in the ncurses library so |
| 481 | that it can be called by the startup code to translate termcap entries |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 482 | on the fly. The embedded version cannot go promiscuously writing |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 483 | everything it translates out to disk -- for one thing, it will |
| 484 | typically be running with non-root permissions. |
| 485 | |
| 486 | So our tic is designed to parse an entire terminfo file into a |
| 487 | doubly-linked circular list of entry structures in-core, and then do |
| 488 | use resolution in-memory before writing everything out. This design |
| 489 | has other advantages: it makes forward and back use-references equally |
| 490 | easy (so we get the latter for free), and it makes checking for name |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 491 | collisions before they are written out easy to do. |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 492 | |
| 493 | And this is exactly how the embedded version works. But the |
| 494 | stand-alone user-accessible version of tic partly reverts to the |
| 495 | historical strategy; it writes to disk (not keeping in core) any entry |
| 496 | with no use references. |
| 497 | |
| 498 | This is strictly a core-economy kluge, implemented because the |
| 499 | terminfo master file is large enough that some core-poor systems swap |
| 500 | like crazy when you compile it all in memory...there have been reports |
| 501 | of this process taking three hours, rather than the twenty seconds or |
| 502 | less typical on the author's development box. |
| 503 | |
| 504 | So. The executable tic passes the entry-parser a hook that immediately |
| 505 | writes out the referenced entry if it has no use capabilities. The |
| 506 | compiler main loop refrains from adding the entry to the in-core list |
| 507 | when this hook fires. If some other entry later needs to reference an |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 508 | entry that got written immediately, that is OK; the resolution code |
| 509 | will fetch it off disk when it cannot find it in core. |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 510 | |
| 511 | Name collisions will still be detected, just not as cleanly. The |
| 512 | write_entry() code complains before overwriting an entry that |
| 513 | postdates the time of tic's first call to write_entry(), Thus it will |
| 514 | complain about overwriting entries newly made during the tic run, but |
| 515 | not about overwriting ones that predate it. |
| 516 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 517 | Source-Form Translation |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 518 | |
| 519 | Another use of tic is to do source translation between various termcap |
| 520 | and terminfo formats. There are more variants out there than you might |
| 521 | think; the ones we know about are described in the captoinfo(1) manual |
| 522 | page. |
| 523 | |
| 524 | The translation output code (dump_entry() in ncurses/dump_entry.c) is |
| 525 | shared with the infocmp(1) utility. It takes the same internal |
| 526 | representation used to generate the binary form and dumps it to |
| 527 | standard output in a specified format. |
| 528 | |
| 529 | The include/Caps file has a header comment describing ways you can |
| 530 | specify source translations for nonstandard capabilities just by |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 531 | altering the master table. It is possible to set up capability |
| 532 | aliasing or tell the compiler to plain ignore a given capability |
| 533 | without writing any C code at all. |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 534 | |
| 535 | For circumstances where you need to do algorithmic translation, there |
| 536 | are functions in parse_entry.c called after the parse of each entry |
| 537 | that are specifically intended to encapsulate such translations. This, |
| 538 | for example, is where the AIX box1 capability get translated to an |
| 539 | acsc string. |
| 540 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 541 | Other Utilities |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 542 | |
| 543 | The infocmp utility is just a wrapper around the same entry-dumping |
| 544 | code used by tic for source translation. Perhaps the one interesting |
| 545 | aspect of the code is the use of a predicate function passed in to |
| 546 | dump_entry() to control which capabilities are dumped. This is |
| 547 | necessary in order to handle both the ordinary De-compilation case and |
| 548 | entry difference reporting. |
| 549 | |
| 550 | The tput and clear utilities just do an entry load followed by a |
| 551 | tputs() of a selected capability. |
| 552 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 553 | Style Tips for Developers |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 554 | |
| 555 | See the TO-DO file in the top-level directory of the source |
| 556 | distribution for additions that would be particularly useful. |
| 557 | |
| 558 | The prefix _nc_ should be used on library public functions that are |
| 559 | not part of the curses API in order to prevent pollution of the |
| 560 | application namespace. If you have to add to or modify the function |
| 561 | prototypes in curses.h.in, read ncurses/MKlib_gen.sh first so you can |
| 562 | avoid breaking XSI conformance. Please join the ncurses mailing list. |
| 563 | See the INSTALL file in the top level of the distribution for details |
| 564 | on the list. |
| 565 | |
| 566 | Look for the string FIXME in source files to tag minor bugs and |
| 567 | potential problems that could use fixing. |
| 568 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 569 | Do not try to auto-detect OS features in the main body of the C code. |
| 570 | That is the job of the configuration system. |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 571 | |
| 572 | To hold down complexity, do make your code data-driven. Especially, if |
| 573 | you can drive logic from a table filtered out of include/Caps, do it. |
| 574 | If you find you need to augment the data in that file in order to |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 575 | generate the proper table, that is still preferable to ad-hoc code -- |
| 576 | that is why the fifth field (flags) is there. |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 577 | |
| 578 | Have fun! |
| 579 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 580 | Porting Hints |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 581 | |
| 582 | The following notes are intended to be a first step towards DOS and |
| 583 | Macintosh ports of the ncurses libraries. |
| 584 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 585 | The following library modules are "pure curses"; they operate only on |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 586 | the curses internal structures, do all output through other curses |
| 587 | calls (not including tputs() and putp()) and do not call any other |
| 588 | UNIX routines such as signal(2) or the stdio library. Thus, they |
| 589 | should not need to be modified for single-terminal ports. |
| 590 | |
| 591 | lib_addch.c lib_addstr.c lib_bkgd.c lib_box.c lib_clear.c |
| 592 | lib_clrbot.c lib_clreol.c lib_delch.c lib_delwin.c lib_erase.c |
| 593 | lib_inchstr.c lib_insch.c lib_insdel.c lib_insstr.c lib_keyname.c |
| 594 | lib_move.c lib_mvwin.c lib_newwin.c lib_overlay.c lib_pad.c |
| 595 | lib_printw.c lib_refresh.c lib_scanw.c lib_scroll.c lib_scrreg.c |
| 596 | lib_set_term.c lib_touch.c lib_tparm.c lib_tputs.c lib_unctrl.c |
| 597 | lib_window.c panel.c |
| 598 | |
| 599 | This module is pure curses, but calls outstr(): |
| 600 | |
| 601 | lib_getstr.c |
| 602 | |
| 603 | These modules are pure curses, except that they use tputs() and |
| 604 | putp(): |
| 605 | |
| 606 | lib_beep.c lib_color.c lib_endwin.c lib_options.c lib_slk.c |
| 607 | lib_vidattr.c |
| 608 | |
| 609 | This modules assist in POSIX emulation on non-POSIX systems: |
| 610 | |
| 611 | sigaction.c |
| 612 | signal calls |
| 613 | |
| 614 | The following source files will not be needed for a |
| 615 | single-terminal-type port. |
| 616 | |
| 617 | alloc_entry.c captoinfo.c clear.c comp_captab.c comp_error.c |
| 618 | comp_hash.c comp_main.c comp_parse.c comp_scan.c dump_entry.c |
| 619 | infocmp.c parse_entry.c read_entry.c tput.c write_entry.c |
| 620 | |
| 621 | The following modules will use open()/read()/write()/close()/lseek() |
| 622 | on files, but no other OS calls. |
| 623 | |
| 624 | lib_screen.c |
| 625 | used to read/write screen dumps |
| 626 | |
| 627 | lib_trace.c |
| 628 | used to write trace data to the logfile |
| 629 | |
| 630 | Modules that would have to be modified for a port start here: |
| 631 | |
micky387 | 9b9f5e7 | 2025-07-08 18:04:53 -0400 | [diff] [blame] | 632 | The following modules are "pure curses" but contain assumptions |
Amit Daniel Kachhap | e6a01f5 | 2011-07-20 11:45:59 +0530 | [diff] [blame] | 633 | inappropriate for a memory-mapped port. |
| 634 | |
| 635 | lib_longname.c |
| 636 | assumes there may be multiple terminals |
| 637 | |
| 638 | lib_acs.c |
| 639 | assumes acs_map as a double indirection |
| 640 | |
| 641 | lib_mvcur.c |
| 642 | assumes cursor moves have variable cost |
| 643 | |
| 644 | lib_termcap.c |
| 645 | assumes there may be multiple terminals |
| 646 | |
| 647 | lib_ti.c |
| 648 | assumes there may be multiple terminals |
| 649 | |
| 650 | The following modules use UNIX-specific calls: |
| 651 | |
| 652 | lib_doupdate.c |
| 653 | input checking |
| 654 | |
| 655 | lib_getch.c |
| 656 | read() |
| 657 | |
| 658 | lib_initscr.c |
| 659 | getenv() |
| 660 | |
| 661 | lib_newterm.c |
| 662 | lib_baudrate.c |
| 663 | lib_kernel.c |
| 664 | various tty-manipulation and system calls |
| 665 | |
| 666 | lib_raw.c |
| 667 | various tty-manipulation calls |
| 668 | |
| 669 | lib_setup.c |
| 670 | various tty-manipulation calls |
| 671 | |
| 672 | lib_restart.c |
| 673 | various tty-manipulation calls |
| 674 | |
| 675 | lib_tstp.c |
| 676 | signal-manipulation calls |
| 677 | |
| 678 | lib_twait.c |
| 679 | gettimeofday(), select(). |
| 680 | _________________________________________________________________ |
| 681 | |
| 682 | |
| 683 | Eric S. Raymond <esr@snark.thyrsus.com> |
| 684 | |
| 685 | (Note: This is not the bug address!) |