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