Home | History | Annotate | Line # | Download | only in libcurses
refresh.c revision 1.13
      1 /*	$NetBSD: refresh.c,v 1.13 1999/06/28 13:27:24 simonb Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1981, 1993, 1994
      5  *	The Regents of the University of California.  All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. All advertising materials mentioning features or use of this software
     16  *    must display the following acknowledgement:
     17  *	This product includes software developed by the University of
     18  *	California, Berkeley and its contributors.
     19  * 4. Neither the name of the University nor the names of its contributors
     20  *    may be used to endorse or promote products derived from this software
     21  *    without specific prior written permission.
     22  *
     23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     33  * SUCH DAMAGE.
     34  */
     35 
     36 #include <sys/cdefs.h>
     37 #ifndef lint
     38 #if 0
     39 static char sccsid[] = "@(#)refresh.c	8.7 (Berkeley) 8/13/94";
     40 #else
     41 __RCSID("$NetBSD: refresh.c,v 1.13 1999/06/28 13:27:24 simonb Exp $");
     42 #endif
     43 #endif				/* not lint */
     44 
     45 #include <string.h>
     46 
     47 #include "curses.h"
     48 
     49 static int curwin;
     50 static short ly, lx;
     51 
     52 static void	domvcur __P((int, int, int, int));
     53 static int	makech __P((WINDOW *, int));
     54 static void	quickch __P((WINDOW *));
     55 static void	scrolln __P((WINDOW *, int, int, int, int, int));
     56 
     57 /*
     58  * wrefresh --
     59  *	Make the current screen look like "win" over the area coverd by
     60  *	win.
     61  */
     62 int
     63 wrefresh(win)
     64 	WINDOW *win;
     65 {
     66 	__LINE *wlp;
     67 	int	retval;
     68 	short	wy;
     69 	int	dnum;
     70 
     71 	/* Check if we need to restart ... */
     72 	if (__endwin) {
     73 		__endwin = 0;
     74 		__restartwin();
     75 	}
     76 
     77 	/* Initialize loop parameters. */
     78 	ly = curscr->cury;
     79 	lx = curscr->curx;
     80 	wy = 0;
     81 	curwin = (win == curscr);
     82 
     83 	if (!curwin)
     84 		for (wy = 0; wy < win->maxy; wy++) {
     85 			wlp = win->lines[wy];
     86 			if (wlp->flags & __ISDIRTY)
     87 				wlp->hash = __hash((char *)(void *)wlp->line,
     88 				    (int) (win->maxx * __LDATASIZE));
     89 		}
     90 
     91 	if (win->flags & __CLEAROK || curscr->flags & __CLEAROK || curwin) {
     92 		if ((win->flags & __FULLWIN) || curscr->flags & __CLEAROK) {
     93 			tputs(CL, 0, __cputchar);
     94 			ly = 0;
     95 			lx = 0;
     96 			if (!curwin) {
     97 				curscr->flags &= ~__CLEAROK;
     98 				curscr->cury = 0;
     99 				curscr->curx = 0;
    100 				werase(curscr);
    101 			}
    102 			__touchwin(win);
    103 		}
    104 		win->flags &= ~__CLEAROK;
    105 	}
    106 	if (!CA) {
    107 		if (win->curx != 0)
    108 			putchar('\n');
    109 		if (!curwin)
    110 			werase(curscr);
    111 	}
    112 #ifdef DEBUG
    113 	__CTRACE("wrefresh: (%0.2o): curwin = %d\n", win, curwin);
    114 	__CTRACE("wrefresh: \tfirstch\tlastch\n");
    115 #endif
    116 
    117 	if ((win->flags & __FULLWIN) && !curwin) {
    118 		/*
    119 		 * Invoke quickch() only if more than a quarter of the lines
    120 		 * in the window are dirty.
    121 		 */
    122 		for (wy = 0, dnum = 0; wy < win->maxy; wy++)
    123 			if (win->lines[wy]->flags & (__ISDIRTY | __FORCEPAINT))
    124 				dnum++;
    125 		if (!__noqch && dnum > (int) win->maxy / 4)
    126 			quickch(win);
    127 	}
    128 
    129 #ifdef DEBUG
    130 	{
    131 		int	i, j;
    132 
    133 		__CTRACE("#####################################\n");
    134 		for (i = 0; i < curscr->maxy; i++) {
    135 			__CTRACE("C: %d:", i);
    136 			__CTRACE(" 0x%x \n", curscr->lines[i]->hash);
    137 			for (j = 0; j < curscr->maxx; j++)
    138 				__CTRACE("%c", curscr->lines[i]->line[j].ch);
    139 			__CTRACE("\n");
    140 			for (j = 0; j < curscr->maxx; j++)
    141 				__CTRACE("%x", curscr->lines[i]->line[j].attr);
    142 			__CTRACE("\n");
    143 			__CTRACE("W: %d:", i);
    144 			__CTRACE(" 0x%x \n", win->lines[i]->hash);
    145 			__CTRACE(" 0x%x ", win->lines[i]->flags);
    146 			for (j = 0; j < win->maxx; j++)
    147 				__CTRACE("%c", win->lines[i]->line[j].ch);
    148 			__CTRACE("\n");
    149 			for (j = 0; j < win->maxx; j++)
    150 				__CTRACE("%x", win->lines[i]->line[j].attr);
    151 			__CTRACE("\n");
    152 		}
    153 	}
    154 #endif				/* DEBUG */
    155 
    156 	for (wy = 0; wy < win->maxy; wy++) {
    157 #ifdef DEBUG
    158 		__CTRACE("wy %d\tf: %d\tl:%d\tflags %x\n",
    159 		    wy, *win->lines[wy]->firstchp, *win->lines[wy]->lastchp,
    160 		    win->lines[wy]->flags);
    161 #endif
    162 		if (!curwin)
    163 			curscr->lines[wy]->hash = win->lines[wy]->hash;
    164 		if (win->lines[wy]->flags & (__ISDIRTY | __FORCEPAINT)) {
    165 			if (makech(win, wy) == ERR)
    166 				return (ERR);
    167 			else {
    168 				if (*win->lines[wy]->firstchp >= win->ch_off)
    169 					*win->lines[wy]->firstchp = win->maxx +
    170 					    win->ch_off;
    171 				if (*win->lines[wy]->lastchp < win->maxx +
    172 				    win->ch_off)
    173 					*win->lines[wy]->lastchp = win->ch_off;
    174 				if (*win->lines[wy]->lastchp <
    175 				    *win->lines[wy]->firstchp) {
    176 #ifdef DEBUG
    177 					__CTRACE("wrefresh: line %d notdirty \n", wy);
    178 #endif
    179 					win->lines[wy]->flags &= ~__ISDIRTY;
    180 				}
    181 			}
    182 
    183 		}
    184 #ifdef DEBUG
    185 		__CTRACE("\t%d\t%d\n", *win->lines[wy]->firstchp,
    186 		    *win->lines[wy]->lastchp);
    187 #endif
    188 	}
    189 
    190 #ifdef DEBUG
    191 	__CTRACE("refresh: ly=%d, lx=%d\n", ly, lx);
    192 #endif
    193 
    194 	if (win == curscr)
    195 		domvcur(ly, lx, (int) win->cury, (int) win->curx);
    196 	else {
    197 		if (win->flags & __LEAVEOK) {
    198 			curscr->cury = ly;
    199 			curscr->curx = lx;
    200 			ly -= win->begy;
    201 			lx -= win->begx;
    202 			if (ly >= 0 && ly < win->maxy && lx >= 0 &&
    203 			    lx < win->maxx) {
    204 				win->cury = ly;
    205 				win->curx = lx;
    206 			} else
    207 				win->cury = win->curx = 0;
    208 		} else {
    209 			domvcur(ly, lx, (int) (win->cury + win->begy),
    210 			    (int) (win->curx + win->begx));
    211 			curscr->cury = win->cury + win->begy;
    212 			curscr->curx = win->curx + win->begx;
    213 		}
    214 	}
    215 	retval = OK;
    216 
    217 	(void) fflush(stdout);
    218 	return (retval);
    219 }
    220 
    221 /*
    222  * makech --
    223  *	Make a change on the screen.
    224  */
    225 static int
    226 makech(win, wy)
    227 	WINDOW *win;
    228 	int	wy;
    229 {
    230 	static __LDATA blank = {' ', 0};
    231 	__LDATA *nsp, *csp, *cp, *cep;
    232 	u_int	force;
    233 	int	clsp, nlsp;	/* Last space in lines. */
    234 	int	lch, wx, y;
    235 	char	*ce;
    236 
    237 #ifdef __GNUC__
    238 	nlsp = 0;		/* XXX gcc -Wuninitialized */
    239 #endif
    240 	/* Is the cursor still on the end of the last line? */
    241 	if (wy > 0 && win->lines[wy - 1]->flags & __ISPASTEOL) {
    242 		domvcur(ly, lx, ly + 1, 0);
    243 		ly++;
    244 		lx = 0;
    245 	}
    246 	wx = *win->lines[wy]->firstchp - win->ch_off;
    247 	if (wx < 0)
    248 		wx = 0;
    249 	else
    250 		if (wx >= win->maxx)
    251 			return (OK);
    252 	lch = *win->lines[wy]->lastchp - win->ch_off;
    253 	if (lch < 0)
    254 		return (OK);
    255 	else
    256 		if (lch >= (int) win->maxx)
    257 			lch = win->maxx - 1;
    258 	y = wy + win->begy;
    259 
    260 	if (curwin)
    261 		csp = &blank;
    262 	else
    263 		csp = &curscr->lines[wy + win->begy]->line[wx + win->begx];
    264 
    265 	nsp = &win->lines[wy]->line[wx];
    266 	force = win->lines[wy]->flags & __FORCEPAINT;
    267 	win->lines[wy]->flags &= ~__FORCEPAINT;
    268 	if (CE && !curwin) {
    269 		for (cp = &win->lines[wy]->line[win->maxx - 1];
    270 		    cp->ch == ' ' && cp->attr == 0; cp--)
    271 			if (cp <= win->lines[wy]->line)
    272 				break;
    273 		nlsp = cp - win->lines[wy]->line;
    274 	}
    275 	if (!curwin)
    276 		ce = CE;
    277 	else
    278 		ce = NULL;
    279 
    280 	if (force) {
    281 		if (CM)
    282 			tputs(tgoto(CM, lx, ly), 0, __cputchar);
    283 		else {
    284 			tputs(HO, 0, __cputchar);
    285 			__mvcur(0, 0, ly, lx, 1);
    286 		}
    287 	}
    288 
    289 	while (wx <= lch) {
    290 		if (!force && memcmp(nsp, csp, sizeof(__LDATA)) == 0) {
    291 			if (wx <= lch) {
    292 				while (wx <= lch &&
    293 				    memcmp(nsp, csp, sizeof(__LDATA)) == 0) {
    294 					nsp++;
    295 					if (!curwin)
    296 						++csp;
    297 					++wx;
    298 				}
    299 				continue;
    300 			}
    301 			break;
    302 		}
    303 		domvcur(ly, lx, y, (int) (wx + win->begx));
    304 
    305 #ifdef DEBUG
    306 		__CTRACE("makech: 1: wx = %d, ly= %d, lx = %d, newy = %d, newx = %d, force =%d\n",
    307 		    wx, ly, lx, y, wx + win->begx, force);
    308 #endif
    309 		ly = y;
    310 		lx = wx + win->begx;
    311 		while ((force || memcmp(nsp, csp, sizeof(__LDATA)) != 0)
    312 		    && wx <= lch) {
    313 
    314 			if (ce != NULL &&
    315 			    win->maxx + win->begx == curscr->maxx &&
    316 			    wx >= nlsp && nsp->ch == ' ' && nsp->attr == 0) {
    317 				/* Check for clear to end-of-line. */
    318 				cep = &curscr->lines[wy]->line[win->maxx - 1];
    319 				while (cep->ch == ' ' && cep->attr == 0)
    320 					if (cep-- <= csp)
    321 						break;
    322 				clsp = cep - curscr->lines[wy]->line -
    323 				    win->begx * __LDATASIZE;
    324 #ifdef DEBUG
    325 				__CTRACE("makech: clsp = %d, nlsp = %d\n", clsp, nlsp);
    326 #endif
    327 				if ((clsp - nlsp >= strlen(CE)
    328 				    && clsp < win->maxx * __LDATASIZE) ||
    329 				    wy == win->maxy - 1) {
    330 					if ((curscr->flags & __WSTANDOUT) &&
    331 					    SE != NULL && UE != NULL &&
    332 					    ME != NULL) {
    333 						tputs(SE, 0, __cputchar);
    334 						curscr->flags &= ~__WSTANDOUT;
    335 						if (*SE == *UE) {
    336 							curscr->flags &=
    337 						    ~__WUNDERSCORE;
    338 						}
    339 						if (*SE == *ME) {
    340 							curscr->flags &=
    341 						    ~__WATTRIBUTES;
    342 						}
    343 					}
    344 					if ((curscr->flags & __WUNDERSCORE) &&
    345 					    UE != NULL && ME != NULL) {
    346 						tputs(UE, 0, __cputchar);
    347 						curscr->flags &= ~__WUNDERSCORE;
    348 						if (*UE == *ME) {
    349 							curscr->flags &=
    350 						    ~__WATTRIBUTES;
    351 						}
    352 					}
    353 					if ((curscr->flags & __WATTRIBUTES) &&
    354 					    UE != NULL) {
    355 						tputs(UE, 0, __cputchar);
    356 						curscr->flags &= ~__WATTRIBUTES;
    357 					}
    358 					tputs(CE, 0, __cputchar);
    359 					lx = wx + win->begx;
    360 					while (wx++ <= clsp) {
    361 						csp->ch = ' ';
    362 						csp->attr = 0;
    363 						csp++;
    364 					}
    365 					return (OK);
    366 				}
    367 				ce = NULL;
    368 			}
    369 
    370 			/*
    371 			 * Unset attributes as appropriate.  Unset first
    372 			 * so that the relevant attributes can be reset
    373 			 * (because termcap 'me' unsets 'mb', 'md', 'mh',
    374 			 * 'mk', 'mp' and 'mr').  However, 'me' might also
    375 			 * do 'se' and/or 'ue'.  If so, we change the
    376 			 * screen flags to reflect this.
    377 			 */
    378 			if ((!(nsp->attr & __BLINK) &&
    379 			    curscr->flags & __WBLINK) ||
    380 			    (!(nsp->attr & __BOLD) &&
    381 			    curscr->flags & __WBOLD) ||
    382 			    (!(nsp->attr & __DIM) &&
    383 			    curscr->flags & __WDIM) ||
    384 			    (!(nsp->attr & __BLANK) &&
    385 			    curscr->flags & __WBLANK) ||
    386 			    (!(nsp->attr & __PROTECT) &&
    387 			    curscr->flags & __WPROTECT) ||
    388 			    (!(nsp->attr & __REVERSE) &&
    389 			    curscr->flags & __WREVERSE) ||
    390 			    (!(nsp->attr & __BOLD) &&
    391 			    curscr->flags & __WBOLD)) {
    392 				if (ME != NULL && SE != NULL && UE != NULL) {
    393 					tputs(ME, 0, __cputchar);
    394 					curscr->flags &= ~__WATTRIBUTES;
    395 					if (*ME == *SE) {
    396 						curscr->flags &= ~__WSTANDOUT;
    397 					}
    398 					if (*ME == *UE) {
    399 						curscr->flags &= ~__WUNDERSCORE;
    400 					}
    401 				}
    402 			}
    403 
    404 			/*
    405 			 * Exit underscore mode if appropriate.
    406 			 *
    407 			 * Note: check to see if we also turn off attributes
    408 			 * and standout.
    409 			 */
    410 			if (!(nsp->attr & __UNDERSCORE) &&
    411 			    (curscr->flags & __WUNDERSCORE) &&
    412 			    UE != NULL && ME != NULL && SE != NULL) {
    413 				tputs(UE, 0, __cputchar);
    414 				curscr->flags &= ~__WUNDERSCORE;
    415 				if (*UE == *ME) {
    416 					curscr->flags &= ~__WATTRIBUTES;
    417 				}
    418 				if (*UE == *SE) {
    419 					curscr->flags &= ~__WSTANDOUT;
    420 				}
    421 			}
    422 
    423 			/*
    424 			 * Enter/exit standout mode as appropriate.
    425 			 * XXX
    426 			 * Should use UC if SO/SE not available.
    427 			 *
    428 			 * Note: check to see if we also turn off attributes
    429 			 * and underscore.  If we have turned off underscore
    430 			 * and it should be on, turn it back on.
    431 			 */
    432 			if (nsp->attr & __STANDOUT) {
    433 				if (!(curscr->flags & __WSTANDOUT) &&
    434 				    SO != NULL && SE != NULL) {
    435 					tputs(SO, 0, __cputchar);
    436 					curscr->flags |= __WSTANDOUT;
    437 				}
    438 			} else {
    439 				if ((curscr->flags & __WSTANDOUT) &&
    440 				    SE != NULL && ME != NULL && UE != NULL) {
    441 					tputs(SE, 0, __cputchar);
    442 					curscr->flags &= ~__WSTANDOUT;
    443 					if (*SE == *ME) {
    444 						curscr->flags &= ~__WATTRIBUTES;
    445 					}
    446 					if (*SE == *UE) {
    447 						curscr->flags &= ~__WUNDERSCORE;
    448 					}
    449 				}
    450 			}
    451 
    452 			/*
    453 			 * Enter underscore mode if appropriate.
    454 			 * XXX
    455 			 * Should use UC if US/UE not available.
    456 			 *
    457 			 * Note: check to see if we also turn off attributes
    458 			 * and standout.
    459 			 */
    460 			if (nsp->attr & __UNDERSCORE &&
    461 			    !(curscr->flags & __WUNDERSCORE) &&
    462 			    US != NULL && UE != NULL) {
    463 				tputs(US, 0, __cputchar);
    464 				curscr->flags |= __WUNDERSCORE;
    465 			}
    466 
    467 			/*
    468 			 * Set other attributes as appropriate.
    469 			 */
    470 			if (nsp->attr & __REVERSE) {
    471 				if (!(curscr->flags & __WREVERSE) &&
    472 				    MR != NULL && ME != NULL) {
    473 					tputs(MR, 0, __cputchar);
    474 					curscr->flags |= __WREVERSE;
    475 				}
    476 			}
    477 			if (nsp->attr & __BLINK) {
    478 				if (!(curscr->flags & __WBLINK) &&
    479 				    MB != NULL && ME != NULL) {
    480 					tputs(MB, 0, __cputchar);
    481 					curscr->flags |= __WBLINK;
    482 				}
    483 			}
    484 			if (nsp->attr & __DIM) {
    485 				if (!(curscr->flags & __WDIM) &&
    486 				    MH != NULL && ME != NULL) {
    487 					tputs(MH, 0, __cputchar);
    488 					curscr->flags |= __WDIM;
    489 				}
    490 			}
    491 			if (nsp->attr & __BOLD) {
    492 				if (!(curscr->flags & __WBOLD) &&
    493 				    MD != NULL && ME != NULL) {
    494 					tputs(MD, 0, __cputchar);
    495 					curscr->flags |= __WBOLD;
    496 				}
    497 			}
    498 			if (nsp->attr & __BLANK) {
    499 				if (!(curscr->flags & __WBLANK) &&
    500 				    MK != NULL && ME != NULL) {
    501 					tputs(MK, 0, __cputchar);
    502 					curscr->flags |= __WBLANK;
    503 				}
    504 			}
    505 			if (nsp->attr & __PROTECT) {
    506 				if (!(curscr->flags & __WPROTECT) &&
    507 				    MP != NULL && ME != NULL) {
    508 					tputs(MP, 0, __cputchar);
    509 					curscr->flags |= __WPROTECT;
    510 				}
    511 			}
    512 
    513 			wx++;
    514 			if (wx >= win->maxx && wy == win->maxy - 1 && !curwin)
    515 				if (win->flags & __SCROLLOK) {
    516 					if (curscr->flags & __WSTANDOUT
    517 					    && win->flags & __ENDLINE)
    518 						if (!MS) {
    519 							tputs(SE, 0,
    520 							    __cputchar);
    521 							curscr->flags &=
    522 							    ~__WSTANDOUT;
    523 						}
    524 					if (curscr->flags & __WUNDERSCORE
    525 					    && win->flags & __ENDLINE)
    526 						if (!MS) {
    527 							tputs(UE, 0,
    528 							    __cputchar);
    529 							curscr->flags &=
    530 							    ~__WUNDERSCORE;
    531 						}
    532 					if (curscr->flags & __WATTRIBUTES
    533 					    && win->flags & __ENDLINE)
    534 						if (!MS) {
    535 							tputs(ME, 0,
    536 							    __cputchar);
    537 							curscr->flags &=
    538 							    ~__WATTRIBUTES;
    539 						}
    540 					if (!(win->flags & __SCROLLWIN)) {
    541 						if (!curwin) {
    542 							csp->attr = nsp->attr;
    543 							putchar(csp->ch = nsp->ch);
    544 						} else
    545 							putchar(nsp->ch);
    546 					}
    547 					if (wx + win->begx < curscr->maxx) {
    548 						domvcur(ly, (int) (wx + win->begx),
    549 						    (int) (win->begy + win->maxy - 1),
    550 						    (int) (win->begx + win->maxx - 1));
    551 					}
    552 					ly = win->begy + win->maxy - 1;
    553 					lx = win->begx + win->maxx - 1;
    554 					return (OK);
    555 				}
    556 			if (wx < win->maxx || wy < win->maxy - 1 ||
    557 			    !(win->flags & __SCROLLWIN)) {
    558 				if (!curwin) {
    559 					csp->attr = nsp->attr;
    560 					putchar(csp->ch = nsp->ch);
    561 					csp++;
    562 				} else
    563 					putchar(nsp->ch);
    564 			}
    565 #ifdef DEBUG
    566 			__CTRACE("makech: putchar(%c)\n", nsp->ch & 0177);
    567 #endif
    568 			if (UC && ((nsp->attr & __STANDOUT) ||
    569 			    (nsp->attr & __UNDERSCORE))) {
    570 				putchar('\b');
    571 				tputs(UC, 0, __cputchar);
    572 			}
    573 			nsp++;
    574 #ifdef DEBUG
    575 			__CTRACE("makech: 2: wx = %d, lx = %d\n", wx, lx);
    576 #endif
    577 		}
    578 		if (lx == wx + win->begx)	/* If no change. */
    579 			break;
    580 		lx = wx + win->begx;
    581 		if (lx >= COLS && AM)
    582 			lx = COLS - 1;
    583 		else
    584 			if (wx >= win->maxx) {
    585 				domvcur(ly, lx, ly, (int) (win->maxx + win->begx - 1));
    586 				lx = win->maxx + win->begx - 1;
    587 			}
    588 #ifdef DEBUG
    589 		__CTRACE("makech: 3: wx = %d, lx = %d\n", wx, lx);
    590 #endif
    591 	}
    592 
    593 	/* Don't leave the screen in standout mode. */
    594 	if (curscr->flags & __WSTANDOUT) {
    595 		tputs(SE, 0, __cputchar);
    596 		curscr->flags &= ~__WSTANDOUT;
    597 	}
    598 	/* Don't leave the screen in underscore mode. */
    599 	if (curscr->flags & __WUNDERSCORE) {
    600 		tputs(UE, 0, __cputchar);
    601 		curscr->flags &= ~__WUNDERSCORE;
    602 	}
    603 	/* Don't leave the screen with attributes set. */
    604 	if (curscr->flags & __WATTRIBUTES) {
    605 		tputs(ME, 0, __cputchar);
    606 		curscr->flags &= ~__WATTRIBUTES;
    607 	}
    608 	return (OK);
    609 }
    610 
    611 /*
    612  * domvcur --
    613  *	Do a mvcur, leaving standout and attribute modes if necessary.
    614  */
    615 static void
    616 domvcur(oy, ox, ny, nx)
    617 	int	oy, ox, ny, nx;
    618 {
    619 	if (curscr->flags & __WSTANDOUT && !MS && SE) {
    620 		tputs(SE, 0, __cputchar);
    621 		curscr->flags &= ~__WSTANDOUT;
    622 	}
    623 	if (curscr->flags & __WUNDERSCORE && !MS && UE) {
    624 		tputs(UE, 0, __cputchar);
    625 		curscr->flags &= ~__WUNDERSCORE;
    626 	}
    627 	if (curscr->flags & __WATTRIBUTES && !MS && ME) {
    628 		tputs(ME, 0, __cputchar);
    629 		curscr->flags &= ~__WATTRIBUTES;
    630 	}
    631 
    632 	__mvcur(oy, ox, ny, nx, 1);
    633 }
    634 
    635 /*
    636  * Quickch() attempts to detect a pattern in the change of the window
    637  * in order to optimize the change, e.g., scroll n lines as opposed to
    638  * repainting the screen line by line.
    639  */
    640 
    641 static void
    642 quickch(win)
    643 	WINDOW *win;
    644 {
    645 #define THRESH		(int) win->maxy / 4
    646 
    647 	__LINE *clp, *tmp1, *tmp2;
    648 	int	bsize, curs, curw, starts, startw, i, j;
    649 	int	n, target, cur_period, bot, top, sc_region;
    650 	__LDATA buf[1024];
    651 	u_int	blank_hash;
    652 
    653 #ifdef __GNUC__
    654 	curs = curw = starts = startw = 0;	/* XXX gcc -Wuninitialized */
    655 #endif
    656 	/*
    657 	 * Find how many lines from the top of the screen are unchanged.
    658 	 */
    659 	for (top = 0; top < win->maxy; top++)
    660 		if (win->lines[top]->flags & __FORCEPAINT ||
    661 		    win->lines[top]->hash != curscr->lines[top]->hash
    662 		    || memcmp(win->lines[top]->line,
    663 			curscr->lines[top]->line,
    664 			win->maxx * __LDATASIZE) != 0)
    665 			break;
    666 		else
    667 			win->lines[top]->flags &= ~__ISDIRTY;
    668 	/*
    669 	 * Find how many lines from bottom of screen are unchanged.
    670 	 */
    671 	for (bot = win->maxy - 1; bot >= 0; bot--)
    672 		if (win->lines[bot]->flags & __FORCEPAINT ||
    673 		    win->lines[bot]->hash != curscr->lines[bot]->hash
    674 		    || memcmp(win->lines[bot]->line,
    675 			curscr->lines[bot]->line,
    676 			win->maxx * __LDATASIZE) != 0)
    677 			break;
    678 		else
    679 			win->lines[bot]->flags &= ~__ISDIRTY;
    680 
    681 #ifdef NO_JERKINESS
    682 	/*
    683 	 * If we have a bottom unchanged region return.  Scrolling the
    684 	 * bottom region up and then back down causes a screen jitter.
    685 	 * This will increase the number of characters sent to the screen
    686 	 * but it looks better.
    687 	 */
    688 	if (bot < win->maxy - 1)
    689 		return;
    690 #endif				/* NO_JERKINESS */
    691 
    692 	/*
    693 	 * Search for the largest block of text not changed.
    694 	 * Invariants of the loop:
    695 	 * - Startw is the index of the beginning of the examined block in win.
    696 	 * - Starts is the index of the beginning of the examined block in
    697 	 *    curscr.
    698 	 * - Curs is the index of one past the end of the exmined block in win.
    699 	 * - Curw is the index of one past the end of the exmined block in
    700 	 *   curscr.
    701 	 * - bsize is the current size of the examined block.
    702 	*/
    703 	for (bsize = bot - top; bsize >= THRESH; bsize--) {
    704 		for (startw = top; startw <= bot - bsize; startw++)
    705 			for (starts = top; starts <= bot - bsize;
    706 			    starts++) {
    707 				for (curw = startw, curs = starts;
    708 				    curs < starts + bsize; curw++, curs++)
    709 					if (win->lines[curw]->flags &
    710 					    __FORCEPAINT ||
    711 					    (win->lines[curw]->hash !=
    712 						curscr->lines[curs]->hash ||
    713 						memcmp(win->lines[curw]->line,
    714 						    curscr->lines[curs]->line,
    715 						    win->maxx * __LDATASIZE) != 0))
    716 						break;
    717 				if (curs == starts + bsize)
    718 					goto done;
    719 			}
    720 	}
    721 done:
    722 	/* Did not find anything */
    723 	if (bsize < THRESH)
    724 		return;
    725 
    726 #ifdef DEBUG
    727 	__CTRACE("quickch:bsize=%d,starts=%d,startw=%d,curw=%d,curs=%d,top=%d,bot=%d\n",
    728 	    bsize, starts, startw, curw, curs, top, bot);
    729 #endif
    730 
    731 	/*
    732 	 * Make sure that there is no overlap between the bottom and top
    733 	 * regions and the middle scrolled block.
    734 	 */
    735 	if (bot < curs)
    736 		bot = curs - 1;
    737 	if (top > starts)
    738 		top = starts;
    739 
    740 	n = startw - starts;
    741 
    742 #ifdef DEBUG
    743 	__CTRACE("#####################################\n");
    744 	for (i = 0; i < curscr->maxy; i++) {
    745 		__CTRACE("C: %d:", i);
    746 		__CTRACE(" 0x%x \n", curscr->lines[i]->hash);
    747 		for (j = 0; j < curscr->maxx; j++)
    748 			__CTRACE("%c", curscr->lines[i]->line[j].ch);
    749 		__CTRACE("\n");
    750 		for (j = 0; j < curscr->maxx; j++)
    751 			__CTRACE("%x", curscr->lines[i]->line[j].attr);
    752 		__CTRACE("\n");
    753 		__CTRACE("W: %d:", i);
    754 		__CTRACE(" 0x%x \n", win->lines[i]->hash);
    755 		__CTRACE(" 0x%x ", win->lines[i]->flags);
    756 		for (j = 0; j < win->maxx; j++)
    757 			__CTRACE("%c", win->lines[i]->line[j].ch);
    758 		__CTRACE("\n");
    759 		for (j = 0; j < win->maxx; j++)
    760 			__CTRACE("%x", win->lines[i]->line[j].attr);
    761 		__CTRACE("\n");
    762 	}
    763 #endif
    764 
    765 	/* So we don't have to call __hash() each time */
    766 	for (i = 0; i < win->maxx; i++) {
    767 		buf[i].ch = ' ';
    768 		buf[i].attr = 0;
    769 	}
    770 	blank_hash = __hash((char *)(void *)buf,
    771 	    (int) (win->maxx * __LDATASIZE));
    772 
    773 	/*
    774 	 * Perform the rotation to maintain the consistency of curscr.
    775 	 * This is hairy since we are doing an *in place* rotation.
    776 	 * Invariants of the loop:
    777 	 * - I is the index of the current line.
    778 	 * - Target is the index of the target of line i.
    779 	 * - Tmp1 points to current line (i).
    780 	 * - Tmp2 and points to target line (target);
    781 	 * - Cur_period is the index of the end of the current period.
    782 	 *   (see below).
    783 	 *
    784 	 * There are 2 major issues here that make this rotation non-trivial:
    785 	 * 1.  Scrolling in a scrolling region bounded by the top
    786 	 *     and bottom regions determined (whose size is sc_region).
    787 	 * 2.  As a result of the use of the mod function, there may be a
    788 	 *     period introduced, i.e., 2 maps to 4, 4 to 6, n-2 to 0, and
    789 	 *     0 to 2, which then causes all odd lines not to be rotated.
    790 	 *     To remedy this, an index of the end ( = beginning) of the
    791 	 *     current 'period' is kept, cur_period, and when it is reached,
    792 	 *     the next period is started from cur_period + 1 which is
    793 	 *     guaranteed not to have been reached since that would mean that
    794 	 *     all records would have been reached. (think about it...).
    795 	 *
    796 	 * Lines in the rotation can have 3 attributes which are marked on the
    797 	 * line so that curscr is consistent with the visual screen.
    798 	 * 1.  Not dirty -- lines inside the scrolled block, top region or
    799 	 *                  bottom region.
    800 	 * 2.  Blank lines -- lines in the differential of the scrolling
    801 	 *		      region adjacent to top and bot regions
    802 	 *                    depending on scrolling direction.
    803 	 * 3.  Dirty line -- all other lines are marked dirty.
    804 	 */
    805 	sc_region = bot - top + 1;
    806 	i = top;
    807 	tmp1 = curscr->lines[top];
    808 	cur_period = top;
    809 	for (j = top; j <= bot; j++) {
    810 		target = (i - top + n + sc_region) % sc_region + top;
    811 		tmp2 = curscr->lines[target];
    812 		curscr->lines[target] = tmp1;
    813 		/* Mark block as clean and blank out scrolled lines. */
    814 		clp = curscr->lines[target];
    815 #ifdef DEBUG
    816 		__CTRACE("quickch: n=%d startw=%d curw=%d i = %d target=%d ",
    817 		    n, startw, curw, i, target);
    818 #endif
    819 		if ((target >= startw && target < curw) || target < top
    820 		    || target > bot) {
    821 #ifdef DEBUG
    822 			__CTRACE("-- notdirty");
    823 #endif
    824 			win->lines[target]->flags &= ~__ISDIRTY;
    825 		} else
    826 			if ((n > 0 && target >= top && target < top + n) ||
    827 			    (n < 0 && target <= bot && target > bot + n)) {
    828 				if (clp->hash != blank_hash || memcmp(clp->line,
    829 				    buf, win->maxx * __LDATASIZE) !=0) {
    830 					(void)memcpy(clp->line,  buf,
    831 					    win->maxx * __LDATASIZE);
    832 #ifdef DEBUG
    833 					__CTRACE("-- blanked out: dirty");
    834 #endif
    835 					clp->hash = blank_hash;
    836 					__touchline(win, target, 0, (int) win->maxx - 1, 0);
    837 				} else {
    838 					__touchline(win, target, 0, (int) win->maxx - 1, 0);
    839 #ifdef DEBUG
    840 					__CTRACE(" -- blank line already: dirty");
    841 #endif
    842 				}
    843 			} else {
    844 #ifdef DEBUG
    845 				__CTRACE(" -- dirty");
    846 #endif
    847 				__touchline(win, target, 0, (int) win->maxx - 1, 0);
    848 			}
    849 #ifdef DEBUG
    850 		__CTRACE("\n");
    851 #endif
    852 		if (target == cur_period) {
    853 			i = target + 1;
    854 			tmp1 = curscr->lines[i];
    855 			cur_period = i;
    856 		} else {
    857 			tmp1 = tmp2;
    858 			i = target;
    859 		}
    860 	}
    861 #ifdef DEBUG
    862 	__CTRACE("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n");
    863 	for (i = 0; i < curscr->maxy; i++) {
    864 		__CTRACE("C: %d:", i);
    865 		for (j = 0; j < curscr->maxx; j++)
    866 			__CTRACE("%c", curscr->lines[i]->line[j].ch);
    867 		__CTRACE("\n");
    868 		__CTRACE("W: %d:", i);
    869 		for (j = 0; j < win->maxx; j++)
    870 			__CTRACE("%c", win->lines[i]->line[j].ch);
    871 		__CTRACE("\n");
    872 	}
    873 #endif
    874 	if (n != 0) {
    875 		WINDOW *wp;
    876 		scrolln(win, starts, startw, curs, bot, top);
    877 		/*
    878 		 * Need to repoint any subwindow lines to the rotated
    879 		 * line structured.
    880 		 */
    881 		for (wp = win->nextp; wp != win; wp = wp->nextp)
    882 			__set_subwin(win, wp);
    883 	}
    884 }
    885 
    886 /*
    887  * scrolln --
    888  *	Scroll n lines, where n is starts - startw.
    889  */
    890 static void /* ARGSUSED */
    891 scrolln(win, starts, startw, curs, bot, top)
    892 	WINDOW *win;
    893 	int	starts, startw, curs, bot, top;
    894 {
    895 	int	i, oy, ox, n;
    896 
    897 	oy = curscr->cury;
    898 	ox = curscr->curx;
    899 	n = starts - startw;
    900 
    901 	/*
    902 	 * XXX
    903 	 * The initial tests that set __noqch don't let us reach here unless
    904 	 * we have either CS + HO + SF/sf/SR/sr, or AL + DL.  SF/sf and SR/sr
    905 	 * scrolling can only shift the entire scrolling region, not just a
    906 	 * part of it, which means that the quickch() routine is going to be
    907 	 * sadly disappointed in us if we don't have CS as well.
    908 	 *
    909 	 * If CS, HO and SF/sf are set, can use the scrolling region.  Because
    910 	 * the cursor position after CS is undefined, we need HO which gives us
    911 	 * the ability to move to somewhere without knowledge of the current
    912 	 * location of the cursor.  Still call __mvcur() anyway, to update its
    913 	 * idea of where the cursor is.
    914 	 *
    915 	 * When the scrolling region has been set, the cursor has to be at the
    916 	 * last line of the region to make the scroll happen.
    917 	 *
    918 	 * Doing SF/SR or AL/DL appears faster on the screen than either sf/sr
    919 	 * or al/dl, and, some terminals have AL/DL, sf/sr, and CS, but not
    920 	 * SF/SR.  So, if we're scrolling almost all of the screen, try and use
    921 	 * AL/DL, otherwise use the scrolling region.  The "almost all" is a
    922 	 * shameless hack for vi.
    923 	 */
    924 	if (n > 0) {
    925 		if (CS != NULL && HO != NULL && (SF != NULL ||
    926 		    ((AL == NULL || DL == NULL ||
    927 		    top > 3 || bot + 3 < win->maxy) && sf != NULL))) {
    928 			tputs(__tscroll(CS, top, bot + 1), 0, __cputchar);
    929 			__mvcur(oy, ox, 0, 0, 1);
    930 			tputs(HO, 0, __cputchar);
    931 			__mvcur(0, 0, bot, 0, 1);
    932 			if (SF != NULL)
    933 				tputs(__tscroll(SF, n, 0), 0, __cputchar);
    934 			else
    935 				for (i = 0; i < n; i++)
    936 					tputs(sf, 0, __cputchar);
    937 			tputs(__tscroll(CS, 0, (int) win->maxy), 0, __cputchar);
    938 			__mvcur(bot, 0, 0, 0, 1);
    939 			tputs(HO, 0, __cputchar);
    940 			__mvcur(0, 0, oy, ox, 1);
    941 			return;
    942 		}
    943 
    944 		/* Scroll up the block. */
    945 		if (SF != NULL && top == 0) {
    946 			__mvcur(oy, ox, bot, 0, 1);
    947 			tputs(__tscroll(SF, n, 0), 0, __cputchar);
    948 		} else
    949 			if (DL != NULL) {
    950 				__mvcur(oy, ox, top, 0, 1);
    951 				tputs(__tscroll(DL, n, 0), 0, __cputchar);
    952 			} else
    953 				if (dl != NULL) {
    954 					__mvcur(oy, ox, top, 0, 1);
    955 					for (i = 0; i < n; i++)
    956 						tputs(dl, 0, __cputchar);
    957 				} else
    958 					if (sf != NULL && top == 0) {
    959 						__mvcur(oy, ox, bot, 0, 1);
    960 						for (i = 0; i < n; i++)
    961 							tputs(sf, 0, __cputchar);
    962 					} else
    963 						abort();
    964 
    965 		/* Push down the bottom region. */
    966 		__mvcur(top, 0, bot - n + 1, 0, 1);
    967 		if (AL != NULL)
    968 			tputs(__tscroll(AL, n, 0), 0, __cputchar);
    969 		else
    970 			if (al != NULL)
    971 				for (i = 0; i < n; i++)
    972 					tputs(al, 0, __cputchar);
    973 			else
    974 				abort();
    975 		__mvcur(bot - n + 1, 0, oy, ox, 1);
    976 	} else {
    977 		/*
    978 		 * !!!
    979 		 * n < 0
    980 		 *
    981 		 * If CS, HO and SR/sr are set, can use the scrolling region.
    982 		 * See the above comments for details.
    983 		 */
    984 		if (CS != NULL && HO != NULL && (SR != NULL ||
    985 		    ((AL == NULL || DL == NULL ||
    986 		    top > 3 || bot + 3 < win->maxy) && sr != NULL))) {
    987 			tputs(__tscroll(CS, top, bot + 1), 0, __cputchar);
    988 			__mvcur(oy, ox, 0, 0, 1);
    989 			tputs(HO, 0, __cputchar);
    990 			__mvcur(0, 0, top, 0, 1);
    991 
    992 			if (SR != NULL)
    993 				tputs(__tscroll(SR, -n, 0), 0, __cputchar);
    994 			else
    995 				for (i = n; i < 0; i++)
    996 					tputs(sr, 0, __cputchar);
    997 			tputs(__tscroll(CS, 0, (int) win->maxy), 0, __cputchar);
    998 			__mvcur(top, 0, 0, 0, 1);
    999 			tputs(HO, 0, __cputchar);
   1000 			__mvcur(0, 0, oy, ox, 1);
   1001 			return;
   1002 		}
   1003 
   1004 		/* Preserve the bottom lines. */
   1005 		__mvcur(oy, ox, bot + n + 1, 0, 1);
   1006 		if (SR != NULL && bot == win->maxy)
   1007 			tputs(__tscroll(SR, -n, 0), 0, __cputchar);
   1008 		else
   1009 			if (DL != NULL)
   1010 				tputs(__tscroll(DL, -n, 0), 0, __cputchar);
   1011 			else
   1012 				if (dl != NULL)
   1013 					for (i = n; i < 0; i++)
   1014 						tputs(dl, 0, __cputchar);
   1015 				else
   1016 					if (sr != NULL && bot == win->maxy)
   1017 						for (i = n; i < 0; i++)
   1018 							tputs(sr, 0, __cputchar);
   1019 					else
   1020 						abort();
   1021 
   1022 		/* Scroll the block down. */
   1023 		__mvcur(bot + n + 1, 0, top, 0, 1);
   1024 		if (AL != NULL)
   1025 			tputs(__tscroll(AL, -n, 0), 0, __cputchar);
   1026 		else
   1027 			if (al != NULL)
   1028 				for (i = n; i < 0; i++)
   1029 					tputs(al, 0, __cputchar);
   1030 			else
   1031 				abort();
   1032 		__mvcur(top, 0, oy, ox, 1);
   1033 	}
   1034 }
   1035