12261Sarnold /*
267082Sbostic * Copyright (c) 1981, 1993, 1994
363945Sbostic * The Regents of the University of California. All rights reserved.
434677Sbostic *
542657Sbostic * %sccs.include.redist.c%
622791Smckusick */
722791Smckusick
822791Smckusick #ifndef lint
9*67665Sbostic static char sccsid[] = "@(#)refresh.c 8.7 (Berkeley) 08/13/94";
1034677Sbostic #endif /* not lint */
1122791Smckusick
1255986Sbostic #include <string.h>
132261Sarnold
1467082Sbostic #include "curses.h"
1567082Sbostic
1655976Sbostic static int curwin;
1755976Sbostic static short ly, lx;
182261Sarnold
1955976Sbostic static void domvcur __P((int, int, int, int));
2055976Sbostic static int makech __P((WINDOW *, int));
2167571Sbostic static void quickch __P((WINDOW *));
2256559Selan static void scrolln __P((WINDOW *, int, int, int, int, int));
2356648Selan
2455976Sbostic /*
2555976Sbostic * wrefresh --
2655976Sbostic * Make the current screen look like "win" over the area coverd by
2755976Sbostic * win.
2855976Sbostic */
2955976Sbostic int
wrefresh(win)302261Sarnold wrefresh(win)
3155976Sbostic register WINDOW *win;
322261Sarnold {
3356648Selan register __LINE *wlp;
3455976Sbostic register int retval;
3555976Sbostic register short wy;
3658019Selan int dnum;
3767571Sbostic
3855976Sbostic /* Initialize loop parameters. */
3956238Selan ly = curscr->cury;
4056238Selan lx = curscr->curx;
412287Sarnold wy = 0;
422287Sarnold curwin = (win == curscr);
432287Sarnold
4456378Selan if (!curwin)
4556378Selan for (wy = 0; wy < win->maxy; wy++) {
4656378Selan wlp = win->lines[wy];
4756378Selan if (wlp->flags & __ISDIRTY)
4867632Sbostic wlp->hash = __hash((char *)wlp->line,
4967632Sbostic win->maxx * __LDATASIZE);
5056378Selan }
5156378Selan
5256238Selan if (win->flags & __CLEAROK || curscr->flags & __CLEAROK || curwin) {
5356238Selan if ((win->flags & __FULLWIN) || curscr->flags & __CLEAROK) {
5455976Sbostic tputs(CL, 0, __cputchar);
5512358Sarnold ly = 0;
5612358Sarnold lx = 0;
5712358Sarnold if (!curwin) {
5856238Selan curscr->flags &= ~__CLEAROK;
5956238Selan curscr->cury = 0;
6056238Selan curscr->curx = 0;
612287Sarnold werase(curscr);
6212358Sarnold }
6356651Selan __touchwin(win);
642261Sarnold }
6556238Selan win->flags &= ~__CLEAROK;
662261Sarnold }
672261Sarnold if (!CA) {
6856238Selan if (win->curx != 0)
6955976Sbostic putchar('\n');
702287Sarnold if (!curwin)
712287Sarnold werase(curscr);
722261Sarnold }
7355976Sbostic #ifdef DEBUG
7460058Sbostic __CTRACE("wrefresh: (%0.2o): curwin = %d\n", win, curwin);
7560058Sbostic __CTRACE("wrefresh: \tfirstch\tlastch\n");
7655976Sbostic #endif
7756302Selan
7856302Selan #ifndef NOQCH
7958019Selan if ((win->flags & __FULLWIN) && !curwin) {
8058019Selan /*
8158019Selan * Invoke quickch() only if more than a quarter of the lines
8258019Selan * in the window are dirty.
8358019Selan */
8458019Selan for (wy = 0, dnum = 0; wy < win->maxy; wy++)
8558019Selan if (win->lines[wy]->flags & (__ISDIRTY | __FORCEPAINT))
8658019Selan dnum++;
8758019Selan if (!__noqch && dnum > (int) win->maxy / 4)
8858019Selan quickch(win);
8958019Selan }
9056302Selan #endif
9159930Selan
9259930Selan #ifdef DEBUG
9359930Selan { int i, j;
9460058Sbostic __CTRACE("#####################################\n");
9559930Selan for (i = 0; i < curscr->maxy; i++) {
9660058Sbostic __CTRACE("C: %d:", i);
9760058Sbostic __CTRACE(" 0x%x \n", curscr->lines[i]->hash);
9867571Sbostic for (j = 0; j < curscr->maxx; j++)
9967571Sbostic __CTRACE("%c",
10059930Selan curscr->lines[i]->line[j].ch);
10160058Sbostic __CTRACE("\n");
10267571Sbostic for (j = 0; j < curscr->maxx; j++)
10367571Sbostic __CTRACE("%x",
10459930Selan curscr->lines[i]->line[j].attr);
10560058Sbostic __CTRACE("\n");
10660058Sbostic __CTRACE("W: %d:", i);
10760058Sbostic __CTRACE(" 0x%x \n", win->lines[i]->hash);
10860058Sbostic __CTRACE(" 0x%x ", win->lines[i]->flags);
10967571Sbostic for (j = 0; j < win->maxx; j++)
11067571Sbostic __CTRACE("%c",
11159930Selan win->lines[i]->line[j].ch);
11260058Sbostic __CTRACE("\n");
11367571Sbostic for (j = 0; j < win->maxx; j++)
11467571Sbostic __CTRACE("%x",
11559930Selan win->lines[i]->line[j].attr);
11660058Sbostic __CTRACE("\n");
11759930Selan }
11859930Selan }
11959930Selan #endif /* DEBUG */
12059930Selan
12156238Selan for (wy = 0; wy < win->maxy; wy++) {
12255976Sbostic #ifdef DEBUG
12360058Sbostic __CTRACE("%d\t%d\t%d\n",
12456715Selan wy, *win->lines[wy]->firstchp, *win->lines[wy]->lastchp);
12555976Sbostic #endif
12656378Selan if (!curwin)
12756378Selan curscr->lines[wy]->hash = win->lines[wy]->hash;
12858369Selan if (win->lines[wy]->flags & (__ISDIRTY | __FORCEPAINT)) {
12957472Sbostic if (makech(win, wy) == ERR)
13057472Sbostic return (ERR);
13119893Sbloom else {
13256715Selan if (*win->lines[wy]->firstchp >= win->ch_off)
13356715Selan *win->lines[wy]->firstchp = win->maxx +
13456238Selan win->ch_off;
13556715Selan if (*win->lines[wy]->lastchp < win->maxx +
13656238Selan win->ch_off)
13756715Selan *win->lines[wy]->lastchp = win->ch_off;
13867571Sbostic if (*win->lines[wy]->lastchp <
13958034Selan *win->lines[wy]->firstchp) {
14058034Selan #ifdef DEBUG
14160058Sbostic __CTRACE("wrefresh: line %d notdirty \n", wy);
14258034Selan #endif
14356238Selan win->lines[wy]->flags &= ~__ISDIRTY;
14458034Selan }
14519893Sbloom }
14658369Selan
14759774Selan }
14855976Sbostic #ifdef DEBUG
14967571Sbostic __CTRACE("\t%d\t%d\n", *win->lines[wy]->firstchp,
15056715Selan *win->lines[wy]->lastchp);
15155976Sbostic #endif
1522261Sarnold }
15367571Sbostic
15456302Selan #ifdef DEBUG
15560058Sbostic __CTRACE("refresh: ly=%d, lx=%d\n", ly, lx);
15656302Selan #endif
15756472Selan
15812358Sarnold if (win == curscr)
15956238Selan domvcur(ly, lx, win->cury, win->curx);
16019893Sbloom else {
16156238Selan if (win->flags & __LEAVEOK) {
16256238Selan curscr->cury = ly;
16356238Selan curscr->curx = lx;
16456238Selan ly -= win->begy;
16556238Selan lx -= win->begx;
16656238Selan if (ly >= 0 && ly < win->maxy && lx >= 0 &&
16756238Selan lx < win->maxx) {
16856238Selan win->cury = ly;
16956238Selan win->curx = lx;
17055976Sbostic } else
17156238Selan win->cury = win->curx = 0;
17255976Sbostic } else {
17356238Selan domvcur(ly, lx, win->cury + win->begy,
17456238Selan win->curx + win->begx);
17556238Selan curscr->cury = win->cury + win->begy;
17656238Selan curscr->curx = win->curx + win->begx;
17719893Sbloom }
1782261Sarnold }
17957472Sbostic retval = OK;
18055986Sbostic
18155976Sbostic (void)fflush(stdout);
18255976Sbostic return (retval);
1832261Sarnold }
1842261Sarnold
1852261Sarnold /*
18655976Sbostic * makech --
18755976Sbostic * Make a change on the screen.
1882261Sarnold */
18955976Sbostic static int
makech(win,wy)1902261Sarnold makech(win, wy)
19155976Sbostic register WINDOW *win;
19255976Sbostic int wy;
1932261Sarnold {
19460614Sbostic static __LDATA blank = {' ', 0};
19567631Sbostic __LDATA *nsp, *csp, *cp, *cep;
19656651Selan u_int force;
19767631Sbostic int clsp, nlsp; /* Last space in lines. */
19867631Sbostic int lch, wx, y;
19956648Selan char *ce;
20056648Selan
20156472Selan /* Is the cursor still on the end of the last line? */
20256472Selan if (wy > 0 && win->lines[wy - 1]->flags & __ISPASTEOL) {
20356551Selan domvcur(ly, lx, ly + 1, 0);
20456551Selan ly++;
20556551Selan lx = 0;
20656551Selan }
20756715Selan wx = *win->lines[wy]->firstchp - win->ch_off;
20859352Selan if (wx < 0)
20959352Selan wx = 0;
21059352Selan else if (wx >= win->maxx)
21157472Sbostic return (OK);
21256715Selan lch = *win->lines[wy]->lastchp - win->ch_off;
21319893Sbloom if (lch < 0)
21457472Sbostic return (OK);
21559352Selan else if (lch >= (int) win->maxx)
21656238Selan lch = win->maxx - 1;
21756238Selan y = wy + win->begy;
21819893Sbloom
2192287Sarnold if (curwin)
22056648Selan csp = ␣
2212287Sarnold else
22256238Selan csp = &curscr->lines[wy + win->begy]->line[wx + win->begx];
22319893Sbloom
22456238Selan nsp = &win->lines[wy]->line[wx];
22556651Selan force = win->lines[wy]->flags & __FORCEPAINT;
22656651Selan win->lines[wy]->flags &= ~__FORCEPAINT;
2272287Sarnold if (CE && !curwin) {
22867571Sbostic for (cp = &win->lines[wy]->line[win->maxx - 1];
22956648Selan cp->ch == ' ' && cp->attr == 0; cp--)
23056648Selan if (cp <= win->lines[wy]->line)
2312261Sarnold break;
23256648Selan nlsp = cp - win->lines[wy]->line;
2332261Sarnold }
2342287Sarnold if (!curwin)
2352287Sarnold ce = CE;
2362287Sarnold else
2372287Sarnold ce = NULL;
23819893Sbloom
23956651Selan if (force) {
24056651Selan if (CM)
24156651Selan tputs(tgoto(CM, lx, ly), 0, __cputchar);
24256651Selan else {
24356651Selan tputs(HO, 0, __cputchar);
24459930Selan __mvcur(0, 0, ly, lx, 1);
24556651Selan }
24656651Selan }
24767631Sbostic
2482261Sarnold while (wx <= lch) {
24957956Selan if (!force && memcmp(nsp, csp, sizeof(__LDATA)) == 0) {
25055986Sbostic if (wx <= lch) {
25163944Sbostic while (wx <= lch &&
25267631Sbostic memcmp(nsp, csp, sizeof(__LDATA)) == 0) {
25367631Sbostic nsp++;
25467631Sbostic if (!curwin)
25567631Sbostic ++csp;
25667631Sbostic ++wx;
25767631Sbostic }
25855986Sbostic continue;
25955986Sbostic }
26055986Sbostic break;
26155986Sbostic }
26256238Selan domvcur(ly, lx, y, wx + win->begx);
26356378Selan
26455976Sbostic #ifdef DEBUG
26567571Sbostic __CTRACE("makech: 1: wx = %d, ly= %d, lx = %d, newy = %d, newx = %d, force =%d\n",
26656651Selan wx, ly, lx, y, wx + win->begx, force);
26755976Sbostic #endif
26855986Sbostic ly = y;
26956238Selan lx = wx + win->begx;
27067571Sbostic while ((force || memcmp(nsp, csp, sizeof(__LDATA)) != 0)
27156651Selan && wx <= lch) {
27255986Sbostic
27367632Sbostic if (ce != NULL &&
27467632Sbostic win->maxx + win->begx == curscr->maxx &&
27567632Sbostic wx >= nlsp && nsp->ch == ' ' && nsp->attr == 0) {
27659775Selan /* Check for clear to end-of-line. */
27759775Selan cep = &curscr->lines[wy]->line[win->maxx - 1];
27859775Selan while (cep->ch == ' ' && cep->attr == 0)
27959775Selan if (cep-- <= csp)
28059775Selan break;
28167571Sbostic clsp = cep - curscr->lines[wy]->line -
28259775Selan win->begx * __LDATASIZE;
28359775Selan #ifdef DEBUG
28460058Sbostic __CTRACE("makech: clsp = %d, nlsp = %d\n", clsp, nlsp);
28559775Selan #endif
28667571Sbostic if ((clsp - nlsp >= strlen(CE)
28759775Selan && clsp < win->maxx * __LDATASIZE) ||
28859775Selan wy == win->maxy - 1) {
28967632Sbostic if (curscr->flags & __WSTANDOUT) {
29067632Sbostic tputs(SE, 0, __cputchar);
29167632Sbostic curscr->flags &= ~__WSTANDOUT;
29267632Sbostic }
29359775Selan tputs(CE, 0, __cputchar);
29459775Selan lx = wx + win->begx;
29559775Selan while (wx++ <= clsp) {
29659775Selan csp->ch = ' ';
29759775Selan csp->attr = 0;
29859775Selan csp++;
29959775Selan }
30059775Selan return (OK);
30159775Selan }
30259775Selan ce = NULL;
30359775Selan }
30459775Selan
30567631Sbostic /*
30667631Sbostic * Enter/exit standout mode as appropriate.
30767631Sbostic * XXX
30867631Sbostic * Should use UC if SO/SE not available.
30967631Sbostic */
31067631Sbostic if (nsp->attr & __STANDOUT) {
31167631Sbostic if (!(curscr->flags & __WSTANDOUT) &&
31267631Sbostic SO != NULL && SE != NULL) {
31355986Sbostic tputs(SO, 0, __cputchar);
31456238Selan curscr->flags |= __WSTANDOUT;
31567631Sbostic }
31667631Sbostic } else
31767631Sbostic if (curscr->flags & __WSTANDOUT &&
31867631Sbostic SE != NULL) {
31955986Sbostic tputs(SE, 0, __cputchar);
32056238Selan curscr->flags &= ~__WSTANDOUT;
3212261Sarnold }
32255986Sbostic
32355986Sbostic wx++;
32456472Selan if (wx >= win->maxx && wy == win->maxy - 1 && !curwin)
32556238Selan if (win->flags & __SCROLLOK) {
32656238Selan if (curscr->flags & __WSTANDOUT
32756238Selan && win->flags & __ENDLINE)
32855986Sbostic if (!MS) {
32955986Sbostic tputs(SE, 0,
33055986Sbostic __cputchar);
33156238Selan curscr->flags &=
33256238Selan ~__WSTANDOUT;
33355976Sbostic }
33459760Selan if (!(win->flags & __SCROLLWIN)) {
33559760Selan if (!curwin) {
33659760Selan csp->attr = nsp->attr;
33759760Selan putchar(csp->ch = nsp->ch);
33859760Selan } else
33959760Selan putchar(nsp->ch);
34059760Selan }
34156599Selan if (wx + win->begx < curscr->maxx) {
34267571Sbostic domvcur(ly, wx + win->begx,
34356599Selan win->begy + win->maxy - 1,
34456599Selan win->begx + win->maxx - 1);
34556599Selan }
34656378Selan ly = win->begy + win->maxy - 1;
34756378Selan lx = win->begx + win->maxx - 1;
34857472Sbostic return (OK);
34967571Sbostic }
35067571Sbostic if (wx < win->maxx || wy < win->maxy - 1 ||
35159760Selan !(win->flags & __SCROLLWIN)) {
35259760Selan if (!curwin) {
35359760Selan csp->attr = nsp->attr;
35459760Selan putchar(csp->ch = nsp->ch);
35559760Selan csp++;
35667571Sbostic } else
35759760Selan putchar(nsp->ch);
35859760Selan }
35955976Sbostic #ifdef DEBUG
36060058Sbostic __CTRACE("makech: putchar(%c)\n", nsp->ch & 0177);
36155976Sbostic #endif
36256648Selan if (UC && (nsp->attr & __STANDOUT)) {
36355986Sbostic putchar('\b');
36455986Sbostic tputs(UC, 0, __cputchar);
3652261Sarnold }
36655986Sbostic nsp++;
36755976Sbostic #ifdef DEBUG
36860058Sbostic __CTRACE("makech: 2: wx = %d, lx = %d\n", wx, lx);
36955976Sbostic #endif
37059760Selan }
37156238Selan if (lx == wx + win->begx) /* If no change. */
37255986Sbostic break;
37356238Selan lx = wx + win->begx;
37459774Selan if (lx >= COLS && AM)
37558040Selan lx = COLS - 1;
37659774Selan else if (wx >= win->maxx) {
37756596Selan domvcur(ly, lx, ly, win->maxx + win->begx - 1);
37856596Selan lx = win->maxx + win->begx - 1;
37955986Sbostic }
38056596Selan
38155976Sbostic #ifdef DEBUG
38260058Sbostic __CTRACE("makech: 3: wx = %d, lx = %d\n", wx, lx);
38355976Sbostic #endif
3842261Sarnold }
38567632Sbostic
38667632Sbostic /* Don't leave the screen in standout mode. */
38767632Sbostic if (curscr->flags & __WSTANDOUT) {
38867632Sbostic tputs(SE, 0, __cputchar);
38967632Sbostic curscr->flags &= ~__WSTANDOUT;
39067632Sbostic }
39157472Sbostic return (OK);
39211736Sarnold }
39311736Sarnold
39411736Sarnold /*
39555976Sbostic * domvcur --
39655976Sbostic * Do a mvcur, leaving standout mode if necessary.
39711736Sarnold */
39855976Sbostic static void
domvcur(oy,ox,ny,nx)39911736Sarnold domvcur(oy, ox, ny, nx)
40055976Sbostic int oy, ox, ny, nx;
40155976Sbostic {
40256238Selan if (curscr->flags & __WSTANDOUT && !MS) {
40355976Sbostic tputs(SE, 0, __cputchar);
40456238Selan curscr->flags &= ~__WSTANDOUT;
4052261Sarnold }
40656596Selan
40759930Selan __mvcur(oy, ox, ny, nx, 1);
4082261Sarnold }
40956302Selan
41056302Selan /*
41156302Selan * Quickch() attempts to detect a pattern in the change of the window
41267571Sbostic * in order to optimize the change, e.g., scroll n lines as opposed to
41356302Selan * repainting the screen line by line.
41456302Selan */
41556302Selan
41656302Selan static void
quickch(win)41756302Selan quickch(win)
41856302Selan WINDOW *win;
41956302Selan {
42058019Selan #define THRESH (int) win->maxy / 4
42156302Selan
42256648Selan register __LINE *clp, *tmp1, *tmp2;
42356378Selan register int bsize, curs, curw, starts, startw, i, j;
42456652Selan int n, target, cur_period, bot, top, sc_region;
42556648Selan __LDATA buf[1024];
42656378Selan u_int blank_hash;
42756302Selan
42867571Sbostic /*
42958019Selan * Find how many lines from the top of the screen are unchanged.
43058019Selan */
43167571Sbostic for (top = 0; top < win->maxy; top++)
43258019Selan if (win->lines[top]->flags & __FORCEPAINT ||
43367571Sbostic win->lines[top]->hash != curscr->lines[top]->hash
43467571Sbostic || memcmp(win->lines[top]->line,
43567571Sbostic curscr->lines[top]->line,
43658019Selan win->maxx * __LDATASIZE) != 0)
43758019Selan break;
43858034Selan else
43958034Selan win->lines[top]->flags &= ~__ISDIRTY;
44058019Selan /*
44167571Sbostic * Find how many lines from bottom of screen are unchanged.
44258019Selan */
44358019Selan for (bot = win->maxy - 1; bot >= 0; bot--)
44458019Selan if (win->lines[bot]->flags & __FORCEPAINT ||
44567571Sbostic win->lines[bot]->hash != curscr->lines[bot]->hash
44667571Sbostic || memcmp(win->lines[bot]->line,
44767571Sbostic curscr->lines[bot]->line,
44858019Selan win->maxx * __LDATASIZE) != 0)
44958019Selan break;
45058034Selan else
45158034Selan win->lines[bot]->flags &= ~__ISDIRTY;
45258019Selan
45359352Selan #ifdef NO_JERKINESS
45456552Selan /*
45559062Selan * If we have a bottom unchanged region return. Scrolling the
45659062Selan * bottom region up and then back down causes a screen jitter.
45759062Selan * This will increase the number of characters sent to the screen
45859062Selan * but it looks better.
45959062Selan */
46059062Selan if (bot < win->maxy - 1)
46159062Selan return;
46259352Selan #endif /* NO_JERKINESS */
46359062Selan
46459062Selan /*
46556552Selan * Search for the largest block of text not changed.
46656652Selan * Invariants of the loop:
46756652Selan * - Startw is the index of the beginning of the examined block in win.
46867571Sbostic * - Starts is the index of the beginning of the examined block in
46956652Selan * curscr.
47056652Selan * - Curs is the index of one past the end of the exmined block in win.
47167571Sbostic * - Curw is the index of one past the end of the exmined block in
47256652Selan * curscr.
47356652Selan * - bsize is the current size of the examined block.
47456552Selan */
47558019Selan for (bsize = bot - top; bsize >= THRESH; bsize--) {
47658019Selan for (startw = top; startw <= bot - bsize; startw++)
47767571Sbostic for (starts = top; starts <= bot - bsize;
47856302Selan starts++) {
47956302Selan for (curw = startw, curs = starts;
48056302Selan curs < starts + bsize; curw++, curs++)
48156651Selan if (win->lines[curw]->flags &
48256651Selan __FORCEPAINT ||
48356651Selan (win->lines[curw]->hash !=
48456552Selan curscr->lines[curs]->hash ||
48567571Sbostic memcmp(win->lines[curw]->line,
48667571Sbostic curscr->lines[curs]->line,
48756651Selan win->maxx * __LDATASIZE) != 0))
48856302Selan break;
48956302Selan if (curs == starts + bsize)
49056302Selan goto done;
49156302Selan }
49258019Selan }
49356302Selan done:
49456651Selan /* Did not find anything */
49567571Sbostic if (bsize < THRESH)
49656302Selan return;
49756302Selan
49856302Selan #ifdef DEBUG
49967571Sbostic __CTRACE("quickch:bsize=%d,starts=%d,startw=%d,curw=%d,curs=%d,top=%d,bot=%d\n",
50056559Selan bsize, starts, startw, curw, curs, top, bot);
50156302Selan #endif
50256378Selan
50367571Sbostic /*
50467571Sbostic * Make sure that there is no overlap between the bottom and top
50556559Selan * regions and the middle scrolled block.
50656559Selan */
50756691Selan if (bot < curs)
50856691Selan bot = curs - 1;
50956691Selan if (top > starts)
51056691Selan top = starts;
51156559Selan
51256378Selan n = startw - starts;
51356378Selan
51456691Selan #ifdef DEBUG
51560058Sbostic __CTRACE("#####################################\n");
51656691Selan for (i = 0; i < curscr->maxy; i++) {
51760058Sbostic __CTRACE("C: %d:", i);
51860058Sbostic __CTRACE(" 0x%x \n", curscr->lines[i]->hash);
51967571Sbostic for (j = 0; j < curscr->maxx; j++)
52067571Sbostic __CTRACE("%c",
52156691Selan curscr->lines[i]->line[j].ch);
52260058Sbostic __CTRACE("\n");
52367571Sbostic for (j = 0; j < curscr->maxx; j++)
52467571Sbostic __CTRACE("%x",
52557356Selan curscr->lines[i]->line[j].attr);
52660058Sbostic __CTRACE("\n");
52760058Sbostic __CTRACE("W: %d:", i);
52860058Sbostic __CTRACE(" 0x%x \n", win->lines[i]->hash);
52960058Sbostic __CTRACE(" 0x%x ", win->lines[i]->flags);
53067571Sbostic for (j = 0; j < win->maxx; j++)
53167571Sbostic __CTRACE("%c",
53256691Selan win->lines[i]->line[j].ch);
53360058Sbostic __CTRACE("\n");
53467571Sbostic for (j = 0; j < win->maxx; j++)
53567571Sbostic __CTRACE("%x",
53657356Selan win->lines[i]->line[j].attr);
53760058Sbostic __CTRACE("\n");
53856691Selan }
53967571Sbostic #endif
54067571Sbostic
54156378Selan /* So we don't have to call __hash() each time */
54256648Selan for (i = 0; i < win->maxx; i++) {
54356648Selan buf[i].ch = ' ';
54456648Selan buf[i].attr = 0;
54556648Selan }
54658192Selan blank_hash = __hash((char *) buf, win->maxx * __LDATASIZE);
54756378Selan
54856378Selan /*
54956378Selan * Perform the rotation to maintain the consistency of curscr.
55056691Selan * This is hairy since we are doing an *in place* rotation.
55156652Selan * Invariants of the loop:
55256652Selan * - I is the index of the current line.
55356652Selan * - Target is the index of the target of line i.
55456652Selan * - Tmp1 points to current line (i).
55556652Selan * - Tmp2 and points to target line (target);
55667571Sbostic * - Cur_period is the index of the end of the current period.
55756652Selan * (see below).
55856652Selan *
55956652Selan * There are 2 major issues here that make this rotation non-trivial:
56056652Selan * 1. Scrolling in a scrolling region bounded by the top
56156652Selan * and bottom regions determined (whose size is sc_region).
56267571Sbostic * 2. As a result of the use of the mod function, there may be a
56356652Selan * period introduced, i.e., 2 maps to 4, 4 to 6, n-2 to 0, and
56456652Selan * 0 to 2, which then causes all odd lines not to be rotated.
56567571Sbostic * To remedy this, an index of the end ( = beginning) of the
56667571Sbostic * current 'period' is kept, cur_period, and when it is reached,
56767571Sbostic * the next period is started from cur_period + 1 which is
56856652Selan * guaranteed not to have been reached since that would mean that
56956652Selan * all records would have been reached. (think about it...).
57067571Sbostic *
57156652Selan * Lines in the rotation can have 3 attributes which are marked on the
57256652Selan * line so that curscr is consistent with the visual screen.
57356705Selan * 1. Not dirty -- lines inside the scrolled block, top region or
57456652Selan * bottom region.
57567571Sbostic * 2. Blank lines -- lines in the differential of the scrolling
57667571Sbostic * region adjacent to top and bot regions
57756705Selan * depending on scrolling direction.
57856652Selan * 3. Dirty line -- all other lines are marked dirty.
57956378Selan */
58056648Selan sc_region = bot - top + 1;
58156648Selan i = top;
58256648Selan tmp1 = curscr->lines[top];
58356652Selan cur_period = top;
58456648Selan for (j = top; j <= bot; j++) {
58556648Selan target = (i - top + n + sc_region) % sc_region + top;
58656378Selan tmp2 = curscr->lines[target];
58756378Selan curscr->lines[target] = tmp1;
58856378Selan /* Mark block as clean and blank out scrolled lines. */
58956378Selan clp = curscr->lines[target];
59056472Selan #ifdef DEBUG
59160058Sbostic __CTRACE("quickch: n=%d startw=%d curw=%d i = %d target=%d ",
59256378Selan n, startw, curw, i, target);
59356472Selan #endif
59467571Sbostic if ((target >= startw && target < curw) || target < top
59556691Selan || target > bot) {
59656472Selan #ifdef DEBUG
59760058Sbostic __CTRACE("-- notdirty");
59856472Selan #endif
59956378Selan win->lines[target]->flags &= ~__ISDIRTY;
60056705Selan } else if ((n > 0 && target >= top && target < top + n) ||
60156705Selan (n < 0 && target <= bot && target > bot + n)) {
60267571Sbostic if (clp->hash != blank_hash || memcmp(clp->line,
60356648Selan buf, win->maxx * __LDATASIZE) !=0) {
60458041Selan (void)memcpy(clp->line, buf,
60556648Selan win->maxx * __LDATASIZE);
60656472Selan #ifdef DEBUG
60760058Sbostic __CTRACE("-- blanked out: dirty");
60856472Selan #endif
60956378Selan clp->hash = blank_hash;
61056652Selan __touchline(win, target, 0, win->maxx - 1, 0);
61156705Selan } else {
61256652Selan __touchline(win, target, 0, win->maxx - 1, 0);
61356472Selan #ifdef DEBUG
61460058Sbostic __CTRACE(" -- blank line already: dirty");
61556472Selan #endif
61656705Selan }
61756378Selan } else {
61856472Selan #ifdef DEBUG
61960058Sbostic __CTRACE(" -- dirty");
62056472Selan #endif
62156651Selan __touchline(win, target, 0, win->maxx - 1, 0);
62256378Selan }
62356472Selan #ifdef DEBUG
62460058Sbostic __CTRACE("\n");
62556472Selan #endif
62656652Selan if (target == cur_period) {
62756378Selan i = target + 1;
62856378Selan tmp1 = curscr->lines[i];
62956652Selan cur_period = i;
63056378Selan } else {
63156378Selan tmp1 = tmp2;
63256378Selan i = target;
63356378Selan }
63456302Selan }
63556472Selan #ifdef DEBUG
63660058Sbostic __CTRACE("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n");
63756648Selan for (i = 0; i < curscr->maxy; i++) {
63860058Sbostic __CTRACE("C: %d:", i);
63967571Sbostic for (j = 0; j < curscr->maxx; j++)
64067571Sbostic __CTRACE("%c",
64156648Selan curscr->lines[i]->line[j].ch);
64260058Sbostic __CTRACE("\n");
64360058Sbostic __CTRACE("W: %d:", i);
64467571Sbostic for (j = 0; j < win->maxx; j++)
64567571Sbostic __CTRACE("%c", win->lines[i]->line[j].ch);
64660058Sbostic __CTRACE("\n");
64756648Selan }
64856472Selan #endif
64960100Selan if (n != 0) {
65060100Selan WINDOW *wp;
65159930Selan scrolln(win, starts, startw, curs, bot, top);
65260100Selan /*
65360100Selan * Need to repoint any subwindow lines to the rotated
65467571Sbostic * line structured.
65560100Selan */
65660100Selan for (wp = win->nextp; wp != win; wp = wp->nextp)
65760100Selan __set_subwin(win, wp);
65860100Selan }
65956302Selan }
66056302Selan
66156652Selan /*
66267571Sbostic * scrolln --
66367571Sbostic * Scroll n lines, where n is starts - startw.
66456652Selan */
66556302Selan static void
scrolln(win,starts,startw,curs,bot,top)66656705Selan scrolln(win, starts, startw, curs, bot, top)
66756302Selan WINDOW *win;
66856705Selan int starts, startw, curs, bot, top;
66956302Selan {
67056302Selan int i, oy, ox, n;
67156302Selan
67256302Selan oy = curscr->cury;
67356302Selan ox = curscr->curx;
67456302Selan n = starts - startw;
67556302Selan
67667583Sbostic /*
67767583Sbostic * XXX
67867583Sbostic * The initial tests that set __noqch don't let us reach here unless
67967583Sbostic * we have either CS + HO + SF/sf/SR/sr, or AL + DL. SF/sf and SR/sr
68067583Sbostic * scrolling can only shift the entire scrolling region, not just a
68167583Sbostic * part of it, which means that the quickch() routine is going to be
68267583Sbostic * sadly disappointed in us if we don't have CS as well.
68367583Sbostic *
68467583Sbostic * If CS, HO and SF/sf are set, can use the scrolling region. Because
68567583Sbostic * the cursor position after CS is undefined, we need HO which gives us
68667583Sbostic * the ability to move to somewhere without knowledge of the current
68767583Sbostic * location of the cursor. Still call __mvcur() anyway, to update its
68867583Sbostic * idea of where the cursor is.
68967583Sbostic *
69067583Sbostic * When the scrolling region has been set, the cursor has to be at the
69167583Sbostic * last line of the region to make the scroll happen.
69267583Sbostic *
69367583Sbostic * Doing SF/SR or AL/DL appears faster on the screen than either sf/sr
69467583Sbostic * or al/dl, and, some terminals have AL/DL, sf/sr, and CS, but not
69567583Sbostic * SF/SR. So, if we're scrolling almost all of the screen, try and use
69667583Sbostic * AL/DL, otherwise use the scrolling region. The "almost all" is a
69767583Sbostic * shameless hack for vi.
69867583Sbostic */
69956302Selan if (n > 0) {
70067583Sbostic if (CS != NULL && HO != NULL && (SF != NULL ||
70167583Sbostic (AL == NULL || DL == NULL ||
70267583Sbostic top > 3 || bot + 3 < win->maxy) && sf != NULL)) {
70367571Sbostic tputs(__tscroll(CS, top, bot + 1), 0, __cputchar);
70467571Sbostic __mvcur(oy, ox, 0, 0, 1);
70567571Sbostic tputs(HO, 0, __cputchar);
70667571Sbostic __mvcur(0, 0, bot, 0, 1);
70767571Sbostic if (SF != NULL)
70867571Sbostic tputs(__tscroll(SF, n, 0), 0, __cputchar);
70967571Sbostic else
71067571Sbostic for (i = 0; i < n; i++)
71167571Sbostic tputs(sf, 0, __cputchar);
71267571Sbostic tputs(__tscroll(CS, 0, win->maxy), 0, __cputchar);
71367571Sbostic __mvcur(bot, 0, 0, 0, 1);
71467571Sbostic tputs(HO, 0, __cputchar);
71567571Sbostic __mvcur(0, 0, oy, ox, 1);
71667571Sbostic return;
71767571Sbostic }
71867571Sbostic
71967583Sbostic /* Scroll up the block. */
720*67665Sbostic if (SF != NULL && top == 0) {
721*67665Sbostic __mvcur(oy, ox, bot, 0, 1);
72267583Sbostic tputs(__tscroll(SF, n, 0), 0, __cputchar);
723*67665Sbostic } else if (DL != NULL) {
724*67665Sbostic __mvcur(oy, ox, top, 0, 1);
72567571Sbostic tputs(__tscroll(DL, n, 0), 0, __cputchar);
726*67665Sbostic } else if (dl != NULL) {
727*67665Sbostic __mvcur(oy, ox, top, 0, 1);
72867571Sbostic for (i = 0; i < n; i++)
72956302Selan tputs(dl, 0, __cputchar);
730*67665Sbostic } else if (sf != NULL && top == 0) {
731*67665Sbostic __mvcur(oy, ox, bot, 0, 1);
73267583Sbostic for (i = 0; i < n; i++)
73367583Sbostic tputs(sf, 0, __cputchar);
734*67665Sbostic } else
73567583Sbostic abort();
73656652Selan
73767571Sbostic /* Push down the bottom region. */
73859930Selan __mvcur(top, 0, bot - n + 1, 0, 1);
73967583Sbostic if (AL != NULL)
74067571Sbostic tputs(__tscroll(AL, n, 0), 0, __cputchar);
74167583Sbostic else if (al != NULL)
74267571Sbostic for (i = 0; i < n; i++)
74356652Selan tputs(al, 0, __cputchar);
74467583Sbostic else
74567583Sbostic abort();
74659930Selan __mvcur(bot - n + 1, 0, oy, ox, 1);
74756302Selan } else {
74867571Sbostic /*
74967583Sbostic * !!!
75067583Sbostic * n < 0
75167583Sbostic *
75267583Sbostic * If CS, HO and SR/sr are set, can use the scrolling region.
75367583Sbostic * See the above comments for details.
75467571Sbostic */
75567583Sbostic if (CS != NULL && HO != NULL && (SR != NULL ||
75667583Sbostic (AL == NULL || DL == NULL ||
75767583Sbostic top > 3 || bot + 3 < win->maxy) && sr != NULL)) {
75867571Sbostic tputs(__tscroll(CS, top, bot + 1), 0, __cputchar);
75967571Sbostic __mvcur(oy, ox, 0, 0, 1);
76067571Sbostic tputs(HO, 0, __cputchar);
76167571Sbostic __mvcur(0, 0, top, 0, 1);
76267571Sbostic
76367571Sbostic if (SR != NULL)
76467583Sbostic tputs(__tscroll(SR, -n, 0), 0, __cputchar);
76567571Sbostic else
76667583Sbostic for (i = n; i < 0; i++)
76767571Sbostic tputs(sr, 0, __cputchar);
76867571Sbostic tputs(__tscroll(CS, 0, win->maxy), 0, __cputchar);
76967571Sbostic __mvcur(top, 0, 0, 0, 1);
77067571Sbostic tputs(HO, 0, __cputchar);
77167571Sbostic __mvcur(0, 0, oy, ox, 1);
77267571Sbostic return;
77367571Sbostic }
77467571Sbostic
77567583Sbostic /* Preserve the bottom lines. */
77667583Sbostic __mvcur(oy, ox, bot + n + 1, 0, 1);
77767583Sbostic if (SR != NULL && bot == win->maxy)
77867583Sbostic tputs(__tscroll(SR, -n, 0), 0, __cputchar);
77967583Sbostic else if (DL != NULL)
78067571Sbostic tputs(__tscroll(DL, -n, 0), 0, __cputchar);
78167583Sbostic else if (dl != NULL)
78267571Sbostic for (i = n; i < 0; i++)
78356652Selan tputs(dl, 0, __cputchar);
78467583Sbostic else if (sr != NULL && bot == win->maxy)
78567583Sbostic for (i = n; i < 0; i++)
78667583Sbostic tputs(sr, 0, __cputchar);
78767583Sbostic else
78867583Sbostic abort();
78956302Selan
79067571Sbostic /* Scroll the block down. */
79167583Sbostic __mvcur(bot + n + 1, 0, top, 0, 1);
79267583Sbostic if (AL != NULL)
79367571Sbostic tputs(__tscroll(AL, -n, 0), 0, __cputchar);
79467583Sbostic else if (al != NULL)
79567571Sbostic for (i = n; i < 0; i++)
79656302Selan tputs(al, 0, __cputchar);
79767583Sbostic else
79867583Sbostic abort();
79959930Selan __mvcur(top, 0, oy, ox, 1);
80067571Sbostic }
80156302Selan }
802