298c11ed0a2117de160ba86445f5245a77872eee
[ncurses.git] / ncurses / tty / tty_update.c
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 */