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