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