1 /* $NetBSD: refresh.c,v 1.35 2000/05/20 15:12:15 mycroft 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.35 2000/05/20 15:12:15 mycroft 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 (wlp->line[wx].battr & __COLOR)) 122 vlp->line[x_off].attr |= 123 wlp->line[wx].battr & __COLOR; 124 if (wlp->line[wx].ch == ' ' && 125 wlp->line[wx].bch != ' ') 126 vlp->line[x_off].ch 127 = wlp->line[wx].bch; 128 else 129 vlp->line[x_off].ch 130 = wlp->line[wx].ch; 131 } 132 133 /* Set flags on "__virtscr" and unset on "win". */ 134 if (wlp->flags & __ISPASTEOL) 135 vlp->flags |= __ISPASTEOL; 136 else 137 vlp->flags &= ~__ISPASTEOL; 138 if (wlp->flags & __ISDIRTY) 139 vlp->flags |= __ISDIRTY; 140 141 #ifdef DEBUG 142 __CTRACE("win: firstch = %d, lastch = %d\n", 143 *wlp->firstchp, *wlp->lastchp); 144 #endif 145 /* Set change pointers on "__virtscr". */ 146 if (*vlp->firstchp > 147 *wlp->firstchp + win->begx - win->ch_off) 148 *vlp->firstchp = 149 *wlp->firstchp + win->begx - win->ch_off; 150 if (*vlp->lastchp < 151 *wlp->lastchp + win->begx - win->ch_off) 152 *vlp->lastchp = 153 *wlp->lastchp + win->begx - win->ch_off; 154 #ifdef DEBUG 155 __CTRACE("__virtscr: firstch = %d, lastch = %d\n", 156 *vlp->firstchp, *vlp->lastchp); 157 #endif 158 159 /* Set change pointers on "win". */ 160 if (*wlp->firstchp >= win->ch_off) 161 *wlp->firstchp = win->maxx + win->ch_off; 162 if (*wlp->lastchp < win->maxx + win->ch_off) 163 *wlp->lastchp = win->ch_off; 164 if (*wlp->lastchp < *wlp->firstchp) { 165 #ifdef DEBUG 166 __CTRACE("wnoutrefresh: line %d notdirty\n", 167 wy); 168 #endif 169 wlp->flags &= ~__ISDIRTY; 170 } 171 } 172 } 173 174 return (OK); 175 } 176 177 /* 178 * wrefresh -- 179 * Make the current screen look like "win" over the area coverd by 180 * win. 181 */ 182 int 183 wrefresh(WINDOW *win) 184 { 185 int retval; 186 187 curwin = (win == curscr); 188 if (!curwin) 189 retval = wnoutrefresh(win); 190 else 191 retval = OK; 192 if (retval == OK) { 193 retval = doupdate(); 194 if (!win->flags & __LEAVEOK) { 195 win->cury = max(0, curscr->cury - win->begy); 196 win->curx = max(0, curscr->curx - win->begx); 197 } 198 } 199 curwin = 0; 200 return(retval); 201 } 202 203 /* 204 * doupdate -- 205 * Make the current screen look like the virtual window "__virtscr". 206 */ 207 int 208 doupdate(void) 209 { 210 WINDOW *win; 211 __LINE *wlp; 212 short wy; 213 int dnum; 214 215 /* Check if we need to restart ... */ 216 if (__endwin) { 217 __endwin = 0; 218 __restartwin(); 219 } 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