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