1 /****************************************************************************
2  * Copyright (c) 1998,1999,2000 Free Software Foundation, Inc.              *
3  *                                                                          *
4  * Permission is hereby granted, free of charge, to any person obtaining a  *
5  * copy of this software and associated documentation files (the            *
6  * "Software"), to deal in the Software without restriction, including      *
7  * without limitation the rights to use, copy, modify, merge, publish,      *
8  * distribute, distribute with modifications, sublicense, and/or sell       *
9  * copies of the Software, and to permit persons to whom the Software is    *
10  * furnished to do so, subject to the following conditions:                 *
11  *                                                                          *
12  * The above copyright notice and this permission notice shall be included  *
13  * in all copies or substantial portions of the Software.                   *
14  *                                                                          *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
18  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
19  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
20  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
21  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
22  *                                                                          *
23  * Except as contained in this notice, the name(s) of the above copyright   *
24  * holders shall not be used in advertising or otherwise to promote the     *
25  * sale, use or other dealings in this Software without prior written       *
26  * authorization.                                                           *
27  ****************************************************************************/
28 
29 /****************************************************************************
30  *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
31  *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
32  ****************************************************************************/
33 
34 /*-----------------------------------------------------------------
35  *
36  *	lib_doupdate.c
37  *
38  *	The routine doupdate() and its dependents.  Also _nc_outstr(),
39  *	so all physical output is concentrated here (except _nc_outch()
40  *	in lib_tputs.c).
41  *
42  *-----------------------------------------------------------------*/
43 
44 #ifdef __BEOS__
45 #include <OS.h>
46 #endif
47 
48 #include <curses.priv.h>
49 
50 #if defined(TRACE) && HAVE_SYS_TIMES_H && HAVE_TIMES
51 #define USE_TRACE_TIMES 1
52 #else
53 #define USE_TRACE_TIMES 0
54 #endif
55 
56 #if HAVE_SYS_TIME_H && HAVE_SYS_TIME_SELECT
57 #include <sys/time.h>
58 #endif
59 
60 #if USE_TRACE_TIMES
61 #include <sys/times.h>
62 #endif
63 
64 #if USE_FUNC_POLL
65 #elif HAVE_SELECT
66 #if HAVE_SYS_SELECT_H
67 #include <sys/select.h>
68 #endif
69 #endif
70 
71 #include <term.h>
72 
73 MODULE_ID("$Id: tty_update.c,v 1.146 2000/10/07 01:11:44 tom Exp $")
74 
75 /*
76  * This define controls the line-breakout optimization.  Every once in a
77  * while during screen refresh, we want to check for input and abort the
78  * update if there's some waiting.  CHECK_INTERVAL controls the number of
79  * changed lines to be emitted between input checks.
80  *
81  * Note: Input-check-and-abort is no longer done if the screen is being
82  * updated from scratch.  This is a feature, not a bug.
83  */
84 #define CHECK_INTERVAL	5
85 
86 #define FILL_BCE() (SP->_coloron && !SP->_default_color && !back_color_erase)
87 
88 /*
89  * Enable checking to see if doupdate and friends are tracking the true
90  * cursor position correctly.  NOTE: this is a debugging hack which will
91  * work ONLY on ANSI-compatible terminals!
92  */
93 /* #define POSITION_DEBUG */
94 
95 static inline chtype ClrBlank(WINDOW *win);
96 static int ClrBottom(int total);
97 static void ClearScreen(chtype blank);
98 static void ClrUpdate(void);
99 static void DelChar(int count);
100 static void InsStr(chtype * line, int count);
101 static void TransformLine(int const lineno);
102 
103 #ifdef POSITION_DEBUG
104 /****************************************************************************
105  *
106  * Debugging code.  Only works on ANSI-standard terminals.
107  *
108  ****************************************************************************/
109 
110 static void
111 position_check(int expected_y, int expected_x, char *legend)
112 /* check to see if the real cursor position matches the virtual */
113 {
114     char buf[20];
115     char *s;
116     int y, x;
117 
118     if (!_nc_tracing || (expected_y < 0 && expected_x < 0))
119 	return;
120 
121     _nc_flush();
122     memset(buf, '\0', sizeof(buf));
123     putp("\033[6n");		/* only works on ANSI-compatibles */
124     _nc_flush();
125     *(s = buf) = 0;
126     do {
127 	int ask = sizeof(buf) - 1 - (s - buf);
128 	int got = read(0, s, ask);
129 	if (got == 0)
130 	    break;
131 	s += got;
132     } while (strchr(buf, 'R') == 0);
133     _tracef("probe returned %s", _nc_visbuf(buf));
134 
135     /* try to interpret as a position report */
136     if (sscanf(buf, "\033[%d;%dR", &y, &x) != 2) {
137 	_tracef("position probe failed in %s", legend);
138     } else {
139 	if (expected_x < 0)
140 	    expected_x = x - 1;
141 	if (expected_y < 0)
142 	    expected_y = y - 1;
143 	if (y - 1 != expected_y || x - 1 != expected_x) {
144 	    beep();
145 	    tputs(tparm("\033[%d;%dH", expected_y + 1, expected_x + 1), 1, _nc_outch);
146 	    _tracef("position seen (%d, %d) doesn't match expected one (%d, %d) in %s",
147 		    y - 1, x - 1, expected_y, expected_x, legend);
148 	} else {
149 	    _tracef("position matches OK in %s", legend);
150 	}
151     }
152 }
153 #else
154 #define position_check(expected_y, expected_x, legend)	/* nothing */
155 #endif /* POSITION_DEBUG */
156 
157 /****************************************************************************
158  *
159  * Optimized update code
160  *
161  ****************************************************************************/
162 
163 static inline void
164 GoTo(int const row, int const col)
165 {
166     chtype oldattr = SP->_current_attr;
167 
168     TR(TRACE_MOVE, ("GoTo(%d, %d) from (%d, %d)",
169 		    row, col, SP->_cursrow, SP->_curscol));
170 
171     position_check(SP->_cursrow, SP->_curscol, "GoTo");
172 
173     /*
174      * Force restore even if msgr is on when we're in an alternate
175      * character set -- these have a strong tendency to screw up the
176      * CR & LF used for local character motions!
177      */
178     if ((oldattr & A_ALTCHARSET)
179 	|| (oldattr && !move_standout_mode)) {
180 	TR(TRACE_CHARPUT, ("turning off (%#lx) %s before move",
181 			   oldattr, _traceattr(oldattr)));
182 	vidattr(A_NORMAL);
183     }
184 
185     mvcur(SP->_cursrow, SP->_curscol, row, col);
186     SP->_cursrow = row;
187     SP->_curscol = col;
188     position_check(SP->_cursrow, SP->_curscol, "GoTo2");
189 }
190 
191 static inline void
192 PutAttrChar(chtype ch)
193 {
194     int data;
195 
196     if (tilde_glitch && (TextOf(ch) == '~'))
197 	ch = ('`' | AttrOf(ch));
198 
199     TR(TRACE_CHARPUT, ("PutAttrChar(%s) at (%d, %d)",
200 		       _tracechtype(ch),
201 		       SP->_cursrow, SP->_curscol));
202     UpdateAttrs(ch);
203     data = TextOf(ch);
204     if (SP->_outch != 0) {
205 	SP->_outch(data);
206     } else {
207 	putc(data, SP->_ofp);	/* macro's fastest... */
208 #ifdef TRACE
209 	_nc_outchars++;
210 #endif /* TRACE */
211     }
212     SP->_curscol++;
213     if (char_padding) {
214 	TPUTS_TRACE("char_padding");
215 	putp(char_padding);
216     }
217 }
218 
219 static bool
220 check_pending(void)
221 /* check for pending input */
222 {
223     bool have_pending = FALSE;
224 
225     /*
226      * Only carry out this check when the flag is zero, otherwise we'll
227      * have the refreshing slow down drastically (or stop) if there's an
228      * unread character available.
229      */
230     if (SP->_fifohold != 0)
231 	return FALSE;
232 
233     if (SP->_checkfd >= 0) {
234 #if USE_FUNC_POLL
235 	struct pollfd fds[1];
236 	fds[0].fd = SP->_checkfd;
237 	fds[0].events = POLLIN;
238 	if (poll(fds, 1, 0) > 0) {
239 	    have_pending = TRUE;
240 	}
241 #elif defined(__BEOS__)
242 	/*
243 	 * BeOS's select() is declared in socket.h, so the configure script does
244 	 * not see it.  That's just as well, since that function works only for
245 	 * sockets.  This (using snooze and ioctl) was distilled from Be's patch
246 	 * for ncurses which uses a separate thread to simulate select().
247 	 *
248 	 * FIXME: the return values from the ioctl aren't very clear if we get
249 	 * interrupted.
250 	 */
251 	int n = 0;
252 	int howmany = ioctl(0, 'ichr', &n);
253 	if (howmany >= 0 && n > 0) {
254 	    have_pending = TRUE;
255 	}
256 #elif HAVE_SELECT
257 	fd_set fdset;
258 	struct timeval ktimeout;
259 
260 	ktimeout.tv_sec =
261 	    ktimeout.tv_usec = 0;
262 
263 	FD_ZERO(&fdset);
264 	FD_SET(SP->_checkfd, &fdset);
265 	if (select(SP->_checkfd + 1, &fdset, NULL, NULL, &ktimeout) != 0) {
266 	    have_pending = TRUE;
267 	}
268 #endif
269     }
270     if (have_pending) {
271 	SP->_fifohold = 5;
272 	_nc_flush();
273     }
274     return FALSE;
275 }
276 
277 /*
278  * No one supports recursive inline functions.  However, gcc is quieter if we
279  * instantiate the recursive part separately.
280  */
281 #if CC_HAS_INLINE_FUNCS
282 static void callPutChar(chtype const);
283 #else
284 #define callPutChar(ch) PutChar(ch)
285 #endif
286 
287 static inline void PutChar(chtype const ch);	/* forward declaration */
288 
289 /* put char at lower right corner */
290 static void
291 PutCharLR(chtype const ch)
292 {
293     if (!auto_right_margin) {
294 	/* we can put the char directly */
295 	PutAttrChar(ch);
296     } else if (enter_am_mode && exit_am_mode) {
297 	/* we can suppress automargin */
298 	TPUTS_TRACE("exit_am_mode");
299 	putp(exit_am_mode);
300 
301 	PutAttrChar(ch);
302 	SP->_curscol--;
303 	position_check(SP->_cursrow, SP->_curscol, "exit_am_mode");
304 
305 	TPUTS_TRACE("enter_am_mode");
306 	putp(enter_am_mode);
307     } else if ((enter_insert_mode && exit_insert_mode)
308 	       || insert_character || parm_ich) {
309 	GoTo(screen_lines - 1, screen_columns - 2);
310 	callPutChar(ch);
311 	GoTo(screen_lines - 1, screen_columns - 2);
312 	InsStr(newscr->_line[screen_lines - 1].text + screen_columns - 2, 1);
313     }
314 }
315 
316 static void
317 wrap_cursor(void)
318 {
319     if (eat_newline_glitch) {
320 	/*
321 	 * xenl can manifest two different ways.  The vt100
322 	 * way is that, when you'd expect the cursor to wrap,
323 	 * it stays hung at the right margin (on top of the
324 	 * character just emitted) and doesn't wrap until the
325 	 * *next* graphic char is emitted.  The c100 way is
326 	 * to ignore LF received just after an am wrap.
327 	 *
328 	 * An aggressive way to handle this would be to
329 	 * emit CR/LF after the char and then assume the wrap
330 	 * is done, you're on the first position of the next
331 	 * line, and the terminal out of its weird state.
332 	 * Here it's safe to just tell the code that the
333 	 * cursor is in hyperspace and let the next mvcur()
334 	 * call straighten things out.
335 	 */
336 	SP->_curscol = -1;
337 	SP->_cursrow = -1;
338     } else if (auto_right_margin) {
339 	SP->_curscol = 0;
340 	SP->_cursrow++;
341     } else {
342 	SP->_curscol--;
343     }
344     position_check(SP->_cursrow, SP->_curscol, "wrap_cursor");
345 }
346 
347 static inline void
348 PutChar(chtype const ch)
349 /* insert character, handling automargin stuff */
350 {
351     if (SP->_cursrow == screen_lines - 1 && SP->_curscol == screen_columns - 1)
352 	PutCharLR(ch);
353     else
354 	PutAttrChar(ch);
355 
356     if (SP->_curscol >= screen_columns)
357 	wrap_cursor();
358 
359     position_check(SP->_cursrow, SP->_curscol, "PutChar");
360 }
361 
362 /*
363  * Check whether the given character can be output by clearing commands.  This
364  * includes test for being a space and not including any 'bad' attributes, such
365  * as A_REVERSE.  All attribute flags which don't affect appearance of a space
366  * or can be output by clearing (A_COLOR in case of bce-terminal) are excluded.
367  */
368 static inline bool
369 can_clear_with(chtype ch)
370 {
371     if (!back_color_erase && SP->_coloron) {
372 	if (ch & A_COLOR)
373 	    return FALSE;
374 #if NCURSES_EXT_FUNCS
375 	if (!SP->_default_color)
376 	    return FALSE;
377 	if (SP->_default_fg != C_MASK || SP->_default_bg != C_MASK)
378 	    return FALSE;
379 #endif
380     }
381     return ((ch & ~(NONBLANK_ATTR | A_COLOR)) == BLANK);
382 }
383 
384 /*
385  * Issue a given span of characters from an array.
386  * Must be functionally equivalent to:
387  *	for (i = 0; i < num; i++)
388  *	    PutChar(ntext[i]);
389  * but can leave the cursor positioned at the middle of the interval.
390  *
391  * Returns: 0 - cursor is at the end of interval
392  *	    1 - cursor is somewhere in the middle
393  *
394  * This code is optimized using ech and rep.
395  */
396 static int
397 EmitRange(const chtype * ntext, int num)
398 {
399     int i;
400 
401     if (erase_chars || repeat_char) {
402 	while (num > 0) {
403 	    int runcount;
404 	    chtype ntext0;
405 
406 	    while (num > 1 && ntext[0] != ntext[1]) {
407 		PutChar(ntext[0]);
408 		ntext++;
409 		num--;
410 	    }
411 	    ntext0 = ntext[0];
412 	    if (num == 1) {
413 		PutChar(ntext0);
414 		return 0;
415 	    }
416 	    runcount = 2;
417 
418 	    while (runcount < num && ntext[runcount] == ntext0)
419 		runcount++;
420 
421 	    /*
422 	     * The cost expression in the middle isn't exactly right.
423 	     * _cup_ch_cost is an upper bound on the cost for moving to the
424 	     * end of the erased area, but not the cost itself (which we
425 	     * can't compute without emitting the move).  This may result
426 	     * in erase_chars not getting used in some situations for
427 	     * which it would be marginally advantageous.
428 	     */
429 	    if (erase_chars
430 		&& runcount > SP->_ech_cost + SP->_cup_ch_cost
431 		&& can_clear_with(ntext0)) {
432 		UpdateAttrs(ntext0);
433 		putp(tparm(erase_chars, runcount));
434 
435 		/*
436 		 * If this is the last part of the given interval,
437 		 * don't bother moving cursor, since it can be the
438 		 * last update on the line.
439 		 */
440 		if (runcount < num) {
441 		    GoTo(SP->_cursrow, SP->_curscol + runcount);
442 		} else {
443 		    return 1;	/* cursor stays in the middle */
444 		}
445 	    } else if (repeat_char && runcount > SP->_rep_cost) {
446 		bool wrap_possible = (SP->_curscol + runcount >= screen_columns);
447 		int rep_count = runcount;
448 
449 		if (wrap_possible)
450 		    rep_count--;
451 
452 		UpdateAttrs(ntext0);
453 		putp(tparm(repeat_char, TextOf(ntext0), rep_count));
454 		SP->_curscol += rep_count;
455 
456 		if (wrap_possible)
457 		    PutChar(ntext0);
458 	    } else {
459 		for (i = 0; i < runcount; i++)
460 		    PutChar(ntext[i]);
461 	    }
462 	    ntext += runcount;
463 	    num -= runcount;
464 	}
465 	return 0;
466     }
467 
468     for (i = 0; i < num; i++)
469 	PutChar(ntext[i]);
470     return 0;
471 }
472 
473 /*
474  * Output the line in the given range [first .. last]
475  *
476  * If there's a run of identical characters that's long enough to justify
477  * cursor movement, use that also.
478  *
479  * Returns: same as EmitRange
480  */
481 static int
482 PutRange(
483 	    const chtype * otext,
484 	    const chtype * ntext,
485 	    int row,
486 	    int first, int last)
487 {
488     int j, run;
489 
490     TR(TRACE_CHARPUT, ("PutRange(%p, %p, %d, %d, %d)",
491 		       otext, ntext, row, first, last));
492 
493     if (otext != ntext
494 	&& (last - first + 1) > SP->_inline_cost) {
495 	for (j = first, run = 0; j <= last; j++) {
496 	    if (otext[j] == ntext[j]) {
497 		run++;
498 	    } else {
499 		if (run > SP->_inline_cost) {
500 		    int before_run = (j - run);
501 		    EmitRange(ntext + first, before_run - first);
502 		    GoTo(row, first = j);
503 		}
504 		run = 0;
505 	    }
506 	}
507     }
508     return EmitRange(ntext + first, last - first + 1);
509 }
510 
511 #if CC_HAS_INLINE_FUNCS
512 static void
513 callPutChar(chtype const ch)
514 {
515     PutChar(ch);
516 }
517 #endif
518 
519 /* leave unbracketed here so 'indent' works */
520 #define MARK_NOCHANGE(win,row) \
521 		win->_line[row].firstchar = _NOCHANGE; \
522 		win->_line[row].lastchar = _NOCHANGE; \
523 		if_USE_SCROLL_HINTS(win->_line[row].oldindex = row)
524 
525 int
526 doupdate(void)
527 {
528     int i;
529     int nonempty;
530 #if USE_TRACE_TIMES
531     struct tms before, after;
532 #endif /* USE_TRACE_TIMES */
533 
534     T((T_CALLED("doupdate()")));
535 
536 #ifdef TRACE
537     if (_nc_tracing & TRACE_UPDATE) {
538 	if (curscr->_clear)
539 	    _tracef("curscr is clear");
540 	else
541 	    _tracedump("curscr", curscr);
542 	_tracedump("newscr", newscr);
543     }
544 #endif /* TRACE */
545 
546     _nc_signal_handler(FALSE);
547 
548     if (SP->_fifohold)
549 	SP->_fifohold--;
550 
551 #if USE_SIZECHANGE
552     if (SP->_endwin || SP->_sig_winch) {
553 	/*
554 	 * This is a transparent extension:  XSI does not address it,
555 	 * and applications need not know that ncurses can do it.
556 	 *
557 	 * Check if the terminal size has changed while curses was off
558 	 * (this can happen in an xterm, for example), and resize the
559 	 * ncurses data structures accordingly.
560 	 */
561 	_nc_update_screensize();
562     }
563 #endif
564 
565     if (SP->_endwin) {
566 
567 	T(("coming back from shell mode"));
568 	reset_prog_mode();
569 
570 	_nc_mvcur_resume();
571 	_nc_screen_resume();
572 	SP->_mouse_resume(SP);
573 
574 	SP->_endwin = FALSE;
575     }
576 #if USE_TRACE_TIMES
577     /* zero the metering machinery */
578     _nc_outchars = 0;
579     (void) times(&before);
580 #endif /* USE_TRACE_TIMES */
581 
582     /*
583      * This is the support for magic-cookie terminals.  The
584      * theory: we scan the virtual screen looking for attribute
585      * turnons.  Where we find one, check to make sure it's
586      * realizable by seeing if the required number of
587      * un-attributed blanks are present before and after the
588      * attributed range; try to shift the range boundaries over
589      * blanks (not changing the screen display) so this becomes
590      * true.  If it is, shift the beginning attribute change
591      * appropriately (the end one, if we've gotten this far, is
592      * guaranteed room for its cookie). If not, nuke the added
593      * attributes out of the span.
594      */
595 #if USE_XMC_SUPPORT
596     if (magic_cookie_glitch > 0) {
597 	int j, k;
598 	attr_t rattr = A_NORMAL;
599 
600 	for (i = 0; i < screen_lines; i++) {
601 	    for (j = 0; j < screen_columns; j++) {
602 		bool failed = FALSE;
603 		chtype turnon = AttrOf(newscr->_line[i].text[j]) & ~rattr;
604 
605 		/* is an attribute turned on here? */
606 		if (turnon == 0) {
607 		    rattr = AttrOf(newscr->_line[i].text[j]);
608 		    continue;
609 		}
610 
611 		TR(TRACE_ATTRS, ("At (%d, %d): from %s...", i, j, _traceattr(rattr)));
612 		TR(TRACE_ATTRS, ("...to %s", _traceattr(turnon)));
613 
614 		/*
615 		 * If the attribute change location is a blank with a
616 		 * "safe" attribute, undo the attribute turnon.  This may
617 		 * ensure there's enough room to set the attribute before
618 		 * the first non-blank in the run.
619 		 */
620 #define SAFE(a)	(!((a) & (chtype)~NONBLANK_ATTR))
621 		if (TextOf(newscr->_line[i].text[j]) == ' ' && SAFE(turnon)) {
622 		    newscr->_line[i].text[j] &= ~turnon;
623 		    continue;
624 		}
625 
626 		/* check that there's enough room at start of span */
627 		for (k = 1; k <= magic_cookie_glitch; k++) {
628 		    if (j - k < 0
629 			|| TextOf(newscr->_line[i].text[j - k]) != ' '
630 			|| !SAFE(AttrOf(newscr->_line[i].text[j - k])))
631 			failed = TRUE;
632 		}
633 		if (!failed) {
634 		    bool end_onscreen = FALSE;
635 		    int m, n = j;
636 
637 		    /* find end of span, if it's onscreen */
638 		    for (m = i; m < screen_lines; m++) {
639 			for (; n < screen_columns; n++) {
640 			    if (AttrOf(newscr->_line[m].text[n]) == rattr) {
641 				end_onscreen = TRUE;
642 				TR(TRACE_ATTRS,
643 				   ("Range attributed with %s ends at (%d, %d)",
644 				    _traceattr(turnon), m, n));
645 				goto foundit;
646 			    }
647 			}
648 			n = 0;
649 		    }
650 		    TR(TRACE_ATTRS,
651 		       ("Range attributed with %s ends offscreen",
652 			_traceattr(turnon)));
653 		  foundit:;
654 
655 		    if (end_onscreen) {
656 			chtype *lastline = newscr->_line[m].text;
657 
658 			/*
659 			 * If there are safely-attributed blanks at the
660 			 * end of the range, shorten the range.  This will
661 			 * help ensure that there is enough room at end
662 			 * of span.
663 			 */
664 			while (n >= 0
665 			       && TextOf(lastline[n]) == ' '
666 			       && SAFE(AttrOf(lastline[n])))
667 			    lastline[n--] &= ~turnon;
668 
669 			/* check that there's enough room at end of span */
670 			for (k = 1; k <= magic_cookie_glitch; k++)
671 			    if (n + k >= screen_columns
672 				|| TextOf(lastline[n + k]) != ' '
673 				|| !SAFE(AttrOf(lastline[n + k])))
674 				failed = TRUE;
675 		    }
676 		}
677 
678 		if (failed) {
679 		    int p, q = j;
680 
681 		    TR(TRACE_ATTRS,
682 		       ("Clearing %s beginning at (%d, %d)",
683 			_traceattr(turnon), i, j));
684 
685 		    /* turn off new attributes over span */
686 		    for (p = i; p < screen_lines; p++) {
687 			for (; q < screen_columns; q++) {
688 			    if (AttrOf(newscr->_line[p].text[q]) == rattr)
689 				goto foundend;
690 			    newscr->_line[p].text[q] &= ~turnon;
691 			}
692 			q = 0;
693 		    }
694 		  foundend:;
695 		} else {
696 		    TR(TRACE_ATTRS,
697 		       ("Cookie space for %s found before (%d, %d)",
698 			_traceattr(turnon), i, j));
699 
700 		    /*
701 		     * back up the start of range so there's room
702 		     * for cookies before the first nonblank character
703 		     */
704 		    for (k = 1; k <= magic_cookie_glitch; k++)
705 			newscr->_line[i].text[j - k] |= turnon;
706 		}
707 
708 		rattr = AttrOf(newscr->_line[i].text[j]);
709 	    }
710 	}
711 
712 #ifdef TRACE
713 	/* show altered highlights after magic-cookie check */
714 	if (_nc_tracing & TRACE_UPDATE) {
715 	    _tracef("After magic-cookie check...");
716 	    _tracedump("newscr", newscr);
717 	}
718 #endif /* TRACE */
719     }
720 #endif /* USE_XMC_SUPPORT */
721 
722     nonempty = 0;
723     if (curscr->_clear || newscr->_clear) {	/* force refresh ? */
724 	TR(TRACE_UPDATE, ("clearing and updating from scratch"));
725 	ClrUpdate();
726 	curscr->_clear = FALSE;	/* reset flag */
727 	newscr->_clear = FALSE;	/* reset flag */
728     } else {
729 	int changedlines = CHECK_INTERVAL;
730 
731 	if (check_pending())
732 	    goto cleanup;
733 
734 	nonempty = min(screen_lines, newscr->_maxy + 1);
735 
736 	if (SP->_scrolling) {
737 	    _nc_scroll_optimize();
738 	}
739 
740 	nonempty = ClrBottom(nonempty);
741 
742 	TR(TRACE_UPDATE, ("Transforming lines, nonempty %d", nonempty));
743 	for (i = 0; i < nonempty; i++) {
744 	    /*
745 	     * Here is our line-breakout optimization.
746 	     */
747 	    if (changedlines == CHECK_INTERVAL) {
748 		if (check_pending())
749 		    goto cleanup;
750 		changedlines = 0;
751 	    }
752 
753 	    /*
754 	     * newscr->line[i].firstchar is normally set
755 	     * by wnoutrefresh.  curscr->line[i].firstchar
756 	     * is normally set by _nc_scroll_window in the
757 	     * vertical-movement optimization code,
758 	     */
759 	    if (newscr->_line[i].firstchar != _NOCHANGE
760 		|| curscr->_line[i].firstchar != _NOCHANGE) {
761 		TransformLine(i);
762 		changedlines++;
763 	    }
764 
765 	    /* mark line changed successfully */
766 	    if (i <= newscr->_maxy) {
767 		MARK_NOCHANGE(newscr, i)
768 	    }
769 	    if (i <= curscr->_maxy) {
770 		MARK_NOCHANGE(curscr, i)
771 	    }
772 	}
773     }
774 
775     /* put everything back in sync */
776     for (i = nonempty; i <= newscr->_maxy; i++) {
777 	MARK_NOCHANGE(newscr, i)
778     }
779     for (i = nonempty; i <= curscr->_maxy; i++) {
780 	MARK_NOCHANGE(curscr, i)
781     }
782 
783     if (!newscr->_leaveok) {
784 	curscr->_curx = newscr->_curx;
785 	curscr->_cury = newscr->_cury;
786 
787 	GoTo(curscr->_cury, curscr->_curx);
788     }
789 
790   cleanup:
791     /*
792      * Keep the physical screen in normal mode in case we get other
793      * processes writing to the screen.
794      */
795     UpdateAttrs(A_NORMAL);
796 
797     _nc_flush();
798     curscr->_attrs = newscr->_attrs;
799 
800 #if USE_TRACE_TIMES
801     (void) times(&after);
802     TR(TRACE_TIMES,
803        ("Update cost: %ld chars, %ld clocks system time, %ld clocks user time",
804 	_nc_outchars,
805 	after.tms_stime - before.tms_stime,
806 	after.tms_utime - before.tms_utime));
807 #endif /* USE_TRACE_TIMES */
808 
809     _nc_signal_handler(TRUE);
810 
811     returnCode(OK);
812 }
813 
814 /*
815  *	ClrBlank(win)
816  *
817  *	Returns the attributed character that corresponds to the "cleared"
818  *	screen.  If the terminal has the back-color-erase feature, this will be
819  *	colored according to the wbkgd() call.
820  *
821  *	We treat 'curscr' specially because it isn't supposed to be set directly
822  *	in the wbkgd() call.  Assume 'stdscr' for this case.
823  */
824 #define BCE_ATTRS (A_NORMAL|A_COLOR)
825 #define BCE_BKGD(win) (((win) == curscr ? stdscr : (win))->_bkgd)
826 
827 static inline chtype
828 ClrBlank(WINDOW *win)
829 {
830     chtype blank = BLANK;
831     if (back_color_erase)
832 	blank |= (BCE_BKGD(win) & BCE_ATTRS);
833     return blank;
834 }
835 
836 /*
837 **	ClrUpdate()
838 **
839 **	Update by clearing and redrawing the entire screen.
840 **
841 */
842 
843 static void
844 ClrUpdate(void)
845 {
846     int i;
847     chtype blank = ClrBlank(stdscr);
848     int nonempty = min(screen_lines, newscr->_maxy + 1);
849 
850     TR(TRACE_UPDATE, ("ClrUpdate() called"));
851 
852     ClearScreen(blank);
853 
854     TR(TRACE_UPDATE, ("updating screen from scratch"));
855 
856     nonempty = ClrBottom(nonempty);
857 
858     for (i = 0; i < nonempty; i++)
859 	TransformLine(i);
860 }
861 
862 /*
863 **	ClrToEOL(blank)
864 **
865 **	Clear to end of current line, starting at the cursor position
866 */
867 
868 static void
869 ClrToEOL(chtype blank, bool needclear)
870 {
871     int j;
872 
873     if (curscr != 0
874 	&& SP->_cursrow >= 0) {
875 	for (j = SP->_curscol; j < screen_columns; j++) {
876 	    if (j >= 0) {
877 		chtype *cp = &(curscr->_line[SP->_cursrow].text[j]);
878 
879 		if (*cp != blank) {
880 		    *cp = blank;
881 		    needclear = TRUE;
882 		}
883 	    }
884 	}
885     } else {
886 	needclear = TRUE;
887     }
888 
889     if (needclear) {
890 	UpdateAttrs(blank);
891 	TPUTS_TRACE("clr_eol");
892 	if (SP->_el_cost > (screen_columns - SP->_curscol)) {
893 	    int count = (screen_columns - SP->_curscol);
894 	    while (count-- > 0)
895 		PutChar(blank);
896 	} else {
897 	    putp(clr_eol);
898 	}
899     }
900 }
901 
902 /*
903 **	ClrToEOS(blank)
904 **
905 **	Clear to end of screen, starting at the cursor position
906 */
907 
908 static void
909 ClrToEOS(chtype blank)
910 {
911     int row, col;
912 
913     row = SP->_cursrow;
914     col = SP->_curscol;
915 
916     UpdateAttrs(blank);
917     TPUTS_TRACE("clr_eos");
918     tputs(clr_eos, screen_lines - row, _nc_outch);
919 
920     while (col < screen_columns)
921 	curscr->_line[row].text[col++] = blank;
922 
923     for (row++; row < screen_lines; row++) {
924 	for (col = 0; col < screen_columns; col++)
925 	    curscr->_line[row].text[col] = blank;
926     }
927 }
928 
929 /*
930  *	ClrBottom(total)
931  *
932  *	Test if clearing the end of the screen would satisfy part of the
933  *	screen-update.  Do this by scanning backwards through the lines in the
934  *	screen, checking if each is blank, and one or more are changed.
935  */
936 static int
937 ClrBottom(int total)
938 {
939     int row;
940     int col;
941     int top = total;
942     int last = min(screen_columns, newscr->_maxx + 1);
943     chtype blank = ClrBlank(stdscr);
944     bool ok;
945 
946     if (clr_eos && can_clear_with(blank)) {
947 
948 	for (row = total - 1; row >= 0; row--) {
949 	    for (col = 0, ok = TRUE; ok && col < last; col++) {
950 		ok = (newscr->_line[row].text[col] == blank);
951 	    }
952 	    if (!ok)
953 		break;
954 
955 	    for (col = 0; ok && col < last; col++) {
956 		ok = (curscr->_line[row].text[col] == blank);
957 	    }
958 	    if (!ok)
959 		top = row;
960 	}
961 
962 	/* don't use clr_eos for just one line if clr_eol available */
963 	if (top < total - 1 || (top < total && !clr_eol && !clr_bol)) {
964 	    GoTo(top, 0);
965 	    ClrToEOS(blank);
966 	    total = top;
967 	    if (SP->oldhash && SP->newhash) {
968 		for (row = top; row < screen_lines; row++)
969 		    SP->oldhash[row] = SP->newhash[row];
970 	    }
971 	}
972     }
973     return total;
974 }
975 
976 /*
977 **	TransformLine(lineno)
978 **
979 **	Transform the given line in curscr to the one in newscr, using
980 **	Insert/Delete Character if _nc_idcok && has_ic().
981 **
982 **		firstChar = position of first different character in line
983 **		oLastChar = position of last different character in old line
984 **		nLastChar = position of last different character in new line
985 **
986 **		move to firstChar
987 **		overwrite chars up to min(oLastChar, nLastChar)
988 **		if oLastChar < nLastChar
989 **			insert newLine[oLastChar+1..nLastChar]
990 **		else
991 **			delete oLastChar - nLastChar spaces
992 */
993 
994 static void
995 TransformLine(int const lineno)
996 {
997     int firstChar, oLastChar, nLastChar;
998     chtype *newLine = newscr->_line[lineno].text;
999     chtype *oldLine = curscr->_line[lineno].text;
1000     int n;
1001     bool attrchanged = FALSE;
1002 
1003     TR(TRACE_UPDATE, ("TransformLine(%d) called", lineno));
1004 
1005     /* copy new hash value to old one */
1006     if (SP->oldhash && SP->newhash)
1007 	SP->oldhash[lineno] = SP->newhash[lineno];
1008 
1009 #define ColorOf(n) ((n) & A_COLOR)
1010 #define unColor(n) ((n) & ALL_BUT_COLOR)
1011     /*
1012      * If we have colors, there is the possibility of having two color pairs
1013      * that display as the same colors.  For instance, Lynx does this.  Check
1014      * for this case, and update the old line with the new line's colors when
1015      * they are equivalent.
1016      */
1017     if (SP->_coloron) {
1018 	chtype oldColor;
1019 	chtype newColor;
1020 	int oldPair;
1021 	int newPair;
1022 
1023 	for (n = 0; n < screen_columns; n++) {
1024 	    if (newLine[n] != oldLine[n]) {
1025 		oldColor = ColorOf(oldLine[n]);
1026 		newColor = ColorOf(newLine[n]);
1027 		if (oldColor != newColor
1028 		    && unColor(oldLine[n]) == unColor(newLine[n])) {
1029 		    oldPair = PAIR_NUMBER(oldColor);
1030 		    newPair = PAIR_NUMBER(newColor);
1031 		    if (oldPair < COLOR_PAIRS
1032 			&& newPair < COLOR_PAIRS
1033 			&& SP->_color_pairs[oldPair] == SP->_color_pairs[newPair]) {
1034 			oldLine[n] &= ~A_COLOR;
1035 			oldLine[n] |= ColorOf(newLine[n]);
1036 		    }
1037 		}
1038 	    }
1039 	}
1040     }
1041 
1042     if (ceol_standout_glitch && clr_eol) {
1043 	firstChar = 0;
1044 	while (firstChar < screen_columns) {
1045 	    if (AttrOf(newLine[firstChar]) != AttrOf(oldLine[firstChar]))
1046 		attrchanged = TRUE;
1047 	    firstChar++;
1048 	}
1049     }
1050 
1051     firstChar = 0;
1052 
1053     if (attrchanged) {		/* we may have to disregard the whole line */
1054 	GoTo(lineno, firstChar);
1055 	ClrToEOL(ClrBlank(curscr), FALSE);
1056 	PutRange(oldLine, newLine, lineno, 0, (screen_columns - 1));
1057 #if USE_XMC_SUPPORT
1058 
1059 #define NEW(r,c) newscr->_line[r].text[c]
1060 #define xmc_turn_on(a,b) ((((a)^(b)) & ~(a) & SP->_xmc_triggers) != 0)
1061 #define xmc_turn_off(a,b) xmc_turn_on(b,a)
1062 
1063 	/*
1064 	 * This is a very simple loop to paint characters which may have the
1065 	 * magic cookie glitch embedded.  It doesn't know much about video
1066 	 * attributes which are continued from one line to the next.  It
1067 	 * assumes that we have filtered out requests for attribute changes
1068 	 * that do not get mapped to blank positions.
1069 	 *
1070 	 * FIXME: we are not keeping track of where we put the cookies, so this
1071 	 * will work properly only once, since we may overwrite a cookie in a
1072 	 * following operation.
1073 	 */
1074     } else if (magic_cookie_glitch > 0) {
1075 	GoTo(lineno, firstChar);
1076 	for (n = 0; n < screen_columns; n++) {
1077 	    int m = n + magic_cookie_glitch;
1078 
1079 	    /* check for turn-on:
1080 	     * If we are writing an attributed blank, where the
1081 	     * previous cell is not attributed.
1082 	     */
1083 	    if (TextOf(newLine[n]) == ' '
1084 		&& ((n > 0
1085 		     && xmc_turn_on(newLine[n - 1], newLine[n]))
1086 		    || (n == 0
1087 			&& lineno > 0
1088 			&& xmc_turn_on(NEW(lineno - 1, screen_columns - 1),
1089 				       newLine[n])))) {
1090 		n = m;
1091 	    }
1092 
1093 	    PutChar(newLine[n]);
1094 
1095 	    /* check for turn-off:
1096 	     * If we are writing an attributed non-blank, where the
1097 	     * next cell is blank, and not attributed.
1098 	     */
1099 	    if (TextOf(newLine[n]) != ' '
1100 		&& ((n + 1 < screen_columns
1101 		     && xmc_turn_off(newLine[n], newLine[n + 1]))
1102 		    || (n + 1 >= screen_columns
1103 			&& lineno + 1 < screen_lines
1104 			&& xmc_turn_off(newLine[n], NEW(lineno + 1, 0))))) {
1105 		n = m;
1106 	    }
1107 
1108 	}
1109 #undef NEW
1110 #endif
1111     } else {
1112 	chtype blank;
1113 
1114 	/* find the first differing character */
1115 	while (firstChar < screen_columns &&
1116 	       newLine[firstChar] == oldLine[firstChar])
1117 	    firstChar++;
1118 
1119 	/* if there wasn't one, we're done */
1120 	if (firstChar >= screen_columns)
1121 	    return;
1122 
1123 	/* it may be cheap to clear leading whitespace with clr_bol */
1124 	if (clr_bol && can_clear_with(blank = newLine[0])) {
1125 	    int oFirstChar, nFirstChar;
1126 
1127 	    for (oFirstChar = 0; oFirstChar < screen_columns; oFirstChar++)
1128 		if (oldLine[oFirstChar] != blank)
1129 		    break;
1130 	    for (nFirstChar = 0; nFirstChar < screen_columns; nFirstChar++)
1131 		if (newLine[nFirstChar] != blank)
1132 		    break;
1133 
1134 	    if (nFirstChar > oFirstChar + SP->_el1_cost) {
1135 		if (nFirstChar >= screen_columns && SP->_el_cost <= SP->_el1_cost) {
1136 		    GoTo(lineno, 0);
1137 		    UpdateAttrs(blank);
1138 		    TPUTS_TRACE("clr_eol");
1139 		    putp(clr_eol);
1140 		} else {
1141 		    GoTo(lineno, nFirstChar - 1);
1142 		    UpdateAttrs(blank);
1143 		    TPUTS_TRACE("clr_bol");
1144 		    putp(clr_bol);
1145 		}
1146 
1147 		while (firstChar < nFirstChar)
1148 		    oldLine[firstChar++] = blank;
1149 
1150 		if (firstChar >= screen_columns)
1151 		    return;
1152 	    }
1153 	}
1154 
1155 	blank = newLine[screen_columns - 1];
1156 
1157 	if (!can_clear_with(blank)) {
1158 	    /* find the last differing character */
1159 	    nLastChar = screen_columns - 1;
1160 
1161 	    while (nLastChar > firstChar
1162 		   && newLine[nLastChar] == oldLine[nLastChar])
1163 		nLastChar--;
1164 
1165 	    if (nLastChar >= firstChar) {
1166 		GoTo(lineno, firstChar);
1167 		PutRange(oldLine, newLine, lineno, firstChar, nLastChar);
1168 		memcpy(oldLine + firstChar,
1169 		       newLine + firstChar,
1170 		       (nLastChar - firstChar + 1) * sizeof(chtype));
1171 	    }
1172 	    return;
1173 	}
1174 
1175 	/* find last non-blank character on old line */
1176 	oLastChar = screen_columns - 1;
1177 	while (oLastChar > firstChar && oldLine[oLastChar] == blank)
1178 	    oLastChar--;
1179 
1180 	/* find last non-blank character on new line */
1181 	nLastChar = screen_columns - 1;
1182 	while (nLastChar > firstChar && newLine[nLastChar] == blank)
1183 	    nLastChar--;
1184 
1185 	if ((nLastChar == firstChar)
1186 	    && (SP->_el_cost < (oLastChar - nLastChar))) {
1187 	    GoTo(lineno, firstChar);
1188 	    if (newLine[firstChar] != blank)
1189 		PutChar(newLine[firstChar]);
1190 	    ClrToEOL(blank, FALSE);
1191 	} else if ((nLastChar != oLastChar)
1192 		   && (newLine[nLastChar] != oldLine[oLastChar]
1193 		       || !(_nc_idcok && has_ic()))) {
1194 	    GoTo(lineno, firstChar);
1195 	    if ((oLastChar - nLastChar) > SP->_el_cost) {
1196 		if (PutRange(oldLine, newLine, lineno, firstChar, nLastChar))
1197 		    GoTo(lineno, nLastChar + 1);
1198 		ClrToEOL(blank, FALSE);
1199 	    } else {
1200 		n = max(nLastChar, oLastChar);
1201 		PutRange(oldLine, newLine, lineno, firstChar, n);
1202 	    }
1203 	} else {
1204 	    int nLastNonblank = nLastChar;
1205 	    int oLastNonblank = oLastChar;
1206 
1207 	    /* find the last characters that really differ */
1208 	    while (newLine[nLastChar] == oldLine[oLastChar]) {
1209 		if (nLastChar != 0
1210 		    && oLastChar != 0) {
1211 		    nLastChar--;
1212 		    oLastChar--;
1213 		} else {
1214 		    break;
1215 		}
1216 	    }
1217 
1218 	    n = min(oLastChar, nLastChar);
1219 	    if (n >= firstChar) {
1220 		GoTo(lineno, firstChar);
1221 		PutRange(oldLine, newLine, lineno, firstChar, n);
1222 	    }
1223 
1224 	    if (oLastChar < nLastChar) {
1225 		int m = max(nLastNonblank, oLastNonblank);
1226 		GoTo(lineno, n + 1);
1227 		if (InsCharCost(nLastChar - oLastChar)
1228 		    > (m - n)) {
1229 		    PutRange(oldLine, newLine, lineno, n + 1, m);
1230 		} else {
1231 		    InsStr(&newLine[n + 1], nLastChar - oLastChar);
1232 		}
1233 	    } else if (oLastChar > nLastChar) {
1234 		GoTo(lineno, n + 1);
1235 		if (DelCharCost(oLastChar - nLastChar)
1236 		    > SP->_el_cost + nLastNonblank - (n + 1)) {
1237 		    if (PutRange(oldLine, newLine, lineno,
1238 				 n + 1, nLastNonblank))
1239 			GoTo(lineno, nLastNonblank + 1);
1240 		    ClrToEOL(blank, FALSE);
1241 		} else {
1242 		    /*
1243 		     * The delete-char sequence will
1244 		     * effectively shift in blanks from the
1245 		     * right margin of the screen.  Ensure
1246 		     * that they are the right color by
1247 		     * setting the video attributes from
1248 		     * the last character on the row.
1249 		     */
1250 		    UpdateAttrs(blank);
1251 		    DelChar(oLastChar - nLastChar);
1252 		}
1253 	    }
1254 	}
1255     }
1256 
1257     /* update the code's internal representation */
1258     if (screen_columns > firstChar)
1259 	memcpy(oldLine + firstChar,
1260 	       newLine + firstChar,
1261 	       (screen_columns - firstChar) * sizeof(chtype));
1262 }
1263 
1264 /*
1265 **	ClearScreen(blank)
1266 **
1267 **	Clear the physical screen and put cursor at home
1268 **
1269 */
1270 
1271 static void
1272 ClearScreen(chtype blank)
1273 {
1274     int i, j;
1275     bool fast_clear = (clear_screen || clr_eos || clr_eol);
1276 
1277     TR(TRACE_UPDATE, ("ClearScreen() called"));
1278 
1279 #if NCURSES_EXT_FUNCS
1280     if (SP->_coloron
1281 	&& !SP->_default_color) {
1282 	_nc_do_color(COLOR_PAIR(SP->_current_attr), 0, FALSE, _nc_outch);
1283 	if (!back_color_erase) {
1284 	    fast_clear = FALSE;
1285 	}
1286     }
1287 #endif
1288 
1289     if (fast_clear) {
1290 	if (clear_screen) {
1291 	    UpdateAttrs(blank);
1292 	    TPUTS_TRACE("clear_screen");
1293 	    putp(clear_screen);
1294 	    SP->_cursrow = SP->_curscol = 0;
1295 	    position_check(SP->_cursrow, SP->_curscol, "ClearScreen");
1296 	} else if (clr_eos) {
1297 	    SP->_cursrow = SP->_curscol = -1;
1298 	    GoTo(0, 0);
1299 
1300 	    UpdateAttrs(blank);
1301 	    TPUTS_TRACE("clr_eos");
1302 	    putp(clr_eos);
1303 	} else if (clr_eol) {
1304 	    SP->_cursrow = SP->_curscol = -1;
1305 
1306 	    UpdateAttrs(blank);
1307 	    for (i = 0; i < screen_lines; i++) {
1308 		GoTo(i, 0);
1309 		TPUTS_TRACE("clr_eol");
1310 		putp(clr_eol);
1311 	    }
1312 	    GoTo(0, 0);
1313 	}
1314     } else {
1315 	UpdateAttrs(blank);
1316 	for (i = 0; i < screen_lines; i++) {
1317 	    GoTo(i, 0);
1318 	    for (j = 0; j < screen_columns; j++)
1319 		PutChar(blank);
1320 	}
1321 	GoTo(0, 0);
1322     }
1323 
1324     for (i = 0; i < screen_lines; i++) {
1325 	for (j = 0; j < screen_columns; j++)
1326 	    curscr->_line[i].text[j] = blank;
1327     }
1328 
1329     TR(TRACE_UPDATE, ("screen cleared"));
1330 }
1331 
1332 /*
1333 **	InsStr(line, count)
1334 **
1335 **	Insert the count characters pointed to by line.
1336 **
1337 */
1338 
1339 static void
1340 InsStr(chtype * line, int count)
1341 {
1342     TR(TRACE_UPDATE, ("InsStr(%p,%d) called", line, count));
1343 
1344     /* Prefer parm_ich as it has the smallest cost - no need to shift
1345      * the whole line on each character. */
1346     /* The order must match that of InsCharCost. */
1347     if (parm_ich) {
1348 	TPUTS_TRACE("parm_ich");
1349 	tputs(tparm(parm_ich, count), count, _nc_outch);
1350 	while (count) {
1351 	    PutAttrChar(*line);
1352 	    line++;
1353 	    count--;
1354 	}
1355     } else if (enter_insert_mode && exit_insert_mode) {
1356 	TPUTS_TRACE("enter_insert_mode");
1357 	putp(enter_insert_mode);
1358 	while (count) {
1359 	    PutAttrChar(*line);
1360 	    if (insert_padding) {
1361 		TPUTS_TRACE("insert_padding");
1362 		putp(insert_padding);
1363 	    }
1364 	    line++;
1365 	    count--;
1366 	}
1367 	TPUTS_TRACE("exit_insert_mode");
1368 	putp(exit_insert_mode);
1369     } else {
1370 	while (count) {
1371 	    TPUTS_TRACE("insert_character");
1372 	    putp(insert_character);
1373 	    PutAttrChar(*line);
1374 	    if (insert_padding) {
1375 		TPUTS_TRACE("insert_padding");
1376 		putp(insert_padding);
1377 	    }
1378 	    line++;
1379 	    count--;
1380 	}
1381     }
1382     position_check(SP->_cursrow, SP->_curscol, "InsStr");
1383 }
1384 
1385 /*
1386 **	DelChar(count)
1387 **
1388 **	Delete count characters at current position
1389 **
1390 */
1391 
1392 static void
1393 DelChar(int count)
1394 {
1395     int n;
1396 
1397     TR(TRACE_UPDATE, ("DelChar(%d) called, position = (%d,%d)", count,
1398 		      newscr->_cury, newscr->_curx));
1399 
1400     if (parm_dch) {
1401 	TPUTS_TRACE("parm_dch");
1402 	tputs(tparm(parm_dch, count), count, _nc_outch);
1403     } else {
1404 	for (n = 0; n < count; n++) {
1405 	    TPUTS_TRACE("delete_character");
1406 	    putp(delete_character);
1407 	}
1408     }
1409 }
1410 
1411 /*
1412 **	_nc_outstr(char *str)
1413 **
1414 **	Emit a string without waiting for update.
1415 */
1416 
1417 void
1418 _nc_outstr(const char *str)
1419 {
1420     (void) putp(str);
1421     _nc_flush();
1422 }
1423 
1424 /*
1425  * Physical-scrolling support
1426  *
1427  * This code was adapted from Keith Bostic's hardware scrolling
1428  * support for 4.4BSD curses.  I (esr) translated it to use terminfo
1429  * capabilities, narrowed the call interface slightly, and cleaned
1430  * up some convoluted tests.  I also added support for the memory_above
1431  * memory_below, and non_dest_scroll_region capabilities.
1432  *
1433  * For this code to work, we must have either
1434  * change_scroll_region and scroll forward/reverse commands, or
1435  * insert and delete line capabilities.
1436  * When the scrolling region has been set, the cursor has to
1437  * be at the last line of the region to make the scroll up
1438  * happen, or on the first line of region to scroll down.
1439  *
1440  * This code makes one aesthetic decision in the opposite way from
1441  * BSD curses.  BSD curses preferred pairs of il/dl operations
1442  * over scrolls, allegedly because il/dl looked faster.  We, on
1443  * the other hand, prefer scrolls because (a) they're just as fast
1444  * on many terminals and (b) using them avoids bouncing an
1445  * unchanged bottom section of the screen up and down, which is
1446  * visually nasty.
1447  *
1448  * (lav): added more cases, used dl/il when bot==maxy and in csr case.
1449  *
1450  * I used assumption that capabilities il/il1/dl/dl1 work inside
1451  * changed scroll region not shifting screen contents outside of it.
1452  * If there are any terminals behaving different way, it would be
1453  * necessary to add some conditions to scroll_csr_forward/backward.
1454  */
1455 
1456 /* Try to scroll up assuming given csr (miny, maxy). Returns ERR on failure */
1457 static int
1458 scroll_csr_forward(int n, int top, int bot, int miny, int maxy, chtype blank)
1459 {
1460     int i, j;
1461 
1462     if (n == 1 && scroll_forward && top == miny && bot == maxy) {
1463 	GoTo(bot, 0);
1464 	UpdateAttrs(blank);
1465 	TPUTS_TRACE("scroll_forward");
1466 	tputs(scroll_forward, 0, _nc_outch);
1467     } else if (n == 1 && delete_line && bot == maxy) {
1468 	GoTo(top, 0);
1469 	UpdateAttrs(blank);
1470 	TPUTS_TRACE("delete_line");
1471 	tputs(delete_line, 0, _nc_outch);
1472     } else if (parm_index && top == miny && bot == maxy) {
1473 	GoTo(bot, 0);
1474 	UpdateAttrs(blank);
1475 	TPUTS_TRACE("parm_index");
1476 	tputs(tparm(parm_index, n, 0), n, _nc_outch);
1477     } else if (parm_delete_line && bot == maxy) {
1478 	GoTo(top, 0);
1479 	UpdateAttrs(blank);
1480 	TPUTS_TRACE("parm_delete_line");
1481 	tputs(tparm(parm_delete_line, n, 0), n, _nc_outch);
1482     } else if (scroll_forward && top == miny && bot == maxy) {
1483 	GoTo(bot, 0);
1484 	UpdateAttrs(blank);
1485 	for (i = 0; i < n; i++) {
1486 	    TPUTS_TRACE("scroll_forward");
1487 	    tputs(scroll_forward, 0, _nc_outch);
1488 	}
1489     } else if (delete_line && bot == maxy) {
1490 	GoTo(top, 0);
1491 	UpdateAttrs(blank);
1492 	for (i = 0; i < n; i++) {
1493 	    TPUTS_TRACE("delete_line");
1494 	    tputs(delete_line, 0, _nc_outch);
1495 	}
1496     } else
1497 	return ERR;
1498 
1499 #if NCURSES_EXT_FUNCS
1500     if (FILL_BCE()) {
1501 	for (i = 0; i < n; i++) {
1502 	    GoTo(bot - i, 0);
1503 	    for (j = 0; j < screen_columns; j++)
1504 		PutChar(blank);
1505 	}
1506     }
1507 #endif
1508     return OK;
1509 }
1510 
1511 /* Try to scroll down assuming given csr (miny, maxy). Returns ERR on failure */
1512 /* n > 0 */
1513 static int
1514 scroll_csr_backward(int n, int top, int bot, int miny, int maxy, chtype blank)
1515 {
1516     int i, j;
1517 
1518     if (n == 1 && scroll_reverse && top == miny && bot == maxy) {
1519 	GoTo(top, 0);
1520 	UpdateAttrs(blank);
1521 	TPUTS_TRACE("scroll_reverse");
1522 	tputs(scroll_reverse, 0, _nc_outch);
1523     } else if (n == 1 && insert_line && bot == maxy) {
1524 	GoTo(top, 0);
1525 	UpdateAttrs(blank);
1526 	TPUTS_TRACE("insert_line");
1527 	tputs(insert_line, 0, _nc_outch);
1528     } else if (parm_rindex && top == miny && bot == maxy) {
1529 	GoTo(top, 0);
1530 	UpdateAttrs(blank);
1531 	TPUTS_TRACE("parm_rindex");
1532 	tputs(tparm(parm_rindex, n, 0), n, _nc_outch);
1533     } else if (parm_insert_line && bot == maxy) {
1534 	GoTo(top, 0);
1535 	UpdateAttrs(blank);
1536 	TPUTS_TRACE("parm_insert_line");
1537 	tputs(tparm(parm_insert_line, n, 0), n, _nc_outch);
1538     } else if (scroll_reverse && top == miny && bot == maxy) {
1539 	GoTo(top, 0);
1540 	UpdateAttrs(blank);
1541 	for (i = 0; i < n; i++) {
1542 	    TPUTS_TRACE("scroll_reverse");
1543 	    tputs(scroll_reverse, 0, _nc_outch);
1544 	}
1545     } else if (insert_line && bot == maxy) {
1546 	GoTo(top, 0);
1547 	UpdateAttrs(blank);
1548 	for (i = 0; i < n; i++) {
1549 	    TPUTS_TRACE("insert_line");
1550 	    tputs(insert_line, 0, _nc_outch);
1551 	}
1552     } else
1553 	return ERR;
1554 
1555 #if NCURSES_EXT_FUNCS
1556     if (FILL_BCE()) {
1557 	for (i = 0; i < n; i++) {
1558 	    GoTo(top + i, 0);
1559 	    for (j = 0; j < screen_columns; j++)
1560 		PutChar(blank);
1561 	}
1562     }
1563 #endif
1564     return OK;
1565 }
1566 
1567 /* scroll by using delete_line at del and insert_line at ins */
1568 /* n > 0 */
1569 static int
1570 scroll_idl(int n, int del, int ins, chtype blank)
1571 {
1572     int i;
1573 
1574     if (!((parm_delete_line || delete_line) && (parm_insert_line || insert_line)))
1575 	return ERR;
1576 
1577     GoTo(del, 0);
1578     UpdateAttrs(blank);
1579     if (n == 1 && delete_line) {
1580 	TPUTS_TRACE("delete_line");
1581 	tputs(delete_line, 0, _nc_outch);
1582     } else if (parm_delete_line) {
1583 	TPUTS_TRACE("parm_delete_line");
1584 	tputs(tparm(parm_delete_line, n, 0), n, _nc_outch);
1585     } else {			/* if (delete_line) */
1586 	for (i = 0; i < n; i++) {
1587 	    TPUTS_TRACE("delete_line");
1588 	    tputs(delete_line, 0, _nc_outch);
1589 	}
1590     }
1591 
1592     GoTo(ins, 0);
1593     UpdateAttrs(blank);
1594     if (n == 1 && insert_line) {
1595 	TPUTS_TRACE("insert_line");
1596 	tputs(insert_line, 0, _nc_outch);
1597     } else if (parm_insert_line) {
1598 	TPUTS_TRACE("parm_insert_line");
1599 	tputs(tparm(parm_insert_line, n, 0), n, _nc_outch);
1600     } else {			/* if (insert_line) */
1601 	for (i = 0; i < n; i++) {
1602 	    TPUTS_TRACE("insert_line");
1603 	    tputs(insert_line, 0, _nc_outch);
1604 	}
1605     }
1606 
1607     return OK;
1608 }
1609 
1610 int
1611 _nc_scrolln(int n, int top, int bot, int maxy)
1612 /* scroll region from top to bot by n lines */
1613 {
1614     chtype blank = ClrBlank(stdscr);
1615     int i;
1616     bool cursor_saved = FALSE;
1617     int res;
1618 
1619     TR(TRACE_MOVE, ("mvcur_scrolln(%d, %d, %d, %d)", n, top, bot, maxy));
1620 
1621 #if USE_XMC_SUPPORT
1622     /*
1623      * If we scroll, we might remove a cookie.
1624      */
1625     if (magic_cookie_glitch > 0) {
1626 	return (ERR);
1627     }
1628 #endif
1629 
1630     if (n > 0) {		/* scroll up (forward) */
1631 	/*
1632 	 * Explicitly clear if stuff pushed off top of region might
1633 	 * be saved by the terminal.
1634 	 */
1635 	res = scroll_csr_forward(n, top, bot, 0, maxy, blank);
1636 
1637 	if (res == ERR && change_scroll_region) {
1638 	    if ((((n == 1 && scroll_forward) || parm_index)
1639 		 && (SP->_cursrow == bot || SP->_cursrow == bot - 1))
1640 		&& save_cursor && restore_cursor) {
1641 		cursor_saved = TRUE;
1642 		TPUTS_TRACE("save_cursor");
1643 		tputs(save_cursor, 0, _nc_outch);
1644 	    }
1645 	    TPUTS_TRACE("change_scroll_region");
1646 	    tputs(tparm(change_scroll_region, top, bot), 0, _nc_outch);
1647 	    if (cursor_saved) {
1648 		TPUTS_TRACE("restore_cursor");
1649 		tputs(restore_cursor, 0, _nc_outch);
1650 	    } else {
1651 		SP->_cursrow = SP->_curscol = -1;
1652 	    }
1653 
1654 	    res = scroll_csr_forward(n, top, bot, top, bot, blank);
1655 
1656 	    TPUTS_TRACE("change_scroll_region");
1657 	    tputs(tparm(change_scroll_region, 0, maxy), 0, _nc_outch);
1658 	    SP->_cursrow = SP->_curscol = -1;
1659 	}
1660 
1661 	if (res == ERR && _nc_idlok)
1662 	    res = scroll_idl(n, top, bot - n + 1, blank);
1663 
1664 	/*
1665 	 * Clear the newly shifted-in text.
1666 	 */
1667 	if (res != ERR
1668 	    && (non_dest_scroll_region || (memory_below && bot == maxy))) {
1669 	    if (bot == maxy && clr_eos) {
1670 		GoTo(bot - n, 0);
1671 		ClrToEOS(BLANK);
1672 	    } else {
1673 		for (i = 0; i < n; i++) {
1674 		    GoTo(bot - i, 0);
1675 		    ClrToEOL(BLANK, FALSE);
1676 		}
1677 	    }
1678 	}
1679 
1680     } else {			/* (n < 0) - scroll down (backward) */
1681 	res = scroll_csr_backward(-n, top, bot, 0, maxy, blank);
1682 
1683 	if (res == ERR && change_scroll_region) {
1684 	    if (top != 0 && (SP->_cursrow == top || SP->_cursrow == top - 1)
1685 		&& save_cursor && restore_cursor) {
1686 		cursor_saved = TRUE;
1687 		TPUTS_TRACE("save_cursor");
1688 		tputs(save_cursor, 0, _nc_outch);
1689 	    }
1690 	    TPUTS_TRACE("change_scroll_region");
1691 	    tputs(tparm(change_scroll_region, top, bot), 0, _nc_outch);
1692 	    if (cursor_saved) {
1693 		TPUTS_TRACE("restore_cursor");
1694 		tputs(restore_cursor, 0, _nc_outch);
1695 	    } else {
1696 		SP->_cursrow = SP->_curscol = -1;
1697 	    }
1698 
1699 	    res = scroll_csr_backward(-n, top, bot, top, bot, blank);
1700 
1701 	    TPUTS_TRACE("change_scroll_region");
1702 	    tputs(tparm(change_scroll_region, 0, maxy), 0, _nc_outch);
1703 	    SP->_cursrow = SP->_curscol = -1;
1704 	}
1705 
1706 	if (res == ERR && _nc_idlok)
1707 	    res = scroll_idl(-n, bot + n + 1, top, blank);
1708 
1709 	/*
1710 	 * Clear the newly shifted-in text.
1711 	 */
1712 	if (res != ERR
1713 	    && (non_dest_scroll_region || (memory_above && top == 0))) {
1714 	    for (i = 0; i < -n; i++) {
1715 		GoTo(i + top, 0);
1716 		ClrToEOL(BLANK, FALSE);
1717 	    }
1718 	}
1719     }
1720 
1721     if (res == ERR)
1722 	return (ERR);
1723 
1724     _nc_scroll_window(curscr, n, top, bot, blank);
1725 
1726     /* shift hash values too - they can be reused */
1727     _nc_scroll_oldhash(n, top, bot);
1728 
1729     return (OK);
1730 }
1731 
1732 void
1733 _nc_screen_resume(void)
1734 {
1735     /* make sure terminal is in a sane known state */
1736     SP->_current_attr = A_NORMAL;
1737     newscr->_clear = TRUE;
1738 
1739     if (SP->_coloron == TRUE && orig_pair)
1740 	putp(orig_pair);
1741     if (exit_attribute_mode)
1742 	putp(exit_attribute_mode);
1743     else {
1744 	/* turn off attributes */
1745 	if (exit_alt_charset_mode)
1746 	    putp(exit_alt_charset_mode);
1747 	if (exit_standout_mode)
1748 	    putp(exit_standout_mode);
1749 	if (exit_underline_mode)
1750 	    putp(exit_underline_mode);
1751     }
1752     if (exit_insert_mode)
1753 	putp(exit_insert_mode);
1754     if (enter_am_mode && exit_am_mode)
1755 	putp(auto_right_margin ? enter_am_mode : exit_am_mode);
1756 }
1757 
1758 void
1759 _nc_screen_init(void)
1760 {
1761     _nc_screen_resume();
1762 }
1763 
1764 /* wrap up screen handling */
1765 void
1766 _nc_screen_wrap(void)
1767 {
1768     UpdateAttrs(A_NORMAL);
1769 #if NCURSES_EXT_FUNCS
1770     if (SP->_coloron
1771 	&& !SP->_default_color) {
1772 	SP->_default_color = TRUE;
1773 	_nc_do_color(-1, 0, FALSE, _nc_outch);
1774 	SP->_default_color = FALSE;
1775 
1776 	mvcur(SP->_cursrow, SP->_curscol, screen_lines - 1, 0);
1777 	SP->_cursrow = screen_lines - 1;
1778 	SP->_curscol = 0;
1779 
1780 	ClrToEOL(BLANK, TRUE);
1781     }
1782 #endif
1783 }
1784 
1785 #if USE_XMC_SUPPORT
1786 void
1787 _nc_do_xmc_glitch(attr_t previous)
1788 {
1789     attr_t chg = XMC_CHANGES(previous ^ SP->_current_attr);
1790 
1791     while (chg != 0) {
1792 	if (chg & 1) {
1793 	    SP->_curscol += magic_cookie_glitch;
1794 	    if (SP->_curscol >= SP->_columns)
1795 		wrap_cursor();
1796 	    TR(TRACE_UPDATE, ("bumped to %d,%d after cookie", SP->_cursrow, SP->_curscol));
1797 	}
1798 	chg >>= 1;
1799     }
1800 }
1801 #endif /* USE_XMC_SUPPORT */
1802