Home | History | Annotate | Line # | Download | only in libcurses
refresh.c revision 1.10
      1 /*	$NetBSD: refresh.c,v 1.10 1998/02/03 19:12:32 perry 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.10 1998/02/03 19:12:32 perry 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 				    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 { int i, j;
    133 		__CTRACE("#####################################\n");
    134 		for (i = 0; i < curscr->maxy; i++) {
    135 			__CTRACE("C: %d:", i);
    136 			__CTRACE(" 0x%x \n", curscr->lines[i]->hash);
    137 			for (j = 0; j < curscr->maxx; j++)
    138 				__CTRACE("%c",
    139 			           curscr->lines[i]->line[j].ch);
    140 			__CTRACE("\n");
    141 			for (j = 0; j < curscr->maxx; j++)
    142 				__CTRACE("%x",
    143 			           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",
    150 			           win->lines[i]->line[j].ch);
    151 			__CTRACE("\n");
    152 			for (j = 0; j < win->maxx; j++)
    153 				__CTRACE("%x",
    154 			           win->lines[i]->line[j].attr);
    155 			__CTRACE("\n");
    156 		}
    157 }
    158 #endif /* DEBUG */
    159 
    160 	for (wy = 0; wy < win->maxy; wy++) {
    161 #ifdef DEBUG
    162 		__CTRACE("%d\t%d\t%d\n",
    163 		    wy, *win->lines[wy]->firstchp, *win->lines[wy]->lastchp);
    164 #endif
    165 		if (!curwin)
    166 			curscr->lines[wy]->hash = win->lines[wy]->hash;
    167 		if (win->lines[wy]->flags & (__ISDIRTY | __FORCEPAINT)) {
    168 			if (makech(win, wy) == ERR)
    169 				return (ERR);
    170 			else {
    171 				if (*win->lines[wy]->firstchp >= win->ch_off)
    172 					*win->lines[wy]->firstchp = win->maxx +
    173 					    win->ch_off;
    174 				if (*win->lines[wy]->lastchp < win->maxx +
    175 				    win->ch_off)
    176 					*win->lines[wy]->lastchp = win->ch_off;
    177 				if (*win->lines[wy]->lastchp <
    178 				    *win->lines[wy]->firstchp) {
    179 #ifdef DEBUG
    180 					__CTRACE("wrefresh: line %d notdirty \n", wy);
    181 #endif
    182 					win->lines[wy]->flags &= ~__ISDIRTY;
    183 				}
    184 			}
    185 
    186 		}
    187 #ifdef DEBUG
    188 		__CTRACE("\t%d\t%d\n", *win->lines[wy]->firstchp,
    189 			*win->lines[wy]->lastchp);
    190 #endif
    191 	}
    192 
    193 #ifdef DEBUG
    194 	__CTRACE("refresh: ly=%d, lx=%d\n", ly, lx);
    195 #endif
    196 
    197 	if (win == curscr)
    198 		domvcur(ly, lx, win->cury, win->curx);
    199 	else {
    200 		if (win->flags & __LEAVEOK) {
    201 			curscr->cury = ly;
    202 			curscr->curx = lx;
    203 			ly -= win->begy;
    204 			lx -= win->begx;
    205 			if (ly >= 0 && ly < win->maxy && lx >= 0 &&
    206 			    lx < win->maxx) {
    207 				win->cury = ly;
    208 				win->curx = lx;
    209 			} else
    210 				win->cury = win->curx = 0;
    211 		} else {
    212 			domvcur(ly, lx, win->cury + win->begy,
    213 			    win->curx + win->begx);
    214 			curscr->cury = win->cury + win->begy;
    215 			curscr->curx = win->curx + win->begx;
    216 		}
    217 	}
    218 	retval = OK;
    219 
    220 	(void)fflush(stdout);
    221 	return (retval);
    222 }
    223 
    224 /*
    225  * makech --
    226  *	Make a change on the screen.
    227  */
    228 static int
    229 makech(win, wy)
    230 	WINDOW *win;
    231 	int wy;
    232 {
    233 	static __LDATA blank = {' ', 0};
    234 	__LDATA *nsp, *csp, *cp, *cep;
    235 	u_int force;
    236 	int clsp, nlsp;			/* Last space in lines. */
    237 	int lch, wx, y;
    238 	char *ce;
    239 
    240 #ifdef __GNUC__
    241 	nlsp = 0;		/* XXX gcc -Wuninitialized */
    242 #endif
    243 	/* Is the cursor still on the end of the last line? */
    244 	if (wy > 0 && win->lines[wy - 1]->flags & __ISPASTEOL) {
    245 		domvcur(ly, lx, ly + 1, 0);
    246 		ly++;
    247 		lx = 0;
    248 	}
    249 	wx = *win->lines[wy]->firstchp - win->ch_off;
    250 	if (wx < 0)
    251 		wx = 0;
    252 	else if (wx >= win->maxx)
    253 		return (OK);
    254 	lch = *win->lines[wy]->lastchp - win->ch_off;
    255 	if (lch < 0)
    256 		return (OK);
    257 	else 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, 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 					}
    335 					tputs(CE, 0, __cputchar);
    336 					lx = wx + win->begx;
    337 					while (wx++ <= clsp) {
    338 						csp->ch = ' ';
    339 						csp->attr = 0;
    340 						csp++;
    341 					}
    342 					return (OK);
    343 				}
    344 				ce = NULL;
    345 			}
    346 
    347 			/*
    348 			 * Enter/exit standout mode as appropriate.
    349 			 * XXX
    350 			 * Should use UC if SO/SE not available.
    351 			 */
    352 			if (nsp->attr & __STANDOUT) {
    353 				if (!(curscr->flags & __WSTANDOUT) &&
    354 				    SO != NULL && SE != NULL) {
    355 					tputs(SO, 0, __cputchar);
    356 					curscr->flags |= __WSTANDOUT;
    357 				}
    358 			} else
    359 				if (curscr->flags & __WSTANDOUT &&
    360 				    SE != NULL) {
    361 					tputs(SE, 0, __cputchar);
    362 					curscr->flags &= ~__WSTANDOUT;
    363 				}
    364 
    365 			wx++;
    366 			if (wx >= win->maxx && wy == win->maxy - 1 && !curwin)
    367 				if (win->flags & __SCROLLOK) {
    368 					if (curscr->flags & __WSTANDOUT
    369 					    && win->flags & __ENDLINE)
    370 						if (!MS) {
    371 							tputs(SE, 0,
    372 							    __cputchar);
    373 							curscr->flags &=
    374 							    ~__WSTANDOUT;
    375 						}
    376 					if (!(win->flags & __SCROLLWIN)) {
    377 						if (!curwin) {
    378 							csp->attr = nsp->attr;
    379 							putchar(csp->ch = nsp->ch);
    380 						} else
    381 							putchar(nsp->ch);
    382 					}
    383 					if (wx + win->begx < curscr->maxx) {
    384 						domvcur(ly, wx + win->begx,
    385 						    win->begy + win->maxy - 1,
    386 						    win->begx + win->maxx - 1);
    387 					}
    388 					ly = win->begy + win->maxy - 1;
    389 					lx = win->begx + win->maxx - 1;
    390 					return (OK);
    391 				}
    392 			if (wx < win->maxx || wy < win->maxy - 1 ||
    393 			    !(win->flags & __SCROLLWIN)) {
    394 				if (!curwin) {
    395 					csp->attr = nsp->attr;
    396 					putchar(csp->ch = nsp->ch);
    397 					csp++;
    398 				} else
    399 					putchar(nsp->ch);
    400 			}
    401 #ifdef DEBUG
    402 			__CTRACE("makech: putchar(%c)\n", nsp->ch & 0177);
    403 #endif
    404 			if (UC && (nsp->attr & __STANDOUT)) {
    405 				putchar('\b');
    406 				tputs(UC, 0, __cputchar);
    407 			}
    408 			nsp++;
    409 #ifdef DEBUG
    410 		__CTRACE("makech: 2: wx = %d, lx = %d\n", wx, lx);
    411 #endif
    412 		}
    413 		if (lx == wx + win->begx)	/* If no change. */
    414 			break;
    415 		lx = wx + win->begx;
    416 		if (lx >= COLS && AM)
    417 			lx = COLS - 1;
    418 		else if (wx >= win->maxx) {
    419 			domvcur(ly, lx, ly, win->maxx + win->begx - 1);
    420 			lx = win->maxx + win->begx - 1;
    421 		}
    422 
    423 #ifdef DEBUG
    424 		__CTRACE("makech: 3: wx = %d, lx = %d\n", wx, lx);
    425 #endif
    426 	}
    427 
    428 	/* Don't leave the screen in standout mode. */
    429 	if (curscr->flags & __WSTANDOUT) {
    430 		tputs(SE, 0, __cputchar);
    431 		curscr->flags &= ~__WSTANDOUT;
    432 	}
    433 	return (OK);
    434 }
    435 
    436 /*
    437  * domvcur --
    438  *	Do a mvcur, leaving standout mode if necessary.
    439  */
    440 static void
    441 domvcur(oy, ox, ny, nx)
    442 	int oy, ox, ny, nx;
    443 {
    444 	if (curscr->flags & __WSTANDOUT && !MS) {
    445 		tputs(SE, 0, __cputchar);
    446 		curscr->flags &= ~__WSTANDOUT;
    447 	}
    448 
    449 	__mvcur(oy, ox, ny, nx, 1);
    450 }
    451 
    452 /*
    453  * Quickch() attempts to detect a pattern in the change of the window
    454  * in order to optimize the change, e.g., scroll n lines as opposed to
    455  * repainting the screen line by line.
    456  */
    457 
    458 static void
    459 quickch(win)
    460 	WINDOW *win;
    461 {
    462 #define THRESH		(int) win->maxy / 4
    463 
    464 	__LINE *clp, *tmp1, *tmp2;
    465 	int bsize, curs, curw, starts, startw, i, j;
    466 	int n, target, cur_period, bot, top, sc_region;
    467 	__LDATA buf[1024];
    468 	u_int blank_hash;
    469 
    470 #ifdef __GNUC__
    471 	curs = curw = starts = startw = 0; /* XXX gcc -Wuninitialized */
    472 #endif
    473 	/*
    474 	 * Find how many lines from the top of the screen are unchanged.
    475 	 */
    476 	for (top = 0; top < win->maxy; top++)
    477 		if (win->lines[top]->flags & __FORCEPAINT ||
    478 		    win->lines[top]->hash != curscr->lines[top]->hash
    479 		    || memcmp(win->lines[top]->line,
    480 		    curscr->lines[top]->line,
    481 		    win->maxx * __LDATASIZE) != 0)
    482 			break;
    483 		else
    484 			win->lines[top]->flags &= ~__ISDIRTY;
    485        /*
    486 	* Find how many lines from bottom of screen are unchanged.
    487 	*/
    488 	for (bot = win->maxy - 1; bot >= 0; bot--)
    489 		if (win->lines[bot]->flags & __FORCEPAINT ||
    490 		    win->lines[bot]->hash != curscr->lines[bot]->hash
    491 		    || memcmp(win->lines[bot]->line,
    492 		    curscr->lines[bot]->line,
    493 		    win->maxx * __LDATASIZE) != 0)
    494 			break;
    495 		else
    496 			win->lines[bot]->flags &= ~__ISDIRTY;
    497 
    498 #ifdef NO_JERKINESS
    499 	/*
    500 	 * If we have a bottom unchanged region return.  Scrolling the
    501 	 * bottom region up and then back down causes a screen jitter.
    502 	 * This will increase the number of characters sent to the screen
    503 	 * but it looks better.
    504 	 */
    505 	if (bot < win->maxy - 1)
    506 		return;
    507 #endif /* NO_JERKINESS */
    508 
    509 	/*
    510 	 * Search for the largest block of text not changed.
    511 	 * Invariants of the loop:
    512 	 * - Startw is the index of the beginning of the examined block in win.
    513          * - Starts is the index of the beginning of the examined block in
    514 	 *    curscr.
    515 	 * - Curs is the index of one past the end of the exmined block in win.
    516 	 * - Curw is the index of one past the end of the exmined block in
    517 	 *   curscr.
    518 	 * - bsize is the current size of the examined block.
    519          */
    520 	for (bsize = bot - top; bsize >= THRESH; bsize--) {
    521 		for (startw = top; startw <= bot - bsize; startw++)
    522 			for (starts = top; starts <= bot - bsize;
    523 			     starts++) {
    524 				for (curw = startw, curs = starts;
    525 				     curs < starts + bsize; curw++, curs++)
    526 					if (win->lines[curw]->flags &
    527 					    __FORCEPAINT ||
    528 					    (win->lines[curw]->hash !=
    529 					    curscr->lines[curs]->hash ||
    530 				            memcmp(win->lines[curw]->line,
    531 					    curscr->lines[curs]->line,
    532 					    win->maxx * __LDATASIZE) != 0))
    533 						break;
    534 				if (curs == starts + bsize)
    535 					goto done;
    536 			}
    537 	}
    538  done:
    539 	/* Did not find anything */
    540 	if (bsize < THRESH)
    541 		return;
    542 
    543 #ifdef DEBUG
    544 	__CTRACE("quickch:bsize=%d,starts=%d,startw=%d,curw=%d,curs=%d,top=%d,bot=%d\n",
    545 		bsize, starts, startw, curw, curs, top, bot);
    546 #endif
    547 
    548 	/*
    549 	 * Make sure that there is no overlap between the bottom and top
    550 	 * regions and the middle scrolled block.
    551 	 */
    552 	if (bot < curs)
    553 		bot = curs - 1;
    554 	if (top > starts)
    555 		top = starts;
    556 
    557 	n = startw - starts;
    558 
    559 #ifdef DEBUG
    560 		__CTRACE("#####################################\n");
    561 		for (i = 0; i < curscr->maxy; i++) {
    562 			__CTRACE("C: %d:", i);
    563 			__CTRACE(" 0x%x \n", curscr->lines[i]->hash);
    564 			for (j = 0; j < curscr->maxx; j++)
    565 				__CTRACE("%c",
    566 			           curscr->lines[i]->line[j].ch);
    567 			__CTRACE("\n");
    568 			for (j = 0; j < curscr->maxx; j++)
    569 				__CTRACE("%x",
    570 			           curscr->lines[i]->line[j].attr);
    571 			__CTRACE("\n");
    572 			__CTRACE("W: %d:", i);
    573 			__CTRACE(" 0x%x \n", win->lines[i]->hash);
    574 			__CTRACE(" 0x%x ", win->lines[i]->flags);
    575 			for (j = 0; j < win->maxx; j++)
    576 				__CTRACE("%c",
    577 			           win->lines[i]->line[j].ch);
    578 			__CTRACE("\n");
    579 			for (j = 0; j < win->maxx; j++)
    580 				__CTRACE("%x",
    581 			           win->lines[i]->line[j].attr);
    582 			__CTRACE("\n");
    583 		}
    584 #endif
    585 
    586 	/* So we don't have to call __hash() each time */
    587 	for (i = 0; i < win->maxx; i++) {
    588 		buf[i].ch = ' ';
    589 		buf[i].attr = 0;
    590 	}
    591 	blank_hash = __hash((char *) buf, win->maxx * __LDATASIZE);
    592 
    593 	/*
    594 	 * Perform the rotation to maintain the consistency of curscr.
    595 	 * This is hairy since we are doing an *in place* rotation.
    596 	 * Invariants of the loop:
    597 	 * - I is the index of the current line.
    598 	 * - Target is the index of the target of line i.
    599 	 * - Tmp1 points to current line (i).
    600 	 * - Tmp2 and points to target line (target);
    601 	 * - Cur_period is the index of the end of the current period.
    602 	 *   (see below).
    603 	 *
    604 	 * There are 2 major issues here that make this rotation non-trivial:
    605 	 * 1.  Scrolling in a scrolling region bounded by the top
    606 	 *     and bottom regions determined (whose size is sc_region).
    607 	 * 2.  As a result of the use of the mod function, there may be a
    608 	 *     period introduced, i.e., 2 maps to 4, 4 to 6, n-2 to 0, and
    609 	 *     0 to 2, which then causes all odd lines not to be rotated.
    610 	 *     To remedy this, an index of the end ( = beginning) of the
    611 	 *     current 'period' is kept, cur_period, and when it is reached,
    612 	 *     the next period is started from cur_period + 1 which is
    613 	 *     guaranteed not to have been reached since that would mean that
    614 	 *     all records would have been reached. (think about it...).
    615 	 *
    616 	 * Lines in the rotation can have 3 attributes which are marked on the
    617 	 * line so that curscr is consistent with the visual screen.
    618 	 * 1.  Not dirty -- lines inside the scrolled block, top region or
    619 	 *                  bottom region.
    620 	 * 2.  Blank lines -- lines in the differential of the scrolling
    621 	 *		      region adjacent to top and bot regions
    622 	 *                    depending on scrolling direction.
    623 	 * 3.  Dirty line -- all other lines are marked dirty.
    624 	 */
    625 	sc_region = bot - top + 1;
    626 	i = top;
    627 	tmp1 = curscr->lines[top];
    628 	cur_period = top;
    629 	for (j = top; j <= bot; j++) {
    630 		target = (i - top + n + sc_region) % sc_region + top;
    631 		tmp2 = curscr->lines[target];
    632 		curscr->lines[target] = tmp1;
    633 		/* Mark block as clean and blank out scrolled lines. */
    634 		clp = curscr->lines[target];
    635 #ifdef DEBUG
    636 		__CTRACE("quickch: n=%d startw=%d curw=%d i = %d target=%d ",
    637 			n, startw, curw, i, target);
    638 #endif
    639 		if ((target >= startw && target < curw) || target < top
    640 		    || target > bot) {
    641 #ifdef DEBUG
    642 			__CTRACE("-- notdirty");
    643 #endif
    644 			win->lines[target]->flags &= ~__ISDIRTY;
    645 		} else if ((n > 0 && target >= top && target < top + n) ||
    646 		           (n < 0 && target <= bot && target > bot + n)) {
    647 			if (clp->hash != blank_hash ||  memcmp(clp->line,
    648 			    buf, win->maxx * __LDATASIZE) !=0) {
    649 				(void)memcpy(clp->line,  buf,
    650 				    win->maxx * __LDATASIZE);
    651 #ifdef DEBUG
    652 				__CTRACE("-- blanked out: dirty");
    653 #endif
    654 				clp->hash = blank_hash;
    655 				__touchline(win, target, 0, win->maxx - 1, 0);
    656 			} else {
    657 				__touchline(win, target, 0, win->maxx - 1, 0);
    658 #ifdef DEBUG
    659 				__CTRACE(" -- blank line already: dirty");
    660 #endif
    661 			}
    662 		} else {
    663 #ifdef DEBUG
    664 			__CTRACE(" -- dirty");
    665 #endif
    666 			__touchline(win, target, 0, win->maxx - 1, 0);
    667 		}
    668 #ifdef DEBUG
    669 		__CTRACE("\n");
    670 #endif
    671 		if (target == cur_period) {
    672 			i = target + 1;
    673 			tmp1 = curscr->lines[i];
    674 			cur_period = i;
    675 		} else {
    676 			tmp1 = tmp2;
    677 			i = target;
    678 		}
    679 	}
    680 #ifdef DEBUG
    681 		__CTRACE("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n");
    682 		for (i = 0; i < curscr->maxy; i++) {
    683 			__CTRACE("C: %d:", i);
    684 			for (j = 0; j < curscr->maxx; j++)
    685 				__CTRACE("%c",
    686 			           curscr->lines[i]->line[j].ch);
    687 			__CTRACE("\n");
    688 			__CTRACE("W: %d:", i);
    689 			for (j = 0; j < win->maxx; j++)
    690 				__CTRACE("%c", win->lines[i]->line[j].ch);
    691 			__CTRACE("\n");
    692 		}
    693 #endif
    694 	if (n != 0) {
    695 		WINDOW *wp;
    696 		scrolln(win, starts, startw, curs, bot, top);
    697 		/*
    698 		 * Need to repoint any subwindow lines to the rotated
    699 		 * line structured.
    700 		 */
    701 		for (wp = win->nextp; wp != win; wp = wp->nextp)
    702 			__set_subwin(win, wp);
    703 	}
    704 }
    705 
    706 /*
    707  * scrolln --
    708  *	Scroll n lines, where n is starts - startw.
    709  */
    710 static void
    711 scrolln(win, starts, startw, curs, bot, top)
    712 	WINDOW *win;
    713 	int starts, startw, curs, bot, top;
    714 {
    715 	int i, oy, ox, n;
    716 
    717 	oy = curscr->cury;
    718 	ox = curscr->curx;
    719 	n = starts - startw;
    720 
    721 	/*
    722 	 * XXX
    723 	 * The initial tests that set __noqch don't let us reach here unless
    724 	 * we have either CS + HO + SF/sf/SR/sr, or AL + DL.  SF/sf and SR/sr
    725 	 * scrolling can only shift the entire scrolling region, not just a
    726 	 * part of it, which means that the quickch() routine is going to be
    727 	 * sadly disappointed in us if we don't have CS as well.
    728 	 *
    729 	 * If CS, HO and SF/sf are set, can use the scrolling region.  Because
    730 	 * the cursor position after CS is undefined, we need HO which gives us
    731 	 * the ability to move to somewhere without knowledge of the current
    732 	 * location of the cursor.  Still call __mvcur() anyway, to update its
    733 	 * idea of where the cursor is.
    734 	 *
    735 	 * When the scrolling region has been set, the cursor has to be at the
    736 	 * last line of the region to make the scroll happen.
    737 	 *
    738 	 * Doing SF/SR or AL/DL appears faster on the screen than either sf/sr
    739 	 * or al/dl, and, some terminals have AL/DL, sf/sr, and CS, but not
    740 	 * SF/SR.  So, if we're scrolling almost all of the screen, try and use
    741 	 * AL/DL, otherwise use the scrolling region.  The "almost all" is a
    742 	 * shameless hack for vi.
    743 	 */
    744 	if (n > 0) {
    745 		if (CS != NULL && HO != NULL && (SF != NULL ||
    746 		    ((AL == NULL || DL == NULL ||
    747 		    top > 3 || bot + 3 < win->maxy) && sf != NULL))) {
    748 			tputs(__tscroll(CS, top, bot + 1), 0, __cputchar);
    749 			__mvcur(oy, ox, 0, 0, 1);
    750 			tputs(HO, 0, __cputchar);
    751 			__mvcur(0, 0, bot, 0, 1);
    752 			if (SF != NULL)
    753 				tputs(__tscroll(SF, n, 0), 0, __cputchar);
    754 			else
    755 				for (i = 0; i < n; i++)
    756 					tputs(sf, 0, __cputchar);
    757 			tputs(__tscroll(CS, 0, win->maxy), 0, __cputchar);
    758 			__mvcur(bot, 0, 0, 0, 1);
    759 			tputs(HO, 0, __cputchar);
    760 			__mvcur(0, 0, oy, ox, 1);
    761 			return;
    762 		}
    763 
    764 		/* Scroll up the block. */
    765 		if (SF != NULL && top == 0) {
    766 			__mvcur(oy, ox, bot, 0, 1);
    767 			tputs(__tscroll(SF, n, 0), 0, __cputchar);
    768 		} else if (DL != NULL) {
    769 			__mvcur(oy, ox, top, 0, 1);
    770 			tputs(__tscroll(DL, n, 0), 0, __cputchar);
    771 		} else if (dl != NULL) {
    772 			__mvcur(oy, ox, top, 0, 1);
    773 			for (i = 0; i < n; i++)
    774 				tputs(dl, 0, __cputchar);
    775 		} else if (sf != NULL && top == 0) {
    776 			__mvcur(oy, ox, bot, 0, 1);
    777 			for (i = 0; i < n; i++)
    778 				tputs(sf, 0, __cputchar);
    779 		} else
    780 			abort();
    781 
    782 		/* Push down the bottom region. */
    783 		__mvcur(top, 0, bot - n + 1, 0, 1);
    784 		if (AL != NULL)
    785 			tputs(__tscroll(AL, n, 0), 0, __cputchar);
    786 		else if (al != NULL)
    787 			for (i = 0; i < n; i++)
    788 				tputs(al, 0, __cputchar);
    789 		else
    790 			abort();
    791 		__mvcur(bot - n + 1, 0, oy, ox, 1);
    792 	} else {
    793 		/*
    794 		 * !!!
    795 		 * n < 0
    796 		 *
    797 		 * If CS, HO and SR/sr are set, can use the scrolling region.
    798 		 * See the above comments for details.
    799 		 */
    800 		if (CS != NULL && HO != NULL && (SR != NULL ||
    801 		    ((AL == NULL || DL == NULL ||
    802 		    top > 3 || bot + 3 < win->maxy) && sr != NULL))) {
    803 			tputs(__tscroll(CS, top, bot + 1), 0, __cputchar);
    804 			__mvcur(oy, ox, 0, 0, 1);
    805 			tputs(HO, 0, __cputchar);
    806 			__mvcur(0, 0, top, 0, 1);
    807 
    808 			if (SR != NULL)
    809 				tputs(__tscroll(SR, -n, 0), 0, __cputchar);
    810 			else
    811 				for (i = n; i < 0; i++)
    812 					tputs(sr, 0, __cputchar);
    813 			tputs(__tscroll(CS, 0, win->maxy), 0, __cputchar);
    814 			__mvcur(top, 0, 0, 0, 1);
    815 			tputs(HO, 0, __cputchar);
    816 			__mvcur(0, 0, oy, ox, 1);
    817 			return;
    818 		}
    819 
    820 		/* Preserve the bottom lines. */
    821 		__mvcur(oy, ox, bot + n + 1, 0, 1);
    822 		if (SR != NULL && bot == win->maxy)
    823 			tputs(__tscroll(SR, -n, 0), 0, __cputchar);
    824 		else if (DL != NULL)
    825 			tputs(__tscroll(DL, -n, 0), 0, __cputchar);
    826 		else if (dl != NULL)
    827 		       	for (i = n; i < 0; i++)
    828 				tputs(dl, 0, __cputchar);
    829 		else if (sr != NULL && bot == win->maxy)
    830 		       	for (i = n; i < 0; i++)
    831 				tputs(sr, 0, __cputchar);
    832 		else
    833 			abort();
    834 
    835 		/* Scroll the block down. */
    836 		__mvcur(bot + n + 1, 0, top, 0, 1);
    837 		if (AL != NULL)
    838 			tputs(__tscroll(AL, -n, 0), 0, __cputchar);
    839 		else if (al != NULL)
    840 			for (i = n; i < 0; i++)
    841 				tputs(al, 0, __cputchar);
    842 		else
    843 			abort();
    844 		__mvcur(top, 0, oy, ox, 1);
    845 	}
    846 }
    847