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