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