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