xref: /csrg-svn/lib/libcurses/refresh.c (revision 58034)
12261Sarnold /*
234677Sbostic  * Copyright (c) 1981 Regents of the University of California.
334677Sbostic  * All rights reserved.
434677Sbostic  *
542657Sbostic  * %sccs.include.redist.c%
622791Smckusick  */
722791Smckusick 
822791Smckusick #ifndef lint
9*58034Selan static char sccsid[] = "@(#)refresh.c	5.31 (Berkeley) 02/17/93";
1034677Sbostic #endif /* not lint */
1122791Smckusick 
1255976Sbostic #include <curses.h>
1355986Sbostic #include <string.h>
142261Sarnold 
1555976Sbostic static int curwin;
1655976Sbostic static short ly, lx;
172261Sarnold 
1855976Sbostic WINDOW *_win;
192261Sarnold 
2055976Sbostic static void	domvcur __P((int, int, int, int));
2155976Sbostic static int	makech __P((WINDOW *, int));
2256302Selan static void	quickch __P((WINDOW *));
2356559Selan static void	scrolln __P((WINDOW *, int, int, int, int, int));
2456648Selan 
2555976Sbostic /*
2655976Sbostic  * wrefresh --
2755976Sbostic  *	Make the current screen look like "win" over the area coverd by
2855976Sbostic  *	win.
2955976Sbostic  */
3055976Sbostic int
312261Sarnold wrefresh(win)
3255976Sbostic 	register WINDOW *win;
332261Sarnold {
3456648Selan 	register __LINE *wlp;
3555976Sbostic 	register int retval;
3655976Sbostic 	register short wy;
3758019Selan 	int dnum;
3858019Selan 
3955976Sbostic 	/* Initialize loop parameters. */
4056238Selan 	ly = curscr->cury;
4156238Selan 	lx = curscr->curx;
422287Sarnold 	wy = 0;
432287Sarnold 	_win = win;
442287Sarnold 	curwin = (win == curscr);
452287Sarnold 
4656378Selan 	if (!curwin)
4756378Selan 		for (wy = 0; wy < win->maxy; wy++) {
4856378Selan 			wlp = win->lines[wy];
4956378Selan 			if (wlp->flags & __ISDIRTY)
5056648Selan 				wlp->hash =
5156648Selan 				   __hash(wlp->line, win->maxx * __LDATASIZE);
5256378Selan 		}
5356378Selan 
5456238Selan 	if (win->flags & __CLEAROK || curscr->flags & __CLEAROK || curwin) {
5556238Selan 		if ((win->flags & __FULLWIN) || curscr->flags & __CLEAROK) {
5655976Sbostic 			tputs(CL, 0, __cputchar);
5712358Sarnold 			ly = 0;
5812358Sarnold 			lx = 0;
5912358Sarnold 			if (!curwin) {
6056238Selan 				curscr->flags &= ~__CLEAROK;
6156238Selan 				curscr->cury = 0;
6256238Selan 				curscr->curx = 0;
632287Sarnold 				werase(curscr);
6412358Sarnold 			}
6556651Selan 			__touchwin(win);
662261Sarnold 		}
6756238Selan 		win->flags &= ~__CLEAROK;
682261Sarnold 	}
692261Sarnold 	if (!CA) {
7056238Selan 		if (win->curx != 0)
7155976Sbostic 			putchar('\n');
722287Sarnold 		if (!curwin)
732287Sarnold 			werase(curscr);
742261Sarnold 	}
7555976Sbostic #ifdef DEBUG
7655976Sbostic 	__TRACE("wrefresh: (%0.2o): curwin = %d\n", win, curwin);
7755976Sbostic 	__TRACE("wrefresh: \tfirstch\tlastch\n");
7855976Sbostic #endif
7956302Selan 
8056302Selan #ifndef NOQCH
8158019Selan 	if ((win->flags & __FULLWIN) && !curwin) {
8258019Selan 		/*
8358019Selan 		 * Invoke quickch() only if more than a quarter of the lines
8458019Selan 		 * in the window are dirty.
8558019Selan 		 */
8658019Selan 		for (wy = 0, dnum = 0; wy < win->maxy; wy++)
8758019Selan 			if (win->lines[wy]->flags & (__ISDIRTY | __FORCEPAINT))
8858019Selan 				dnum++;
8958019Selan 		if (!__noqch && dnum > (int) win->maxy / 4)
9058019Selan 			quickch(win);
9158019Selan 	}
9256302Selan #endif
9356238Selan 	for (wy = 0; wy < win->maxy; wy++) {
9455976Sbostic #ifdef DEBUG
9555976Sbostic 		__TRACE("%d\t%d\t%d\n",
9656715Selan 		    wy, *win->lines[wy]->firstchp, *win->lines[wy]->lastchp);
9755976Sbostic #endif
9856378Selan 		if (!curwin)
9956378Selan 			curscr->lines[wy]->hash = win->lines[wy]->hash;
10058019Selan 		if (win->lines[wy]->flags & (__ISDIRTY | __FORCEPAINT))
10157472Sbostic 			if (makech(win, wy) == ERR)
10257472Sbostic 				return (ERR);
10319893Sbloom 			else {
10456715Selan 				if (*win->lines[wy]->firstchp >= win->ch_off)
10556715Selan 					*win->lines[wy]->firstchp = win->maxx +
10656238Selan 					    win->ch_off;
10756715Selan 				if (*win->lines[wy]->lastchp < win->maxx +
10856238Selan 				    win->ch_off)
10956715Selan 					*win->lines[wy]->lastchp = win->ch_off;
11056715Selan 				if (*win->lines[wy]->lastchp <
111*58034Selan 				    *win->lines[wy]->firstchp) {
112*58034Selan #ifdef DEBUG
113*58034Selan 					__TRACE("wrefresh: line %d notdirty \n", wy);
114*58034Selan #endif
11556238Selan 					win->lines[wy]->flags &= ~__ISDIRTY;
116*58034Selan 				}
11719893Sbloom 			}
11855976Sbostic #ifdef DEBUG
11956715Selan 		__TRACE("\t%d\t%d\n", *win->lines[wy]->firstchp,
12056715Selan 			*win->lines[wy]->lastchp);
12155976Sbostic #endif
1222261Sarnold 	}
12356238Selan 
12456302Selan #ifdef DEBUG
12556238Selan 	__TRACE("refresh: ly=%d, lx=%d\n", ly, lx);
12656302Selan #endif
12756472Selan 
12812358Sarnold 	if (win == curscr)
12956238Selan 		domvcur(ly, lx, win->cury, win->curx);
13019893Sbloom 	else {
13156238Selan 		if (win->flags & __LEAVEOK) {
13256238Selan 			curscr->cury = ly;
13356238Selan 			curscr->curx = lx;
13456238Selan 			ly -= win->begy;
13556238Selan 			lx -= win->begx;
13656238Selan 			if (ly >= 0 && ly < win->maxy && lx >= 0 &&
13756238Selan 			    lx < win->maxx) {
13856238Selan 				win->cury = ly;
13956238Selan 				win->curx = lx;
14055976Sbostic 			} else
14156238Selan 				win->cury = win->curx = 0;
14255976Sbostic 		} else {
14356238Selan 			domvcur(ly, lx, win->cury + win->begy,
14456238Selan 			    win->curx + win->begx);
14556238Selan 			curscr->cury = win->cury + win->begy;
14656238Selan 			curscr->curx = win->curx + win->begx;
14719893Sbloom 		}
1482261Sarnold 	}
14957472Sbostic 	retval = OK;
15055986Sbostic 
1512261Sarnold 	_win = NULL;
15255976Sbostic 	(void)fflush(stdout);
15355976Sbostic 	return (retval);
1542261Sarnold }
1552261Sarnold 
1562261Sarnold /*
15755976Sbostic  * makech --
15855976Sbostic  *	Make a change on the screen.
1592261Sarnold  */
16055976Sbostic static int
1612261Sarnold makech(win, wy)
16255976Sbostic 	register WINDOW *win;
16355976Sbostic 	int wy;
1642261Sarnold {
16555976Sbostic 	register int nlsp, clsp;		/* Last space in lines. */
16655976Sbostic 	register short wx, lch, y;
16756648Selan 	register __LDATA *nsp, *csp, *cp;
16856651Selan 	u_int force;
16956648Selan 	char *ce;
17056648Selan 	__LDATA blank = {' ', 0};
17156648Selan 
17256472Selan 	/* Is the cursor still on the end of the last line? */
17356472Selan 	if (wy > 0 && win->lines[wy - 1]->flags & __ISPASTEOL) {
17456551Selan 		win->lines[wy - 1]->flags &= ~__ISPASTEOL;
17556551Selan 		domvcur(ly, lx, ly + 1, 0);
17656551Selan 		ly++;
17756551Selan 		lx = 0;
17856551Selan 	}
17956238Selan 	if (!(win->lines[wy]->flags & __ISDIRTY))
18057472Sbostic 		return (OK);
18156715Selan 	wx = *win->lines[wy]->firstchp - win->ch_off;
18256238Selan 	if (wx >= win->maxx)
18357472Sbostic 		return (OK);
18419893Sbloom 	else if (wx < 0)
18519893Sbloom 		wx = 0;
18656715Selan 	lch = *win->lines[wy]->lastchp - win->ch_off;
18719893Sbloom 	if (lch < 0)
18857472Sbostic 		return (OK);
18956238Selan 	else if (lch >= win->maxx)
19056238Selan 		lch = win->maxx - 1;
19156238Selan 	y = wy + win->begy;
19219893Sbloom 
1932287Sarnold 	if (curwin)
19456648Selan 		csp = &blank;
1952287Sarnold 	else
19656238Selan 		csp = &curscr->lines[wy + win->begy]->line[wx + win->begx];
19719893Sbloom 
19856238Selan 	nsp = &win->lines[wy]->line[wx];
19956651Selan 	force = win->lines[wy]->flags & __FORCEPAINT;
20056651Selan 	win->lines[wy]->flags &= ~__FORCEPAINT;
2012287Sarnold 	if (CE && !curwin) {
20256648Selan 		for (cp = &win->lines[wy]->line[win->maxx - 1];
20356648Selan 		     cp->ch == ' ' && cp->attr == 0; cp--)
20456648Selan 			if (cp <= win->lines[wy]->line)
2052261Sarnold 				break;
20656648Selan 		nlsp = cp - win->lines[wy]->line;
2072261Sarnold 	}
2082287Sarnold 	if (!curwin)
2092287Sarnold 		ce = CE;
2102287Sarnold 	else
2112287Sarnold 		ce = NULL;
21219893Sbloom 
21356651Selan 	if (force) {
21456651Selan 		if (CM)
21556651Selan 			tputs(tgoto(CM, lx, ly), 0, __cputchar);
21656651Selan 		else {
21756651Selan 			tputs(HO, 0, __cputchar);
21856651Selan 			mvcur(0, 0, ly, lx);
21956651Selan 		}
22056651Selan 	}
2212261Sarnold 	while (wx <= lch) {
22257956Selan 		if (!force && memcmp(nsp, csp, sizeof(__LDATA)) == 0) {
22355986Sbostic 			if (wx <= lch) {
22457956Selan 				while (memcmp(nsp, csp, sizeof(__LDATA)) == 0 &&
22556648Selan 			            wx <= lch) {
22656596Selan 					    nsp++;
22756648Selan 					    if (!curwin)
22856648Selan 						    csp++;
22956648Selan 					    ++wx;
23056648Selan 				    }
23155986Sbostic 				continue;
23255986Sbostic 			}
23355986Sbostic 			break;
23455986Sbostic 		}
23556238Selan 		domvcur(ly, lx, y, wx + win->begx);
23656378Selan 
23755976Sbostic #ifdef DEBUG
23856651Selan 		__TRACE("makech: 1: wx = %d, ly= %d, lx = %d, newy = %d, newx = %d, force =%d\n",
23956651Selan 		    wx, ly, lx, y, wx + win->begx, force);
24055976Sbostic #endif
24155986Sbostic 		ly = y;
24256238Selan 		lx = wx + win->begx;
24357956Selan 		while ((force || memcmp(nsp, csp, sizeof(__LDATA)) != 0)
24456651Selan 		    && wx <= lch) {
24556378Selan #ifdef notdef
24656378Selan 			/* XXX
24756378Selan 			 * The problem with this code is that we can't count on
24856378Selan 			 * terminals wrapping around after the
24956378Selan 			 * last character on the previous line has been output
25056378Selan 			 * In effect, what then could happen is that the CE
25156378Selan 			 * clear the previous line and do nothing to the
25256378Selan 			 * next line.
25356378Selan 			 */
25456648Selan 			if (ce != NULL && wx >= nlsp &&
25556648Selan 			    nsp->ch == ' ') {
25655986Sbostic 				/* Check for clear to end-of-line. */
25756596Selan 				ce = &curscr->lines[wy]->line[COLS - 1];
25856648Selan 				while (ce->ch == ' ' && ce->attr = 0)
25955986Sbostic 					if (ce-- <= csp)
26055986Sbostic 						break;
26156596Selan 				clsp = ce - curscr->lines[wy]->line -
26256648Selan 				       win->begx * __LDATASIZE;
26355976Sbostic #ifdef DEBUG
26455986Sbostic 			__TRACE("makech: clsp = %d, nlsp = %d\n", clsp, nlsp);
26555976Sbostic #endif
26656648Selan 				if (clsp - nlsp >= strlen(CE)
26756648Selan 				    && clsp < win->maxx * __LDATASIZE) {
26855976Sbostic #ifdef DEBUG
26955986Sbostic 					__TRACE("makech: using CE\n");
27055976Sbostic #endif
27155986Sbostic 					tputs(CE, 0, __cputchar);
27256238Selan 					lx = wx + win->begx;
27356596Selan 					while (wx++ <= clsp) {
27456648Selan 						csp->ch = ' ';
27556648Selan 						csp->attr = 0;
27656648Selan 						csp++;
27756596Selan 					}
27857472Sbostic 					return (OK);
2792261Sarnold 				}
28055986Sbostic 				ce = NULL;
28155986Sbostic 			}
28256378Selan #endif
28355986Sbostic 
28455986Sbostic 			/* Enter/exit standout mode as appropriate. */
28556648Selan 			if (SO && (nsp->attr & __STANDOUT) !=
28656378Selan 			    (curscr->flags & __WSTANDOUT)) {
28756648Selan 				if (nsp->attr & __STANDOUT) {
28855986Sbostic 					tputs(SO, 0, __cputchar);
28956238Selan 					curscr->flags |= __WSTANDOUT;
29055986Sbostic 				} else {
29155986Sbostic 					tputs(SE, 0, __cputchar);
29256238Selan 					curscr->flags &= ~__WSTANDOUT;
2932261Sarnold 				}
29455986Sbostic 			}
29555986Sbostic 
29655986Sbostic 			wx++;
29756472Selan 			if (wx >= win->maxx && wy == win->maxy - 1 && !curwin)
29856238Selan 				if (win->flags & __SCROLLOK) {
29956238Selan 					if (curscr->flags & __WSTANDOUT
30056238Selan 					    && win->flags & __ENDLINE)
30155986Sbostic 						if (!MS) {
30255986Sbostic 							tputs(SE, 0,
30355986Sbostic 							    __cputchar);
30456238Selan 							curscr->flags &=
30556238Selan 							    ~__WSTANDOUT;
30655976Sbostic 						}
30756596Selan 					if (!curwin) {
30856648Selan 						csp->attr = nsp->attr;
30956648Selan 						putchar(csp->ch = nsp->ch);
31056596Selan 					} else
31156648Selan 						putchar(nsp->ch);
31256648Selan #ifdef notdef /* XXX why is this here? */
31356472Selan 					if (win->flags & __FULLWIN && !curwin)
31455986Sbostic 						scroll(curscr);
31556378Selan #endif
31656599Selan 					if (wx + win->begx < curscr->maxx) {
31756599Selan 						domvcur(ly, wx + win->begx,
31856599Selan 						    win->begy + win->maxy - 1,
31956599Selan 						    win->begx + win->maxx - 1);
32056599Selan 					}
32156378Selan 					ly = win->begy + win->maxy - 1;
32256378Selan 					lx = win->begx + win->maxx - 1;
32357472Sbostic 					return (OK);
32455986Sbostic 				} else
32556238Selan 					if (win->flags & __SCROLLWIN) {
32655986Sbostic 						lx = --wx;
32757472Sbostic 						return (ERR);
32855986Sbostic 					}
32956596Selan 			if (!curwin) {
33056648Selan 				csp->attr = nsp->attr;
33156648Selan 				putchar(csp->ch = nsp->ch);
33256648Selan 				csp++;
33356648Selan 		       	} else
33456648Selan 				putchar(nsp->ch);
33556472Selan 
33655976Sbostic #ifdef DEBUG
33756648Selan 			__TRACE("makech: putchar(%c)\n", nsp->ch & 0177);
33855976Sbostic #endif
33956648Selan 			if (UC && (nsp->attr & __STANDOUT)) {
34055986Sbostic 				putchar('\b');
34155986Sbostic 				tputs(UC, 0, __cputchar);
3422261Sarnold 			}
34355986Sbostic 			nsp++;
34455986Sbostic 		}
34555976Sbostic #ifdef DEBUG
34655986Sbostic 		__TRACE("makech: 2: wx = %d, lx = %d\n", wx, lx);
34755976Sbostic #endif
34856238Selan 		if (lx == wx + win->begx)	/* If no change. */
34955986Sbostic 			break;
35056238Selan 		lx = wx + win->begx;
35155986Sbostic 		if (lx >= COLS && AM) {
35255986Sbostic 			/*
35355986Sbostic 			 * xn glitch: chomps a newline after auto-wrap.
35455986Sbostic 			 * we just feed it now and forget about it.
35555986Sbostic 			 */
35655986Sbostic 			if (XN) {
35756472Selan 				lx = 0;
35856472Selan 				ly++;
35955986Sbostic 				putchar('\n');
36055986Sbostic 				putchar('\r');
36156472Selan 			} else {
36256472Selan 				if (wy != LINES)
36356472Selan 					win->lines[wy]->flags |= __ISPASTEOL;
36456472Selan 				lx = COLS - 1;
36522789Smckusick 			}
36656596Selan 		} else if (wx >= win->maxx) {
36756596Selan 			if (wy != win->maxy)
36856596Selan 				win->lines[wy]->flags |= __ISPASTEOL;
36956596Selan 			domvcur(ly, lx, ly, win->maxx + win->begx - 1);
37056596Selan 			lx = win->maxx + win->begx - 1;
37155986Sbostic 		}
37256596Selan 
37355976Sbostic #ifdef DEBUG
37455976Sbostic 		__TRACE("makech: 3: wx = %d, lx = %d\n", wx, lx);
37555976Sbostic #endif
3762261Sarnold 	}
37757472Sbostic 	return (OK);
37811736Sarnold }
37911736Sarnold 
38011736Sarnold /*
38155976Sbostic  * domvcur --
38255976Sbostic  *	Do a mvcur, leaving standout mode if necessary.
38311736Sarnold  */
38455976Sbostic static void
38511736Sarnold domvcur(oy, ox, ny, nx)
38655976Sbostic 	int oy, ox, ny, nx;
38755976Sbostic {
38856238Selan 	if (curscr->flags & __WSTANDOUT && !MS) {
38955976Sbostic 		tputs(SE, 0, __cputchar);
39056238Selan 		curscr->flags &= ~__WSTANDOUT;
3912261Sarnold 	}
39256596Selan 
39311736Sarnold 	mvcur(oy, ox, ny, nx);
3942261Sarnold }
39556302Selan 
39656302Selan /*
39756302Selan  * Quickch() attempts to detect a pattern in the change of the window
39856552Selan  * in order to optimize the change, e.g., scroll n lines as opposed to
39956302Selan  * repainting the screen line by line.
40056302Selan  */
40156302Selan 
40256715Selan 
40356302Selan static void
40456302Selan quickch(win)
40556302Selan 	WINDOW *win;
40656302Selan {
40758019Selan #define THRESH		(int) win->maxy / 4
40856302Selan 
40956648Selan 	register __LINE *clp, *tmp1, *tmp2;
41056378Selan 	register int bsize, curs, curw, starts, startw, i, j;
41156652Selan 	int n, target, cur_period, bot, top, sc_region;
41256648Selan 	__LDATA buf[1024];
41356378Selan 	u_int blank_hash;
41456302Selan 
41558019Selan 	/*
41658019Selan 	 * Find how many lines from the top of the screen are unchanged.
41758019Selan 	 */
418*58034Selan 	for (top = 0; top < win->maxy; top++)
41958019Selan 		if (win->lines[top]->flags & __FORCEPAINT ||
42058019Selan 		    win->lines[top]->hash != curscr->lines[top]->hash
42158019Selan 		    || memcmp(win->lines[top]->line,
42258019Selan 		    curscr->lines[top]->line,
42358019Selan 		    win->maxx * __LDATASIZE) != 0)
42458019Selan 			break;
425*58034Selan 		else
426*58034Selan 			win->lines[top]->flags &= ~__ISDIRTY;
42758019Selan        /*
42858019Selan 	* Find how many lines from bottom of screen are unchanged.
42958019Selan 	*/
43058019Selan 	for (bot = win->maxy - 1; bot >= 0; bot--)
43158019Selan 		if (win->lines[bot]->flags & __FORCEPAINT ||
43258019Selan 		    win->lines[bot]->hash != curscr->lines[bot]->hash
43358019Selan 		    || memcmp(win->lines[bot]->line,
43458019Selan 		    curscr->lines[bot]->line,
43558019Selan 		    win->maxx * __LDATASIZE) != 0)
43658019Selan 			break;
437*58034Selan 		else
438*58034Selan 			win->lines[bot]->flags &= ~__ISDIRTY;
43958019Selan 
44056552Selan 	/*
44156552Selan 	 * Search for the largest block of text not changed.
44256652Selan 	 * Invariants of the loop:
44356652Selan 	 * - Startw is the index of the beginning of the examined block in win.
44456652Selan          * - Starts is the index of the beginning of the examined block in
44556652Selan 	 *    curscr.
44656652Selan 	 * - Curs is the index of one past the end of the exmined block in win.
44756652Selan 	 * - Curw is the index of one past the end of the exmined block in
44856652Selan 	 *   curscr.
44956652Selan 	 * - bsize is the current size of the examined block.
45056552Selan          */
45158019Selan 	for (bsize = bot - top; bsize >= THRESH; bsize--) {
45258019Selan 		for (startw = top; startw <= bot - bsize; startw++)
45358019Selan 			for (starts = top; starts <= bot - bsize;
45456302Selan 			     starts++) {
45556302Selan 				for (curw = startw, curs = starts;
45656302Selan 				     curs < starts + bsize; curw++, curs++)
45756651Selan 					if (win->lines[curw]->flags &
45856651Selan 					    __FORCEPAINT ||
45956651Selan 					    (win->lines[curw]->hash !=
46056552Selan 					    curscr->lines[curs]->hash ||
46157956Selan 				            memcmp(win->lines[curw]->line,
46256648Selan 					    curscr->lines[curs]->line,
46356651Selan 					    win->maxx * __LDATASIZE) != 0))
46456302Selan 						break;
46556302Selan 				if (curs == starts + bsize)
46656302Selan 					goto done;
46756302Selan 			}
46858019Selan 	}
46956302Selan  done:
47056651Selan 	/* Did not find anything */
47156651Selan 	if (bsize < THRESH)
47256302Selan 		return;
47356302Selan 
47456302Selan #ifdef DEBUG
47556559Selan 	__TRACE("quickch:bsize=%d,starts=%d,startw=%d,curw=%d,curs=%d,top=%d,bot=%d\n",
47656559Selan 		bsize, starts, startw, curw, curs, top, bot);
47756302Selan #endif
47856378Selan 
47956559Selan 	/*
48056559Selan 	 * Make sure that there is no overlap between the bottom and top
48156559Selan 	 * regions and the middle scrolled block.
48256559Selan 	 */
48356691Selan 	if (bot < curs)
48456691Selan 		bot = curs - 1;
48556691Selan 	if (top > starts)
48656691Selan 		top = starts;
48756559Selan 
48856378Selan 	n = startw - starts;
48956378Selan 
49056691Selan #ifdef DEBUG
49156691Selan 		__TRACE("#####################################\n");
49256691Selan 		for (i = 0; i < curscr->maxy; i++) {
49356691Selan 			__TRACE("C: %d:", i);
49457356Selan 			__TRACE(" 0x%x \n", curscr->lines[i]->hash);
49556691Selan 			for (j = 0; j < curscr->maxx; j++)
49656691Selan 				__TRACE("%c",
49756691Selan 			           curscr->lines[i]->line[j].ch);
49856691Selan 			__TRACE("\n");
49957356Selan 			for (j = 0; j < curscr->maxx; j++)
50057356Selan 				__TRACE("%x",
50157356Selan 			           curscr->lines[i]->line[j].attr);
50257356Selan 			__TRACE("\n");
50356691Selan 			__TRACE("W: %d:", i);
50457356Selan 			__TRACE(" 0x%x \n", win->lines[i]->hash);
50557356Selan 			__TRACE(" 0x%x ", win->lines[i]->flags);
50656691Selan 			for (j = 0; j < win->maxx; j++)
50756691Selan 				__TRACE("%c",
50856691Selan 			           win->lines[i]->line[j].ch);
50956691Selan 			__TRACE("\n");
51057356Selan 			for (j = 0; j < win->maxx; j++)
51157356Selan 				__TRACE("%x",
51257356Selan 			           win->lines[i]->line[j].attr);
51357356Selan 			__TRACE("\n");
51456691Selan 		}
51556691Selan #endif
51656651Selan 	if (n != 0)
51756705Selan 		scrolln(win, starts, startw, curs, bot, top);
51858019Selan 
51956378Selan 	/* So we don't have to call __hash() each time */
52056648Selan 	for (i = 0; i < win->maxx; i++) {
52156648Selan 		buf[i].ch = ' ';
52256648Selan 		buf[i].attr = 0;
52356648Selan 	}
52456648Selan 	blank_hash = __hash(buf, win->maxx * __LDATASIZE);
52556378Selan 
52656378Selan 	/*
52756378Selan 	 * Perform the rotation to maintain the consistency of curscr.
52856691Selan 	 * This is hairy since we are doing an *in place* rotation.
52956652Selan 	 * Invariants of the loop:
53056652Selan 	 * - I is the index of the current line.
53156652Selan 	 * - Target is the index of the target of line i.
53256652Selan 	 * - Tmp1 points to current line (i).
53356652Selan 	 * - Tmp2 and points to target line (target);
53456652Selan 	 * - Cur_period is the index of the end of the current period.
53556652Selan 	 *   (see below).
53656652Selan 	 *
53756652Selan 	 * There are 2 major issues here that make this rotation non-trivial:
53856652Selan 	 * 1.  Scrolling in a scrolling region bounded by the top
53956652Selan 	 *     and bottom regions determined (whose size is sc_region).
54056652Selan 	 * 2.  As a result of the use of the mod function, there may be a
54156652Selan 	 *     period introduced, i.e., 2 maps to 4, 4 to 6, n-2 to 0, and
54256652Selan 	 *     0 to 2, which then causes all odd lines not to be rotated.
54356652Selan 	 *     To remedy this, an index of the end ( = beginning) of the
54456652Selan 	 *     current 'period' is kept, cur_period, and when it is reached,
54556652Selan 	 *     the next period is started from cur_period + 1 which is
54656652Selan 	 *     guaranteed not to have been reached since that would mean that
54756652Selan 	 *     all records would have been reached. (think about it...).
54856652Selan 	 *
54956652Selan 	 * Lines in the rotation can have 3 attributes which are marked on the
55056652Selan 	 * line so that curscr is consistent with the visual screen.
55156705Selan 	 * 1.  Not dirty -- lines inside the scrolled block, top region or
55256652Selan 	 *                  bottom region.
55356705Selan 	 * 2.  Blank lines -- lines in the differential of the scrolling
55456705Selan 	 *		      region adjacent to top and bot regions
55556705Selan 	 *                    depending on scrolling direction.
55656652Selan 	 * 3.  Dirty line -- all other lines are marked dirty.
55756378Selan 	 */
55856648Selan 	sc_region = bot - top + 1;
55956648Selan 	i = top;
56056648Selan 	tmp1 = curscr->lines[top];
56156652Selan 	cur_period = top;
56256648Selan 	for (j = top; j <= bot; j++) {
56356648Selan 		target = (i - top + n + sc_region) % sc_region + top;
56456378Selan 		tmp2 = curscr->lines[target];
56556378Selan 		curscr->lines[target] = tmp1;
56656378Selan 		/* Mark block as clean and blank out scrolled lines. */
56756378Selan 		clp = curscr->lines[target];
56856472Selan #ifdef DEBUG
56956378Selan 		__TRACE("quickch: n=%d startw=%d curw=%d i = %d target=%d ",
57056378Selan 			n, startw, curw, i, target);
57156472Selan #endif
57256691Selan 		if ((target >= startw && target < curw) || target < top
57356691Selan 		    || target > bot) {
57456472Selan #ifdef DEBUG
57556378Selan 			__TRACE("-- notdirty");
57656472Selan #endif
57756378Selan 			win->lines[target]->flags &= ~__ISDIRTY;
57856705Selan 		} else if ((n > 0 && target >= top && target < top + n) ||
57956705Selan 		           (n < 0 && target <= bot && target > bot + n)) {
58057956Selan 			if (clp->hash != blank_hash ||  memcmp(clp->line,
58156648Selan 			    buf, win->maxx * __LDATASIZE) !=0) {
58256648Selan 				(void)bcopy(buf, clp->line,
58356648Selan 				    win->maxx * __LDATASIZE);
58456472Selan #ifdef DEBUG
58556648Selan 				__TRACE("-- blanked out: dirty");
58656472Selan #endif
58756378Selan 				clp->hash = blank_hash;
58856652Selan 				__touchline(win, target, 0, win->maxx - 1, 0);
58956705Selan 			} else {
59056652Selan 				__touchline(win, target, 0, win->maxx - 1, 0);
59156472Selan #ifdef DEBUG
59256648Selan 				__TRACE(" -- blank line already: dirty");
59356472Selan #endif
59456705Selan 			}
59556378Selan 		} else {
59656472Selan #ifdef DEBUG
59756648Selan 			__TRACE(" -- dirty");
59856472Selan #endif
59956651Selan 			__touchline(win, target, 0, win->maxx - 1, 0);
60056378Selan 		}
60156472Selan #ifdef DEBUG
60256378Selan 		__TRACE("\n");
60356472Selan #endif
60456652Selan 		if (target == cur_period) {
60556378Selan 			i = target + 1;
60656378Selan 			tmp1 = curscr->lines[i];
60756652Selan 			cur_period = i;
60856378Selan 		} else {
60956378Selan 			tmp1 = tmp2;
61056378Selan 			i = target;
61156378Selan 		}
61256302Selan 	}
61356472Selan #ifdef DEBUG
61456648Selan 		__TRACE("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n");
61556648Selan 		for (i = 0; i < curscr->maxy; i++) {
61656691Selan 			__TRACE("C: %d:", i);
61756648Selan 			for (j = 0; j < curscr->maxx; j++)
61856648Selan 				__TRACE("%c",
61956648Selan 			           curscr->lines[i]->line[j].ch);
62056648Selan 			__TRACE("\n");
62156691Selan 			__TRACE("W: %d:", i);
62256691Selan 			for (j = 0; j < win->maxx; j++)
62356691Selan 				__TRACE("%c",
62456691Selan 			           win->lines[i]->line[j].ch);
62556691Selan 			__TRACE("\n");
62656648Selan 		}
62756472Selan #endif
62856302Selan }
62956302Selan 
63056652Selan /*
63156652Selan  * Scrolln performs the scroll by n lines, where n is starts - startw.
63256652Selan  */
63356302Selan static void
63456705Selan scrolln(win, starts, startw, curs, bot, top)
63556302Selan 	WINDOW *win;
63656705Selan 	int starts, startw, curs, bot, top;
63756302Selan {
63856302Selan 	int i, oy, ox, n;
63956302Selan 
64056302Selan 	oy = curscr->cury;
64156302Selan 	ox = curscr->curx;
64256302Selan 	n = starts - startw;
64356302Selan 
64456302Selan 	if (n > 0) {
64556559Selan 		mvcur(oy, ox, top, 0);
64656559Selan 		/* Scroll up the block */
64756302Selan 		if (DL)
64857475Sbostic 			tputs(__tscroll(DL, n), 0, __cputchar);
64956302Selan 		else
65056302Selan 			for(i = 0; i < n; i++)
65156302Selan 				tputs(dl, 0, __cputchar);
65256652Selan 
65356651Selan 		/*
65456652Selan 		 * Push down the bottom region.
65556651Selan 		 */
65656705Selan 		mvcur(top, 0, bot - n + 1, 0);
65758019Selan 		if (AL)
65857475Sbostic 			tputs(__tscroll(AL, n), 0, __cputchar);
65956652Selan 		else
66056652Selan 			for(i = 0; i < n; i++)
66156652Selan 				tputs(al, 0, __cputchar);
66256705Selan 		mvcur(bot - n + 1, 0, oy, ox);
66356302Selan 	} else {
66456651Selan 		/* Preserve the bottom lines */
66556705Selan 		mvcur(oy, ox, bot + n + 1, 0);	/* n < 0 */
66656652Selan 		if (DL)
66757475Sbostic 			tputs(__tscroll(DL, -n), 0, __cputchar);
66856652Selan 		else
66956652Selan 		       	for(i = n; i < 0; i++)
67056652Selan 				tputs(dl, 0, __cputchar);
67156705Selan 		mvcur(bot + n + 1, 0, top, 0);
67256302Selan 
67356302Selan 		/* Scroll the block down */
67458019Selan 		if (AL)
67557475Sbostic 			tputs(__tscroll(AL, -n), 0, __cputchar);
67656302Selan 		else
67756302Selan 			for(i = n; i < 0; i++)
67856302Selan 				tputs(al, 0, __cputchar);
67956705Selan 		mvcur(top, 0, oy, ox);
68056378Selan 	}
68156302Selan }
68256378Selan 
68356378Selan 
684