Home | History | Annotate | Line # | Download | only in libcurses
refresh.c revision 1.5
      1 /*
      2  * Copyright (c) 1981, 1993
      3  *	The Regents of the University of California.  All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  * 1. Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  * 2. Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in the
     12  *    documentation and/or other materials provided with the distribution.
     13  * 3. All advertising materials mentioning features or use of this software
     14  *    must display the following acknowledgement:
     15  *	This product includes software developed by the University of
     16  *	California, Berkeley and its contributors.
     17  * 4. Neither the name of the University nor the names of its contributors
     18  *    may be used to endorse or promote products derived from this software
     19  *    without specific prior written permission.
     20  *
     21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     31  * SUCH DAMAGE.
     32  */
     33 
     34 #ifndef lint
     35 static char sccsid[] = "@(#)refresh.c	8.1 (Berkeley) 7/20/93";
     36 #endif /* not lint */
     37 
     38 #include <curses.h>
     39 #include <string.h>
     40 
     41 static int curwin;
     42 static short ly, lx;
     43 
     44 static void	domvcur __P((int, int, int, int));
     45 static int	makech __P((WINDOW *, int));
     46 static void	quickch __P((WINDOW *));
     47 static void	scrolln __P((WINDOW *, int, int, int, int, int));
     48 
     49 /*
     50  * wrefresh --
     51  *	Make the current screen look like "win" over the area coverd by
     52  *	win.
     53  */
     54 int
     55 wrefresh(win)
     56 	register WINDOW *win;
     57 {
     58 	register __LINE *wlp;
     59 	register int retval;
     60 	register short wy;
     61 	int dnum;
     62 
     63 	/* Initialize loop parameters. */
     64 	ly = curscr->cury;
     65 	lx = curscr->curx;
     66 	wy = 0;
     67 	curwin = (win == curscr);
     68 
     69 	if (!curwin)
     70 		for (wy = 0; wy < win->maxy; wy++) {
     71 			wlp = win->lines[wy];
     72 			if (wlp->flags & __ISDIRTY)
     73 				wlp->hash =
     74 				   __hash((char *) wlp->line, win->maxx * __LDATASIZE);
     75 		}
     76 
     77 	if (win->flags & __CLEAROK || curscr->flags & __CLEAROK || curwin) {
     78 		if ((win->flags & __FULLWIN) || curscr->flags & __CLEAROK) {
     79 			tputs(CL, 0, __cputchar);
     80 			ly = 0;
     81 			lx = 0;
     82 			if (!curwin) {
     83 				curscr->flags &= ~__CLEAROK;
     84 				curscr->cury = 0;
     85 				curscr->curx = 0;
     86 				werase(curscr);
     87 			}
     88 			__touchwin(win);
     89 		}
     90 		win->flags &= ~__CLEAROK;
     91 	}
     92 	if (!CA) {
     93 		if (win->curx != 0)
     94 			putchar('\n');
     95 		if (!curwin)
     96 			werase(curscr);
     97 	}
     98 #ifdef DEBUG
     99 	__CTRACE("wrefresh: (%0.2o): curwin = %d\n", win, curwin);
    100 	__CTRACE("wrefresh: \tfirstch\tlastch\n");
    101 #endif
    102 
    103 #ifndef NOQCH
    104 	if ((win->flags & __FULLWIN) && !curwin) {
    105 		/*
    106 		 * Invoke quickch() only if more than a quarter of the lines
    107 		 * in the window are dirty.
    108 		 */
    109 		for (wy = 0, dnum = 0; wy < win->maxy; wy++)
    110 			if (win->lines[wy]->flags & (__ISDIRTY | __FORCEPAINT))
    111 				dnum++;
    112 		if (!__noqch && dnum > (int) win->maxy / 4)
    113 			quickch(win);
    114 	}
    115 #endif
    116 
    117 #ifdef DEBUG
    118 { int i, j;
    119 		__CTRACE("#####################################\n");
    120 		for (i = 0; i < curscr->maxy; i++) {
    121 			__CTRACE("C: %d:", i);
    122 			__CTRACE(" 0x%x \n", curscr->lines[i]->hash);
    123 			for (j = 0; j < curscr->maxx; j++)
    124 				__CTRACE("%c",
    125 			           curscr->lines[i]->line[j].ch);
    126 			__CTRACE("\n");
    127 			for (j = 0; j < curscr->maxx; j++)
    128 				__CTRACE("%x",
    129 			           curscr->lines[i]->line[j].attr);
    130 			__CTRACE("\n");
    131 			__CTRACE("W: %d:", i);
    132 			__CTRACE(" 0x%x \n", win->lines[i]->hash);
    133 			__CTRACE(" 0x%x ", win->lines[i]->flags);
    134 			for (j = 0; j < win->maxx; j++)
    135 				__CTRACE("%c",
    136 			           win->lines[i]->line[j].ch);
    137 			__CTRACE("\n");
    138 			for (j = 0; j < win->maxx; j++)
    139 				__CTRACE("%x",
    140 			           win->lines[i]->line[j].attr);
    141 			__CTRACE("\n");
    142 		}
    143 }
    144 #endif /* DEBUG */
    145 
    146 	for (wy = 0; wy < win->maxy; wy++) {
    147 #ifdef DEBUG
    148 		__CTRACE("%d\t%d\t%d\n",
    149 		    wy, *win->lines[wy]->firstchp, *win->lines[wy]->lastchp);
    150 #endif
    151 		if (!curwin)
    152 			curscr->lines[wy]->hash = win->lines[wy]->hash;
    153 		if (win->lines[wy]->flags & (__ISDIRTY | __FORCEPAINT)) {
    154 			if (makech(win, wy) == ERR)
    155 				return (ERR);
    156 			else {
    157 				if (*win->lines[wy]->firstchp >= win->ch_off)
    158 					*win->lines[wy]->firstchp = win->maxx +
    159 					    win->ch_off;
    160 				if (*win->lines[wy]->lastchp < win->maxx +
    161 				    win->ch_off)
    162 					*win->lines[wy]->lastchp = win->ch_off;
    163 				if (*win->lines[wy]->lastchp <
    164 				    *win->lines[wy]->firstchp) {
    165 #ifdef DEBUG
    166 					__CTRACE("wrefresh: line %d notdirty \n", wy);
    167 #endif
    168 					win->lines[wy]->flags &= ~__ISDIRTY;
    169 				}
    170 			}
    171 
    172 		}
    173 #ifdef DEBUG
    174 		__CTRACE("\t%d\t%d\n", *win->lines[wy]->firstchp,
    175 			*win->lines[wy]->lastchp);
    176 #endif
    177 	}
    178 
    179 #ifdef DEBUG
    180 	__CTRACE("refresh: ly=%d, lx=%d\n", ly, lx);
    181 #endif
    182 
    183 	if (win == curscr)
    184 		domvcur(ly, lx, win->cury, win->curx);
    185 	else {
    186 		if (win->flags & __LEAVEOK) {
    187 			curscr->cury = ly;
    188 			curscr->curx = lx;
    189 			ly -= win->begy;
    190 			lx -= win->begx;
    191 			if (ly >= 0 && ly < win->maxy && lx >= 0 &&
    192 			    lx < win->maxx) {
    193 				win->cury = ly;
    194 				win->curx = lx;
    195 			} else
    196 				win->cury = win->curx = 0;
    197 		} else {
    198 			domvcur(ly, lx, win->cury + win->begy,
    199 			    win->curx + win->begx);
    200 			curscr->cury = win->cury + win->begy;
    201 			curscr->curx = win->curx + win->begx;
    202 		}
    203 	}
    204 	retval = OK;
    205 
    206 	(void)fflush(stdout);
    207 	return (retval);
    208 }
    209 
    210 /*
    211  * makech --
    212  *	Make a change on the screen.
    213  */
    214 static int
    215 makech(win, wy)
    216 	register WINDOW *win;
    217 	int wy;
    218 {
    219 	static __LDATA blank = {' ', 0};
    220 	register int nlsp, clsp;		/* Last space in lines. */
    221 	register int wx, lch, y;
    222 	register __LDATA *nsp, *csp, *cp, *cep;
    223 	u_int force;
    224 	char *ce;
    225 
    226 	/* Is the cursor still on the end of the last line? */
    227 	if (wy > 0 && win->lines[wy - 1]->flags & __ISPASTEOL) {
    228 		domvcur(ly, lx, ly + 1, 0);
    229 		ly++;
    230 		lx = 0;
    231 	}
    232 	wx = *win->lines[wy]->firstchp - win->ch_off;
    233 	if (wx < 0)
    234 		wx = 0;
    235 	else if (wx >= win->maxx)
    236 		return (OK);
    237 	lch = *win->lines[wy]->lastchp - win->ch_off;
    238 	if (lch < 0)
    239 		return (OK);
    240 	else if (lch >= (int) win->maxx)
    241 		lch = win->maxx - 1;
    242 	y = wy + win->begy;
    243 
    244 	if (curwin)
    245 		csp = &blank;
    246 	else
    247 		csp = &curscr->lines[wy + win->begy]->line[wx + win->begx];
    248 
    249 	nsp = &win->lines[wy]->line[wx];
    250 	force = win->lines[wy]->flags & __FORCEPAINT;
    251 	win->lines[wy]->flags &= ~__FORCEPAINT;
    252 	if (CE && !curwin) {
    253 		for (cp = &win->lines[wy]->line[win->maxx - 1];
    254 		     cp->ch == ' ' && cp->attr == 0; cp--)
    255 			if (cp <= win->lines[wy]->line)
    256 				break;
    257 		nlsp = cp - win->lines[wy]->line;
    258 	}
    259 	if (!curwin)
    260 		ce = CE;
    261 	else
    262 		ce = NULL;
    263 
    264 	if (force) {
    265 		if (CM)
    266 			tputs(tgoto(CM, lx, ly), 0, __cputchar);
    267 		else {
    268 			tputs(HO, 0, __cputchar);
    269 			__mvcur(0, 0, ly, lx, 1);
    270 		}
    271 	}
    272 	while (wx <= lch) {
    273 		if (!force && memcmp(nsp, csp, sizeof(__LDATA)) == 0) {
    274 			if (wx <= lch) {
    275 				while (wx <= lch &&
    276 				       memcmp(nsp, csp, sizeof(__LDATA)) == 0) {
    277 					    nsp++;
    278 					    if (!curwin)
    279 						    csp++;
    280 					    ++wx;
    281 				    }
    282 				continue;
    283 			}
    284 			break;
    285 		}
    286 		domvcur(ly, lx, y, wx + win->begx);
    287 
    288 #ifdef DEBUG
    289 		__CTRACE("makech: 1: wx = %d, ly= %d, lx = %d, newy = %d, newx = %d, force =%d\n",
    290 		    wx, ly, lx, y, wx + win->begx, force);
    291 #endif
    292 		ly = y;
    293 		lx = wx + win->begx;
    294 		while ((force || memcmp(nsp, csp, sizeof(__LDATA)) != 0)
    295 		    && wx <= lch) {
    296 
    297 			if (ce != NULL && win->maxx + win->begx ==
    298 			    curscr->maxx && wx >= nlsp && nsp->ch == ' ') {
    299 				/* Check for clear to end-of-line. */
    300 				cep = &curscr->lines[wy]->line[win->maxx - 1];
    301 				while (cep->ch == ' ' && cep->attr == 0)
    302 					if (cep-- <= csp)
    303 						break;
    304 				clsp = cep - curscr->lines[wy]->line -
    305 				       win->begx * __LDATASIZE;
    306 #ifdef DEBUG
    307 			__CTRACE("makech: clsp = %d, nlsp = %d\n", clsp, nlsp);
    308 #endif
    309 				if ((clsp - nlsp >= strlen(CE)
    310 				    && clsp < win->maxx * __LDATASIZE) ||
    311 				    wy == win->maxy - 1) {
    312 #ifdef DEBUG
    313 					__CTRACE("makech: using CE\n");
    314 #endif
    315 					tputs(CE, 0, __cputchar);
    316 					lx = wx + win->begx;
    317 					while (wx++ <= clsp) {
    318 						csp->ch = ' ';
    319 						csp->attr = 0;
    320 						csp++;
    321 					}
    322 					return (OK);
    323 				}
    324 				ce = NULL;
    325 			}
    326 
    327 			/* Enter/exit standout mode as appropriate. */
    328 			if (SO && (nsp->attr & __STANDOUT) !=
    329 			    (curscr->flags & __WSTANDOUT)) {
    330 				if (nsp->attr & __STANDOUT) {
    331 					tputs(SO, 0, __cputchar);
    332 					curscr->flags |= __WSTANDOUT;
    333 				} else {
    334 					tputs(SE, 0, __cputchar);
    335 					curscr->flags &= ~__WSTANDOUT;
    336 				}
    337 			}
    338 
    339 			wx++;
    340 			if (wx >= win->maxx && wy == win->maxy - 1 && !curwin)
    341 				if (win->flags & __SCROLLOK) {
    342 					if (curscr->flags & __WSTANDOUT
    343 					    && win->flags & __ENDLINE)
    344 						if (!MS) {
    345 							tputs(SE, 0,
    346 							    __cputchar);
    347 							curscr->flags &=
    348 							    ~__WSTANDOUT;
    349 						}
    350 					if (!(win->flags & __SCROLLWIN)) {
    351 						if (!curwin) {
    352 							csp->attr = nsp->attr;
    353 							putchar(csp->ch = nsp->ch);
    354 						} else
    355 							putchar(nsp->ch);
    356 					}
    357 					if (wx + win->begx < curscr->maxx) {
    358 						domvcur(ly, wx + win->begx,
    359 						    win->begy + win->maxy - 1,
    360 						    win->begx + win->maxx - 1);
    361 					}
    362 					ly = win->begy + win->maxy - 1;
    363 					lx = win->begx + win->maxx - 1;
    364 					return (OK);
    365 				}
    366 			if (wx < win->maxx || wy < win->maxy - 1 ||
    367 			    !(win->flags & __SCROLLWIN)) {
    368 				if (!curwin) {
    369 					csp->attr = nsp->attr;
    370 					putchar(csp->ch = nsp->ch);
    371 					csp++;
    372 				} else
    373 					putchar(nsp->ch);
    374 			}
    375 #ifdef DEBUG
    376 			__CTRACE("makech: putchar(%c)\n", nsp->ch & 0177);
    377 #endif
    378 			if (UC && (nsp->attr & __STANDOUT)) {
    379 				putchar('\b');
    380 				tputs(UC, 0, __cputchar);
    381 			}
    382 			nsp++;
    383 #ifdef DEBUG
    384 		__CTRACE("makech: 2: wx = %d, lx = %d\n", wx, lx);
    385 #endif
    386 		}
    387 		if (lx == wx + win->begx)	/* If no change. */
    388 			break;
    389 		lx = wx + win->begx;
    390 		if (lx >= COLS && AM)
    391 			lx = COLS - 1;
    392 		else if (wx >= win->maxx) {
    393 			domvcur(ly, lx, ly, win->maxx + win->begx - 1);
    394 			lx = win->maxx + win->begx - 1;
    395 		}
    396 
    397 #ifdef DEBUG
    398 		__CTRACE("makech: 3: wx = %d, lx = %d\n", wx, lx);
    399 #endif
    400 	}
    401 	return (OK);
    402 }
    403 
    404 /*
    405  * domvcur --
    406  *	Do a mvcur, leaving standout mode if necessary.
    407  */
    408 static void
    409 domvcur(oy, ox, ny, nx)
    410 	int oy, ox, ny, nx;
    411 {
    412 	if (curscr->flags & __WSTANDOUT && !MS) {
    413 		tputs(SE, 0, __cputchar);
    414 		curscr->flags &= ~__WSTANDOUT;
    415 	}
    416 
    417 	__mvcur(oy, ox, ny, nx, 1);
    418 }
    419 
    420 /*
    421  * Quickch() attempts to detect a pattern in the change of the window
    422  * in order to optimize the change, e.g., scroll n lines as opposed to
    423  * repainting the screen line by line.
    424  */
    425 
    426 static void
    427 quickch(win)
    428 	WINDOW *win;
    429 {
    430 #define THRESH		(int) win->maxy / 4
    431 
    432 	register __LINE *clp, *tmp1, *tmp2;
    433 	register int bsize, curs, curw, starts, startw, i, j;
    434 	int n, target, cur_period, bot, top, sc_region;
    435 	__LDATA buf[1024];
    436 	u_int blank_hash;
    437 
    438 	/*
    439 	 * Find how many lines from the top of the screen are unchanged.
    440 	 */
    441 	for (top = 0; top < win->maxy; top++)
    442 		if (win->lines[top]->flags & __FORCEPAINT ||
    443 		    win->lines[top]->hash != curscr->lines[top]->hash
    444 		    || memcmp(win->lines[top]->line,
    445 		    curscr->lines[top]->line,
    446 		    win->maxx * __LDATASIZE) != 0)
    447 			break;
    448 		else
    449 			win->lines[top]->flags &= ~__ISDIRTY;
    450        /*
    451 	* Find how many lines from bottom of screen are unchanged.
    452 	*/
    453 	for (bot = win->maxy - 1; bot >= 0; bot--)
    454 		if (win->lines[bot]->flags & __FORCEPAINT ||
    455 		    win->lines[bot]->hash != curscr->lines[bot]->hash
    456 		    || memcmp(win->lines[bot]->line,
    457 		    curscr->lines[bot]->line,
    458 		    win->maxx * __LDATASIZE) != 0)
    459 			break;
    460 		else
    461 			win->lines[bot]->flags &= ~__ISDIRTY;
    462 
    463 #ifdef NO_JERKINESS
    464 	/*
    465 	 * If we have a bottom unchanged region return.  Scrolling the
    466 	 * bottom region up and then back down causes a screen jitter.
    467 	 * This will increase the number of characters sent to the screen
    468 	 * but it looks better.
    469 	 */
    470 	if (bot < win->maxy - 1)
    471 		return;
    472 #endif /* NO_JERKINESS */
    473 
    474 	/*
    475 	 * Search for the largest block of text not changed.
    476 	 * Invariants of the loop:
    477 	 * - Startw is the index of the beginning of the examined block in win.
    478          * - Starts is the index of the beginning of the examined block in
    479 	 *    curscr.
    480 	 * - Curs is the index of one past the end of the exmined block in win.
    481 	 * - Curw is the index of one past the end of the exmined block in
    482 	 *   curscr.
    483 	 * - bsize is the current size of the examined block.
    484          */
    485 	for (bsize = bot - top; bsize >= THRESH; bsize--) {
    486 		for (startw = top; startw <= bot - bsize; startw++)
    487 			for (starts = top; starts <= bot - bsize;
    488 			     starts++) {
    489 				for (curw = startw, curs = starts;
    490 				     curs < starts + bsize; curw++, curs++)
    491 					if (win->lines[curw]->flags &
    492 					    __FORCEPAINT ||
    493 					    (win->lines[curw]->hash !=
    494 					    curscr->lines[curs]->hash ||
    495 				            memcmp(win->lines[curw]->line,
    496 					    curscr->lines[curs]->line,
    497 					    win->maxx * __LDATASIZE) != 0))
    498 						break;
    499 				if (curs == starts + bsize)
    500 					goto done;
    501 			}
    502 	}
    503  done:
    504 	/* Did not find anything */
    505 	if (bsize < THRESH)
    506 		return;
    507 
    508 #ifdef DEBUG
    509 	__CTRACE("quickch:bsize=%d,starts=%d,startw=%d,curw=%d,curs=%d,top=%d,bot=%d\n",
    510 		bsize, starts, startw, curw, curs, top, bot);
    511 #endif
    512 
    513 	/*
    514 	 * Make sure that there is no overlap between the bottom and top
    515 	 * regions and the middle scrolled block.
    516 	 */
    517 	if (bot < curs)
    518 		bot = curs - 1;
    519 	if (top > starts)
    520 		top = starts;
    521 
    522 	n = startw - starts;
    523 
    524 #ifdef DEBUG
    525 		__CTRACE("#####################################\n");
    526 		for (i = 0; i < curscr->maxy; i++) {
    527 			__CTRACE("C: %d:", i);
    528 			__CTRACE(" 0x%x \n", curscr->lines[i]->hash);
    529 			for (j = 0; j < curscr->maxx; j++)
    530 				__CTRACE("%c",
    531 			           curscr->lines[i]->line[j].ch);
    532 			__CTRACE("\n");
    533 			for (j = 0; j < curscr->maxx; j++)
    534 				__CTRACE("%x",
    535 			           curscr->lines[i]->line[j].attr);
    536 			__CTRACE("\n");
    537 			__CTRACE("W: %d:", i);
    538 			__CTRACE(" 0x%x \n", win->lines[i]->hash);
    539 			__CTRACE(" 0x%x ", win->lines[i]->flags);
    540 			for (j = 0; j < win->maxx; j++)
    541 				__CTRACE("%c",
    542 			           win->lines[i]->line[j].ch);
    543 			__CTRACE("\n");
    544 			for (j = 0; j < win->maxx; j++)
    545 				__CTRACE("%x",
    546 			           win->lines[i]->line[j].attr);
    547 			__CTRACE("\n");
    548 		}
    549 #endif
    550 
    551 	/* So we don't have to call __hash() each time */
    552 	for (i = 0; i < win->maxx; i++) {
    553 		buf[i].ch = ' ';
    554 		buf[i].attr = 0;
    555 	}
    556 	blank_hash = __hash((char *) buf, win->maxx * __LDATASIZE);
    557 
    558 	/*
    559 	 * Perform the rotation to maintain the consistency of curscr.
    560 	 * This is hairy since we are doing an *in place* rotation.
    561 	 * Invariants of the loop:
    562 	 * - I is the index of the current line.
    563 	 * - Target is the index of the target of line i.
    564 	 * - Tmp1 points to current line (i).
    565 	 * - Tmp2 and points to target line (target);
    566 	 * - Cur_period is the index of the end of the current period.
    567 	 *   (see below).
    568 	 *
    569 	 * There are 2 major issues here that make this rotation non-trivial:
    570 	 * 1.  Scrolling in a scrolling region bounded by the top
    571 	 *     and bottom regions determined (whose size is sc_region).
    572 	 * 2.  As a result of the use of the mod function, there may be a
    573 	 *     period introduced, i.e., 2 maps to 4, 4 to 6, n-2 to 0, and
    574 	 *     0 to 2, which then causes all odd lines not to be rotated.
    575 	 *     To remedy this, an index of the end ( = beginning) of the
    576 	 *     current 'period' is kept, cur_period, and when it is reached,
    577 	 *     the next period is started from cur_period + 1 which is
    578 	 *     guaranteed not to have been reached since that would mean that
    579 	 *     all records would have been reached. (think about it...).
    580 	 *
    581 	 * Lines in the rotation can have 3 attributes which are marked on the
    582 	 * line so that curscr is consistent with the visual screen.
    583 	 * 1.  Not dirty -- lines inside the scrolled block, top region or
    584 	 *                  bottom region.
    585 	 * 2.  Blank lines -- lines in the differential of the scrolling
    586 	 *		      region adjacent to top and bot regions
    587 	 *                    depending on scrolling direction.
    588 	 * 3.  Dirty line -- all other lines are marked dirty.
    589 	 */
    590 	sc_region = bot - top + 1;
    591 	i = top;
    592 	tmp1 = curscr->lines[top];
    593 	cur_period = top;
    594 	for (j = top; j <= bot; j++) {
    595 		target = (i - top + n + sc_region) % sc_region + top;
    596 		tmp2 = curscr->lines[target];
    597 		curscr->lines[target] = tmp1;
    598 		/* Mark block as clean and blank out scrolled lines. */
    599 		clp = curscr->lines[target];
    600 #ifdef DEBUG
    601 		__CTRACE("quickch: n=%d startw=%d curw=%d i = %d target=%d ",
    602 			n, startw, curw, i, target);
    603 #endif
    604 		if ((target >= startw && target < curw) || target < top
    605 		    || target > bot) {
    606 #ifdef DEBUG
    607 			__CTRACE("-- notdirty");
    608 #endif
    609 			win->lines[target]->flags &= ~__ISDIRTY;
    610 		} else if ((n > 0 && target >= top && target < top + n) ||
    611 		           (n < 0 && target <= bot && target > bot + n)) {
    612 			if (clp->hash != blank_hash ||  memcmp(clp->line,
    613 			    buf, win->maxx * __LDATASIZE) !=0) {
    614 				(void)memcpy(clp->line,  buf,
    615 				    win->maxx * __LDATASIZE);
    616 #ifdef DEBUG
    617 				__CTRACE("-- blanked out: dirty");
    618 #endif
    619 				clp->hash = blank_hash;
    620 				__touchline(win, target, 0, win->maxx - 1, 0);
    621 			} else {
    622 				__touchline(win, target, 0, win->maxx - 1, 0);
    623 #ifdef DEBUG
    624 				__CTRACE(" -- blank line already: dirty");
    625 #endif
    626 			}
    627 		} else {
    628 #ifdef DEBUG
    629 			__CTRACE(" -- dirty");
    630 #endif
    631 			__touchline(win, target, 0, win->maxx - 1, 0);
    632 		}
    633 #ifdef DEBUG
    634 		__CTRACE("\n");
    635 #endif
    636 		if (target == cur_period) {
    637 			i = target + 1;
    638 			tmp1 = curscr->lines[i];
    639 			cur_period = i;
    640 		} else {
    641 			tmp1 = tmp2;
    642 			i = target;
    643 		}
    644 	}
    645 #ifdef DEBUG
    646 		__CTRACE("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n");
    647 		for (i = 0; i < curscr->maxy; i++) {
    648 			__CTRACE("C: %d:", i);
    649 			for (j = 0; j < curscr->maxx; j++)
    650 				__CTRACE("%c",
    651 			           curscr->lines[i]->line[j].ch);
    652 			__CTRACE("\n");
    653 			__CTRACE("W: %d:", i);
    654 			for (j = 0; j < win->maxx; j++)
    655 				__CTRACE("%c",
    656 			           win->lines[i]->line[j].ch);
    657 			__CTRACE("\n");
    658 		}
    659 #endif
    660 	if (n != 0) {
    661 		WINDOW *wp;
    662 		scrolln(win, starts, startw, curs, bot, top);
    663 		/*
    664 		 * Need to repoint any subwindow lines to the rotated
    665 		 * line structured.
    666 		 */
    667 		for (wp = win->nextp; wp != win; wp = wp->nextp)
    668 			__set_subwin(win, wp);
    669 	}
    670 }
    671 
    672 /*
    673  * Scrolln performs the scroll by n lines, where n is starts - startw.
    674  */
    675 static void
    676 scrolln(win, starts, startw, curs, bot, top)
    677 	WINDOW *win;
    678 	int starts, startw, curs, bot, top;
    679 {
    680 	int i, oy, ox, n;
    681 
    682 	oy = curscr->cury;
    683 	ox = curscr->curx;
    684 	n = starts - startw;
    685 
    686 	if (n > 0) {
    687 		__mvcur(oy, ox, top, 0, 1);
    688 		/* Scroll up the block */
    689 		if (DL)
    690 			tputs(__tscroll(DL, n), 0, __cputchar);
    691 		else
    692 			for(i = 0; i < n; i++)
    693 				tputs(dl, 0, __cputchar);
    694 
    695 		/*
    696 		 * Push down the bottom region.
    697 		 */
    698 		__mvcur(top, 0, bot - n + 1, 0, 1);
    699 		if (AL)
    700 			tputs(__tscroll(AL, n), 0, __cputchar);
    701 		else
    702 			for(i = 0; i < n; i++)
    703 				tputs(al, 0, __cputchar);
    704 		__mvcur(bot - n + 1, 0, oy, ox, 1);
    705 	} else {
    706 		/* Preserve the bottom lines */
    707 		__mvcur(oy, ox, bot + n + 1, 0, 1);	/* n < 0 */
    708 		if (DL)
    709 			tputs(__tscroll(DL, -n), 0, __cputchar);
    710 		else
    711 		       	for(i = n; i < 0; i++)
    712 				tputs(dl, 0, __cputchar);
    713 		__mvcur(bot + n + 1, 0, top, 0, 1);
    714 
    715 		/* Scroll the block down */
    716 		if (AL)
    717 			tputs(__tscroll(AL, -n), 0, __cputchar);
    718 		else
    719 			for(i = n; i < 0; i++)
    720 				tputs(al, 0, __cputchar);
    721 		__mvcur(top, 0, oy, ox, 1);
    722 	}
    723 }
    724