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 = ␣
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