Back to index

tetex-bin  3.0
tty_update.c
Go to the documentation of this file.
00001 /****************************************************************************
00002  * Copyright (c) 1998-2003,2004 Free Software Foundation, Inc.              *
00003  *                                                                          *
00004  * Permission is hereby granted, free of charge, to any person obtaining a  *
00005  * copy of this software and associated documentation files (the            *
00006  * "Software"), to deal in the Software without restriction, including      *
00007  * without limitation the rights to use, copy, modify, merge, publish,      *
00008  * distribute, distribute with modifications, sublicense, and/or sell       *
00009  * copies of the Software, and to permit persons to whom the Software is    *
00010  * furnished to do so, subject to the following conditions:                 *
00011  *                                                                          *
00012  * The above copyright notice and this permission notice shall be included  *
00013  * in all copies or substantial portions of the Software.                   *
00014  *                                                                          *
00015  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
00016  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
00017  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
00018  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
00019  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
00020  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
00021  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
00022  *                                                                          *
00023  * Except as contained in this notice, the name(s) of the above copyright   *
00024  * holders shall not be used in advertising or otherwise to promote the     *
00025  * sale, use or other dealings in this Software without prior written       *
00026  * authorization.                                                           *
00027  ****************************************************************************/
00028 
00029 /****************************************************************************
00030  *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
00031  *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
00032  *     and: Thomas E. Dickey                        1996-on                 *
00033  ****************************************************************************/
00034 
00035 /*-----------------------------------------------------------------
00036  *
00037  *     lib_doupdate.c
00038  *
00039  *     The routine doupdate() and its dependents.
00040  *     All physical output is concentrated here (except _nc_outch()
00041   *    in lib_tputs.c).
00042  *
00043  *-----------------------------------------------------------------*/
00044 
00045 #include <curses.priv.h>
00046 
00047 #ifdef __BEOS__
00048 #undef false
00049 #undef true
00050 #include <OS.h>
00051 #endif
00052 
00053 #if defined(TRACE) && HAVE_SYS_TIMES_H && HAVE_TIMES
00054 #define USE_TRACE_TIMES 1
00055 #else
00056 #define USE_TRACE_TIMES 0
00057 #endif
00058 
00059 #if HAVE_SYS_TIME_H && HAVE_SYS_TIME_SELECT
00060 #include <sys/time.h>
00061 #endif
00062 
00063 #if USE_TRACE_TIMES
00064 #include <sys/times.h>
00065 #endif
00066 
00067 #if USE_FUNC_POLL
00068 #elif HAVE_SELECT
00069 #if HAVE_SYS_SELECT_H
00070 #include <sys/select.h>
00071 #endif
00072 #endif
00073 
00074 #include <ctype.h>
00075 #include <term.h>
00076 
00077 MODULE_ID("$Id: tty_update.c,v 1.210 2004/10/16 16:01:40 tom Exp $")
00078 
00079 /*
00080  * This define controls the line-breakout optimization.  Every once in a
00081  * while during screen refresh, we want to check for input and abort the
00082  * update if there's some waiting.  CHECK_INTERVAL controls the number of
00083  * changed lines to be emitted between input checks.
00084  *
00085  * Note: Input-check-and-abort is no longer done if the screen is being
00086  * updated from scratch.  This is a feature, not a bug.
00087  */
00088 #define CHECK_INTERVAL      5
00089 
00090 #define FILL_BCE() (SP->_coloron && !SP->_default_color && !back_color_erase)
00091 
00092 /*
00093  * Enable checking to see if doupdate and friends are tracking the true
00094  * cursor position correctly.  NOTE: this is a debugging hack which will
00095  * work ONLY on ANSI-compatible terminals!
00096  */
00097 /* #define POSITION_DEBUG */
00098 
00099 static inline NCURSES_CH_T ClrBlank(WINDOW *win);
00100 static int ClrBottom(int total);
00101 static void ClearScreen(NCURSES_CH_T blank);
00102 static void ClrUpdate(void);
00103 static void DelChar(int count);
00104 static void InsStr(NCURSES_CH_T * line, int count);
00105 static void TransformLine(int const lineno);
00106 
00107 #ifdef POSITION_DEBUG
00108 /****************************************************************************
00109  *
00110  * Debugging code.  Only works on ANSI-standard terminals.
00111  *
00112  ****************************************************************************/
00113 
00114 static void
00115 position_check(int expected_y, int expected_x, char *legend)
00116 /* check to see if the real cursor position matches the virtual */
00117 {
00118     char buf[20];
00119     char *s;
00120     int y, x;
00121 
00122     if (!_nc_tracing || (expected_y < 0 && expected_x < 0))
00123        return;
00124 
00125     _nc_flush();
00126     memset(buf, '\0', sizeof(buf));
00127     putp("\033[6n");        /* only works on ANSI-compatibles */
00128     _nc_flush();
00129     *(s = buf) = 0;
00130     do {
00131        int ask = sizeof(buf) - 1 - (s - buf);
00132        int got = read(0, s, ask);
00133        if (got == 0)
00134            break;
00135        s += got;
00136     } while (strchr(buf, 'R') == 0);
00137     _tracef("probe returned %s", _nc_visbuf(buf));
00138 
00139     /* try to interpret as a position report */
00140     if (sscanf(buf, "\033[%d;%dR", &y, &x) != 2) {
00141        _tracef("position probe failed in %s", legend);
00142     } else {
00143        if (expected_x < 0)
00144            expected_x = x - 1;
00145        if (expected_y < 0)
00146            expected_y = y - 1;
00147        if (y - 1 != expected_y || x - 1 != expected_x) {
00148            beep();
00149            tputs(tparm("\033[%d;%dH", expected_y + 1, expected_x + 1), 1, _nc_outch);
00150            _tracef("position seen (%d, %d) doesn't match expected one (%d, %d) in %s",
00151                   y - 1, x - 1, expected_y, expected_x, legend);
00152        } else {
00153            _tracef("position matches OK in %s", legend);
00154        }
00155     }
00156 }
00157 #else
00158 #define position_check(expected_y, expected_x, legend)  /* nothing */
00159 #endif /* POSITION_DEBUG */
00160 
00161 /****************************************************************************
00162  *
00163  * Optimized update code
00164  *
00165  ****************************************************************************/
00166 
00167 static inline void
00168 GoTo(int const row, int const col)
00169 {
00170     TR(TRACE_MOVE, ("GoTo(%d, %d) from (%d, %d)",
00171                   row, col, SP->_cursrow, SP->_curscol));
00172 
00173     position_check(SP->_cursrow, SP->_curscol, "GoTo");
00174 
00175     mvcur(SP->_cursrow, SP->_curscol, row, col);
00176     position_check(SP->_cursrow, SP->_curscol, "GoTo2");
00177 }
00178 
00179 static inline void
00180 PutAttrChar(CARG_CH_T ch)
00181 {
00182     int chlen = 1;
00183     NCURSES_CH_T my_ch;
00184     PUTC_DATA;
00185     NCURSES_CH_T tilde;
00186     NCURSES_ATTR_T attr = AttrOfD(ch);
00187 
00188     TR(TRACE_CHARPUT, ("PutAttrChar(%s) at (%d, %d)",
00189                      _tracech_t(ch),
00190                      SP->_cursrow, SP->_curscol));
00191 #if USE_WIDEC_SUPPORT
00192     /*
00193      * If this is not a valid character, there is nothing more to do.
00194      */
00195     if (isnac(CHDEREF(ch)))
00196        return;
00197     /*
00198      * Determine the number of character cells which the 'ch' value will use
00199      * on the screen.  It should be at least one.
00200      */
00201     if ((chlen = wcwidth(CharOf(CHDEREF(ch)))) <= 0) {
00202        static NCURSES_CH_T blank = NewChar(BLANK_TEXT);
00203 
00204        if (is8bits(CharOf(CHDEREF(ch)))
00205            && (isprint(CharOf(CHDEREF(ch)))
00206               || (SP->_legacy_coding && CharOf(CHDEREF(ch)) >= 160))) {
00207            ;
00208        } else {
00209            ch = CHREF(blank);
00210            TR(TRACE_CHARPUT, ("forced to blank"));
00211        }
00212        chlen = 1;
00213     }
00214 #endif
00215 
00216     if ((attr & A_ALTCHARSET)
00217        && SP->_acs_map != 0
00218        && CharOfD(ch) < ACS_LEN) {
00219        my_ch = CHDEREF(ch); /* work around const param */
00220 #if USE_WIDEC_SUPPORT
00221        /*
00222         * This is crude & ugly, but works most of the time.  It checks if the
00223         * acs_chars string specified that we have a mapping for this
00224         * character, and uses the wide-character mapping when we expect the
00225         * normal one to be broken (by mis-design ;-).
00226         */
00227        if (SP->_screen_acs_fix
00228            && SP->_screen_acs_map[CharOf(my_ch)]) {
00229            attr &= ~(A_ALTCHARSET);
00230            my_ch = _nc_wacs[CharOf(my_ch)];
00231        }
00232 #endif
00233        /*
00234         * If we (still) have alternate character set, it is the normal 8bit
00235         * flavor.  The _screen_acs_map[] array tells if the character was
00236         * really in acs_chars, needed because of the way wide/normal line
00237         * drawing flavors are integrated.
00238         */
00239        if (attr & A_ALTCHARSET) {
00240            int j = CharOfD(ch);
00241            chtype temp = UChar(SP->_acs_map[j]);
00242 
00243            if (!(SP->_screen_acs_map[j]))
00244               attr &= ~(A_ALTCHARSET);
00245            if (temp != 0)
00246               SetChar(my_ch, temp, attr);
00247        }
00248        ch = CHREF(my_ch);
00249     }
00250     if (tilde_glitch && (CharOfD(ch) == L('~'))) {
00251        SetChar(tilde, L('`'), attr);
00252        ch = CHREF(tilde);
00253     }
00254 
00255     UpdateAttrs(attr);
00256 #if !USE_WIDEC_SUPPORT
00257     /* FIXME - we do this special case for signal handling, should see how to
00258      * make it work for wide characters.
00259      */
00260     if (SP->_outch != 0) {
00261        SP->_outch(UChar(ch));
00262     } else
00263 #endif
00264     {
00265        PUTC(CHDEREF(ch), SP->_ofp);       /* macro's fastest... */
00266        TRACE_OUTCHARS(1);
00267     }
00268     SP->_curscol += chlen;
00269     if (char_padding) {
00270        TPUTS_TRACE("char_padding");
00271        putp(char_padding);
00272     }
00273 }
00274 
00275 static bool
00276 check_pending(void)
00277 /* check for pending input */
00278 {
00279     bool have_pending = FALSE;
00280 
00281     /*
00282      * Only carry out this check when the flag is zero, otherwise we'll
00283      * have the refreshing slow down drastically (or stop) if there's an
00284      * unread character available.
00285      */
00286     if (SP->_fifohold != 0)
00287        return FALSE;
00288 
00289     if (SP->_checkfd >= 0) {
00290 #if USE_FUNC_POLL
00291        struct pollfd fds[1];
00292        fds[0].fd = SP->_checkfd;
00293        fds[0].events = POLLIN;
00294        if (poll(fds, 1, 0) > 0) {
00295            have_pending = TRUE;
00296        }
00297 #elif defined(__BEOS__)
00298        /*
00299         * BeOS's select() is declared in socket.h, so the configure script does
00300         * not see it.  That's just as well, since that function works only for
00301         * sockets.  This (using snooze and ioctl) was distilled from Be's patch
00302         * for ncurses which uses a separate thread to simulate select().
00303         *
00304         * FIXME: the return values from the ioctl aren't very clear if we get
00305         * interrupted.
00306         */
00307        int n = 0;
00308        int howmany = ioctl(0, 'ichr', &n);
00309        if (howmany >= 0 && n > 0) {
00310            have_pending = TRUE;
00311        }
00312 #elif HAVE_SELECT
00313        fd_set fdset;
00314        struct timeval ktimeout;
00315 
00316        ktimeout.tv_sec =
00317            ktimeout.tv_usec = 0;
00318 
00319        FD_ZERO(&fdset);
00320        FD_SET(SP->_checkfd, &fdset);
00321        if (select(SP->_checkfd + 1, &fdset, NULL, NULL, &ktimeout) != 0) {
00322            have_pending = TRUE;
00323        }
00324 #endif
00325     }
00326     if (have_pending) {
00327        SP->_fifohold = 5;
00328        _nc_flush();
00329     }
00330     return FALSE;
00331 }
00332 
00333 /* put char at lower right corner */
00334 static void
00335 PutCharLR(const ARG_CH_T ch)
00336 {
00337     if (!auto_right_margin) {
00338        /* we can put the char directly */
00339        PutAttrChar(ch);
00340     } else if (enter_am_mode && exit_am_mode) {
00341        /* we can suppress automargin */
00342        TPUTS_TRACE("exit_am_mode");
00343        putp(exit_am_mode);
00344 
00345        PutAttrChar(ch);
00346        SP->_curscol--;
00347        position_check(SP->_cursrow, SP->_curscol, "exit_am_mode");
00348 
00349        TPUTS_TRACE("enter_am_mode");
00350        putp(enter_am_mode);
00351     } else if ((enter_insert_mode && exit_insert_mode)
00352               || insert_character || parm_ich) {
00353        GoTo(screen_lines - 1, screen_columns - 2);
00354        PutAttrChar(ch);
00355        GoTo(screen_lines - 1, screen_columns - 2);
00356        InsStr(newscr->_line[screen_lines - 1].text + screen_columns - 2, 1);
00357     }
00358 }
00359 
00360 static void
00361 wrap_cursor(void)
00362 {
00363     if (eat_newline_glitch) {
00364        /*
00365         * xenl can manifest two different ways.  The vt100
00366         * way is that, when you'd expect the cursor to wrap,
00367         * it stays hung at the right margin (on top of the
00368         * character just emitted) and doesn't wrap until the
00369         * *next* graphic char is emitted.  The c100 way is
00370         * to ignore LF received just after an am wrap.
00371         *
00372         * An aggressive way to handle this would be to
00373         * emit CR/LF after the char and then assume the wrap
00374         * is done, you're on the first position of the next
00375         * line, and the terminal out of its weird state.
00376         * Here it's safe to just tell the code that the
00377         * cursor is in hyperspace and let the next mvcur()
00378         * call straighten things out.
00379         */
00380        SP->_curscol = -1;
00381        SP->_cursrow = -1;
00382     } else if (auto_right_margin) {
00383        SP->_curscol = 0;
00384        SP->_cursrow++;
00385     } else {
00386        SP->_curscol--;
00387     }
00388     position_check(SP->_cursrow, SP->_curscol, "wrap_cursor");
00389 }
00390 
00391 static inline void
00392 PutChar(const ARG_CH_T ch)
00393 /* insert character, handling automargin stuff */
00394 {
00395     if (SP->_cursrow == screen_lines - 1 && SP->_curscol == screen_columns - 1)
00396        PutCharLR(ch);
00397     else
00398        PutAttrChar(ch);
00399 
00400     if (SP->_curscol >= screen_columns)
00401        wrap_cursor();
00402 
00403     position_check(SP->_cursrow, SP->_curscol, "PutChar");
00404 }
00405 
00406 /*
00407  * Check whether the given character can be output by clearing commands.  This
00408  * includes test for being a space and not including any 'bad' attributes, such
00409  * as A_REVERSE.  All attribute flags which don't affect appearance of a space
00410  * or can be output by clearing (A_COLOR in case of bce-terminal) are excluded.
00411  */
00412 static inline bool
00413 can_clear_with(ARG_CH_T ch)
00414 {
00415     if (!back_color_erase && SP->_coloron) {
00416 #if NCURSES_EXT_FUNCS
00417        if (!SP->_default_color)
00418            return FALSE;
00419        if (SP->_default_fg != C_MASK || SP->_default_bg != C_MASK)
00420            return FALSE;
00421        if (AttrOfD(ch) & A_COLOR) {
00422            short fg, bg;
00423            pair_content(PAIR_NUMBER(AttrOfD(ch)), &fg, &bg);
00424            if (fg != C_MASK || bg != C_MASK)
00425               return FALSE;
00426        }
00427 #else
00428        if (AttrOfD(ch) & A_COLOR)
00429            return FALSE;
00430 #endif
00431     }
00432     return (ISBLANK(CHDEREF(ch)) &&
00433            (AttrOfD(ch) & ~(NONBLANK_ATTR | A_COLOR)) == BLANK_ATTR);
00434 }
00435 
00436 /*
00437  * Issue a given span of characters from an array.
00438  * Must be functionally equivalent to:
00439  *     for (i = 0; i < num; i++)
00440  *         PutChar(ntext[i]);
00441  * but can leave the cursor positioned at the middle of the interval.
00442  *
00443  * Returns: 0 - cursor is at the end of interval
00444  *         1 - cursor is somewhere in the middle
00445  *
00446  * This code is optimized using ech and rep.
00447  */
00448 static int
00449 EmitRange(const NCURSES_CH_T * ntext, int num)
00450 {
00451     int i;
00452 
00453     if (erase_chars || repeat_char) {
00454        while (num > 0) {
00455            int runcount;
00456            NCURSES_CH_T ntext0;
00457 
00458            while (num > 1 && !CharEq(ntext[0], ntext[1])) {
00459               PutChar(CHREF(ntext[0]));
00460               ntext++;
00461               num--;
00462            }
00463            ntext0 = ntext[0];
00464            if (num == 1) {
00465               PutChar(CHREF(ntext0));
00466               return 0;
00467            }
00468            runcount = 2;
00469 
00470            while (runcount < num && CharEq(ntext[runcount], ntext0))
00471               runcount++;
00472 
00473            /*
00474             * The cost expression in the middle isn't exactly right.
00475             * _cup_ch_cost is an upper bound on the cost for moving to the
00476             * end of the erased area, but not the cost itself (which we
00477             * can't compute without emitting the move).  This may result
00478             * in erase_chars not getting used in some situations for
00479             * which it would be marginally advantageous.
00480             */
00481            if (erase_chars
00482               && runcount > SP->_ech_cost + SP->_cup_ch_cost
00483               && can_clear_with(CHREF(ntext0))) {
00484               UpdateAttrs(AttrOf(ntext0));
00485               putp(tparm(erase_chars, runcount));
00486 
00487               /*
00488                * If this is the last part of the given interval,
00489                * don't bother moving cursor, since it can be the
00490                * last update on the line.
00491                */
00492               if (runcount < num) {
00493                   GoTo(SP->_cursrow, SP->_curscol + runcount);
00494               } else {
00495                   return 1; /* cursor stays in the middle */
00496               }
00497            } else if (repeat_char && runcount > SP->_rep_cost) {
00498               bool wrap_possible = (SP->_curscol + runcount >= screen_columns);
00499               int rep_count = runcount;
00500 
00501               if (wrap_possible)
00502                   rep_count--;
00503 
00504               UpdateAttrs(AttrOf(ntext0));
00505               tputs(tparm(repeat_char, CharOf(ntext0), rep_count),
00506                     rep_count, _nc_outch);
00507               SP->_curscol += rep_count;
00508 
00509               if (wrap_possible)
00510                   PutChar(CHREF(ntext0));
00511            } else {
00512               for (i = 0; i < runcount; i++)
00513                   PutChar(CHREF(ntext[i]));
00514            }
00515            ntext += runcount;
00516            num -= runcount;
00517        }
00518        return 0;
00519     }
00520 
00521     for (i = 0; i < num; i++)
00522        PutChar(CHREF(ntext[i]));
00523     return 0;
00524 }
00525 
00526 /*
00527  * Output the line in the given range [first .. last]
00528  *
00529  * If there's a run of identical characters that's long enough to justify
00530  * cursor movement, use that also.
00531  *
00532  * Returns: same as EmitRange
00533  */
00534 static int
00535 PutRange(const NCURSES_CH_T * otext,
00536         const NCURSES_CH_T * ntext,
00537         int row,
00538         int first, int last)
00539 {
00540     int i, j, same;
00541 
00542     TR(TRACE_CHARPUT, ("PutRange(%p, %p, %d, %d, %d)",
00543                      otext, ntext, row, first, last));
00544 
00545     if (otext != ntext
00546        && (last - first + 1) > SP->_inline_cost) {
00547        for (j = first, same = 0; j <= last; j++) {
00548            if (!same && isnac(otext[j]))
00549               continue;
00550            if (CharEq(otext[j], ntext[j])) {
00551               same++;
00552            } else {
00553               if (same > SP->_inline_cost) {
00554                   EmitRange(ntext + first, j - same - first);
00555                   GoTo(row, first = j);
00556               }
00557               same = 0;
00558            }
00559        }
00560        i = EmitRange(ntext + first, j - same - first);
00561        /*
00562         * Always return 1 for the next GoTo() after a PutRange() if we found
00563         * identical characters at end of interval
00564         */
00565        return (same == 0 ? i : 1);
00566     }
00567     return EmitRange(ntext + first, last - first + 1);
00568 }
00569 
00570 /* leave unbracketed here so 'indent' works */
00571 #define MARK_NOCHANGE(win,row) \
00572               win->_line[row].firstchar = _NOCHANGE; \
00573               win->_line[row].lastchar = _NOCHANGE; \
00574               if_USE_SCROLL_HINTS(win->_line[row].oldindex = row)
00575 
00576 NCURSES_EXPORT(int)
00577 doupdate(void)
00578 {
00579     int i;
00580     int nonempty;
00581 #if USE_TRACE_TIMES
00582     struct tms before, after;
00583 #endif /* USE_TRACE_TIMES */
00584 
00585     T((T_CALLED("doupdate()")));
00586 
00587 #ifdef TRACE
00588     if (_nc_tracing & TRACE_UPDATE) {
00589        if (curscr->_clear)
00590            _tracef("curscr is clear");
00591        else
00592            _tracedump("curscr", curscr);
00593        _tracedump("newscr", newscr);
00594     }
00595 #endif /* TRACE */
00596 
00597     _nc_signal_handler(FALSE);
00598 
00599     if (SP->_fifohold)
00600        SP->_fifohold--;
00601 
00602 #if USE_SIZECHANGE
00603     if (SP->_endwin || SP->_sig_winch) {
00604        /*
00605         * This is a transparent extension:  XSI does not address it,
00606         * and applications need not know that ncurses can do it.
00607         *
00608         * Check if the terminal size has changed while curses was off
00609         * (this can happen in an xterm, for example), and resize the
00610         * ncurses data structures accordingly.
00611         */
00612        _nc_update_screensize();
00613     }
00614 #endif
00615 
00616     if (SP->_endwin) {
00617 
00618        T(("coming back from shell mode"));
00619        reset_prog_mode();
00620 
00621        _nc_mvcur_resume();
00622        _nc_screen_resume();
00623        SP->_mouse_resume(SP);
00624 
00625        SP->_endwin = FALSE;
00626     }
00627 #if USE_TRACE_TIMES
00628     /* zero the metering machinery */
00629     _nc_outchars = 0;
00630     (void) times(&before);
00631 #endif /* USE_TRACE_TIMES */
00632 
00633     /*
00634      * This is the support for magic-cookie terminals.  The
00635      * theory: we scan the virtual screen looking for attribute
00636      * turnons.  Where we find one, check to make sure it's
00637      * realizable by seeing if the required number of
00638      * un-attributed blanks are present before and after the
00639      * attributed range; try to shift the range boundaries over
00640      * blanks (not changing the screen display) so this becomes
00641      * true.  If it is, shift the beginning attribute change
00642      * appropriately (the end one, if we've gotten this far, is
00643      * guaranteed room for its cookie). If not, nuke the added
00644      * attributes out of the span.
00645      */
00646 #if USE_XMC_SUPPORT
00647     if (magic_cookie_glitch > 0) {
00648        int j, k;
00649        attr_t rattr = A_NORMAL;
00650 
00651        for (i = 0; i < screen_lines; i++) {
00652            for (j = 0; j < screen_columns; j++) {
00653               bool failed = FALSE;
00654               attr_t turnon = AttrOf(newscr->_line[i].text[j]) & ~rattr;
00655 
00656               /* is an attribute turned on here? */
00657               if (turnon == 0) {
00658                   rattr = AttrOf(newscr->_line[i].text[j]);
00659                   continue;
00660               }
00661 
00662               TR(TRACE_ATTRS, ("At (%d, %d): from %s...", i, j, _traceattr(rattr)));
00663               TR(TRACE_ATTRS, ("...to %s", _traceattr(turnon)));
00664 
00665               /*
00666                * If the attribute change location is a blank with a
00667                * "safe" attribute, undo the attribute turnon.  This may
00668                * ensure there's enough room to set the attribute before
00669                * the first non-blank in the run.
00670                */
00671 #define SAFE(a)      (!((a) & (attr_t)~NONBLANK_ATTR))
00672               if (ISBLANK(newscr->_line[i].text[j]) && SAFE(turnon)) {
00673                   RemAttr(newscr->_line[i].text[j], turnon);
00674                   continue;
00675               }
00676 
00677               /* check that there's enough room at start of span */
00678               for (k = 1; k <= magic_cookie_glitch; k++) {
00679                   if (j - k < 0
00680                      || !ISBLANK(newscr->_line[i].text[j - k])
00681                      || !SAFE(AttrOf(newscr->_line[i].text[j - k])))
00682                      failed = TRUE;
00683               }
00684               if (!failed) {
00685                   bool end_onscreen = FALSE;
00686                   int m, n = j;
00687 
00688                   /* find end of span, if it's onscreen */
00689                   for (m = i; m < screen_lines; m++) {
00690                      for (; n < screen_columns; n++) {
00691                          if (AttrOf(newscr->_line[m].text[n]) == rattr) {
00692                             end_onscreen = TRUE;
00693                             TR(TRACE_ATTRS,
00694                                ("Range attributed with %s ends at (%d, %d)",
00695                                 _traceattr(turnon), m, n));
00696                             goto foundit;
00697                          }
00698                      }
00699                      n = 0;
00700                   }
00701                   TR(TRACE_ATTRS,
00702                      ("Range attributed with %s ends offscreen",
00703                      _traceattr(turnon)));
00704                 foundit:;
00705 
00706                   if (end_onscreen) {
00707                      NCURSES_CH_T *lastline = newscr->_line[m].text;
00708 
00709                      /*
00710                       * If there are safely-attributed blanks at the
00711                       * end of the range, shorten the range.  This will
00712                       * help ensure that there is enough room at end
00713                       * of span.
00714                       */
00715                      while (n >= 0
00716                             && ISBLANK(lastline[n])
00717                             && SAFE(AttrOf(lastline[n])))
00718                          RemAttr(lastline[n--], turnon);
00719 
00720                      /* check that there's enough room at end of span */
00721                      for (k = 1; k <= magic_cookie_glitch; k++)
00722                          if (n + k >= screen_columns
00723                             || !ISBLANK(lastline[n + k])
00724                             || !SAFE(AttrOf(lastline[n + k])))
00725                             failed = TRUE;
00726                   }
00727               }
00728 
00729               if (failed) {
00730                   int p, q = j;
00731 
00732                   TR(TRACE_ATTRS,
00733                      ("Clearing %s beginning at (%d, %d)",
00734                      _traceattr(turnon), i, j));
00735 
00736                   /* turn off new attributes over span */
00737                   for (p = i; p < screen_lines; p++) {
00738                      for (; q < screen_columns; q++) {
00739                          if (AttrOf(newscr->_line[p].text[q]) == rattr)
00740                             goto foundend;
00741                          RemAttr(newscr->_line[p].text[q], turnon);
00742                      }
00743                      q = 0;
00744                   }
00745                 foundend:;
00746               } else {
00747                   TR(TRACE_ATTRS,
00748                      ("Cookie space for %s found before (%d, %d)",
00749                      _traceattr(turnon), i, j));
00750 
00751                   /*
00752                    * back up the start of range so there's room
00753                    * for cookies before the first nonblank character
00754                    */
00755                   for (k = 1; k <= magic_cookie_glitch; k++)
00756                      AddAttr(newscr->_line[i].text[j - k], turnon);
00757               }
00758 
00759               rattr = AttrOf(newscr->_line[i].text[j]);
00760            }
00761        }
00762 
00763 #ifdef TRACE
00764        /* show altered highlights after magic-cookie check */
00765        if (_nc_tracing & TRACE_UPDATE) {
00766            _tracef("After magic-cookie check...");
00767            _tracedump("newscr", newscr);
00768        }
00769 #endif /* TRACE */
00770     }
00771 #endif /* USE_XMC_SUPPORT */
00772 
00773     nonempty = 0;
00774     if (curscr->_clear || newscr->_clear) {      /* force refresh ? */
00775        TR(TRACE_UPDATE, ("clearing and updating from scratch"));
00776        ClrUpdate();
00777        curscr->_clear = FALSE;     /* reset flag */
00778        newscr->_clear = FALSE;     /* reset flag */
00779     } else {
00780        int changedlines = CHECK_INTERVAL;
00781 
00782        if (check_pending())
00783            goto cleanup;
00784 
00785        nonempty = min(screen_lines, newscr->_maxy + 1);
00786 
00787        if (SP->_scrolling) {
00788            _nc_scroll_optimize();
00789        }
00790 
00791        nonempty = ClrBottom(nonempty);
00792 
00793        TR(TRACE_UPDATE, ("Transforming lines, nonempty %d", nonempty));
00794        for (i = 0; i < nonempty; i++) {
00795            /*
00796             * Here is our line-breakout optimization.
00797             */
00798            if (changedlines == CHECK_INTERVAL) {
00799               if (check_pending())
00800                   goto cleanup;
00801               changedlines = 0;
00802            }
00803 
00804            /*
00805             * newscr->line[i].firstchar is normally set
00806             * by wnoutrefresh.  curscr->line[i].firstchar
00807             * is normally set by _nc_scroll_window in the
00808             * vertical-movement optimization code,
00809             */
00810            if (newscr->_line[i].firstchar != _NOCHANGE
00811               || curscr->_line[i].firstchar != _NOCHANGE) {
00812               TransformLine(i);
00813               changedlines++;
00814            }
00815 
00816            /* mark line changed successfully */
00817            if (i <= newscr->_maxy) {
00818               MARK_NOCHANGE(newscr, i);
00819            }
00820            if (i <= curscr->_maxy) {
00821               MARK_NOCHANGE(curscr, i);
00822            }
00823        }
00824     }
00825 
00826     /* put everything back in sync */
00827     for (i = nonempty; i <= newscr->_maxy; i++) {
00828        MARK_NOCHANGE(newscr, i);
00829     }
00830     for (i = nonempty; i <= curscr->_maxy; i++) {
00831        MARK_NOCHANGE(curscr, i);
00832     }
00833 
00834     if (!newscr->_leaveok) {
00835        curscr->_curx = newscr->_curx;
00836        curscr->_cury = newscr->_cury;
00837 
00838        GoTo(curscr->_cury, curscr->_curx);
00839     }
00840 
00841   cleanup:
00842     /*
00843      * Keep the physical screen in normal mode in case we get other
00844      * processes writing to the screen.
00845      */
00846     UpdateAttrs(A_NORMAL);
00847 
00848     _nc_flush();
00849     curscr->_attrs = newscr->_attrs;
00850 
00851 #if USE_TRACE_TIMES
00852     (void) times(&after);
00853     TR(TRACE_TIMES,
00854        ("Update cost: %ld chars, %ld clocks system time, %ld clocks user time",
00855        _nc_outchars,
00856        (long) (after.tms_stime - before.tms_stime),
00857        (long) (after.tms_utime - before.tms_utime)));
00858 #endif /* USE_TRACE_TIMES */
00859 
00860     _nc_signal_handler(TRUE);
00861 
00862     returnCode(OK);
00863 }
00864 
00865 /*
00866  *     ClrBlank(win)
00867  *
00868  *     Returns the attributed character that corresponds to the "cleared"
00869  *     screen.  If the terminal has the back-color-erase feature, this will be
00870  *     colored according to the wbkgd() call.
00871  *
00872  *     We treat 'curscr' specially because it isn't supposed to be set directly
00873  *     in the wbkgd() call.  Assume 'stdscr' for this case.
00874  */
00875 #define BCE_ATTRS (A_NORMAL|A_COLOR)
00876 #define BCE_BKGD(win) (((win) == curscr ? stdscr : (win))->_nc_bkgd)
00877 
00878 static inline NCURSES_CH_T
00879 ClrBlank(WINDOW *win)
00880 {
00881     NCURSES_CH_T blank = NewChar(BLANK_TEXT);
00882     if (back_color_erase)
00883        AddAttr(blank, (AttrOf(BCE_BKGD(win)) & BCE_ATTRS));
00884     return blank;
00885 }
00886 
00887 /*
00888 **     ClrUpdate()
00889 **
00890 **     Update by clearing and redrawing the entire screen.
00891 **
00892 */
00893 
00894 static void
00895 ClrUpdate(void)
00896 {
00897     int i;
00898     NCURSES_CH_T blank = ClrBlank(stdscr);
00899     int nonempty = min(screen_lines, newscr->_maxy + 1);
00900 
00901     TR(TRACE_UPDATE, ("ClrUpdate() called"));
00902 
00903     ClearScreen(blank);
00904 
00905     TR(TRACE_UPDATE, ("updating screen from scratch"));
00906 
00907     nonempty = ClrBottom(nonempty);
00908 
00909     for (i = 0; i < nonempty; i++)
00910        TransformLine(i);
00911 }
00912 
00913 /*
00914 **     ClrToEOL(blank)
00915 **
00916 **     Clear to end of current line, starting at the cursor position
00917 */
00918 
00919 static void
00920 ClrToEOL(NCURSES_CH_T blank, bool needclear)
00921 {
00922     int j;
00923 
00924     if (curscr != 0
00925        && SP->_cursrow >= 0) {
00926        for (j = SP->_curscol; j < screen_columns; j++) {
00927            if (j >= 0) {
00928               NCURSES_CH_T *cp = &(curscr->_line[SP->_cursrow].text[j]);
00929 
00930               if (!CharEq(*cp, blank)) {
00931                   *cp = blank;
00932                   needclear = TRUE;
00933               }
00934            }
00935        }
00936     } else {
00937        needclear = TRUE;
00938     }
00939 
00940     if (needclear) {
00941        UpdateAttrs(AttrOf(blank));
00942        TPUTS_TRACE("clr_eol");
00943        if (clr_eol && SP->_el_cost <= (screen_columns - SP->_curscol)) {
00944            putp(clr_eol);
00945        } else {
00946            int count = (screen_columns - SP->_curscol);
00947            while (count-- > 0)
00948               PutChar(CHREF(blank));
00949        }
00950     }
00951 }
00952 
00953 /*
00954 **     ClrToEOS(blank)
00955 **
00956 **     Clear to end of screen, starting at the cursor position
00957 */
00958 
00959 static void
00960 ClrToEOS(NCURSES_CH_T blank)
00961 {
00962     int row, col;
00963 
00964     row = SP->_cursrow;
00965     col = SP->_curscol;
00966 
00967     UpdateAttrs(AttrOf(blank));
00968     TPUTS_TRACE("clr_eos");
00969     tputs(clr_eos, screen_lines - row, _nc_outch);
00970 
00971     while (col < screen_columns)
00972        curscr->_line[row].text[col++] = blank;
00973 
00974     for (row++; row < screen_lines; row++) {
00975        for (col = 0; col < screen_columns; col++)
00976            curscr->_line[row].text[col] = blank;
00977     }
00978 }
00979 
00980 /*
00981  *     ClrBottom(total)
00982  *
00983  *     Test if clearing the end of the screen would satisfy part of the
00984  *     screen-update.  Do this by scanning backwards through the lines in the
00985  *     screen, checking if each is blank, and one or more are changed.
00986  */
00987 static int
00988 ClrBottom(int total)
00989 {
00990     int row;
00991     int col;
00992     int top = total;
00993     int last = min(screen_columns, newscr->_maxx + 1);
00994     NCURSES_CH_T blank = newscr->_line[total - 1].text[last - 1];
00995     bool ok;
00996 
00997     if (clr_eos && can_clear_with(CHREF(blank))) {
00998 
00999        for (row = total - 1; row >= 0; row--) {
01000            for (col = 0, ok = TRUE; ok && col < last; col++) {
01001               ok = (CharEq(newscr->_line[row].text[col], blank));
01002            }
01003            if (!ok)
01004               break;
01005 
01006            for (col = 0; ok && col < last; col++) {
01007               ok = (CharEq(curscr->_line[row].text[col], blank));
01008            }
01009            if (!ok)
01010               top = row;
01011        }
01012 
01013        /* don't use clr_eos for just one line if clr_eol available */
01014        if (top < total) {
01015            GoTo(top, 0);
01016            ClrToEOS(blank);
01017            if (SP->oldhash && SP->newhash) {
01018               for (row = top; row < screen_lines; row++)
01019                   SP->oldhash[row] = SP->newhash[row];
01020            }
01021        }
01022     }
01023     return top;
01024 }
01025 
01026 #if USE_XMC_SUPPORT
01027 #if USE_WIDEC_SUPPORT
01028 static inline bool
01029 check_xmc_transition(NCURSES_CH_T * a, NCURSES_CH_T * b)
01030 {
01031     if (((a->attr ^ b->attr) & ~(a->attr) & SP->_xmc_triggers) != 0) {
01032        return TRUE;
01033     }
01034     return FALSE;
01035 }
01036 #define xmc_turn_on(a,b) check_xmc_transition(&(a), &(b))
01037 #else
01038 #define xmc_turn_on(a,b) ((((a)^(b)) & ~(a) & SP->_xmc_triggers) != 0)
01039 #endif
01040 
01041 #define xmc_new(r,c) newscr->_line[r].text[c]
01042 #define xmc_turn_off(a,b) xmc_turn_on(b,a)
01043 #endif /* USE_XMC_SUPPORT */
01044 
01045 /*
01046 **     TransformLine(lineno)
01047 **
01048 **     Transform the given line in curscr to the one in newscr, using
01049 **     Insert/Delete Character if _nc_idcok && has_ic().
01050 **
01051 **            firstChar = position of first different character in line
01052 **            oLastChar = position of last different character in old line
01053 **            nLastChar = position of last different character in new line
01054 **
01055 **            move to firstChar
01056 **            overwrite chars up to min(oLastChar, nLastChar)
01057 **            if oLastChar < nLastChar
01058 **                   insert newLine[oLastChar+1..nLastChar]
01059 **            else
01060 **                   delete oLastChar - nLastChar spaces
01061 */
01062 
01063 static void
01064 TransformLine(int const lineno)
01065 {
01066     int firstChar, oLastChar, nLastChar;
01067     NCURSES_CH_T *newLine = newscr->_line[lineno].text;
01068     NCURSES_CH_T *oldLine = curscr->_line[lineno].text;
01069     int n;
01070     bool attrchanged = FALSE;
01071 
01072     TR(TRACE_UPDATE, ("TransformLine(%d) called", lineno));
01073 
01074     /* copy new hash value to old one */
01075     if (SP->oldhash && SP->newhash)
01076        SP->oldhash[lineno] = SP->newhash[lineno];
01077 
01078 #define ColorOf(n) (AttrOf(n) & A_COLOR)
01079 #define unColor(n) (AttrOf(n) & ALL_BUT_COLOR)
01080     /*
01081      * If we have colors, there is the possibility of having two color pairs
01082      * that display as the same colors.  For instance, Lynx does this.  Check
01083      * for this case, and update the old line with the new line's colors when
01084      * they are equivalent.
01085      */
01086     if (SP->_coloron) {
01087        attr_t oldColor;
01088        attr_t newColor;
01089        int oldPair;
01090        int newPair;
01091 
01092        for (n = 0; n < screen_columns; n++) {
01093            if (!CharEq(newLine[n], oldLine[n])) {
01094               oldColor = ColorOf(oldLine[n]);
01095               newColor = ColorOf(newLine[n]);
01096               if (oldColor != newColor
01097                   && unColor(oldLine[n]) == unColor(newLine[n])) {
01098                   oldPair = PAIR_NUMBER(oldColor);
01099                   newPair = PAIR_NUMBER(newColor);
01100                   if (oldPair < COLOR_PAIRS
01101                      && newPair < COLOR_PAIRS
01102                      && SP->_color_pairs[oldPair] == SP->_color_pairs[newPair]) {
01103                      RemAttr(oldLine[n], A_COLOR);
01104                      AddAttr(oldLine[n], ColorOf(newLine[n]));
01105                   }
01106               }
01107            }
01108        }
01109     }
01110 
01111     if (ceol_standout_glitch && clr_eol) {
01112        firstChar = 0;
01113        while (firstChar < screen_columns) {
01114            if (AttrOf(newLine[firstChar]) != AttrOf(oldLine[firstChar])) {
01115               attrchanged = TRUE;
01116               break;
01117            }
01118            firstChar++;
01119        }
01120     }
01121 
01122     firstChar = 0;
01123 
01124     if (attrchanged) {             /* we may have to disregard the whole line */
01125        GoTo(lineno, firstChar);
01126        ClrToEOL(ClrBlank(curscr), FALSE);
01127        PutRange(oldLine, newLine, lineno, 0, (screen_columns - 1));
01128 #if USE_XMC_SUPPORT
01129 
01130        /*
01131         * This is a very simple loop to paint characters which may have the
01132         * magic cookie glitch embedded.  It doesn't know much about video
01133         * attributes which are continued from one line to the next.  It
01134         * assumes that we have filtered out requests for attribute changes
01135         * that do not get mapped to blank positions.
01136         *
01137         * FIXME: we are not keeping track of where we put the cookies, so this
01138         * will work properly only once, since we may overwrite a cookie in a
01139         * following operation.
01140         */
01141     } else if (magic_cookie_glitch > 0) {
01142        GoTo(lineno, firstChar);
01143        for (n = 0; n < screen_columns; n++) {
01144            int m = n + magic_cookie_glitch;
01145 
01146            /* check for turn-on:
01147             * If we are writing an attributed blank, where the
01148             * previous cell is not attributed.
01149             */
01150            if (ISBLANK(newLine[n])
01151               && ((n > 0
01152                    && xmc_turn_on(newLine[n - 1], newLine[n]))
01153                   || (n == 0
01154                      && lineno > 0
01155                      && xmc_turn_on(xmc_new(lineno - 1, screen_columns - 1),
01156                                    newLine[n])))) {
01157               n = m;
01158            }
01159 
01160            PutChar(CHREF(newLine[n]));
01161 
01162            /* check for turn-off:
01163             * If we are writing an attributed non-blank, where the
01164             * next cell is blank, and not attributed.
01165             */
01166            if (!ISBLANK(newLine[n])
01167               && ((n + 1 < screen_columns
01168                    && xmc_turn_off(newLine[n], newLine[n + 1]))
01169                   || (n + 1 >= screen_columns
01170                      && lineno + 1 < screen_lines
01171                      && xmc_turn_off(newLine[n], xmc_new(lineno + 1, 0))))) {
01172               n = m;
01173            }
01174 
01175        }
01176 #endif
01177     } else {
01178        NCURSES_CH_T blank;
01179 
01180        /* it may be cheap to clear leading whitespace with clr_bol */
01181        blank = newLine[0];
01182        if (clr_bol && can_clear_with(CHREF(blank))) {
01183            int oFirstChar, nFirstChar;
01184 
01185            for (oFirstChar = 0; oFirstChar < screen_columns; oFirstChar++)
01186               if (!CharEq(oldLine[oFirstChar], blank))
01187                   break;
01188            for (nFirstChar = 0; nFirstChar < screen_columns; nFirstChar++)
01189               if (!CharEq(newLine[nFirstChar], blank))
01190                   break;
01191 
01192            if (nFirstChar == oFirstChar) {
01193               firstChar = nFirstChar;
01194               /* find the first differing character */
01195               while (firstChar < screen_columns
01196                      && CharEq(newLine[firstChar], oldLine[firstChar]))
01197                   firstChar++;
01198            } else if (oFirstChar > nFirstChar) {
01199               firstChar = nFirstChar;
01200            } else {         /* oFirstChar < nFirstChar */
01201               firstChar = oFirstChar;
01202               if (SP->_el1_cost < nFirstChar - oFirstChar) {
01203                   if (nFirstChar >= screen_columns
01204                      && SP->_el_cost <= SP->_el1_cost) {
01205                      GoTo(lineno, 0);
01206                      UpdateAttrs(AttrOf(blank));
01207                      TPUTS_TRACE("clr_eol");
01208                      putp(clr_eol);
01209                   } else {
01210                      GoTo(lineno, nFirstChar - 1);
01211                      UpdateAttrs(AttrOf(blank));
01212                      TPUTS_TRACE("clr_bol");
01213                      putp(clr_bol);
01214                   }
01215 
01216                   while (firstChar < nFirstChar)
01217                      oldLine[firstChar++] = blank;
01218               }
01219            }
01220        } else {
01221            /* find the first differing character */
01222            while (firstChar < screen_columns
01223                  && CharEq(newLine[firstChar], oldLine[firstChar]))
01224               firstChar++;
01225        }
01226        /* if there wasn't one, we're done */
01227        if (firstChar >= screen_columns)
01228            return;
01229 
01230        blank = newLine[screen_columns - 1];
01231 
01232        if (!can_clear_with(CHREF(blank))) {
01233            /* find the last differing character */
01234            nLastChar = screen_columns - 1;
01235 
01236            while (nLastChar > firstChar
01237                  && CharEq(newLine[nLastChar], oldLine[nLastChar]))
01238               nLastChar--;
01239 
01240            if (nLastChar >= firstChar) {
01241               GoTo(lineno, firstChar);
01242               PutRange(oldLine, newLine, lineno, firstChar, nLastChar);
01243               memcpy(oldLine + firstChar,
01244                      newLine + firstChar,
01245                      (nLastChar - firstChar + 1) * sizeof(NCURSES_CH_T));
01246            }
01247            return;
01248        }
01249 
01250        /* find last non-blank character on old line */
01251        oLastChar = screen_columns - 1;
01252        while (oLastChar > firstChar && CharEq(oldLine[oLastChar], blank))
01253            oLastChar--;
01254 
01255        /* find last non-blank character on new line */
01256        nLastChar = screen_columns - 1;
01257        while (nLastChar > firstChar && CharEq(newLine[nLastChar], blank))
01258            nLastChar--;
01259 
01260        if ((nLastChar == firstChar)
01261            && (SP->_el_cost < (oLastChar - nLastChar))) {
01262            GoTo(lineno, firstChar);
01263            if (!CharEq(newLine[firstChar], blank))
01264               PutChar(CHREF(newLine[firstChar]));
01265            ClrToEOL(blank, FALSE);
01266        } else if ((nLastChar != oLastChar)
01267                  && (!CharEq(newLine[nLastChar], oldLine[oLastChar])
01268                      || !(_nc_idcok && has_ic()))) {
01269            GoTo(lineno, firstChar);
01270            if ((oLastChar - nLastChar) > SP->_el_cost) {
01271               if (PutRange(oldLine, newLine, lineno, firstChar, nLastChar))
01272                   GoTo(lineno, nLastChar + 1);
01273               ClrToEOL(blank, FALSE);
01274            } else {
01275               n = max(nLastChar, oLastChar);
01276               PutRange(oldLine, newLine, lineno, firstChar, n);
01277            }
01278        } else {
01279            int nLastNonblank = nLastChar;
01280            int oLastNonblank = oLastChar;
01281 
01282            /* find the last characters that really differ */
01283            /* can be -1 if no characters differ */
01284            while (CharEq(newLine[nLastChar], oldLine[oLastChar])) {
01285               /* don't split a wide char */
01286               if (isnac(newLine[nLastChar]) &&
01287                   !CharEq(newLine[nLastChar - 1], oldLine[oLastChar - 1]))
01288                   break;
01289               nLastChar--;
01290               oLastChar--;
01291               if (nLastChar == -1 || oLastChar == -1)
01292                   break;
01293            }
01294 
01295            n = min(oLastChar, nLastChar);
01296            if (n >= firstChar) {
01297               GoTo(lineno, firstChar);
01298               PutRange(oldLine, newLine, lineno, firstChar, n);
01299            }
01300 
01301            if (oLastChar < nLastChar) {
01302               int m = max(nLastNonblank, oLastNonblank);
01303               GoTo(lineno, n + 1);
01304               if (InsCharCost(nLastChar - oLastChar)
01305                   > (m - n)) {
01306                   PutRange(oldLine, newLine, lineno, n + 1, m);
01307               } else {
01308                   InsStr(&newLine[n + 1], nLastChar - oLastChar);
01309               }
01310            } else if (oLastChar > nLastChar) {
01311               GoTo(lineno, n + 1);
01312               if (DelCharCost(oLastChar - nLastChar)
01313                   > SP->_el_cost + nLastNonblank - (n + 1)) {
01314                   if (PutRange(oldLine, newLine, lineno,
01315                              n + 1, nLastNonblank))
01316                      GoTo(lineno, nLastNonblank + 1);
01317                   ClrToEOL(blank, FALSE);
01318               } else {
01319                   /*
01320                    * The delete-char sequence will
01321                    * effectively shift in blanks from the
01322                    * right margin of the screen.  Ensure
01323                    * that they are the right color by
01324                    * setting the video attributes from
01325                    * the last character on the row.
01326                    */
01327                   UpdateAttrs(AttrOf(blank));
01328                   DelChar(oLastChar - nLastChar);
01329               }
01330            }
01331        }
01332     }
01333 
01334     /* update the code's internal representation */
01335     if (screen_columns > firstChar)
01336        memcpy(oldLine + firstChar,
01337               newLine + firstChar,
01338               (screen_columns - firstChar) * sizeof(NCURSES_CH_T));
01339 }
01340 
01341 /*
01342 **     ClearScreen(blank)
01343 **
01344 **     Clear the physical screen and put cursor at home
01345 **
01346 */
01347 
01348 static void
01349 ClearScreen(NCURSES_CH_T blank)
01350 {
01351     int i, j;
01352     bool fast_clear = (clear_screen || clr_eos || clr_eol);
01353 
01354     TR(TRACE_UPDATE, ("ClearScreen() called"));
01355 
01356 #if NCURSES_EXT_FUNCS
01357     if (SP->_coloron
01358        && !SP->_default_color) {
01359        _nc_do_color((int) COLOR_PAIR(SP->_current_attr), 0, FALSE, _nc_outch);
01360        if (!back_color_erase) {
01361            fast_clear = FALSE;
01362        }
01363     }
01364 #endif
01365 
01366     if (fast_clear) {
01367        if (clear_screen) {
01368            UpdateAttrs(AttrOf(blank));
01369            TPUTS_TRACE("clear_screen");
01370            putp(clear_screen);
01371            SP->_cursrow = SP->_curscol = 0;
01372            position_check(SP->_cursrow, SP->_curscol, "ClearScreen");
01373        } else if (clr_eos) {
01374            SP->_cursrow = SP->_curscol = -1;
01375            GoTo(0, 0);
01376 
01377            UpdateAttrs(AttrOf(blank));
01378            TPUTS_TRACE("clr_eos");
01379            tputs(clr_eos, screen_lines, _nc_outch);
01380        } else if (clr_eol) {
01381            SP->_cursrow = SP->_curscol = -1;
01382 
01383            UpdateAttrs(AttrOf(blank));
01384            for (i = 0; i < screen_lines; i++) {
01385               GoTo(i, 0);
01386               TPUTS_TRACE("clr_eol");
01387               putp(clr_eol);
01388            }
01389            GoTo(0, 0);
01390        }
01391     } else {
01392        UpdateAttrs(AttrOf(blank));
01393        for (i = 0; i < screen_lines; i++) {
01394            GoTo(i, 0);
01395            for (j = 0; j < screen_columns; j++)
01396               PutChar(CHREF(blank));
01397        }
01398        GoTo(0, 0);
01399     }
01400 
01401     for (i = 0; i < screen_lines; i++) {
01402        for (j = 0; j < screen_columns; j++)
01403            curscr->_line[i].text[j] = blank;
01404     }
01405 
01406     TR(TRACE_UPDATE, ("screen cleared"));
01407 }
01408 
01409 /*
01410 **     InsStr(line, count)
01411 **
01412 **     Insert the count characters pointed to by line.
01413 **
01414 */
01415 
01416 static void
01417 InsStr(NCURSES_CH_T * line, int count)
01418 {
01419     TR(TRACE_UPDATE, ("InsStr(%p,%d) called", line, count));
01420 
01421     /* Prefer parm_ich as it has the smallest cost - no need to shift
01422      * the whole line on each character. */
01423     /* The order must match that of InsCharCost. */
01424     if (parm_ich) {
01425        TPUTS_TRACE("parm_ich");
01426        tputs(tparm(parm_ich, count), count, _nc_outch);
01427        while (count) {
01428            PutAttrChar(CHREF(*line));
01429            line++;
01430            count--;
01431        }
01432     } else if (enter_insert_mode && exit_insert_mode) {
01433        TPUTS_TRACE("enter_insert_mode");
01434        putp(enter_insert_mode);
01435        while (count) {
01436            PutAttrChar(CHREF(*line));
01437            if (insert_padding) {
01438               TPUTS_TRACE("insert_padding");
01439               putp(insert_padding);
01440            }
01441            line++;
01442            count--;
01443        }
01444        TPUTS_TRACE("exit_insert_mode");
01445        putp(exit_insert_mode);
01446     } else {
01447        while (count) {
01448            TPUTS_TRACE("insert_character");
01449            putp(insert_character);
01450            PutAttrChar(CHREF(*line));
01451            if (insert_padding) {
01452               TPUTS_TRACE("insert_padding");
01453               putp(insert_padding);
01454            }
01455            line++;
01456            count--;
01457        }
01458     }
01459     position_check(SP->_cursrow, SP->_curscol, "InsStr");
01460 }
01461 
01462 /*
01463 **     DelChar(count)
01464 **
01465 **     Delete count characters at current position
01466 **
01467 */
01468 
01469 static void
01470 DelChar(int count)
01471 {
01472     int n;
01473 
01474     TR(TRACE_UPDATE, ("DelChar(%d) called, position = (%d,%d)", count,
01475                     newscr->_cury, newscr->_curx));
01476 
01477     if (parm_dch) {
01478        TPUTS_TRACE("parm_dch");
01479        tputs(tparm(parm_dch, count), count, _nc_outch);
01480     } else {
01481        for (n = 0; n < count; n++) {
01482            TPUTS_TRACE("delete_character");
01483            putp(delete_character);
01484        }
01485     }
01486 }
01487 
01488 /*
01489  * Physical-scrolling support
01490  *
01491  * This code was adapted from Keith Bostic's hardware scrolling
01492  * support for 4.4BSD curses.  I (esr) translated it to use terminfo
01493  * capabilities, narrowed the call interface slightly, and cleaned
01494  * up some convoluted tests.  I also added support for the memory_above
01495  * memory_below, and non_dest_scroll_region capabilities.
01496  *
01497  * For this code to work, we must have either
01498  * change_scroll_region and scroll forward/reverse commands, or
01499  * insert and delete line capabilities.
01500  * When the scrolling region has been set, the cursor has to
01501  * be at the last line of the region to make the scroll up
01502  * happen, or on the first line of region to scroll down.
01503  *
01504  * This code makes one aesthetic decision in the opposite way from
01505  * BSD curses.  BSD curses preferred pairs of il/dl operations
01506  * over scrolls, allegedly because il/dl looked faster.  We, on
01507  * the other hand, prefer scrolls because (a) they're just as fast
01508  * on many terminals and (b) using them avoids bouncing an
01509  * unchanged bottom section of the screen up and down, which is
01510  * visually nasty.
01511  *
01512  * (lav): added more cases, used dl/il when bot==maxy and in csr case.
01513  *
01514  * I used assumption that capabilities il/il1/dl/dl1 work inside
01515  * changed scroll region not shifting screen contents outside of it.
01516  * If there are any terminals behaving different way, it would be
01517  * necessary to add some conditions to scroll_csr_forward/backward.
01518  */
01519 
01520 /* Try to scroll up assuming given csr (miny, maxy). Returns ERR on failure */
01521 static int
01522 scroll_csr_forward(int n, int top, int bot, int miny, int maxy, NCURSES_CH_T blank)
01523 {
01524     int i;
01525 
01526     if (n == 1 && scroll_forward && top == miny && bot == maxy) {
01527        GoTo(bot, 0);
01528        UpdateAttrs(AttrOf(blank));
01529        TPUTS_TRACE("scroll_forward");
01530        putp(scroll_forward);
01531     } else if (n == 1 && delete_line && bot == maxy) {
01532        GoTo(top, 0);
01533        UpdateAttrs(AttrOf(blank));
01534        TPUTS_TRACE("delete_line");
01535        putp(delete_line);
01536     } else if (parm_index && top == miny && bot == maxy) {
01537        GoTo(bot, 0);
01538        UpdateAttrs(AttrOf(blank));
01539        TPUTS_TRACE("parm_index");
01540        tputs(tparm(parm_index, n, 0), n, _nc_outch);
01541     } else if (parm_delete_line && bot == maxy) {
01542        GoTo(top, 0);
01543        UpdateAttrs(AttrOf(blank));
01544        TPUTS_TRACE("parm_delete_line");
01545        tputs(tparm(parm_delete_line, n, 0), n, _nc_outch);
01546     } else if (scroll_forward && top == miny && bot == maxy) {
01547        GoTo(bot, 0);
01548        UpdateAttrs(AttrOf(blank));
01549        for (i = 0; i < n; i++) {
01550            TPUTS_TRACE("scroll_forward");
01551            putp(scroll_forward);
01552        }
01553     } else if (delete_line && bot == maxy) {
01554        GoTo(top, 0);
01555        UpdateAttrs(AttrOf(blank));
01556        for (i = 0; i < n; i++) {
01557            TPUTS_TRACE("delete_line");
01558            putp(delete_line);
01559        }
01560     } else
01561        return ERR;
01562 
01563 #if NCURSES_EXT_FUNCS
01564     if (FILL_BCE()) {
01565        int j;
01566        for (i = 0; i < n; i++) {
01567            GoTo(bot - i, 0);
01568            for (j = 0; j < screen_columns; j++)
01569               PutChar(CHREF(blank));
01570        }
01571     }
01572 #endif
01573     return OK;
01574 }
01575 
01576 /* Try to scroll down assuming given csr (miny, maxy). Returns ERR on failure */
01577 /* n > 0 */
01578 static int
01579 scroll_csr_backward(int n, int top, int bot, int miny, int maxy,
01580                   NCURSES_CH_T blank)
01581 {
01582     int i;
01583 
01584     if (n == 1 && scroll_reverse && top == miny && bot == maxy) {
01585        GoTo(top, 0);
01586        UpdateAttrs(AttrOf(blank));
01587        TPUTS_TRACE("scroll_reverse");
01588        putp(scroll_reverse);
01589     } else if (n == 1 && insert_line && bot == maxy) {
01590        GoTo(top, 0);
01591        UpdateAttrs(AttrOf(blank));
01592        TPUTS_TRACE("insert_line");
01593        putp(insert_line);
01594     } else if (parm_rindex && top == miny && bot == maxy) {
01595        GoTo(top, 0);
01596        UpdateAttrs(AttrOf(blank));
01597        TPUTS_TRACE("parm_rindex");
01598        tputs(tparm(parm_rindex, n, 0), n, _nc_outch);
01599     } else if (parm_insert_line && bot == maxy) {
01600        GoTo(top, 0);
01601        UpdateAttrs(AttrOf(blank));
01602        TPUTS_TRACE("parm_insert_line");
01603        tputs(tparm(parm_insert_line, n, 0), n, _nc_outch);
01604     } else if (scroll_reverse && top == miny && bot == maxy) {
01605        GoTo(top, 0);
01606        UpdateAttrs(AttrOf(blank));
01607        for (i = 0; i < n; i++) {
01608            TPUTS_TRACE("scroll_reverse");
01609            putp(scroll_reverse);
01610        }
01611     } else if (insert_line && bot == maxy) {
01612        GoTo(top, 0);
01613        UpdateAttrs(AttrOf(blank));
01614        for (i = 0; i < n; i++) {
01615            TPUTS_TRACE("insert_line");
01616            putp(insert_line);
01617        }
01618     } else
01619        return ERR;
01620 
01621 #if NCURSES_EXT_FUNCS
01622     if (FILL_BCE()) {
01623        int j;
01624        for (i = 0; i < n; i++) {
01625            GoTo(top + i, 0);
01626            for (j = 0; j < screen_columns; j++)
01627               PutChar(CHREF(blank));
01628        }
01629     }
01630 #endif
01631     return OK;
01632 }
01633 
01634 /* scroll by using delete_line at del and insert_line at ins */
01635 /* n > 0 */
01636 static int
01637 scroll_idl(int n, int del, int ins, NCURSES_CH_T blank)
01638 {
01639     int i;
01640 
01641     if (!((parm_delete_line || delete_line) && (parm_insert_line || insert_line)))
01642        return ERR;
01643 
01644     GoTo(del, 0);
01645     UpdateAttrs(AttrOf(blank));
01646     if (n == 1 && delete_line) {
01647        TPUTS_TRACE("delete_line");
01648        putp(delete_line);
01649     } else if (parm_delete_line) {
01650        TPUTS_TRACE("parm_delete_line");
01651        tputs(tparm(parm_delete_line, n, 0), n, _nc_outch);
01652     } else {                /* if (delete_line) */
01653        for (i = 0; i < n; i++) {
01654            TPUTS_TRACE("delete_line");
01655            putp(delete_line);
01656        }
01657     }
01658 
01659     GoTo(ins, 0);
01660     UpdateAttrs(AttrOf(blank));
01661     if (n == 1 && insert_line) {
01662        TPUTS_TRACE("insert_line");
01663        putp(insert_line);
01664     } else if (parm_insert_line) {
01665        TPUTS_TRACE("parm_insert_line");
01666        tputs(tparm(parm_insert_line, n, 0), n, _nc_outch);
01667     } else {                /* if (insert_line) */
01668        for (i = 0; i < n; i++) {
01669            TPUTS_TRACE("insert_line");
01670            putp(insert_line);
01671        }
01672     }
01673 
01674     return OK;
01675 }
01676 
01677 /*
01678  * Note:  some terminals require the cursor to be within the scrolling margins
01679  * before setting them.  Generally, the cursor must be at the appropriate end
01680  * of the scrolling margins when issuing an indexing operation (it is not
01681  * apparent whether it must also be at the left margin; we do this just to be
01682  * safe).  To make the related cursor movement a little faster, we use the
01683  * save/restore cursor capabilities if the terminal has them.
01684  */
01685 NCURSES_EXPORT(int)
01686 _nc_scrolln(int n, int top, int bot, int maxy)
01687 /* scroll region from top to bot by n lines */
01688 {
01689     NCURSES_CH_T blank = ClrBlank(stdscr);
01690     int i;
01691     bool cursor_saved = FALSE;
01692     int res;
01693 
01694     TR(TRACE_MOVE, ("mvcur_scrolln(%d, %d, %d, %d)", n, top, bot, maxy));
01695 
01696 #if USE_XMC_SUPPORT
01697     /*
01698      * If we scroll, we might remove a cookie.
01699      */
01700     if (magic_cookie_glitch > 0) {
01701        return (ERR);
01702     }
01703 #endif
01704 
01705     if (n > 0) {            /* scroll up (forward) */
01706        /*
01707         * Explicitly clear if stuff pushed off top of region might
01708         * be saved by the terminal.
01709         */
01710        res = scroll_csr_forward(n, top, bot, 0, maxy, blank);
01711 
01712        if (res == ERR && change_scroll_region) {
01713            if ((((n == 1 && scroll_forward) || parm_index)
01714                && (SP->_cursrow == bot || SP->_cursrow == bot - 1))
01715               && save_cursor && restore_cursor) {
01716               cursor_saved = TRUE;
01717               TPUTS_TRACE("save_cursor");
01718               putp(save_cursor);
01719            }
01720            TPUTS_TRACE("change_scroll_region");
01721            putp(tparm(change_scroll_region, top, bot));
01722            if (cursor_saved) {
01723               TPUTS_TRACE("restore_cursor");
01724               putp(restore_cursor);
01725            } else {
01726               SP->_cursrow = SP->_curscol = -1;
01727            }
01728 
01729            res = scroll_csr_forward(n, top, bot, top, bot, blank);
01730 
01731            TPUTS_TRACE("change_scroll_region");
01732            putp(tparm(change_scroll_region, 0, maxy));
01733            SP->_cursrow = SP->_curscol = -1;
01734        }
01735 
01736        if (res == ERR && _nc_idlok)
01737            res = scroll_idl(n, top, bot - n + 1, blank);
01738 
01739        /*
01740         * Clear the newly shifted-in text.
01741         */
01742        if (res != ERR
01743            && (non_dest_scroll_region || (memory_below && bot == maxy))) {
01744            NCURSES_CH_T blank2 = NewChar(BLANK_TEXT);
01745            if (bot == maxy && clr_eos) {
01746               GoTo(bot - n + 1, 0);
01747               ClrToEOS(blank2);
01748            } else {
01749               for (i = 0; i < n; i++) {
01750                   GoTo(bot - i, 0);
01751                   ClrToEOL(blank2, FALSE);
01752               }
01753            }
01754        }
01755 
01756     } else {                /* (n < 0) - scroll down (backward) */
01757        res = scroll_csr_backward(-n, top, bot, 0, maxy, blank);
01758 
01759        if (res == ERR && change_scroll_region) {
01760            if (top != 0 && (SP->_cursrow == top || SP->_cursrow == top - 1)
01761               && save_cursor && restore_cursor) {
01762               cursor_saved = TRUE;
01763               TPUTS_TRACE("save_cursor");
01764               putp(save_cursor);
01765            }
01766            TPUTS_TRACE("change_scroll_region");
01767            putp(tparm(change_scroll_region, top, bot));
01768            if (cursor_saved) {
01769               TPUTS_TRACE("restore_cursor");
01770               putp(restore_cursor);
01771            } else {
01772               SP->_cursrow = SP->_curscol = -1;
01773            }
01774 
01775            res = scroll_csr_backward(-n, top, bot, top, bot, blank);
01776 
01777            TPUTS_TRACE("change_scroll_region");
01778            putp(tparm(change_scroll_region, 0, maxy));
01779            SP->_cursrow = SP->_curscol = -1;
01780        }
01781 
01782        if (res == ERR && _nc_idlok)
01783            res = scroll_idl(-n, bot + n + 1, top, blank);
01784 
01785        /*
01786         * Clear the newly shifted-in text.
01787         */
01788        if (res != ERR
01789            && (non_dest_scroll_region || (memory_above && top == 0))) {
01790            NCURSES_CH_T blank2 = NewChar(BLANK_TEXT);
01791            for (i = 0; i < -n; i++) {
01792               GoTo(i + top, 0);
01793               ClrToEOL(blank2, FALSE);
01794            }
01795        }
01796     }
01797 
01798     if (res == ERR)
01799        return (ERR);
01800 
01801     _nc_scroll_window(curscr, n, top, bot, blank);
01802 
01803     /* shift hash values too - they can be reused */
01804     _nc_scroll_oldhash(n, top, bot);
01805 
01806     return (OK);
01807 }
01808 
01809 NCURSES_EXPORT(void)
01810 _nc_screen_resume(void)
01811 {
01812     /* make sure terminal is in a sane known state */
01813     SP->_current_attr = A_NORMAL;
01814     newscr->_clear = TRUE;
01815 
01816     /* reset color pairs and definitions */
01817     if (SP->_coloron || SP->_color_defs)
01818        _nc_reset_colors();
01819 
01820     /* restore user-defined colors, if any */
01821     if (SP->_color_defs < 0) {
01822        int n;
01823        SP->_color_defs = -(SP->_color_defs);
01824        for (n = 0; n < SP->_color_defs; ++n) {
01825            if (SP->_color_table[n].init) {
01826               init_color(n,
01827                         SP->_color_table[n].r,
01828                         SP->_color_table[n].g,
01829                         SP->_color_table[n].b);
01830            }
01831        }
01832     }
01833 
01834     if (exit_attribute_mode)
01835        putp(exit_attribute_mode);
01836     else {
01837        /* turn off attributes */
01838        if (exit_alt_charset_mode)
01839            putp(exit_alt_charset_mode);
01840        if (exit_standout_mode)
01841            putp(exit_standout_mode);
01842        if (exit_underline_mode)
01843            putp(exit_underline_mode);
01844     }
01845     if (exit_insert_mode)
01846        putp(exit_insert_mode);
01847     if (enter_am_mode && exit_am_mode)
01848        putp(auto_right_margin ? enter_am_mode : exit_am_mode);
01849 }
01850 
01851 NCURSES_EXPORT(void)
01852 _nc_screen_init(void)
01853 {
01854     _nc_screen_resume();
01855 }
01856 
01857 /* wrap up screen handling */
01858 NCURSES_EXPORT(void)
01859 _nc_screen_wrap(void)
01860 {
01861     UpdateAttrs(A_NORMAL);
01862 #if NCURSES_EXT_FUNCS
01863     if (SP->_coloron
01864        && !SP->_default_color) {
01865        NCURSES_CH_T blank = NewChar(BLANK_TEXT);
01866        SP->_default_color = TRUE;
01867        _nc_do_color(-1, 0, FALSE, _nc_outch);
01868        SP->_default_color = FALSE;
01869 
01870        mvcur(SP->_cursrow, SP->_curscol, screen_lines - 1, 0);
01871 
01872        ClrToEOL(blank, TRUE);
01873     }
01874 #endif
01875     if (SP->_color_defs) {
01876        _nc_reset_colors();
01877     }
01878 }
01879 
01880 #if USE_XMC_SUPPORT
01881 NCURSES_EXPORT(void)
01882 _nc_do_xmc_glitch(attr_t previous)
01883 {
01884     attr_t chg = XMC_CHANGES(previous ^ SP->_current_attr);
01885 
01886     while (chg != 0) {
01887        if (chg & 1) {
01888            SP->_curscol += magic_cookie_glitch;
01889            if (SP->_curscol >= SP->_columns)
01890               wrap_cursor();
01891            TR(TRACE_UPDATE, ("bumped to %d,%d after cookie", SP->_cursrow, SP->_curscol));
01892        }
01893        chg >>= 1;
01894     }
01895 }
01896 #endif /* USE_XMC_SUPPORT */