refresh.c revision 1.37 1 /* $NetBSD: refresh.c,v 1.37 2000/06/15 21:20:16 jdc Exp $ */
2
3 /*
4 * Copyright (c) 1981, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 #include <sys/cdefs.h>
37 #ifndef lint
38 #if 0
39 static char sccsid[] = "@(#)refresh.c 8.7 (Berkeley) 8/13/94";
40 #else
41 __RCSID("$NetBSD: refresh.c,v 1.37 2000/06/15 21:20:16 jdc Exp $");
42 #endif
43 #endif /* not lint */
44
45 #include <string.h>
46
47 #include "curses.h"
48 #include "curses_private.h"
49
50 /* the following is defined and set up in setterm.c */
51 extern struct tinfo *_cursesi_genbuf;
52
53 static int curwin = 0;
54 static short ly, lx;
55
56 static void domvcur __P((int, int, int, int));
57 static int makech __P((int));
58 static void quickch __P((void));
59 static void scrolln __P((int, int, int, int, int));
60
61 #ifndef _CURSES_USE_MACROS
62
63 /*
64 * refresh --
65 * Make the current screen look like "stdscr" over the area covered by
66 * stdscr.
67 */
68 int
69 refresh(void)
70 {
71 return wrefresh(stdscr);
72 }
73
74 #endif
75
76 /*
77 * wnoutrefresh --
78 * Add the contents of "win" to the virtual window.
79 */
80 int
81 wnoutrefresh(WINDOW *win)
82 {
83 short wy, wx, x_off;
84 __LINE *wlp, *vlp;
85
86 #ifdef DEBUG
87 __CTRACE("wnoutrefresh: win %0.2o, flags 0x%08x\n", win, win->flags);
88 #endif
89
90 if (curwin)
91 return(OK);
92 __virtscr->cury = win->cury + win->begy;
93 __virtscr->curx = win->curx + win->begx;
94
95 /* Copy the window flags from "win" to "__virtscr" */
96 if (win->flags & __CLEAROK) {
97 if (win->flags & __FULLWIN)
98 __virtscr->flags |= __CLEAROK;
99 win->flags &= ~__CLEAROK;
100 }
101 __virtscr->flags &= ~__LEAVEOK;
102 __virtscr->flags |= win->flags;
103
104 for (wy = 0; wy < win->maxy && wy < __virtscr->maxy - win->begy; wy++) {
105 wlp = win->lines[wy];
106 #ifdef DEBUG
107 __CTRACE("wnoutrefresh: wy %d\tf: %d\tl:%d\tflags %x\n", wy,
108 *wlp->firstchp, *wlp->lastchp, wlp->flags);
109 #endif
110 if ((wlp->flags & __ISDIRTY) == 0)
111 continue;
112 vlp = __virtscr->lines[wy + win->begy];
113
114 if (*wlp->firstchp < win->maxx + win->ch_off &&
115 *wlp->lastchp >= win->ch_off) {
116 /* Copy line from "win" to "__virtscr". */
117 for (wx = 0, x_off = win->begx; wx < win->maxx &&
118 x_off < __virtscr->maxx; wx++, x_off++) {
119 vlp->line[x_off].attr = wlp->line[wx].attr;
120 if (wlp->line[wx].attr & __COLOR)
121 vlp->line[x_off].attr |=
122 wlp->line[wx].battr & ~__COLOR;
123 else
124 vlp->line[x_off].attr |=
125 wlp->line[wx].battr;
126 if (wlp->line[wx].ch == ' ' &&
127 wlp->line[wx].bch != ' ')
128 vlp->line[x_off].ch
129 = wlp->line[wx].bch;
130 else
131 vlp->line[x_off].ch
132 = wlp->line[wx].ch;
133 }
134
135 /* Set flags on "__virtscr" and unset on "win". */
136 if (wlp->flags & __ISPASTEOL)
137 vlp->flags |= __ISPASTEOL;
138 else
139 vlp->flags &= ~__ISPASTEOL;
140 if (wlp->flags & __ISDIRTY)
141 vlp->flags |= __ISDIRTY;
142
143 #ifdef DEBUG
144 __CTRACE("win: firstch = %d, lastch = %d\n",
145 *wlp->firstchp, *wlp->lastchp);
146 #endif
147 /* Set change pointers on "__virtscr". */
148 if (*vlp->firstchp >
149 *wlp->firstchp + win->begx - win->ch_off)
150 *vlp->firstchp =
151 *wlp->firstchp + win->begx - win->ch_off;
152 if (*vlp->lastchp <
153 *wlp->lastchp + win->begx - win->ch_off)
154 *vlp->lastchp =
155 *wlp->lastchp + win->begx - win->ch_off;
156 #ifdef DEBUG
157 __CTRACE("__virtscr: firstch = %d, lastch = %d\n",
158 *vlp->firstchp, *vlp->lastchp);
159 #endif
160
161 /* Set change pointers on "win". */
162 if (*wlp->firstchp >= win->ch_off)
163 *wlp->firstchp = win->maxx + win->ch_off;
164 if (*wlp->lastchp < win->maxx + win->ch_off)
165 *wlp->lastchp = win->ch_off;
166 if (*wlp->lastchp < *wlp->firstchp) {
167 #ifdef DEBUG
168 __CTRACE("wnoutrefresh: line %d notdirty\n",
169 wy);
170 #endif
171 wlp->flags &= ~__ISDIRTY;
172 }
173 }
174 }
175
176 return (OK);
177 }
178
179 /*
180 * wrefresh --
181 * Make the current screen look like "win" over the area coverd by
182 * win.
183 */
184 int
185 wrefresh(WINDOW *win)
186 {
187 int retval;
188
189 curwin = (win == curscr);
190 if (!curwin)
191 retval = wnoutrefresh(win);
192 else
193 retval = OK;
194 if (retval == OK) {
195 retval = doupdate();
196 if (!win->flags & __LEAVEOK) {
197 win->cury = max(0, curscr->cury - win->begy);
198 win->curx = max(0, curscr->curx - win->begx);
199 }
200 }
201 curwin = 0;
202 return(retval);
203 }
204
205 /*
206 * doupdate --
207 * Make the current screen look like the virtual window "__virtscr".
208 */
209 int
210 doupdate(void)
211 {
212 WINDOW *win;
213 __LINE *wlp;
214 short wy;
215 int dnum;
216
217 /* Check if we need to restart ... */
218 if (__endwin)
219 __restartwin();
220
221 if (curwin)
222 win = curscr;
223 else
224 win = __virtscr;
225
226 /* Initialize loop parameters. */
227 ly = curscr->cury;
228 lx = curscr->curx;
229 wy = 0;
230
231 if (!curwin)
232 for (wy = 0; wy < win->maxy; wy++) {
233 wlp = win->lines[wy];
234 if (wlp->flags & __ISDIRTY)
235 wlp->hash = __hash((char *)(void *)wlp->line,
236 (int) (win->maxx * __LDATASIZE));
237 }
238
239 if ((win->flags & __CLEAROK) || (curscr->flags & __CLEAROK) || curwin) {
240 if (curscr->wattr & __COLOR)
241 __unsetattr(0);
242 tputs(CL, 0, __cputchar);
243 ly = 0;
244 lx = 0;
245 if (!curwin) {
246 curscr->flags &= ~__CLEAROK;
247 curscr->cury = 0;
248 curscr->curx = 0;
249 werase(curscr);
250 }
251 __touchwin(win);
252 win->flags &= ~__CLEAROK;
253 }
254 if (!CA) {
255 if (win->curx != 0)
256 putchar('\n');
257 if (!curwin)
258 werase(curscr);
259 }
260 #ifdef DEBUG
261 __CTRACE("doupdate: (%0.2o): curwin = %d\n", win, curwin);
262 __CTRACE("doupdate: \tfirstch\tlastch\n");
263 #endif
264
265 if (!curwin) {
266 /*
267 * Invoke quickch() only if more than a quarter of the lines
268 * in the window are dirty.
269 */
270 for (wy = 0, dnum = 0; wy < win->maxy; wy++)
271 if (win->lines[wy]->flags & __ISDIRTY)
272 dnum++;
273 if (!__noqch && dnum > (int) win->maxy / 4)
274 quickch();
275 }
276
277 #ifdef DEBUG
278 {
279 int i, j;
280
281 __CTRACE("#####################################\n");
282 for (i = 0; i < curscr->maxy; i++) {
283 __CTRACE("C: %d:", i);
284 __CTRACE(" 0x%x \n", curscr->lines[i]->hash);
285 for (j = 0; j < curscr->maxx; j++)
286 __CTRACE("%c", curscr->lines[i]->line[j].ch);
287 __CTRACE("\n");
288 __CTRACE(" attr:");
289 for (j = 0; j < curscr->maxx; j++)
290 __CTRACE(" %x", curscr->lines[i]->line[j].attr);
291 __CTRACE("\n");
292 __CTRACE("W: %d:", i);
293 __CTRACE(" 0x%x \n", win->lines[i]->hash);
294 __CTRACE(" 0x%x ", win->lines[i]->flags);
295 for (j = 0; j < win->maxx; j++)
296 __CTRACE("%c", win->lines[i]->line[j].ch);
297 __CTRACE("\n");
298 __CTRACE(" attr:");
299 for (j = 0; j < win->maxx; j++)
300 __CTRACE(" %x",
301 win->lines[i]->line[j].attr);
302 __CTRACE("\n");
303 }
304 }
305 #endif /* DEBUG */
306
307 for (wy = 0; wy < win->maxy; wy++) {
308 wlp = win->lines[wy];
309 /* XXX: remove this debug */
310 #ifdef DEBUG
311 __CTRACE("doupdate: wy %d\tf: %d\tl:%d\tflags %x\n", wy,
312 *wlp->firstchp, *wlp->lastchp, wlp->flags);
313 #endif
314 if (!curwin)
315 curscr->lines[wy]->hash = wlp->hash;
316 if (wlp->flags & __ISDIRTY) {
317 if (makech(wy) == ERR)
318 return (ERR);
319 else {
320 if (*wlp->firstchp >= 0)
321 *wlp->firstchp = win->maxx;
322 if (*wlp->lastchp < win->maxx)
323 *wlp->lastchp = 0;
324 if (*wlp->lastchp < *wlp->firstchp) {
325 #ifdef DEBUG
326 __CTRACE("doupdate: line %d notdirty\n", wy);
327 #endif
328 wlp->flags &= ~__ISDIRTY;
329 }
330 }
331
332 }
333 #ifdef DEBUG
334 __CTRACE("\t%d\t%d\n", *wlp->firstchp, *wlp->lastchp);
335 #endif
336 }
337
338 #ifdef DEBUG
339 __CTRACE("doupdate: ly=%d, lx=%d\n", ly, lx);
340 #endif
341
342 if (curwin)
343 domvcur(ly, lx, (int) win->cury, (int) win->curx);
344 else {
345 if (win->flags & __LEAVEOK) {
346 curscr->cury = ly;
347 curscr->curx = lx;
348 } else {
349 domvcur(ly, lx, win->cury, win->curx);
350 curscr->cury = win->cury;
351 curscr->curx = win->curx;
352 }
353 }
354
355 /* Don't leave the screen with attributes set. */
356 __unsetattr(0);
357 (void) fflush(stdout);
358 return (OK);
359 }
360
361 /*
362 * makech --
363 * Make a change on the screen.
364 */
365 static int
366 makech(wy)
367 int wy;
368 {
369 WINDOW *win;
370 static __LDATA blank = {' ', 0, ' ', 0};
371 __LDATA *nsp, *csp, *cp, *cep;
372 int clsp, nlsp; /* Last space in lines. */
373 int lch, wx;
374 char *ce;
375 attr_t lspc; /* Last space colour */
376 attr_t off, on;
377
378 #ifdef __GNUC__
379 nlsp = lspc = 0; /* XXX gcc -Wuninitialized */
380 #endif
381 if (curwin)
382 win = curscr;
383 else
384 win = __virtscr;
385 /* Is the cursor still on the end of the last line? */
386 if (wy > 0 && win->lines[wy - 1]->flags & __ISPASTEOL) {
387 domvcur(ly, lx, ly + 1, 0);
388 ly++;
389 lx = 0;
390 }
391 wx = *win->lines[wy]->firstchp;
392 if (wx < 0)
393 wx = 0;
394 else
395 if (wx >= win->maxx)
396 return (OK);
397 lch = *win->lines[wy]->lastchp;
398 if (lch < 0)
399 return (OK);
400 else
401 if (lch >= (int) win->maxx)
402 lch = win->maxx - 1;
403
404 if (curwin)
405 csp = ␣
406 else
407 csp = &curscr->lines[wy]->line[wx];
408
409 nsp = &win->lines[wy]->line[wx];
410 if (CE && !curwin) {
411 cp = &win->lines[wy]->line[win->maxx - 1];
412 lspc = cp->attr & __COLOR;
413 while (cp->ch == ' ' && cp->attr == lspc)
414 if (cp-- <= win->lines[wy]->line)
415 break;
416 nlsp = cp - win->lines[wy]->line;
417 }
418 if (!curwin)
419 ce = CE;
420 else
421 ce = NULL;
422
423 while (wx <= lch) {
424 if (memcmp(nsp, csp, sizeof(__LDATA)) == 0) {
425 if (wx <= lch) {
426 while (wx <= lch &&
427 memcmp(nsp, csp, sizeof(__LDATA)) == 0) {
428 nsp++;
429 if (!curwin)
430 ++csp;
431 ++wx;
432 }
433 continue;
434 }
435 break;
436 }
437 domvcur(ly, lx, wy, wx);
438
439 #ifdef DEBUG
440 __CTRACE("makech: 1: wx = %d, ly= %d, lx = %d, newy = %d, newx = %d\n",
441 wx, ly, lx, wy, wx);
442 #endif
443 ly = wy;
444 lx = wx;
445 while (memcmp(nsp, csp, sizeof(__LDATA)) != 0 && wx <= lch) {
446 if (ce != NULL &&
447 wx >= nlsp && nsp->ch == ' ' && nsp->attr == lspc) {
448 /* Check for clear to end-of-line. */
449 cep = &curscr->lines[wy]->line[win->maxx - 1];
450 while (cep->ch == ' ' && cep->attr == lspc)
451 if (cep-- <= csp)
452 break;
453 clsp = cep - curscr->lines[wy]->line -
454 win->begx * __LDATASIZE;
455 #ifdef DEBUG
456 __CTRACE("makech: clsp = %d, nlsp = %d\n",
457 clsp, nlsp);
458 #endif
459 if (((clsp - nlsp >= strlen(CE) &&
460 clsp < win->maxx * __LDATASIZE) ||
461 wy == win->maxy - 1) &&
462 (!(lspc & __COLOR) ||
463 ((lspc & __COLOR) && UT))) {
464 __unsetattr(0);
465 if ((lspc & __COLOR) !=
466 (curscr->wattr & __COLOR)) {
467 __set_color(lspc);
468 curscr->wattr &= ~__COLOR;
469 curscr->wattr |= lspc & __COLOR;
470 }
471 tputs(CE, 0, __cputchar);
472 lx = wx + win->begx;
473 while (wx++ <= clsp) {
474 csp->ch = ' ';
475 csp->attr = lspc;
476 csp++;
477 }
478 return (OK);
479 }
480 ce = NULL;
481 }
482
483 /*
484 * Unset colour if appropriate. Check to see
485 * if we also turn off standout, underscore and
486 * attributes.
487 */
488 if (!(nsp->attr & __COLOR) &&
489 (curscr->wattr & __COLOR)) {
490 if (OC != NULL && CC == NULL)
491 tputs(OC, 0, __cputchar);
492 if (OP != NULL) {
493 tputs(OP, 0, __cputchar);
494 curscr->wattr &= __mask_OP;
495 }
496 }
497
498 off = ~nsp->attr & curscr->wattr;
499
500 /*
501 * Unset attributes as appropriate. Unset first
502 * so that the relevant attributes can be reset
503 * (because 'me' unsets 'mb', 'md', 'mh', 'mk',
504 * 'mp' and 'mr'). Check to see if we also turn off
505 * standout, attributes and colour.
506 */
507 if (off & __TERMATTR && ME != NULL) {
508 tputs(ME, 0, __cputchar);
509 curscr->wattr &= __mask_ME;
510 off &= __mask_ME;
511 }
512
513 /*
514 * Exit underscore mode if appropriate.
515 * Check to see if we also turn off standout,
516 * attributes and colour.
517 */
518 if (off & __UNDERSCORE && UE != NULL) {
519 tputs(UE, 0, __cputchar);
520 curscr->wattr &= __mask_UE;
521 off &= __mask_UE;
522 }
523
524 /*
525 * Exit standout mode as appropriate.
526 * Check to see if we also turn off underscore,
527 * attributes and colour.
528 * XXX
529 * Should use UC if SO/SE not available.
530 */
531 if (off & __STANDOUT && SE != NULL) {
532 tputs(SE, 0, __cputchar);
533 curscr->wattr &= __mask_SE;
534 off &= __mask_SE;
535 }
536
537 if (off & __ALTCHARSET && AE != NULL) {
538 tputs(AE, 0, __cputchar);
539 curscr->wattr &= ~__ALTCHARSET;
540 }
541
542 on = nsp->attr & ~curscr->wattr;
543
544 /*
545 * Enter standout mode if appropriate.
546 */
547 if (on & __STANDOUT && SO != NULL && SE != NULL) {
548 tputs(SO, 0, __cputchar);
549 curscr->wattr |= __STANDOUT;
550 }
551
552 /*
553 * Enter underscore mode if appropriate.
554 * XXX
555 * Should use UC if US/UE not available.
556 */
557 if (on & __UNDERSCORE && US != NULL && UE != NULL) {
558 tputs(US, 0, __cputchar);
559 curscr->wattr |= __UNDERSCORE;
560 }
561
562 /*
563 * Set other attributes as appropriate.
564 */
565 if (ME != NULL) {
566 if (on & __BLINK && MB != NULL) {
567 tputs(MB, 0, __cputchar);
568 curscr->wattr |= __BLINK;
569 }
570 if (on & __BOLD && MD != NULL) {
571 tputs(MD, 0, __cputchar);
572 curscr->wattr |= __BOLD;
573 }
574 if (on & __DIM && MH != NULL) {
575 tputs(MH, 0, __cputchar);
576 curscr->wattr |= __DIM;
577 }
578 if (on & __BLANK && MK != NULL) {
579 tputs(MK, 0, __cputchar);
580 curscr->wattr |= __BLANK;
581 }
582 if (on & __PROTECT && MP != NULL) {
583 tputs(MP, 0, __cputchar);
584 curscr->wattr |= __PROTECT;
585 }
586 if (on & __REVERSE && MR != NULL) {
587 tputs(MR, 0, __cputchar);
588 curscr->wattr |= __REVERSE;
589 }
590 }
591
592 /* Set/change colour as appropriate. */
593 if ((nsp->attr & __COLOR) &&
594 cO != NULL && (OC != NULL || OP != NULL)) {
595 if ((nsp->attr & __COLOR) !=
596 (curscr->wattr & __COLOR)) {
597 __set_color(nsp->attr);
598 curscr->wattr &= ~__COLOR;
599 curscr->wattr |= nsp->attr &
600 __COLOR;
601 }
602 }
603
604 /* Enter/exit altcharset mode as appropriate. */
605 if (on & __ALTCHARSET && AS != NULL && AE != NULL) {
606 tputs(AS, 0, __cputchar);
607 curscr->wattr |= __ALTCHARSET;
608 }
609
610 wx++;
611 if (wx >= win->maxx &&
612 wy == win->maxy - 1 && !curwin)
613 if (win->flags & __SCROLLOK) {
614 if (win->flags & __ENDLINE)
615 __unsetattr(1);
616 if (!(win->flags & __SCROLLWIN)) {
617 if (!curwin) {
618 csp->attr = nsp->attr;
619 putchar((int)
620 (csp->ch =
621 nsp->ch));
622 } else
623 putchar((int) nsp->ch);
624 }
625 if (wx < curscr->maxx) {
626 domvcur(ly, wx,
627 (int) (win->maxy - 1),
628 (int) (win->maxx - 1));
629 }
630 ly = win->maxy - 1;
631 lx = win->maxx - 1;
632 return (OK);
633 }
634 if (wx < win->maxx || wy < win->maxy - 1 ||
635 !(win->flags & __SCROLLWIN)) {
636 if (!curwin) {
637 csp->attr = nsp->attr;
638 putchar((int) (csp->ch = nsp->ch));
639 csp++;
640 } else
641 putchar((int) nsp->ch);
642 }
643 #ifdef DEBUG
644 __CTRACE("makech: putchar(%c)\n", nsp->ch & 0177);
645 #endif
646 if (UC && ((nsp->attr & __STANDOUT) ||
647 (nsp->attr & __UNDERSCORE))) {
648 putchar('\b');
649 tputs(UC, 0, __cputchar);
650 }
651 nsp++;
652 #ifdef DEBUG
653 __CTRACE("makech: 2: wx = %d, lx = %d\n", wx, lx);
654 #endif
655 }
656 if (lx == wx) /* If no change. */
657 break;
658 lx = wx;
659 if (lx >= COLS && AM)
660 lx = COLS - 1;
661 else
662 if (wx >= win->maxx) {
663 domvcur(ly, lx, ly,
664 (int) (win->maxx - 1));
665 lx = win->maxx - 1;
666 }
667 #ifdef DEBUG
668 __CTRACE("makech: 3: wx = %d, lx = %d\n", wx, lx);
669 #endif
670 }
671
672 return (OK);
673 }
674
675 /*
676 * domvcur --
677 * Do a mvcur, leaving attributes if necessary.
678 */
679 static void
680 domvcur(oy, ox, ny, nx)
681 int oy, ox, ny, nx;
682 {
683 __unsetattr(1);
684 __mvcur(oy, ox, ny, nx, 1);
685 }
686
687 /*
688 * Quickch() attempts to detect a pattern in the change of the window
689 * in order to optimize the change, e.g., scroll n lines as opposed to
690 * repainting the screen line by line.
691 */
692
693 static void
694 quickch(void)
695 {
696 #define THRESH (int) __virtscr->maxy / 4
697
698 __LINE *clp, *tmp1, *tmp2;
699 int bsize, curs, curw, starts, startw, i, j;
700 int n, target, cur_period, bot, top, sc_region;
701 __LDATA buf[1024];
702 u_int blank_hash;
703 attr_t bcolor;
704
705 #ifdef __GNUC__
706 curs = curw = starts = startw = 0; /* XXX gcc -Wuninitialized */
707 #endif
708 /*
709 * Find how many lines from the top of the screen are unchanged.
710 */
711 for (top = 0; top < __virtscr->maxy; top++)
712 if (__virtscr->lines[top]->flags & __ISDIRTY &&
713 (__virtscr->lines[top]->hash != curscr->lines[top]->hash ||
714 memcmp(__virtscr->lines[top]->line,
715 curscr->lines[top]->line,
716 (size_t) __virtscr->maxx * __LDATASIZE) != 0))
717 break;
718 else
719 __virtscr->lines[top]->flags &= ~__ISDIRTY;
720 /*
721 * Find how many lines from bottom of screen are unchanged.
722 */
723 for (bot = __virtscr->maxy - 1; bot >= 0; bot--)
724 if (__virtscr->lines[bot]->flags & __ISDIRTY &&
725 (__virtscr->lines[bot]->hash != curscr->lines[bot]->hash ||
726 memcmp(__virtscr->lines[bot]->line,
727 curscr->lines[bot]->line,
728 (size_t) __virtscr->maxx * __LDATASIZE) != 0))
729 break;
730 else
731 __virtscr->lines[bot]->flags &= ~__ISDIRTY;
732
733 /*
734 * Work round an xterm bug where inserting lines causes all the
735 * inserted lines to be covered with the background colour we
736 * set on the first line (even if we unset it for subsequent
737 * lines).
738 */
739 bcolor = __virtscr->lines[min(top,
740 __virtscr->maxy - 1)]->line[0].attr & __COLOR;
741 for (i = top + 1, j = 0; i < bot; i++) {
742 if ((__virtscr->lines[i]->line[0].attr & __COLOR) != bcolor) {
743 bcolor = __virtscr->lines[i]->line[__virtscr->maxx].
744 attr & __COLOR;
745 j = i - top;
746 } else
747 break;
748 }
749 top += j;
750
751 #ifdef NO_JERKINESS
752 /*
753 * If we have a bottom unchanged region return. Scrolling the
754 * bottom region up and then back down causes a screen jitter.
755 * This will increase the number of characters sent to the screen
756 * but it looks better.
757 */
758 if (bot < __virtscr->maxy - 1)
759 return;
760 #endif /* NO_JERKINESS */
761
762 /*
763 * Search for the largest block of text not changed.
764 * Invariants of the loop:
765 * - Startw is the index of the beginning of the examined block in
766 * __virtscr.
767 * - Starts is the index of the beginning of the examined block in
768 * curscr.
769 * - Curw is the index of one past the end of the exmined block in
770 * __virtscr.
771 * - Curs is the index of one past the end of the exmined block in
772 * curscr.
773 * - bsize is the current size of the examined block.
774 */
775
776 for (bsize = bot - top; bsize >= THRESH; bsize--) {
777 for (startw = top; startw <= bot - bsize; startw++)
778 for (starts = top; starts <= bot - bsize;
779 starts++) {
780 for (curw = startw, curs = starts;
781 curs < starts + bsize; curw++, curs++)
782 if (__virtscr->lines[curw]->hash !=
783 curscr->lines[curs]->hash)
784 break;
785 if (curs != starts + bsize)
786 continue;
787 for (curw = startw, curs = starts;
788 curs < starts + bsize; curw++, curs++)
789 if (memcmp(__virtscr->lines[curw]->line,
790 curscr->lines[curs]->line,
791 (size_t) __virtscr->maxx *
792 __LDATASIZE) != 0)
793 break;
794 if (curs == starts + bsize)
795 goto done;
796 }
797 }
798 done:
799
800 /* Did not find anything */
801 if (bsize < THRESH)
802 return;
803
804 #ifdef DEBUG
805 __CTRACE("quickch:bsize=%d,starts=%d,startw=%d,curw=%d,curs=%d,top=%d,bot=%d\n",
806 bsize, starts, startw, curw, curs, top, bot);
807 #endif
808
809 /*
810 * Make sure that there is no overlap between the bottom and top
811 * regions and the middle scrolled block.
812 */
813 if (bot < curs)
814 bot = curs - 1;
815 if (top > starts)
816 top = starts;
817
818 n = startw - starts;
819
820 #ifdef DEBUG
821 __CTRACE("#####################################\n");
822 for (i = 0; i < curscr->maxy; i++) {
823 __CTRACE("C: %d:", i);
824 __CTRACE(" 0x%x \n", curscr->lines[i]->hash);
825 for (j = 0; j < curscr->maxx; j++)
826 __CTRACE("%c", curscr->lines[i]->line[j].ch);
827 __CTRACE("\n");
828 __CTRACE(" attr:");
829 for (j = 0; j < curscr->maxx; j++)
830 __CTRACE(" %x", curscr->lines[i]->line[j].attr);
831 __CTRACE("\n");
832 __CTRACE("W: %d:", i);
833 __CTRACE(" 0x%x \n", __virtscr->lines[i]->hash);
834 __CTRACE(" 0x%x ", __virtscr->lines[i]->flags);
835 for (j = 0; j < __virtscr->maxx; j++)
836 __CTRACE("%c", __virtscr->lines[i]->line[j].ch);
837 __CTRACE("\n");
838 __CTRACE(" attr:");
839 for (j = 0; j < __virtscr->maxx; j++)
840 __CTRACE(" %x", __virtscr->lines[i]->line[j].attr);
841 __CTRACE("\n");
842 }
843 #endif
844
845 /* So we don't have to call __hash() each time */
846 for (i = 0; i < __virtscr->maxx; i++) {
847 buf[i].ch = ' ';
848 buf[i].bch = ' ';
849 buf[i].attr = 0;
850 buf[i].battr = 0;
851 }
852 blank_hash = __hash((char *)(void *)buf,
853 (int) (__virtscr->maxx * __LDATASIZE));
854
855 /*
856 * Perform the rotation to maintain the consistency of curscr.
857 * This is hairy since we are doing an *in place* rotation.
858 * Invariants of the loop:
859 * - I is the index of the current line.
860 * - Target is the index of the target of line i.
861 * - Tmp1 points to current line (i).
862 * - Tmp2 and points to target line (target);
863 * - Cur_period is the index of the end of the current period.
864 * (see below).
865 *
866 * There are 2 major issues here that make this rotation non-trivial:
867 * 1. Scrolling in a scrolling region bounded by the top
868 * and bottom regions determined (whose size is sc_region).
869 * 2. As a result of the use of the mod function, there may be a
870 * period introduced, i.e., 2 maps to 4, 4 to 6, n-2 to 0, and
871 * 0 to 2, which then causes all odd lines not to be rotated.
872 * To remedy this, an index of the end ( = beginning) of the
873 * current 'period' is kept, cur_period, and when it is reached,
874 * the next period is started from cur_period + 1 which is
875 * guaranteed not to have been reached since that would mean that
876 * all records would have been reached. (think about it...).
877 *
878 * Lines in the rotation can have 3 attributes which are marked on the
879 * line so that curscr is consistent with the visual screen.
880 * 1. Not dirty -- lines inside the scrolled block, top region or
881 * bottom region.
882 * 2. Blank lines -- lines in the differential of the scrolling
883 * region adjacent to top and bot regions
884 * depending on scrolling direction.
885 * 3. Dirty line -- all other lines are marked dirty.
886 */
887 sc_region = bot - top + 1;
888 i = top;
889 tmp1 = curscr->lines[top];
890 cur_period = top;
891 for (j = top; j <= bot; j++) {
892 target = (i - top + n + sc_region) % sc_region + top;
893 tmp2 = curscr->lines[target];
894 curscr->lines[target] = tmp1;
895 /* Mark block as clean and blank out scrolled lines. */
896 clp = curscr->lines[target];
897 #ifdef DEBUG
898 __CTRACE("quickch: n=%d startw=%d curw=%d i = %d target=%d ",
899 n, startw, curw, i, target);
900 #endif
901 if ((target >= startw && target < curw) || target < top
902 || target > bot) {
903 #ifdef DEBUG
904 __CTRACE("-- notdirty\n");
905 #endif
906 __virtscr->lines[target]->flags &= ~__ISDIRTY;
907 } else
908 if ((n > 0 && target >= top && target < top + n) ||
909 (n < 0 && target <= bot && target > bot + n)) {
910 if (clp->hash != blank_hash || memcmp(clp->line,
911 buf, (size_t) __virtscr->maxx * __LDATASIZE) !=0) {
912 (void)memcpy(clp->line, buf,
913 (size_t) __virtscr->maxx * __LDATASIZE);
914 #ifdef DEBUG
915 __CTRACE("-- blanked out: dirty\n");
916 #endif
917 clp->hash = blank_hash;
918 __touchline(__virtscr, target, 0, (int) __virtscr->maxx - 1);
919 } else {
920 #ifdef DEBUG
921 __CTRACE(" -- blank line already: dirty\n");
922 #endif
923 __touchline(__virtscr, target, 0, (int) __virtscr->maxx - 1);
924 }
925 } else {
926 #ifdef DEBUG
927 __CTRACE(" -- dirty\n");
928 #endif
929 __touchline(__virtscr, target, 0, (int) __virtscr->maxx - 1);
930 }
931 if (target == cur_period) {
932 i = target + 1;
933 tmp1 = curscr->lines[i];
934 cur_period = i;
935 } else {
936 tmp1 = tmp2;
937 i = target;
938 }
939 }
940 #ifdef DEBUG
941 __CTRACE("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n");
942 for (i = 0; i < curscr->maxy; i++) {
943 __CTRACE("C: %d:", i);
944 for (j = 0; j < curscr->maxx; j++)
945 __CTRACE("%c", curscr->lines[i]->line[j].ch);
946 __CTRACE("\n");
947 __CTRACE("W: %d:", i);
948 for (j = 0; j < __virtscr->maxx; j++)
949 __CTRACE("%c", __virtscr->lines[i]->line[j].ch);
950 __CTRACE("\n");
951 }
952 #endif
953 if (n != 0)
954 scrolln(starts, startw, curs, bot, top);
955 }
956
957 /*
958 * scrolln --
959 * Scroll n lines, where n is starts - startw.
960 */
961 static void /* ARGSUSED */
962 scrolln(starts, startw, curs, bot, top)
963 int starts, startw, curs, bot, top;
964 {
965 int i, oy, ox, n;
966
967 oy = curscr->cury;
968 ox = curscr->curx;
969 n = starts - startw;
970
971 /*
972 * XXX
973 * The initial tests that set __noqch don't let us reach here unless
974 * we have either CS + HO + SF/sf/SR/sr, or AL + DL. SF/sf and SR/sr
975 * scrolling can only shift the entire scrolling region, not just a
976 * part of it, which means that the quickch() routine is going to be
977 * sadly disappointed in us if we don't have CS as well.
978 *
979 * If CS, HO and SF/sf are set, can use the scrolling region. Because
980 * the cursor position after CS is undefined, we need HO which gives us
981 * the ability to move to somewhere without knowledge of the current
982 * location of the cursor. Still call __mvcur() anyway, to update its
983 * idea of where the cursor is.
984 *
985 * When the scrolling region has been set, the cursor has to be at the
986 * last line of the region to make the scroll happen.
987 *
988 * Doing SF/SR or AL/DL appears faster on the screen than either sf/sr
989 * or al/dl, and, some terminals have AL/DL, sf/sr, and CS, but not
990 * SF/SR. So, if we're scrolling almost all of the screen, try and use
991 * AL/DL, otherwise use the scrolling region. The "almost all" is a
992 * shameless hack for vi.
993 */
994 if (n > 0) {
995 if (CS != NULL && HO != NULL && (SF != NULL ||
996 ((AL == NULL || DL == NULL ||
997 top > 3 || bot + 3 < __virtscr->maxy) && sf != NULL))) {
998 tputs(__tscroll(CS, top, bot + 1), 0, __cputchar);
999 __mvcur(oy, ox, 0, 0, 1);
1000 tputs(HO, 0, __cputchar);
1001 __mvcur(0, 0, bot, 0, 1);
1002 if (SF != NULL)
1003 tputs(__tscroll(SF, n, 0), 0, __cputchar);
1004 else
1005 for (i = 0; i < n; i++)
1006 tputs(sf, 0, __cputchar);
1007 tputs(__tscroll(CS, 0, (int) __virtscr->maxy), 0, __cputchar);
1008 __mvcur(bot, 0, 0, 0, 1);
1009 tputs(HO, 0, __cputchar);
1010 __mvcur(0, 0, oy, ox, 1);
1011 return;
1012 }
1013
1014 /* Scroll up the block. */
1015 if (SF != NULL && top == 0) {
1016 __mvcur(oy, ox, bot, 0, 1);
1017 tputs(__tscroll(SF, n, 0), 0, __cputchar);
1018 } else
1019 if (DL != NULL) {
1020 __mvcur(oy, ox, top, 0, 1);
1021 tputs(__tscroll(DL, n, 0), 0, __cputchar);
1022 } else
1023 if (dl != NULL) {
1024 __mvcur(oy, ox, top, 0, 1);
1025 for (i = 0; i < n; i++)
1026 tputs(dl, 0, __cputchar);
1027 } else
1028 if (sf != NULL && top == 0) {
1029 __mvcur(oy, ox, bot, 0, 1);
1030 for (i = 0; i < n; i++)
1031 tputs(sf, 0, __cputchar);
1032 } else
1033 abort();
1034
1035 /* Push down the bottom region. */
1036 __mvcur(top, 0, bot - n + 1, 0, 1);
1037 if (AL != NULL)
1038 tputs(__tscroll(AL, n, 0), 0, __cputchar);
1039 else
1040 if (al != NULL)
1041 for (i = 0; i < n; i++)
1042 tputs(al, 0, __cputchar);
1043 else
1044 abort();
1045 __mvcur(bot - n + 1, 0, oy, ox, 1);
1046 } else {
1047 /*
1048 * !!!
1049 * n < 0
1050 *
1051 * If CS, HO and SR/sr are set, can use the scrolling region.
1052 * See the above comments for details.
1053 */
1054 if (CS != NULL && HO != NULL && (SR != NULL ||
1055 ((AL == NULL || DL == NULL ||
1056 top > 3 || bot + 3 < __virtscr->maxy) && sr != NULL))) {
1057 tputs(__tscroll(CS, top, bot + 1), 0, __cputchar);
1058 __mvcur(oy, ox, 0, 0, 1);
1059 tputs(HO, 0, __cputchar);
1060 __mvcur(0, 0, top, 0, 1);
1061
1062 if (SR != NULL)
1063 tputs(__tscroll(SR, -n, 0), 0, __cputchar);
1064 else
1065 for (i = n; i < 0; i++)
1066 tputs(sr, 0, __cputchar);
1067 tputs(__tscroll(CS, 0, (int) __virtscr->maxy), 0, __cputchar);
1068 __mvcur(top, 0, 0, 0, 1);
1069 tputs(HO, 0, __cputchar);
1070 __mvcur(0, 0, oy, ox, 1);
1071 return;
1072 }
1073
1074 /* Preserve the bottom lines. */
1075 __mvcur(oy, ox, bot + n + 1, 0, 1);
1076 if (SR != NULL && bot == __virtscr->maxy)
1077 tputs(__tscroll(SR, -n, 0), 0, __cputchar);
1078 else
1079 if (DL != NULL)
1080 tputs(__tscroll(DL, -n, 0), 0, __cputchar);
1081 else
1082 if (dl != NULL)
1083 for (i = n; i < 0; i++)
1084 tputs(dl, 0, __cputchar);
1085 else
1086 if (sr != NULL && bot == __virtscr->maxy)
1087 for (i = n; i < 0; i++)
1088 tputs(sr, 0, __cputchar);
1089 else
1090 abort();
1091
1092 /* Scroll the block down. */
1093 __mvcur(bot + n + 1, 0, top, 0, 1);
1094 if (AL != NULL)
1095 tputs(__tscroll(AL, -n, 0), 0, __cputchar);
1096 else
1097 if (al != NULL)
1098 for (i = n; i < 0; i++)
1099 tputs(al, 0, __cputchar);
1100 else
1101 abort();
1102 __mvcur(top, 0, oy, ox, 1);
1103 }
1104 }
1105
1106 /*
1107 * __unsetattr --
1108 * Unset attributes on curscr. Leave standout, attribute and colour
1109 * modes if necessary (!MS). Always leave altcharset (xterm at least
1110 * ignores a cursor move if we don't).
1111 */
1112 void /* ARGSUSED */
1113 __unsetattr(int checkms)
1114 {
1115 int isms;
1116
1117 if (checkms)
1118 if (!MS) {
1119 isms = 1;
1120 } else {
1121 isms = 0;
1122 }
1123 else
1124 isms = 1;
1125 #ifdef DEBUG
1126 __CTRACE("__unsetattr: checkms = %d, MS = %s, wattr = %08x\n",
1127 checkms, MS ? "TRUE" : "FALSE", curscr->wattr);
1128 #endif
1129
1130 /*
1131 * Don't leave the screen in standout mode (check against MS). Check
1132 * to see if we also turn off underscore, attributes and colour.
1133 */
1134 if (curscr->wattr & __STANDOUT && isms) {
1135 tputs(SE, 0, __cputchar);
1136 curscr->wattr &= __mask_SE;
1137 }
1138 /*
1139 * Don't leave the screen in underscore mode (check against MS).
1140 * Check to see if we also turn off attributes. Assume that we
1141 * also turn off colour.
1142 */
1143 if (curscr->wattr & __UNDERSCORE && isms) {
1144 tputs(UE, 0, __cputchar);
1145 curscr->wattr &= __mask_UE;
1146 }
1147 /*
1148 * Don't leave the screen with attributes set (check against MS).
1149 * Assume that also turn off colour.
1150 */
1151 if (curscr->wattr & __TERMATTR && isms) {
1152 tputs(ME, 0, __cputchar);
1153 curscr->wattr &= __mask_ME;
1154 }
1155 /* Don't leave the screen with altcharset set (don't check MS). */
1156 if (curscr->wattr & __ALTCHARSET) {
1157 tputs(AE, 0, __cputchar);
1158 curscr->wattr &= ~__ALTCHARSET;
1159 }
1160 /* Don't leave the screen with colour set (check against MS). */
1161 if (curscr->wattr & __COLOR && isms) {
1162 if (OC != NULL && CC == NULL)
1163 tputs(OC, 0, __cputchar);
1164 if (OP != NULL) {
1165 tputs(OP, 0, __cputchar);
1166 curscr->wattr &= __mask_OP;
1167 }
1168 }
1169 }
1170