. Screen Updating and Cursor Movement Optimization: A Library Package _K_e_n_n_e_t_h _C. _R. _C. _A_r_n_o_l_d Computer Science Division Department of Electrical Engineering and Computer Science University of California, Berkeley Berkeley, California 94720 _A_B_S_T_R_A_C_T This document describes a package of C library functions which allow the user to: 1) update a screen with reasonable optimization, 2) get input from the terminal in a screen-oriented fashion, and 3) independent from the above, move the cursor op- timally from one point to another. These routines all use the /_e_t_c/_t_e_r_m_c_a_p database to describe the capabilities of the terminal. _A_c_k_n_o_w_l_e_d_g_e_m_e_n_t_s This package would not exist without the work of Bill Joy, who, in writing his editor, created the capability to generally describe terminals, wrote the routines which read this database, and, most importantly, those which implement optimal cursor movement, which routines I have simply lifted nearly intact. Doug Merritt and Kurt Shoens also were ex- tremely important, as were both willing to waste time listening to me rant and rave. The help and/or support of Ken Abrams, Alan Char, Mark Horton, and Joe Kalash, was, and is, also greatly appreciated. . Screen Package _1. _O_v_e_r_v_i_e_w In making available the generalized terminal descrip- tions in /_e_t_c/_t_e_r_m_c_a_p, much information was made available to the programmer, but little work was taken out of one's hands. The purpose of this package is to allow the C pro- grammer to do the most common type of terminal dependent functions, those of movement optimization and optimal screen updating, without doing any of the dirty work, and (hopeful- ly) with nearly as much ease as is necessary to simply print or read things. The package is split into three parts: (1) Screen up- dating; (2) Screen updating with user input; and (3) Cursor motion optimization. It is possible to use the motion optimization without using either of the other two, and screen updating and input can be done without any programmer knowledge of the motion optimization, or indeed the database itself. _1._1. _T_e_r_m_i_n_o_l_o_g_y (_o_r, _W_o_r_d_s _Y_o_u _C_a_n _S_a_y _t_o _S_o_u_n_d _B_r_i_l_l_i_- _a_n_t) In this document, the following terminology is kept to with reasonable consistency: _w_i_n_d_o_w: An internal representation containing an image of what a section of the terminal screen may look like at some point in time. This subsection can either encom- pass the entire terminal screen, or any smaller portion down to a single character within that screen. _t_e_r_m_i_n_a_l: Sometimes called _t_e_r_m_i_n_a_l _s_c_r_e_e_n. The package's idea of what the terminal's screen currently looks like, i.e., what the user sees now. This is a special _s_c_r_e_e_n: _s_c_r_e_e_n: This is a subset of windows which are as large as the terminal screen, i.e., they start at the upper left hand corner and encompass the lower right hand corner. One of these, _s_t_d_s_c_r, is automatically provided for the programmer. _1._2. _C_o_m_p_i_l_i_n_g _T_h_i_n_g_s In order to use the library, it is necessary to have certain types and variables defined. Therefore, the pro- grammer must have a line: #_i_n_c_l_u_d_e <_c_u_r_s_e_s._h> at the top of the program source. The header file - 1 - . Screen Package <_c_u_r_s_e_s._h> needs to include <_s_g_t_t_y._h>, so the one should not do so oneself[1]. Also, compilations should have the fol- lowing form: _c_c [ flags ] file ... -_l_c_u_r_s_e_s -_l_t_e_r_m_l_i_b _1._3. _S_c_r_e_e_n _U_p_d_a_t_i_n_g In order to update the screen optimally, it is neces- sary 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 _W_I_N_- _D_O_W is defined which describes a window image to the rou- tines, including its starting position on the screen (the (y, x) co-ordinates of the upper left hand corner) and its size. One of these (called _c_u_r_s_c_r for _c_u_r_r_e_n_t _s_c_r_e_e_n) is a screen image of what the terminal currently looks like. Another screen (called _s_t_d_s_c_r, for _s_t_a_n_d_a_r_d _s_c_r_e_e_n) is pro- vided by default to make changes on. A window is a purely internal representation. It is used to build and store a potential image of a portion of the terminal. It doesn't bear any necessary relation to what is really on the terminal screen. It is more like an array of characters on which to make changes. When one has a window which describes what some part the terminal should look like, the routine _r_e_f_r_e_s_h() (or _w_r_e_f_r_e_s_h() if the window is not _s_t_d_s_c_r) is called. _r_e_- _f_r_e_s_h() makes the terminal, in the area covered by the win- dow, look like that window. Note, therefore, that changing something on a window _d_o_e_s _n_o_t _c_h_a_n_g_e _t_h_e _t_e_r_m_i_n_a_l. Actual updates to the terminal screen are made only by calling _r_e_- _f_r_e_s_h() or _w_r_e_f_r_e_s_h(). This allows the programmer to main- tain several different ideas of what a portion of the termi- nal screen should look like. 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 worry about the best way to do this. _1._4. _N_a_m_i_n_g _C_o_n_v_e_n_t_i_o_n_s As hinted above, the routines can use several windows, but two are automatically given: _c_u_r_s_c_r, which knows what the terminal looks like, and _s_t_d_s_c_r, which is what the pro- ____________________ [1] The screen package also uses the Standard I/O li- brary, so <_c_u_r_s_e_s._h> includes <_s_t_d_i_o._h>. It is redundant (but harmless) for the programmer to do it, too. - 2 - . Screen Package grammer wants the terminal to look like next. The user should never really access _c_u_r_s_c_r directly. Changes should be made to the appropriate screen, and then the routine _r_e_- _f_r_e_s_h() (or _w_r_e_f_r_e_s_h()) should be called. Many functions are set up to deal with _s_t_d_s_c_r as a de- fault screen. For example, to add a character to _s_t_d_s_c_r, one calls _a_d_d_c_h() with the desired character. If a dif- ferent window is to be used, the routine _w_a_d_d_c_h() (for _window-specific _a_d_d_c_h()) is provided[2]. This convention of prepending function names with a "_w" when they are to be ap- plied to specific windows is consistent. The only routines which do _n_o_t do this are those to which a window must always be specified. In order to move the current (y, x) co-ordinates from one point to another, the routines _m_o_v_e() and _w_m_o_v_e() are provided. However, it is often desirable to first move and then perform some I/O operation. In order to avoid clumsy- ness, most I/O routines can be preceded by the prefix "_m_v" and the desired (y, x) co-ordinates then can be added 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 (_w_i_n) comes before the added (y, x) co-ordinates. If such pointers are need, they are always the first parameters passed. _2. _V_a_r_i_a_b_l_e_s Many variables which are used to describe the terminal environment are available to the programmer. They are: ____________________ [2] Actually, _a_d_d_c_h() is really a "#define" macro with arguments, as are most of the "functions" which deal with _s_t_d_s_c_r as a default. - 3 - . Screen Package type name description __________________________________________________________________ WINDOW *curscr current version of the screen (terminal screen). WINDOW *stdscr standard screen. Most updates are usually done here. char * Def_term default terminal type if type cannot be determined bool My_term use the terminal specification in _D_e_f__t_e_r_m as ter- minal, irrelevant of real terminal type char * ttytype full name of the current terminal. int LINES number of lines on the terminal int COLS number of columns on the terminal int ERR error flag returned by routines on a fail. int OK error flag returned by routines when things go right. There are also several "#define" constants and types which are of general usefulness: reg storage class ``register'' (e.g., _r_e_g _i_n_t _i;) bool boolean type, actually a ``char'' (e.g., _b_o_o_l _d_o_n_e_i_t;) TRUE boolean ``true'' flag (1). FALSE boolean ``false'' flag (0). _3. _U_s_a_g_e This is a description of how to actually use the screen package. In it, we assume all updating, reading, etc. is applied to _s_t_d_s_c_r. All instructions will work on any win- dow, with changing the function name and parameters as men- tioned above. _3._1. _S_t_a_r_t_i_n_g _u_p In order to use the screen package, the routines must know about terminal characteristics, and the space for _c_u_r_s_c_r and _s_t_d_s_c_r must be allocated. These functions are performed by _i_n_i_t_s_c_r(). Since it must allocate space for the windows, it can overflow core when attempting to do so. On this rather rare occasion, _i_n_i_t_s_c_r() returns ERR. _i_n_- _i_t_s_c_r() must _a_l_w_a_y_s 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 _c_u_r_s_c_r or _s_t_d_s_c_r are refer- enced. 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 _n_l() and _c_r_m_o_d_e() should be called after _i_n_i_t_s_c_r(). Now that the screen windows have been allocated, you can set them up for the run. If you want to, say, allow the window to scroll, use _s_c_r_o_l_l_o_k(). If you want the cursor to - 4 - . Screen Package be left after the last change, use _l_e_a_v_e_o_k(). If this isn't done, _r_e_f_r_e_s_h() will move the cursor to the window's current (y, x) co-ordinates after updating it. New windows of your own can be created, too, by using the functions _n_e_w_w_i_n() and _s_u_b_w_i_n(). _d_e_l_w_i_n() will allow you to get rid of old win- dows. If you wish to change the official size of the termi- nal by hand, just set the variables _L_I_N_E_S and _C_O_L_S to be what you want, and then call _i_n_i_t_s_c_r(). This is best done before, but can be done either before or after, the first call to _i_n_i_t_s_c_r(), as it will always delete any existing _s_t_d_s_c_r and/or _c_u_r_s_c_r before creating new ones. _3._2. _T_h_e _N_i_t_t_y-_G_r_i_t_t_y _3._2._1. _O_u_t_p_u_t Now that we have set things up, we will want to actual- ly update the terminal. The basic functions used to change what will go on a window are _a_d_d_c_h() and _m_o_v_e(). _a_d_d_c_h() adds a character at the current (y, x) co-ordinates, return- ing ERR if it would cause the window to illegally scroll, i.e., printing a character in the lower right-hand corner of a terminal which automatically scrolls if scrolling is not allowed. _m_o_v_e() changes the current (y, x) co-ordinates to whatever you want them to be. It returns ERR if you try to move off the window when scrolling is not allowed. As men- tioned above, you can combine the two into _m_v_a_d_d_c_h() to do both things in one fell swoop. The other output functions, such as _a_d_d_s_t_r() and _p_r_i_n_t_w(), all call _a_d_d_c_h() 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 _r_e_f_r_e_s_h(). In order to optimize finding changes, _r_e_f_r_e_s_h() assumes that any part of the window not changed since the last _r_e_f_r_e_s_h() 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 _t_o_u_c_h_w_i_n() is provided to make it look like the entire win- dow has been changed, thus making _r_e_f_r_e_s_h() check the whole subsection of the terminal for changes. If you call _w_r_e_f_r_e_s_h() with _c_u_r_s_c_r, it will make the screen look like _c_u_r_s_c_r thinks it looks like. This is use- ful for implementing a command which would redraw the screen in case it get messed up. _3._2._2. _I_n_p_u_t Input is essentially a mirror image of output. The complementary function to _a_d_d_c_h() is _g_e_t_c_h() which, if echo - 5 - . Screen Package is set, will call _a_d_d_c_h() 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. If it is not, _g_e_t_c_h() sets it to be cbreak, and then reads in the character. _3._2._3. _M_i_s_c_e_l_l_a_n_e_o_u_s All sorts of fun functions exists for maintaining and changing information about the windows. For the most part, the descriptions in section 5.4. should suffice. _3._3. _F_i_n_i_s_h_i_n_g _u_p In order to do certain optimizations, and, on some ter- minals, to work at all, some things must be done before the screen routines start up. These functions are performed in _g_e_t_t_t_m_o_d_e() and _s_e_t_t_e_r_m(), which are called by _i_n_i_t_s_c_r(). In order to clean up after the routines, the routine _e_n_d_w_i_n() is provided. It restores tty modes to what they were when _i_n_i_t_s_c_r() was first called. Thus, anytime after the call to initscr, _e_n_d_w_i_n() should be called before exit- ing. _4. _C_u_r_s_o_r _M_o_t_i_o_n _O_p_t_i_m_i_z_a_t_i_o_n: _S_t_a_n_d_i_n_g _A_l_o_n_e It is possible to use the cursor optimization functions of this screen package without the overhead and additional size of the screen updating functions. The screen updating functions are designed for uses where parts of the screen are changed, but the overall image remains the same. This includes such programs as _e_y_e and _v_i[3]. Certain other pro- grams will find it difficult to use these functions in this manner without considerable unnecessary program overhead. For such applications, such as some "_c_r_t _h_a_c_k_s"[4] and op- timizing _c_a_t(1)-type programs, all that is needed is the mo- tion optimizations. This, therefore, is a description of what some of what goes on at the lower levels of this screen package. The descriptions assume a certain amount of fami- liarity with programming problems and some finer points of C. None of it is terribly difficult, but you should be forewarned. ____________________ [3] _E_y_e actually uses these functions, _v_i does not. [4] Graphics programs designed to run on character- oriented terminals. I could name many, but they come and go, so the list would be quickly out of date. Recently, there have been programs such as _r_o_c_k_e_t and _g_u_n. - 6 - . Screen Package _4._1. _T_e_r_m_i_n_a_l _I_n_f_o_r_m_a_t_i_o_n In order to use a terminal's features to the best of a program's abilities, it must first know what they are[5]. The /_e_t_c/_t_e_r_m_c_a_p database describes these, but a certain amount of decoding is necessary, and there are, of course, both efficient and inefficient ways of reading them in. The algorithm that the uses is taken from _v_i and is hideously efficient. It reads them in a tight loop into a set of variables whose names are two uppercase letters with some mnemonic value. For example, _H_O is a string which moves the cursor to the "home" position[6]. As there are two types of variables involving ttys, there are two routines. The first, _g_e_t_t_m_o_d_e(), sets some variables based upon the tty modes accessed by _g_t_t_y(2) and _s_t_t_y(2) The second, _s_e_t_t_e_r_m(), a larger task by reading in the descriptions from the /_e_t_c/_t_e_r_m_c_a_p database. This is the way these routines are used by _i_n_i_t_s_c_r(): _i_f (isatty(0)) { gettmode(); _i_f (sp=getenv("TERM")) setterm(sp); } _e_l_s_e setterm(Def_term); _puts(TI); _puts(VS); _i_s_a_t_t_y() checks to see if file descriptor 0 is a termi- nal[7]. If it is, _g_e_t_t_m_o_d_e() sets the terminal description modes from a _g_t_t_y(2) _g_e_t_e_n_v() is then called to get the name of the terminal, and that value (if there is one) is passed to _s_e_t_t_e_r_m(), which reads in the variables from /_e_t_c/_t_e_r_m_c_a_p associated with that terminal. (_g_e_t_e_n_v() returns a pointer to a string containing the name of the terminal, which we save in the character pointer _s_p.) If _i_s_a_t_t_y() returns ____________________ [5] If this comes as any surprise to you, there's this tower in Paris they're thinking of junking that I can let you have for a song. [6] These names are identical to those variables used in the /_e_t_c/_t_e_r_m_c_a_p database to describe each capability. See Appendix A for a complete list of those read, and _t_e_r_m_c_a_p(5) for a full description. [7] _i_s_a_t_t_y() is defined in the default C library function routines. It does a _g_t_t_y(2) on the descriptor and checks the return value. - 7 - . Screen Package false, the default terminal _D_e_f__t_e_r_m is used. The _T_I and _V_S sequences initialize the terminal (__p_u_t_s() is a macro which uses _t_p_u_t_s() (see _t_e_r_m_c_a_p(3)) to put out a string). It is these things which _e_n_d_w_i_n() undoes. _4._2. _M_o_v_e_m_e_n_t _O_p_t_i_m_i_z_a_t_i_o_n_s, _o_r, _G_e_t_t_i_n_g _O_v_e_r _Y_o_n_d_e_r Now that we have all this useful information, it would be nice to do something with it[8]. The most difficult thing to do properly is motion optimization. When you con- sider how many different features various terminals have (tabs, backtabs, non-destructive space, home sequences, ab- solute tabs, .....) you can see that deciding how to get from here to there can be a decidedly non-trivial task. The editor _v_i uses many of these features, and the routines it uses to do this take up many pages of code. Fortunately, I was able to liberate them with the author's permission, and use them here. After using _g_e_t_t_m_o_d_e() and _s_e_t_t_e_r_m() to get the termi- nal descriptions, the function _m_v_c_u_r() deals with this task. It usage is simple: you simply tell it where you are now and where you want to go. For example mvcur(0, 0, LINES/2, COLS/2) would move the cursor from the home position (0, 0) to the middle of the screen. If you wish to force absolute ad- dressing, you can use the function _t_g_o_t_o() from the _t_e_r_m_- _l_i_b(7) routines, or you can tell _m_v_c_u_r() that you are impos- sibly far away, like Cleveland. For example, to absolutely address the lower left hand corner of the screen from any- where just claim that you are in the upper right hand corner: mvcur(0, COLS-1, LINES-1, 0) _5. _T_h_e _F_u_n_c_t_i_o_n_s In the following definitions, "[*]" means that the "function" is really a "#define" macro with arguments. This means that it will not show up in stack traces in the de- bugger, or, in the case of such functions as _a_d_d_c_h(), it will show up as it's "_w" counterpart. The arguments are given to show the order and type of each. Their names are ____________________ [8] Actually, it _c_a_n be emotionally fulfilling just to get the information. This is usually only true, however, if you have the social life of a kumquat. - 8 - . Screen Package not mandatory, just suggestive. _5._1. _O_u_t_p_u_t _F_u_n_c_t_i_o_n_s _a_d_d_c_h(_c_h) [*] char ch; _w_a_d_d_c_h(_w_i_n, _c_h) _W_I_N_D_O_W *_w_i_n; _c_h_a_r _c_h; Add the character _c_h on the window at the current (y, x) co-ordinates. If the character is a newline ('\n') the line will be cleared to the end, and the current (y, x) co-ordinates will be changed to the be- ginning off the next line if newline mapping is on, or to the next line at the same x co-ordinate if it is off. A return ('\r') will move to the beginning of the line on the window. Tabs ('\t') will be expanded into spaces in the normal tabstop positions of every eight characters. This returns ERR if it would cause the screen to scroll illegally. _a_d_d_s_t_r(_s_t_r) [*] _c_h_a_r *_s_t_r; _w_a_d_d_s_t_r(_w_i_n, _s_t_r) _W_I_N_D_O_W *_w_i_n; _c_h_a_r *_s_t_r; Add the string pointed to by _s_t_r on the window at the current (y, x) co-ordinates. This returns ERR if it would cause the screen to scroll illegally. In this case, it will put on as much as it can. _b_o_x(_w_i_n, _v_e_r_t, _h_o_r) _W_I_N_D_O_W *_w_i_n; _c_h_a_r _v_e_r_t, _h_o_r; Draws a box around the window using _v_e_r_t as the charac- ter for drawing the vertical sides, and _h_o_r for drawing the horizontal lines. If scrolling is not allowed, and the window encompasses the lower right-hand corner of the terminal, the corners are left blank to avoid a scroll. - 9 - . Screen Package _c_l_e_a_r() [*] _w_c_l_e_a_r(_w_i_n) _W_I_N_D_O_W *_w_i_n; Resets the entire window to blanks. If _w_i_n is a screen, this sets the clear flag, which will cause a clear-screen sequence to be sent on the next _r_e_f_r_e_s_h() call. This also moves the current (y, x) co-ordinates to (0, 0). _c_l_e_a_r_o_k(_s_c_r, _b_o_o_l_f) [*] _W_I_N_D_O_W *_s_c_r; _b_o_o_l _b_o_o_l_f; Sets the clear flag for the screen _s_c_r. If _b_o_o_l_f is TRUE, this will force a clear-screen to be printed on the next _r_e_f_r_e_s_h(), or stop it from doing so if _b_o_o_l_f is FALSE. This only works on screens, and, unlike _c_l_e_a_r(), does not alter the contents of the screen. If _s_c_r is _c_u_r_s_c_r, the next _r_e_f_r_e_s_h() call will cause a clear-screen, even if the window passed to _r_e_f_r_e_s_h() is not a screen. _c_l_r_t_o_b_o_t() [*] _w_c_l_r_t_o_b_o_t(_w_i_n) _W_I_N_D_O_W *_w_i_n; Wipes the window clear from the current (y, x) co- ordinates to the bottom. This does not force a clear- screen sequence on the next refresh under any cir- cumstances. This has no associated "mv" command. _c_l_r_t_o_e_o_l() [*] _w_c_l_r_t_o_e_o_l(_w_i_n) _W_I_N_D_O_W *_w_i_n; Wipes the window clear from the current (y, x) co- ordinates to the end of the line. This has no associ- ated "mv" command. _d_e_l_c_h() - 10 - . Screen Package _w_d_e_l_c_h(_w_i_n) _W_I_N_D_O_W *_w_i_n; Delete the character at the current (y, x) co- ordinates. Each character after it on the line shifts to the left, and the last character becomes blank. _d_e_l_e_t_e_l_n() _w_d_e_l_e_t_e_l_n(_w_i_n) _W_I_N_D_O_W *_w_i_n; Delete the current line. Every line below the current one will move up, and the bottom line will become blank. The current (y, x) co-ordinates will remain un- changed. _e_r_a_s_e() [*] _w_e_r_a_s_e(_w_i_n) _W_I_N_D_O_W *_w_i_n; Erases the window to blanks without setting the clear flag. This is analagous to _c_l_e_a_r(), except that it never causes a clear-screen sequence to be generated on a _r_e_f_r_e_s_h(). This has no associated "mv" command. _i_n_s_c_h(_c) _c_h_a_r _c; _w_i_n_s_c_h(_w_i_n, _c) _W_I_N_D_O_W *_w_i_n; _c_h_a_r _c; Insert _c at the current (y, x) co-ordinates Each char- acter after it shifts to the right, and the last char- acter disappears. This returns ERR if it would cause the screen to scroll illegally. _i_n_s_e_r_t_l_n() _w_i_n_s_e_r_t_l_n(_w_i_n) _W_I_N_D_O_W *_w_i_n; - 11 - . Screen Package Insert a line above the current one. Every line below the current line will be shifted down, and the bottom line will disappear. The current line will become blank, and the current (y, x) co-ordinates will remain unchanged. This returns ERR if it would cause the screen to scroll illegally. _m_o_v_e(_y, _x) [*] _i_n_t _y, _x; _w_m_o_v_e(_w_i_n, _y, _x) _W_I_N_D_O_W *_w_i_n; _i_n_t _y, _x; Change the current (y, x) co-ordinates of the window to (_y, _x). This returns ERR if it would cause the screen to scroll illegally. _o_v_e_r_l_a_y(_w_i_n_1, _w_i_n_2) _W_I_N_D_O_W *_w_i_n_1, *_w_i_n_2; Overlay _w_i_n_1 on _w_i_n_2. The contents of _w_i_n_1, insofar as they fit, are placed on _w_i_n_2 at their starting (y, x) co-ordinates. This is done non-destructively, i.e., blanks on _w_i_n_1 leave the contents of the space on _w_i_n_2 untouched. _o_v_e_r_w_r_i_t_e(_w_i_n_1, _w_i_n_2) _W_I_N_D_O_W *_w_i_n_1, *_w_i_n_2; Overwrite _w_i_n_1 on _w_i_n_2. The contents of _w_i_n_1, insofar as they fit, are placed on _w_i_n_2 at their starting (y, x) co-ordinates. This is done destructively, i.e., blanks on _w_i_n_1 become blank on _w_i_n_2. _p_r_i_n_t_w(_f_m_t, _a_r_g_1, _a_r_g_2, ...) _c_h_a_r *_f_m_t; _w_p_r_i_n_t_w(_w_i_n, _f_m_t, _a_r_g_1, _a_r_g_2, ...) _W_I_N_D_O_W *_w_i_n; _c_h_a_r *_f_m_t; Performs a _p_r_i_n_t_f() on the window starting at the current (y, x) co-ordinates. It uses _a_d_d_s_t_r() to add the string on the window. It is often advisable to use - 12 - . Screen Package the field width options of _p_r_i_n_t_f() to avoid leaving things on the window from earlier calls. This returns ERR if it would cause the screen to scroll illegally. _r_e_f_r_e_s_h() [*] _w_r_e_f_r_e_s_h(_w_i_n) _W_I_N_D_O_W *_w_i_n; Synchronize the terminal screen with the desired win- dow. If the window is not a screen, only that part covered by it is updated. This returns ERR if it would cause the screen to scroll illegally. In this case, it will update whatever it can without causing the scroll. _s_t_a_n_d_o_u_t() [*] _w_s_t_a_n_d_o_u_t(_w_i_n) _W_I_N_D_O_W *_w_i_n; _s_t_a_n_d_e_n_d() [*] _w_s_t_a_n_d_e_n_d(_w_i_n) _W_I_N_D_O_W *_w_i_n; Start and stop putting characters onto _w_i_n in standout mode. _s_t_a_n_d_o_u_t() causes any characters added to the window to be put in standout mode on the terminal (if it has that capability). _s_t_a_n_d_e_n_d() stops this. The sequences _S_O and _S_E (or _U_S and _U_E if they are not de- fined) are used (see Appendix A). _5._2. _I_n_p_u_t _F_u_n_c_t_i_o_n_s _c_r_m_o_d_e() [*] _n_o_c_r_m_o_d_e() [*] Set or unset the terminal to/from cbreak mode. _e_c_h_o() [*] _n_o_e_c_h_o() [*] - 13 - . Screen Package Sets the terminal to echo or not echo characters. _g_e_t_c_h() [*] _w_g_e_t_c_h(_w_i_n) _W_I_N_D_O_W *_w_i_n; Gets a character from the terminal and (if necessary) echos it on the window. This returns ERR if it would cause the screen to scroll illegally. Otherwise, the character gotten is returned. If _n_o_e_c_h_o has been set, then the window is left unaltered. In order to retain control of the terminal, it is necessary to have one of _n_o_e_c_h_o, _c_b_r_e_a_k, or _r_a_w_m_o_d_e set. If you do not set one, whatever routine you call to read characters will set _c_b_r_e_a_k for you, and then reset to the original mode when finished. _g_e_t_s_t_r(_s_t_r) [*] _c_h_a_r *_s_t_r; _w_g_e_t_s_t_r(_w_i_n, _s_t_r) _W_I_N_D_O_W *_w_i_n; _c_h_a_r *_s_t_r; Get a string through the window and put it in the loca- tion pointed to by _s_t_r, which is assumed to be large enough to handle it. It sets tty modes if necessary, and then calls _g_e_t_c_h() (or _w_g_e_t_c_h(_w_i_n)) to get the characters needed to fill in the string until a newline or EOF is encountered. The newline stripped off the string. This returns ERR if it would cause the screen to scroll illegally. _r_a_w() [*] _n_o_r_a_w() [*] Set or unset the terminal to/from raw mode. On version 7 _U_N_I_X[_9] this also turns of newline mapping (see _n_l()). ____________________ [9] _U_N_I_X is a trademark of Bell Laboratories. - 14 - . Screen Package _s_c_a_n_w(_f_m_t, _a_r_g_1, _a_r_g_2, ...) _c_h_a_r *_f_m_t; _w_s_c_a_n_w(_w_i_n, _f_m_t, _a_r_g_1, _a_r_g_2, ...) _W_I_N_D_O_W *_w_i_n; _c_h_a_r *_f_m_t; Perform a _s_c_a_n_f() through the window using _f_m_t. It does this using consecutive _g_e_t_c_h()'s (or _w_g_e_t_c_h(_w_i_n)'s). This returns ERR if it would cause the screen to scroll illegally. _5._3. _M_i_s_c_e_l_l_a_n_e_o_u_s _F_u_n_c_t_i_o_n_s _d_e_l_w_i_n(_w_i_n) _W_I_N_D_O_W *_w_i_n; Deletes the window from existence. All resources are freed for future use by _c_a_l_l_o_c(3). If a window has a _s_u_b_w_i_n() allocated window inside of it, deleting the outer window the subwindow is not affected, even though this does invalidate it. Therefore, subwindows should be deleted before their outer windows are. _e_n_d_w_i_n() Finish up window routines before exit. This restores the terminal to the state it was before _i_n_i_t_s_c_r() (or _g_e_t_t_m_o_d_e() and _s_e_t_t_e_r_m()) was called. It should always be called before exiting. It does not exit. This is especially useful for resetting tty stats when trapping rubouts via _s_i_g_n_a_l(2). _g_e_t_y_x(_w_i_n, _y, _x) [*] _W_I_N_D_O_W *_w_i_n; _i_n_t _y, _x; Puts the current (y, x) co-ordinates of _w_i_n in the variables _y and _x. Since it is a macro, not a func- tion, you do not pass the address of _y and _x. _i_n_c_h() [*] _w_i_n_c_h(_w_i_n) [*] - 15 - . Screen Package _W_I_N_D_O_W *_w_i_n; Returns the character at the current (y, x) co- ordinates on the given window. This does not make any changes to the window. This has no associated "mv" command. _i_n_i_t_s_c_r() Initialize the screen routines. This must be called before any of the screen routines are used. It ini- tializes the terminal-type data and such, and without it, none of the routines can operate. If standard in- put is not a tty, it sets the specifications to the terminal whose name is pointed to by _D_e_f__t_e_r_m (initialy "dumb"). If the boolean _M_y__t_e_r_m is true, _D_e_f__t_e_r_m is always used. _l_e_a_v_e_o_k(_w_i_n, _b_o_o_l_f) [*] _W_I_N_D_O_W *_w_i_n; _b_o_o_l _b_o_o_l_f; Sets the boolean flag for leaving the cursor after the last change. If _b_o_o_l_f is TRUE, the cursor will be left after the last update on the terminal, and the current (y, x) co-ordinates for _w_i_n will be changed according- ly. If it is FALSE, it will be moved to the current (y, x) co-ordinates. This flag (initialy FALSE) re- tains its value until changed by the user. _l_o_n_g_n_a_m_e(_t_e_r_m_b_u_f, _n_a_m_e) _c_h_a_r *_t_e_r_m_b_u_f, *_n_a_m_e; Fills in _n_a_m_e with the long (full) name of the terminal described by the termcap entry in _t_e_r_m_b_u_f. It is gen- erally of little use, but is nice for telling the user in a readable format what terminal we think he has. This is available in the global variable _t_t_y_t_y_p_e. _T_e_r_m_b_u_f is usually set via the termlib routine _t_g_e_t_e_n_t(). _m_v_w_i_n(_w_i_n, _y, _x) _W_I_N_D_O_W *_w_i_n; _i_n_t _y, _x; - 16 - . Screen Package Move the home position of the window _w_i_n from its current starting coordinates to (_y, _x). If that would put part or all of the window off the edge of the ter- minal screen, _m_v_w_i_n() returns ERR and does not change anything. _W_I_N_D_O_W * _n_e_w_w_i_n(_l_i_n_e_s, _c_o_l_s, _b_e_g_i_n__y, _b_e_g_i_n__x) _i_n_t _l_i_n_e_s, _c_o_l_s, _b_e_g_i_n__y, _b_e_g_i_n__x; Create a new window with _l_i_n_e_s lines and _c_o_l_s columns starting at position (_b_e_g_i_n__y, _b_e_g_i_n__x). If either _l_i_n_e_s or _c_o_l_s is 0 (zero), that dimension will be set to (_L_I_N_E_S - _b_e_g_i_n__y) or (_C_O_L_S - _b_e_g_i_n__x) respectively. Thus, to get a new window of dimensions _L_I_N_E_S x _C_O_L_S, use _n_e_w_w_i_n(_0, _0, _0, _0). _n_l() [*] _n_o_n_l() [*] Set or unset the terminal to/from nl mode, i.e., start/stop the system from mapping <_R_E_T_U_R_N> to <_L_I_N_E- _F_E_E_D>. If the mapping is not done, _r_e_f_r_e_s_h() can do more optimization, so it is recommended, but not re- quired, to turn it off. _s_c_r_o_l_l_o_k(_w_i_n, _b_o_o_l_f) [*] _W_I_N_D_O_W *_w_i_n; _b_o_o_l _b_o_o_l_f; Set the scroll flag for the given window. If _b_o_o_l_f is FALSE, scrolling is not allowed. This is its default setting. _t_o_u_c_h_w_i_n(_w_i_n) _W_I_N_D_O_W *_w_i_n; Make it appear that the every location on the window has been changed. This is usually only needed for re- freshes with overlapping windows. _W_I_N_D_O_W * _s_u_b_w_i_n(_w_i_n, _l_i_n_e_s, _c_o_l_s, _b_e_g_i_n__y, _b_e_g_i_n__x) _W_I_N_D_O_W *_w_i_n; - 17 - . Screen Package _i_n_t _l_i_n_e_s, _c_o_l_s, _b_e_g_i_n__y, _b_e_g_i_n__x; Create a new window with _l_i_n_e_s lines and _c_o_l_s columns starting at position (_b_e_g_i_n__y, _b_e_g_i_n__x) in the middle of the window _w_i_n. This means that any change made to either window in the area covered by the subwindow will be made on both windows. _b_e_g_i_n__y, _b_e_g_i_n__x are speci- fied relative to the overall screen, not the relative (0, 0) of _w_i_n. If either _l_i_n_e_s or _c_o_l_s is 0 (zero), that dimension will be set to (_L_I_N_E_S - _b_e_g_i_n__y) or (_C_O_L_S - _b_e_g_i_n__x) respectively. _u_n_c_t_r_l(_c_h) [*] _c_h_a_r _c_h; This is actually a debug function for the library, but it is of general usefulness. It returns a string which is a representation of _c_h. Control characters become their upper-case equivalents preceded by a "^". Other letters stay just as they are. To use _u_n_c_t_r_l(), you must have #_i_n_c_l_u_d_e <_u_n_c_t_r_l._h> in your file. _5._4. _D_e_t_a_i_l_s _g_e_t_t_m_o_d_e() Get the tty stats. This is normally called by _i_n_- _i_t_s_c_r(). _m_v_c_u_r(_l_a_s_t_y, _l_a_s_t_x, _n_e_w_y, _n_e_w_x) _i_n_t _l_a_s_t_y, _l_a_s_t_x, _n_e_w_y, _n_e_w_x; Moves the terminal's cursor from (_l_a_s_t_y, _l_a_s_t_x) to (_n_e_w_y, _n_e_w_x) in an approximation of optimal fashion. This routine uses the functions borrowed from _e_x ver- sion 2.6. It is possible to use this optimization without the benefit of the screen routines. With the screen routines, this should not be called by the user. _m_o_v_e() and _r_e_f_r_e_s_h() should be used to move the cursor position, so that the routines know what's going on. _s_c_r_o_l_l(_w_i_n) _W_I_N_D_O_W *_w_i_n; - 18 - . Screen Package Scroll the window upward one line. This is normally not used by the user. _s_a_v_e_t_t_y() [*] _r_e_s_e_t_t_y() [*] _s_a_v_e_t_t_y() saves the current tty characteristic flags. _r_e_s_e_t_t_y() restores them to what _s_a_v_e_t_t_y() stored. These functions are performed automatically by _i_n_- _i_t_s_c_r() and _e_n_d_w_i_n(). _s_e_t_t_e_r_m(_n_a_m_e) _c_h_a_r *_n_a_m_e; Set the terminal characteristics to be those of the terminal named _n_a_m_e. This is normally called by _i_n_- _i_t_s_c_r(). _t_s_t_p() If the new _t_t_y(4) driver is in use, this function will save the current tty state and then put the process to sleep. When the process gets restarted, it restores the tty state and then calls _w_r_e_f_r_e_s_h(_c_u_r_s_c_r) to redraw the screen. _i_n_i_t_s_c_r() sets the signal SIGTSTP to trap to this routine. - 19 - . _A_p_p_e_n_d_i_x _A _1. _C_a_p_a_b_i_l_i_t_i_e_s _f_r_o_m _t_e_r_m_c_a_p _1._1. _D_i_s_c_l_a_i_m_e_r The description of terminals is a difficult business, and we only attempt to summarize the capabilities here: for a full description see the paper describing termcap. _1._2. _O_v_e_r_v_i_e_w Capabilities from termcap are of three kinds: string valued options, numeric valued options, and boolean options. The string valued options are the most complicated, since they may include padding information, which we describe now. Intelligent terminals often require padding on intelli- gent operations at high (and sometimes even low) speed. This is specified by a number before the string in the capa- bility, and has meaning for the capabilities which have a _P at the front of their comment. This normally is a number of milliseconds to pad the operation. In the current system which has no true programmable delays, we do this by sending a sequence of pad characters (normally nulls, but can be changed (specified by _P_C)). In some cases, the pad is better computed as some number of milliseconds times the number of affected lines (to the bottom of the screen usual- ly, except when terminals have insert modes which will shift several lines.) This is specified as, e.g., _1_2*. before the capability, to say 12 milliseconds per affected whatever (currently always line). Capabilities where this makes sense say _P*. _1._3. _V_a_r_i_a_b_l_e_s _S_e_t _B_y _s_e_t_t_e_r_m() variables set by _s_e_t_t_e_r_m() Type Name Pad Description _____________________________________________________________ char * AL P* Add new blank Line bool AM Automatic Margins char * BC Back Cursor movement bool BS BackSpace works char * BT P Back Tab bool CA Cursor Addressable char * CD P* Clear to end of Display char * CE P Clear to End of line char * CL P* CLear screen char * CM P Cursor Motion char * DC P* Delete Character char * DL P* Delete Line sequence char * DM Delete Mode (enter) - 20 - . _A_p_p_e_n_d_i_x _A variables set by _s_e_t_t_e_r_m() Type Name Pad Description _____________________________________________________________ char * DO DOwn line sequence char * ED End Delete mode bool EO can Erase Overstrikes with ' ' char * EI End Insert mode char * HO HOme cursor bool HZ HaZeltine ~ braindamage char * IC P Insert Character bool IN Insert-Null blessing char * IM enter Insert Mode (IC usually set, too) char * IP P* Pad after char Inserted using IM+IE char * LL quick to Last Line, column 0 char * MA ctrl character MAp for cmd mode bool MI can Move in Insert mode bool NC No Cr: \r sends \r\n then eats \n char * ND Non-Destructive space bool OS OverStrike works char PC Pad Character char * SE Standout End (may leave space) char * SF P Scroll Forwards char * SO Stand Out begin (may leave space) char * SR P Scroll in Reverse char * TA P TAb (not ^I or with padding) char * TE Terminal address enable Ending sequence char * TI Terminal address enable Initialization char * UC Underline a single Character char * UE Underline Ending sequence bool UL UnderLining works even though !OS char * UP UPline char * US Underline Starting sequence[10] char * VB Visible Bell char * VE Visual End sequence char * VS Visual Start sequence bool XN a Newline gets eaten after wrap Names starting with _X are reserved for severely nauseous glitches _1._4. _V_a_r_i_a_b_l_e_s _S_e_t _B_y _g_e_t_t_m_o_d_e() variables set by _g_e_t_t_m_o_d_e() type name description ____________________________________________________________ ____________________ [10] US and UE, if they do not exist in the termcap en- try, are copied from SO and SE in _s_e_t_t_e_r_m() - 21 - . _A_p_p_e_n_d_i_x _A variables set by _g_e_t_t_m_o_d_e() type name description ____________________________________________________________ bool NONL Term can't hack linefeeds doing a CR bool GT Gtty indicates Tabs bool UPPERCASE Terminal generates only uppercase letters - 22 - . _A_p_p_e_n_d_i_x _B _1. _T_h_e _W_I_N_D_O_W _s_t_r_u_c_t_u_r_e The WINDOW structure is defined as follows: # _d_e_f_i_n_e WINDOW _s_t_r_u_c_t _win_st _s_t_r_u_c_t _win_st { _s_h_o_r_t _cury, _curx; _s_h_o_r_t _maxy, _maxx; _s_h_o_r_t _begy, _begx; _s_h_o_r_t _flags; bool _clear; bool _leave; bool _scroll; _c_h_a_r **_y; _s_h_o_r_t *_firstch; _s_h_o_r_t *_lastch; }; # _d_e_f_i_n_e _SUBWIN 01 # _d_e_f_i_n_e _ENDLINE 02 # _d_e_f_i_n_e _FULLWIN 04 # _d_e_f_i_n_e _SCROLLWIN 010 # _d_e_f_i_n_e _STANDOUT 0200 __c_u_r_y and __c_u_r_x are the current (y, x) co-ordinates for the window. New characters added to the screen are added at this point. __m_a_x_y and __m_a_x_x are the maximum values allowed for (__c_u_r_y, __c_u_r_x). __b_e_g_y and __b_e_g_x are the starting (y, x) co-ordinates on the terminal for the window, i.e., the window's home. __c_u_r_y, __c_u_r_x, __m_a_x_y, and __m_a_x_x are measured relative to (__b_e_g_y, __b_e_g_x), not the terminal's home. __c_l_e_a_r tells if a clear-screen sequence is to be gen- erated on the next _r_e_f_r_e_s_h() call. This is only meaningful for screens. The initial clear-screen for the first _r_e_- _f_r_e_s_h() call is generated by initially setting clear to be TRUE for _c_u_r_s_c_r, which always generates a clear-screen if set, irrelevant of the dimensions of the window involved. __l_e_a_v_e is TRUE if the current (y, x) co-ordinates and the cursor are to be left after the last character changed on the terminal, or not moved if there is no change. __s_c_r_o_l_l is TRUE if scrolling is allowed. __y is a pointer to an array of lines which describe the terminal. Thus: _y[i] is a pointer to the _ith line, and _y[i][j] is the _jth character on the _ith line. __f_l_a_g_s can have one or more values or'd into it. __S_U_B_W_I_N means that the window is a subwindow, which indi- cates to _d_e_l_w_i_n() that the space for the lines is not to be freed. __E_N_D_L_I_N_E says that the end of the line for this win- dow is also the end of a screen. __F_U_L_L_W_I_N says that this window is a screen. __S_C_R_O_L_L_W_I_N indicates that the last character of this screen is at the lower right-hand corner of the terminal; _i._e., if a character was put there, the terminal would scroll. __S_T_A_N_D_O_U_T says that all characters added to the screen are in standout mode. _1. _E_x_a_m_p_l_e_s Here we present a few examples of how to use the pack- age. They attempt to be representative, though not comprehensive. _2. _S_c_r_e_e_n _U_p_d_a_t_i_n_g The following examples are intended to demonstrate the basic structure of a program using the screen updating sec- tions of the package. Several of the programs require cal- culational sections which are irrelevant of to the example, and are therefore usually not included. It is hoped that the data structure definitions give enough of an idea to al- low understanding of what the relevant portions do. The rest is left as an exercise to the reader, and will not be on the final. _2._1. _T_w_i_n_k_l_e This is a moderately simple program which prints pretty patterns on the screen that might even hold your interest for 30 seconds or more. It switches between patterns of as- terisks, putting them on one by one in random order, and then taking them off in the same fashion. It is more effi- cient to write this using only the motion optimization, as is demonstrated below. # _i_n_c_l_u_d_e <curses.h> # _i_n_c_l_u_d_e <signal.h> /* * _t_h_e _i_d_e_a _f_o_r _t_h_i_s _p_r_o_g_r_a_m _w_a_s _a _p_r_o_d_u_c_t _o_f _t_h_e _i_m_a_g_i_n_a_t_i_o_n _o_f * _K_u_r_t _S_c_h_o_e_n_s. _N_o_t _r_e_s_p_o_n_s_i_b_l_e _f_o_r _m_i_n_d_s _l_o_s_t _o_r _s_t_o_l_e_n. */ # _d_e_f_i_n_e NCOLS 80 # _d_e_f_i_n_e NLINES 24 # _d_e_f_i_n_e MAXPATTERNS 4 _s_t_r_u_c_t locs { _c_h_a_r y, x; }; _t_y_p_e_d_e_f _s_t_r_u_c_t locs LOCS; LOCS Layout[NCOLS * NLINES]; /* _c_u_r_r_e_n_t _b_o_a_r_d _l_a_y_o_u_t */ _i_n_t Pattern, /* _c_u_r_r_e_n_t _p_a_t_t_e_r_n _n_u_m_b_e_r */ Numstars; /* _n_u_m_b_e_r _o_f _s_t_a_r_s _i_n _p_a_t_t_e_r_n */ mainmain() { _c_h_a_r *getenv(); _i_n_t die(); srand(getpid()); /* _i_n_i_t_i_a_l_i_z_e _r_a_n_d_o_m _s_e_q_u_e_n_c_e */ initscr(); signal(SIGINT, die); noecho(); nonl(); leaveok(stdscr, TRUE); scrollok(stdscr, FALSE); _f_o_r (;;) { makeboard(); /* _m_a_k_e _t_h_e _b_o_a_r_d _s_e_t_u_p */ puton('*'); /* _p_u_t _o_n '*'_s */ puton(' '); /* _c_o_v_e_r _u_p _w_i_t_h ' '_s */ } } /* * _O_n _p_r_o_g_r_a_m _e_x_i_t, _m_o_v_e _t_h_e _c_u_r_s_o_r _t_o _t_h_e _l_o_w_e_r _l_e_f_t _c_o_r_n_e_r _b_y * _d_i_r_e_c_t _a_d_d_r_e_s_s_i_n_g, _s_i_n_c_e _c_u_r_r_e_n_t _l_o_c_a_t_i_o_n _i_s _n_o_t _g_u_a_r_a_n_t_e_e_d. * _W_e _l_i_e _a_n_d _s_a_y _w_e _u_s_e_d _t_o _b_e _a_t _t_h_e _u_p_p_e_r _r_i_g_h_t _c_o_r_n_e_r _t_o _g_u_a_r_a_n_t_e_e * _a_b_s_o_l_u_t_e _a_d_d_r_e_s_s_i_n_g. */ diedie() { signal(SIGINT, SIG_IGN); mvcur(0, COLS-1, LINES-1, 0); endwin(); exit(0); } /* * _M_a_k_e _t_h_e _c_u_r_r_e_n_t _b_o_a_r_d _s_e_t_u_p. _I_t _p_i_c_k_s _a _r_a_n_d_o_m _p_a_t_t_e_r_n _a_n_d * _c_a_l_l_s _i_s_o_n() _t_o _d_e_t_e_r_m_i_n_e _i_f _t_h_e _c_h_a_r_a_c_t_e_r _i_s _o_n _t_h_a_t _p_a_t_t_e_r_n * _o_r _n_o_t. */ makeboardmakeboard() { reg _i_n_t y, x; reg LOCS *lp; Pattern = rand() % MAXPATTERNS; lp = Layout; _f_o_r (y = 0; y < NLINES; y++) _f_o_r (x = 0; x < NCOLS; x++) _i_f (ison(y, x)) { lp->y = y; lp++->x = x; } Numstars = lp - Layout; } /* * _R_e_t_u_r_n _T_R_U_E _i_f (_y, _x) _i_s _o_n _t_h_e _c_u_r_r_e_n_t _p_a_t_t_e_r_n. */ isonison(y, x) reg _i_n_t y, x; { _s_w_i_t_c_h (Pattern) { _c_a_s_e 0: /* _a_l_t_e_r_n_a_t_i_n_g _l_i_n_e_s */ _r_e_t_u_r_n !(y & 01); _c_a_s_e 1: /* _b_o_x */ _i_f (x >= LINES && y >= NCOLS) _r_e_t_u_r_n FALSE; _i_f (y < 3 || y >= NLINES - 3) _r_e_t_u_r_n TRUE; _r_e_t_u_r_n (x < 3 || x >= NCOLS - 3); _c_a_s_e 2: /* _h_o_l_y _p_a_t_t_e_r_n! */ _r_e_t_u_r_n ((x + y) & 01); _c_a_s_e 3: /* _b_a_r _a_c_r_o_s_s _c_e_n_t_e_r */ _r_e_t_u_r_n (y >= 9 && y <= 15); } /* _N_O_T_R_E_A_C_H_E_D */ } putonputon(ch) reg _c_h_a_r ch; { reg LOCS *lp; reg _i_n_t r; reg LOCS *end; LOCS temp; end = &Layout[Numstars]; _f_o_r (lp = Layout; lp < end; lp++) { r = rand() % Numstars; temp = *lp; *lp = Layout[r]; Layout[r] = temp; } _f_o_r (lp = Layout; lp < end; lp++) { mvaddch(lp->y, lp->x, ch); refresh(); } } _2._2. _L_i_f_e This program plays the famous computer pattern game of life (Scientific American, May, 1974). The calculational routines create a linked list of structures defining where each piece is. Nothing here claims to be optimal, merely demonstrative. This program, however, is a very good place to use the screen updating routines, as it allows them to worry about what the last position looked like, so you don't have to. It also demonstrates some of the input routines. # _i_n_c_l_u_d_e <curses.h> # _i_n_c_l_u_d_e <signal.h> /* * _R_u_n _a _l_i_f_e _g_a_m_e. _T_h_i_s _i_s _a _d_e_m_o_n_s_t_r_a_t_i_o_n _p_r_o_g_r_a_m _f_o_r * _t_h_e _S_c_r_e_e_n _U_p_d_a_t_i_n_g _s_e_c_t_i_o_n _o_f _t_h_e -_l_c_u_r_s_e_s _c_u_r_s_o_r _p_a_c_k_a_g_e. */ _s_t_r_u_c_t lst_st { /* _l_i_n_k_e_d _l_i_s_t _e_l_e_m_e_n_t */ _i_n_t y, x; /* (_y, _x) _p_o_s_i_t_i_o_n _o_f _p_i_e_c_e */ _s_t_r_u_c_t lst_st *next, *last; /* _d_o_u_b_l_y _l_i_n_k_e_d */ }; _t_y_p_e_d_e_f _s_t_r_u_c_t lst_st LIST; LIST *Head; /* _h_e_a_d _o_f _l_i_n_k_e_d _l_i_s_t */ mainmain(ac, av) _i_n_t ac; _c_h_a_r *av[]; { _i_n_t die(); evalargs(ac, av); /* _e_v_a_l_u_a_t_e _a_r_g_u_m_e_n_t_s */ initscr(); /* _i_n_i_t_i_a_l_i_z_e _s_c_r_e_e_n _p_a_c_k_a_g_e */ signal(SIGINT, die); /* _s_e_t _t_o _r_e_s_t_o_r_e _t_t_y _s_t_a_t_s */ crmode(); /* _s_e_t _f_o_r _c_h_a_r-_b_y-_c_h_a_r */ noecho(); /* _i_n_p_u_t */ nonl(); /* _f_o_r _o_p_t_i_m_i_z_a_t_i_o_n */ getstart(); /* _g_e_t _s_t_a_r_t_i_n_g _p_o_s_i_t_i_o_n */ _f_o_r (;;) { prboard(); /* _p_r_i_n_t _o_u_t _c_u_r_r_e_n_t _b_o_a_r_d */ update(); /* _u_p_d_a_t_e _b_o_a_r_d _p_o_s_i_t_i_o_n */ } } /* * _T_h_i_s _i_s _t_h_e _r_o_u_t_i_n_e _w_h_i_c_h _i_s _c_a_l_l_e_d _w_h_e_n _r_u_b_o_u_t _i_s _h_i_t. * _I_t _r_e_s_e_t_s _t_h_e _t_t_y _s_t_a_t_s _t_o _t_h_e_i_r _o_r_i_g_i_n_a_l _v_a_l_u_e_s. _T_h_i_s * _i_s _t_h_e _n_o_r_m_a_l _w_a_y _o_f _l_e_a_v_i_n_g _t_h_e _p_r_o_g_r_a_m. */ diedie() { signal(SIGINT, SIG_IGN); /* _i_g_n_o_r_e _r_u_b_o_u_t_s */ mvcur(0, COLS-1, LINES-1, 0); /* _g_o _t_o _b_o_t_t_o_m _o_f _s_c_r_e_e_n */ endwin(); /* _s_e_t _t_e_r_m_i_n_a_l _t_o _i_n_i_t_i_a_l _s_t_a_t_e */ exit(0); } /* * _G_e_t _t_h_e _s_t_a_r_t_i_n_g _p_o_s_i_t_i_o_n _f_r_o_m _t_h_e _u_s_e_r. _T_h_e_y _k_e_y_s _u, _i, _o, _j, _l, * _m, ,, _a_n_d . _a_r_e _u_s_e_d _f_o_r _m_o_v_i_n_g _t_h_e_i_r _r_e_l_a_t_i_v_e _d_i_r_e_c_t_i_o_n_s _f_r_o_m _t_h_e * _k _k_e_y. _T_h_u_s, _u _m_o_v_e _d_i_a_g_o_n_a_l_l_y _u_p _t_o _t_h_e _l_e_f_t, , _m_o_v_e_s _d_i_r_e_c_t_l_y _d_o_w_n, * _e_t_c. _x _p_l_a_c_e_s _a _p_i_e_c_e _a_t _t_h_e _c_u_r_r_e_n_t _p_o_s_i_t_i_o_n, " " _t_a_k_e_s _i_t _a_w_a_y. * _T_h_e _i_n_p_u_t _c_a_n _a_l_s_o _b_e _f_r_o_m _a _f_i_l_e. _T_h_e _l_i_s_t _i_s _b_u_i_l_t _a_f_t_e_r _t_h_e * _b_o_a_r_d _s_e_t_u_p _i_s _r_e_a_d_y. */ getstartgetstart() { reg _c_h_a_r c; reg _i_n_t x, y; box(stdscr, '|', '_'); /* _b_o_x _i_n _t_h_e _s_c_r_e_e_n */ move(1, 1); /* _m_o_v_e _t_o _u_p_p_e_r _l_e_f_t _c_o_r_n_e_r */ _d_o { refresh(); /* _p_r_i_n_t _c_u_r_r_e_n_t _p_o_s_i_t_i_o_n */ _i_f ((c=getch()) == 'q') _b_r_e_a_k; _s_w_i_t_c_h (c) { _c_a_s_e 'u': _c_a_s_e 'i': _c_a_s_e 'o': _c_a_s_e 'j': _c_a_s_e 'l': _c_a_s_e 'm': _c_a_s_e ',': _c_a_s_e '.': adjustyx(c); _b_r_e_a_k; _c_a_s_e 'f': mvaddstr(0, 0, "File name: "); getstr(buf); readfile(buf); _b_r_e_a_k; _c_a_s_e 'x': addch('X'); _b_r_e_a_k; _c_a_s_e ' ': addch(' '); _b_r_e_a_k; } } _i_f (Head != NULL) /* _s_t_a_r_t _n_e_w _l_i_s_t */ dellist(Head); Head = malloc(_s_i_z_e_o_f (LIST)); /* * _l_o_o_p _t_h_r_o_u_g_h _t_h_e _s_c_r_e_e_n _l_o_o_k_i_n_g _f_o_r '_x'_s, _a_n_d _a_d_d _a _l_i_s_t * _e_l_e_m_e_n_t _f_o_r _e_a_c_h _o_n_e */ _f_o_r (y = 1; y < LINES - 1; y++) _f_o_r (x = 1; x < COLS - 1; x++) { move(y, x); _i_f (inch() == 'x') addlist(y, x); } } /* * _P_r_i_n_t _o_u_t _t_h_e _c_u_r_r_e_n_t _b_o_a_r_d _p_o_s_i_t_i_o_n _f_r_o_m _t_h_e _l_i_n_k_e_d _l_i_s_t */ prboardprboard() { reg LIST *hp; erase(); /* _c_l_e_a_r _o_u_t _l_a_s_t _p_o_s_i_t_i_o_n */ box(stdscr, '|', '_'); /* _b_o_x _i_n _t_h_e _s_c_r_e_e_n */ /* * _g_o _t_h_r_o_u_g_h _t_h_e _l_i_s_t _a_d_d_i_n_g _e_a_c_h _p_i_e_c_e _t_o _t_h_e _n_e_w_l_y * _b_l_a_n_k _b_o_a_r_d */ _f_o_r (hp = Head; hp; hp = hp->next) mvaddch(hp->y, hp->x, 'X'); refresh(); } _3. _M_o_t_i_o_n _o_p_t_i_m_i_z_a_t_i_o_n The following example shows how motion optimization is written on its own. Programs which flit from one place to another without regard for what is already there usually do not need the overhead of both space and time associated with screen updating. They should instead use motion optimiza- tion. _3._1. _T_w_i_n_k_l_e The _t_w_i_n_k_l_e program is a good candidate for simple mo- tion optimization. Here is how it could be written (only the routines that have been changed are shown): mainmain() { reg _c_h_a_r *sp; _c_h_a_r *getenv(); _i_n_t _putchar(), die(); srand(getpid()); /* _i_n_i_t_i_a_l_i_z_e _r_a_n_d_o_m _s_e_q_u_e_n_c_e */ _i_f (isatty(0)) { gettmode(); _i_f (sp=getenv("TERM")) setterm(sp); signal(SIGINT, die); } _e_l_s_e { printf("Need a terminal on %d\n", _tty_ch); exit(1); } _puts(TI); _puts(VS); noecho(); nonl(); tputs(CL, NLINES, _putchar); _f_o_r (;;) { makeboard(); /* _m_a_k_e _t_h_e _b_o_a_r_d _s_e_t_u_p */ puton('*'); /* _p_u_t _o_n '*'_s */ puton(' '); /* _c_o_v_e_r _u_p _w_i_t_h ' '_s */ } } /* * __p_u_t_c_h_a_r _d_e_f_i_n_e_d _f_o_r _t_p_u_t_s() (_a_n_d __p_u_t_s()) */ _putchar_putchar(c) reg _c_h_a_r c; { putchar(c); } putonputon(ch) _c_h_a_r ch; { _s_t_a_t_i_c _i_n_t lasty, lastx; reg LOCS *lp; reg _i_n_t r; reg LOCS *end; LOCS temp; end = &Layout[Numstars]; _f_o_r (lp = Layout; lp < end; lp++) { r = rand() % Numstars; temp = *lp; *lp = Layout[r]; Layout[r] = temp; } _f_o_r (lp = Layout; lp < end; lp++) /* _p_r_e_v_e_n_t _s_c_r_o_l_l_i_n_g */ _i_f (!AM || (lp->y < NLINES - 1 || lp->x < NCOLS - 1)) { mvcur(lasty, lastx, lp->y, lp->x); putchar(ch); lasty = lp->y; _i_f ((lastx = lp->x + 1) >= NCOLS) _i_f (AM) { lastx = 0; lasty++; } _e_l_s_e lastx = NCOLS - 1; } } _C_o_n_t_e_n_t_s 1 Overview ............................................ 1 1.1 Terminology (or, Words You Can Say to Sound Brilliant) ......................................... 1 1.2 Compiling Things ............................... 1 1.3 Screen Updating ................................ 2 1.4 Naming Conventions ............................. 2 2 Variables ........................................... 3 3 Usage ............................................... 4 3.1 Starting up .................................... 4 3.2 The Nitty-Gritty ............................... 5 3.2.1 Output .................................... 5 3.2.2 Input ..................................... 5 3.2.3 Miscellaneous ............................. 6 3.3 Finishing up ................................... 6 4 Cursor Motion Optimization: Standing Alone .......... 6 4.1 Terminal Information ........................... 7 4.2 Movement Optimizations, or, Getting Over Yonder ............................................. 8 5 The Functions ....................................... 8 5.1 Output Functions ............................... 9 5.2 Input Functions ................................ 13 5.3 Miscellaneous Functions ........................ 15 5.4 Details ........................................ 18 _A_p_p_e_n_d_i_x_e_s _A_p_p_e_n_d_i_x _A ............................................ 20 1 Capabilities from termcap ........................... 20 1.1 Disclaimer ..................................... 20 1.2 Overview ....................................... 20 1.3 Variables Set By setterm() ..................... 20 1.4 Variables Set By gettmode() .................... 21 _A_p_p_e_n_d_i_x _B ............................................ 23 1 The WINDOW structure ................................ 23 _A_p_p_e_n_d_i_x _C ............................................ 25 1 Examples ............................................ 25 2 Screen Updating ..................................... 25 2.1 Twinkle ........................................ 25 2.2 Life ........................................... 27 3 Motion optimization ................................. 29 3.1 Twinkle ........................................ 29