1 /* $NetBSD: refresh.c,v 1.40 2000/08/02 10:50:49 itojun 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.40 2000/08/02 10:50:49 itojun 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(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 (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 } 419 if (!curwin) 420 ce = CE; 421 else 422 ce = NULL; 423 424 while (wx <= lch) { 425 if (memcmp(nsp, csp, sizeof(__LDATA)) == 0) { 426 if (wx <= lch) { 427 while (wx <= lch && 428 memcmp(nsp, csp, sizeof(__LDATA)) == 0) { 429 nsp++; 430 if (!curwin) 431 ++csp; 432 ++wx; 433 } 434 continue; 435 } 436 break; 437 } 438 domvcur(ly, lx, wy, wx); 439 440 #ifdef DEBUG 441 __CTRACE("makech: 1: wx = %d, ly= %d, lx = %d, newy = %d, newx = %d\n", 442 wx, ly, lx, wy, wx); 443 #endif 444 ly = wy; 445 lx = wx; 446 while (memcmp(nsp, csp, sizeof(__LDATA)) != 0 && wx <= lch) { 447 if (ce != NULL && 448 wx >= nlsp && nsp->ch == ' ' && nsp->attr == lspc) { 449 /* Check for clear to end-of-line. */ 450 cep = &curscr->lines[wy]->line[win->maxx - 1]; 451 while (cep->ch == ' ' && cep->attr == lspc) 452 if (cep-- <= csp) 453 break; 454 clsp = cep - curscr->lines[wy]->line - 455 win->begx * __LDATASIZE; 456 #ifdef DEBUG 457 __CTRACE("makech: clsp = %d, nlsp = %d\n", 458 clsp, nlsp); 459 #endif 460 if (((clsp - nlsp >= strlen(CE) && 461 clsp < win->maxx * __LDATASIZE) || 462 wy == win->maxy - 1) && 463 (!(lspc & __COLOR) || 464 ((lspc & __COLOR) && UT))) { 465 __unsetattr(0); 466 if ((lspc & __COLOR) != 467 (curscr->wattr & __COLOR)) { 468 __set_color(lspc); 469 curscr->wattr &= ~__COLOR; 470 curscr->wattr |= lspc & __COLOR; 471 } 472 tputs(CE, 0, __cputchar); 473 lx = wx + win->begx; 474 while (wx++ <= clsp) { 475 csp->ch = ' '; 476 csp->attr = lspc; 477 csp++; 478 } 479 return (OK); 480 } 481 ce = NULL; 482 } 483 484 /* 485 * Unset colour if appropriate. Check to see 486 * if we also turn off standout, underscore and 487 * attributes. 488 */ 489 if (!(nsp->attr & __COLOR) && 490 (curscr->wattr & __COLOR)) { 491 if (OC != NULL && CC == NULL) 492 tputs(OC, 0, __cputchar); 493 if (OP != NULL) { 494 tputs(OP, 0, __cputchar); 495 curscr->wattr &= __mask_OP; 496 } 497 } 498 499 off = ~nsp->attr & curscr->wattr; 500 501 /* 502 * Unset attributes as appropriate. Unset first 503 * so that the relevant attributes can be reset 504 * (because 'me' unsets 'mb', 'md', 'mh', 'mk', 505 * 'mp' and 'mr'). Check to see if we also turn off 506 * standout, attributes and colour. 507 */ 508 if (off & __TERMATTR && ME != NULL) { 509 tputs(ME, 0, __cputchar); 510 curscr->wattr &= __mask_ME; 511 off &= __mask_ME; 512 } 513 514 /* 515 * Exit underscore mode if appropriate. 516 * Check to see if we also turn off standout, 517 * attributes and colour. 518 */ 519 if (off & __UNDERSCORE && UE != NULL) { 520 tputs(UE, 0, __cputchar); 521 curscr->wattr &= __mask_UE; 522 off &= __mask_UE; 523 } 524 525 /* 526 * Exit standout mode as appropriate. 527 * Check to see if we also turn off underscore, 528 * attributes and colour. 529 * XXX 530 * Should use UC if SO/SE not available. 531 */ 532 if (off & __STANDOUT && SE != NULL) { 533 tputs(SE, 0, __cputchar); 534 curscr->wattr &= __mask_SE; 535 off &= __mask_SE; 536 } 537 538 if (off & __ALTCHARSET && AE != NULL) { 539 tputs(AE, 0, __cputchar); 540 curscr->wattr &= ~__ALTCHARSET; 541 } 542 543 on = nsp->attr & ~curscr->wattr; 544 545 /* 546 * Enter standout mode if appropriate. 547 */ 548 if (on & __STANDOUT && SO != NULL && SE != NULL) { 549 tputs(SO, 0, __cputchar); 550 curscr->wattr |= __STANDOUT; 551 } 552 553 /* 554 * Enter underscore mode if appropriate. 555 * XXX 556 * Should use UC if US/UE not available. 557 */ 558 if (on & __UNDERSCORE && US != NULL && UE != NULL) { 559 tputs(US, 0, __cputchar); 560 curscr->wattr |= __UNDERSCORE; 561 } 562 563 /* 564 * Set other attributes as appropriate. 565 */ 566 if (ME != NULL) { 567 if (on & __BLINK && MB != NULL) { 568 tputs(MB, 0, __cputchar); 569 curscr->wattr |= __BLINK; 570 } 571 if (on & __BOLD && MD != NULL) { 572 tputs(MD, 0, __cputchar); 573 curscr->wattr |= __BOLD; 574 } 575 if (on & __DIM && MH != NULL) { 576 tputs(MH, 0, __cputchar); 577 curscr->wattr |= __DIM; 578 } 579 if (on & __BLANK && MK != NULL) { 580 tputs(MK, 0, __cputchar); 581 curscr->wattr |= __BLANK; 582 } 583 if (on & __PROTECT && MP != NULL) { 584 tputs(MP, 0, __cputchar); 585 curscr->wattr |= __PROTECT; 586 } 587 if (on & __REVERSE && MR != NULL) { 588 tputs(MR, 0, __cputchar); 589 curscr->wattr |= __REVERSE; 590 } 591 } 592 593 /* Set/change colour as appropriate. */ 594 if ((nsp->attr & __COLOR) && 595 cO != NULL && (OC != NULL || OP != NULL)) { 596 if ((nsp->attr & __COLOR) != 597 (curscr->wattr & __COLOR)) { 598 __set_color(nsp->attr); 599 curscr->wattr &= ~__COLOR; 600 curscr->wattr |= nsp->attr & 601 __COLOR; 602 } 603 } 604 605 /* Enter/exit altcharset mode as appropriate. */ 606 if (on & __ALTCHARSET && AS != NULL && AE != NULL) { 607 tputs(AS, 0, __cputchar); 608 curscr->wattr |= __ALTCHARSET; 609 } 610 611 wx++; 612 if (wx >= win->maxx && 613 wy == win->maxy - 1 && !curwin) 614 if (win->flags & __SCROLLOK) { 615 if (win->flags & __ENDLINE) 616 __unsetattr(1); 617 if (!(win->flags & __SCROLLWIN)) { 618 if (!curwin) { 619 csp->attr = nsp->attr; 620 __cputchar((int) 621 (csp->ch = 622 nsp->ch)); 623 } else 624 __cputchar((int) nsp->ch); 625 } 626 if (wx < curscr->maxx) { 627 domvcur(ly, wx, 628 (int) (win->maxy - 1), 629 (int) (win->maxx - 1)); 630 } 631 ly = win->maxy - 1; 632 lx = win->maxx - 1; 633 return (OK); 634 } 635 if (wx < win->maxx || wy < win->maxy - 1 || 636 !(win->flags & __SCROLLWIN)) { 637 if (!curwin) { 638 csp->attr = nsp->attr; 639 __cputchar((int) (csp->ch = nsp->ch)); 640 csp++; 641 } else 642 __cputchar((int) nsp->ch); 643 } 644 #ifdef DEBUG 645 __CTRACE("makech: putchar(%c)\n", nsp->ch & 0177); 646 #endif 647 if (UC && ((nsp->attr & __STANDOUT) || 648 (nsp->attr & __UNDERSCORE))) { 649 __cputchar('\b'); 650 tputs(UC, 0, __cputchar); 651 } 652 nsp++; 653 #ifdef DEBUG 654 __CTRACE("makech: 2: wx = %d, lx = %d\n", wx, lx); 655 #endif 656 } 657 if (lx == wx) /* If no change. */ 658 break; 659 lx = wx; 660 if (lx >= COLS && AM) 661 lx = COLS - 1; 662 else 663 if (wx >= win->maxx) { 664 domvcur(ly, lx, ly, 665 (int) (win->maxx - 1)); 666 lx = win->maxx - 1; 667 } 668 #ifdef DEBUG 669 __CTRACE("makech: 3: wx = %d, lx = %d\n", wx, lx); 670 #endif 671 } 672 673 return (OK); 674 } 675 676 /* 677 * domvcur -- 678 * Do a mvcur, leaving attributes if necessary. 679 */ 680 static void 681 domvcur(oy, ox, ny, nx) 682 int oy, ox, ny, nx; 683 { 684 __unsetattr(1); 685 __mvcur(oy, ox, ny, nx, 1); 686 } 687 688 /* 689 * Quickch() attempts to detect a pattern in the change of the window 690 * in order to optimize the change, e.g., scroll n lines as opposed to 691 * repainting the screen line by line. 692 */ 693 694 static void 695 quickch(void) 696 { 697 #define THRESH (int) __virtscr->maxy / 4 698 699 __LINE *clp, *tmp1, *tmp2; 700 int bsize, curs, curw, starts, startw, i, j; 701 int n, target, cur_period, bot, top, sc_region; 702 __LDATA buf[1024]; 703 u_int blank_hash; 704 attr_t bcolor; 705 706 #ifdef __GNUC__ 707 curs = curw = starts = startw = 0; /* XXX gcc -Wuninitialized */ 708 #endif 709 /* 710 * Find how many lines from the top of the screen are unchanged. 711 */ 712 for (top = 0; top < __virtscr->maxy; top++) 713 if (__virtscr->lines[top]->flags & __ISDIRTY && 714 (__virtscr->lines[top]->hash != curscr->lines[top]->hash || 715 memcmp(__virtscr->lines[top]->line, 716 curscr->lines[top]->line, 717 (size_t) __virtscr->maxx * __LDATASIZE) != 0)) 718 break; 719 else 720 __virtscr->lines[top]->flags &= ~__ISDIRTY; 721 /* 722 * Find how many lines from bottom of screen are unchanged. 723 */ 724 for (bot = __virtscr->maxy - 1; bot >= 0; bot--) 725 if (__virtscr->lines[bot]->flags & __ISDIRTY && 726 (__virtscr->lines[bot]->hash != curscr->lines[bot]->hash || 727 memcmp(__virtscr->lines[bot]->line, 728 curscr->lines[bot]->line, 729 (size_t) __virtscr->maxx * __LDATASIZE) != 0)) 730 break; 731 else 732 __virtscr->lines[bot]->flags &= ~__ISDIRTY; 733 734 /* 735 * Work round an xterm bug where inserting lines causes all the 736 * inserted lines to be covered with the background colour we 737 * set on the first line (even if we unset it for subsequent 738 * lines). 739 */ 740 bcolor = __virtscr->lines[min(top, 741 __virtscr->maxy - 1)]->line[0].attr & __COLOR; 742 for (i = top + 1, j = 0; i < bot; i++) { 743 if ((__virtscr->lines[i]->line[0].attr & __COLOR) != bcolor) { 744 bcolor = __virtscr->lines[i]->line[__virtscr->maxx]. 745 attr & __COLOR; 746 j = i - top; 747 } else 748 break; 749 } 750 top += j; 751 752 #ifdef NO_JERKINESS 753 /* 754 * If we have a bottom unchanged region return. Scrolling the 755 * bottom region up and then back down causes a screen jitter. 756 * This will increase the number of characters sent to the screen 757 * but it looks better. 758 */ 759 if (bot < __virtscr->maxy - 1) 760 return; 761 #endif /* NO_JERKINESS */ 762 763 /* 764 * Search for the largest block of text not changed. 765 * Invariants of the loop: 766 * - Startw is the index of the beginning of the examined block in 767 * __virtscr. 768 * - Starts is the index of the beginning of the examined block in 769 * curscr. 770 * - Curw is the index of one past the end of the exmined block in 771 * __virtscr. 772 * - Curs is the index of one past the end of the exmined block in 773 * curscr. 774 * - bsize is the current size of the examined block. 775 */ 776 777 for (bsize = bot - top; bsize >= THRESH; bsize--) { 778 for (startw = top; startw <= bot - bsize; startw++) 779 for (starts = top; starts <= bot - bsize; 780 starts++) { 781 for (curw = startw, curs = starts; 782 curs < starts + bsize; curw++, curs++) 783 if (__virtscr->lines[curw]->hash != 784 curscr->lines[curs]->hash) 785 break; 786 if (curs != starts + bsize) 787 continue; 788 for (curw = startw, curs = starts; 789 curs < starts + bsize; curw++, curs++) 790 if (memcmp(__virtscr->lines[curw]->line, 791 curscr->lines[curs]->line, 792 (size_t) __virtscr->maxx * 793 __LDATASIZE) != 0) 794 break; 795 if (curs == starts + bsize) 796 goto done; 797 } 798 } 799 done: 800 801 /* Did not find anything */ 802 if (bsize < THRESH) 803 return; 804 805 #ifdef DEBUG 806 __CTRACE("quickch:bsize=%d,starts=%d,startw=%d,curw=%d,curs=%d,top=%d,bot=%d\n", 807 bsize, starts, startw, curw, curs, top, bot); 808 #endif 809 810 /* 811 * Make sure that there is no overlap between the bottom and top 812 * regions and the middle scrolled block. 813 */ 814 if (bot < curs) 815 bot = curs - 1; 816 if (top > starts) 817 top = starts; 818 819 n = startw - starts; 820 821 #ifdef DEBUG 822 __CTRACE("#####################################\n"); 823 for (i = 0; i < curscr->maxy; i++) { 824 __CTRACE("C: %d:", i); 825 __CTRACE(" 0x%x \n", curscr->lines[i]->hash); 826 for (j = 0; j < curscr->maxx; j++) 827 __CTRACE("%c", curscr->lines[i]->line[j].ch); 828 __CTRACE("\n"); 829 __CTRACE(" attr:"); 830 for (j = 0; j < curscr->maxx; j++) 831 __CTRACE(" %x", curscr->lines[i]->line[j].attr); 832 __CTRACE("\n"); 833 __CTRACE("W: %d:", i); 834 __CTRACE(" 0x%x \n", __virtscr->lines[i]->hash); 835 __CTRACE(" 0x%x ", __virtscr->lines[i]->flags); 836 for (j = 0; j < __virtscr->maxx; j++) 837 __CTRACE("%c", __virtscr->lines[i]->line[j].ch); 838 __CTRACE("\n"); 839 __CTRACE(" attr:"); 840 for (j = 0; j < __virtscr->maxx; j++) 841 __CTRACE(" %x", __virtscr->lines[i]->line[j].attr); 842 __CTRACE("\n"); 843 } 844 #endif 845 846 /* So we don't have to call __hash() each time */ 847 for (i = 0; i < __virtscr->maxx; i++) { 848 buf[i].ch = ' '; 849 buf[i].bch = ' '; 850 buf[i].attr = 0; 851 buf[i].battr = 0; 852 } 853 blank_hash = __hash((char *)(void *)buf, 854 (int) (__virtscr->maxx * __LDATASIZE)); 855 856 /* 857 * Perform the rotation to maintain the consistency of curscr. 858 * This is hairy since we are doing an *in place* rotation. 859 * Invariants of the loop: 860 * - I is the index of the current line. 861 * - Target is the index of the target of line i. 862 * - Tmp1 points to current line (i). 863 * - Tmp2 and points to target line (target); 864 * - Cur_period is the index of the end of the current period. 865 * (see below). 866 * 867 * There are 2 major issues here that make this rotation non-trivial: 868 * 1. Scrolling in a scrolling region bounded by the top 869 * and bottom regions determined (whose size is sc_region). 870 * 2. As a result of the use of the mod function, there may be a 871 * period introduced, i.e., 2 maps to 4, 4 to 6, n-2 to 0, and 872 * 0 to 2, which then causes all odd lines not to be rotated. 873 * To remedy this, an index of the end ( = beginning) of the 874 * current 'period' is kept, cur_period, and when it is reached, 875 * the next period is started from cur_period + 1 which is 876 * guaranteed not to have been reached since that would mean that 877 * all records would have been reached. (think about it...). 878 * 879 * Lines in the rotation can have 3 attributes which are marked on the 880 * line so that curscr is consistent with the visual screen. 881 * 1. Not dirty -- lines inside the scrolled block, top region or 882 * bottom region. 883 * 2. Blank lines -- lines in the differential of the scrolling 884 * region adjacent to top and bot regions 885 * depending on scrolling direction. 886 * 3. Dirty line -- all other lines are marked dirty. 887 */ 888 sc_region = bot - top + 1; 889 i = top; 890 tmp1 = curscr->lines[top]; 891 cur_period = top; 892 for (j = top; j <= bot; j++) { 893 target = (i - top + n + sc_region) % sc_region + top; 894 tmp2 = curscr->lines[target]; 895 curscr->lines[target] = tmp1; 896 /* Mark block as clean and blank out scrolled lines. */ 897 clp = curscr->lines[target]; 898 #ifdef DEBUG 899 __CTRACE("quickch: n=%d startw=%d curw=%d i = %d target=%d ", 900 n, startw, curw, i, target); 901 #endif 902 if ((target >= startw && target < curw) || target < top 903 || target > bot) { 904 #ifdef DEBUG 905 __CTRACE("-- notdirty\n"); 906 #endif 907 __virtscr->lines[target]->flags &= ~__ISDIRTY; 908 } else 909 if ((n > 0 && target >= top && target < top + n) || 910 (n < 0 && target <= bot && target > bot + n)) { 911 if (clp->hash != blank_hash || memcmp(clp->line, 912 buf, (size_t) __virtscr->maxx * __LDATASIZE) !=0) { 913 (void)memcpy(clp->line, buf, 914 (size_t) __virtscr->maxx * __LDATASIZE); 915 #ifdef DEBUG 916 __CTRACE("-- blanked out: dirty\n"); 917 #endif 918 clp->hash = blank_hash; 919 __touchline(__virtscr, target, 0, (int) __virtscr->maxx - 1); 920 } else { 921 #ifdef DEBUG 922 __CTRACE(" -- blank line already: dirty\n"); 923 #endif 924 __touchline(__virtscr, target, 0, (int) __virtscr->maxx - 1); 925 } 926 } else { 927 #ifdef DEBUG 928 __CTRACE(" -- dirty\n"); 929 #endif 930 __touchline(__virtscr, target, 0, (int) __virtscr->maxx - 1); 931 } 932 if (target == cur_period) { 933 i = target + 1; 934 tmp1 = curscr->lines[i]; 935 cur_period = i; 936 } else { 937 tmp1 = tmp2; 938 i = target; 939 } 940 } 941 #ifdef DEBUG 942 __CTRACE("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n"); 943 for (i = 0; i < curscr->maxy; i++) { 944 __CTRACE("C: %d:", i); 945 for (j = 0; j < curscr->maxx; j++) 946 __CTRACE("%c", curscr->lines[i]->line[j].ch); 947 __CTRACE("\n"); 948 __CTRACE("W: %d:", i); 949 for (j = 0; j < __virtscr->maxx; j++) 950 __CTRACE("%c", __virtscr->lines[i]->line[j].ch); 951 __CTRACE("\n"); 952 } 953 #endif 954 if (n != 0) 955 scrolln(starts, startw, curs, bot, top); 956 } 957 958 /* 959 * scrolln -- 960 * Scroll n lines, where n is starts - startw. 961 */ 962 static void /* ARGSUSED */ 963 scrolln(starts, startw, curs, bot, top) 964 int starts, startw, curs, bot, top; 965 { 966 int i, oy, ox, n; 967 968 oy = curscr->cury; 969 ox = curscr->curx; 970 n = starts - startw; 971 972 /* 973 * XXX 974 * The initial tests that set __noqch don't let us reach here unless 975 * we have either CS + HO + SF/sf/SR/sr, or AL + DL. SF/sf and SR/sr 976 * scrolling can only shift the entire scrolling region, not just a 977 * part of it, which means that the quickch() routine is going to be 978 * sadly disappointed in us if we don't have CS as well. 979 * 980 * If CS, HO and SF/sf are set, can use the scrolling region. Because 981 * the cursor position after CS is undefined, we need HO which gives us 982 * the ability to move to somewhere without knowledge of the current 983 * location of the cursor. Still call __mvcur() anyway, to update its 984 * idea of where the cursor is. 985 * 986 * When the scrolling region has been set, the cursor has to be at the 987 * last line of the region to make the scroll happen. 988 * 989 * Doing SF/SR or AL/DL appears faster on the screen than either sf/sr 990 * or al/dl, and, some terminals have AL/DL, sf/sr, and CS, but not 991 * SF/SR. So, if we're scrolling almost all of the screen, try and use 992 * AL/DL, otherwise use the scrolling region. The "almost all" is a 993 * shameless hack for vi. 994 */ 995 if (n > 0) { 996 if (CS != NULL && HO != NULL && (SF != NULL || 997 ((AL == NULL || DL == NULL || 998 top > 3 || bot + 3 < __virtscr->maxy) && sf != NULL))) { 999 tputs(__tscroll(CS, top, bot + 1), 0, __cputchar); 1000 __mvcur(oy, ox, 0, 0, 1); 1001 tputs(HO, 0, __cputchar); 1002 __mvcur(0, 0, bot, 0, 1); 1003 if (SF != NULL) 1004 tputs(__tscroll(SF, n, 0), 0, __cputchar); 1005 else 1006 for (i = 0; i < n; i++) 1007 tputs(sf, 0, __cputchar); 1008 tputs(__tscroll(CS, 0, (int) __virtscr->maxy), 0, __cputchar); 1009 __mvcur(bot, 0, 0, 0, 1); 1010 tputs(HO, 0, __cputchar); 1011 __mvcur(0, 0, oy, ox, 1); 1012 return; 1013 } 1014 1015 /* Scroll up the block. */ 1016 if (SF != NULL && top == 0) { 1017 __mvcur(oy, ox, bot, 0, 1); 1018 tputs(__tscroll(SF, n, 0), 0, __cputchar); 1019 } else 1020 if (DL != NULL) { 1021 __mvcur(oy, ox, top, 0, 1); 1022 tputs(__tscroll(DL, n, 0), 0, __cputchar); 1023 } else 1024 if (dl != NULL) { 1025 __mvcur(oy, ox, top, 0, 1); 1026 for (i = 0; i < n; i++) 1027 tputs(dl, 0, __cputchar); 1028 } else 1029 if (sf != NULL && top == 0) { 1030 __mvcur(oy, ox, bot, 0, 1); 1031 for (i = 0; i < n; i++) 1032 tputs(sf, 0, __cputchar); 1033 } else 1034 abort(); 1035 1036 /* Push down the bottom region. */ 1037 __mvcur(top, 0, bot - n + 1, 0, 1); 1038 if (AL != NULL) 1039 tputs(__tscroll(AL, n, 0), 0, __cputchar); 1040 else 1041 if (al != NULL) 1042 for (i = 0; i < n; i++) 1043 tputs(al, 0, __cputchar); 1044 else 1045 abort(); 1046 __mvcur(bot - n + 1, 0, oy, ox, 1); 1047 } else { 1048 /* 1049 * !!! 1050 * n < 0 1051 * 1052 * If CS, HO and SR/sr are set, can use the scrolling region. 1053 * See the above comments for details. 1054 */ 1055 if (CS != NULL && HO != NULL && (SR != NULL || 1056 ((AL == NULL || DL == NULL || 1057 top > 3 || bot + 3 < __virtscr->maxy) && sr != NULL))) { 1058 tputs(__tscroll(CS, top, bot + 1), 0, __cputchar); 1059 __mvcur(oy, ox, 0, 0, 1); 1060 tputs(HO, 0, __cputchar); 1061 __mvcur(0, 0, top, 0, 1); 1062 1063 if (SR != NULL) 1064 tputs(__tscroll(SR, -n, 0), 0, __cputchar); 1065 else 1066 for (i = n; i < 0; i++) 1067 tputs(sr, 0, __cputchar); 1068 tputs(__tscroll(CS, 0, (int) __virtscr->maxy), 0, __cputchar); 1069 __mvcur(top, 0, 0, 0, 1); 1070 tputs(HO, 0, __cputchar); 1071 __mvcur(0, 0, oy, ox, 1); 1072 return; 1073 } 1074 1075 /* Preserve the bottom lines. */ 1076 __mvcur(oy, ox, bot + n + 1, 0, 1); 1077 if (SR != NULL && bot == __virtscr->maxy) 1078 tputs(__tscroll(SR, -n, 0), 0, __cputchar); 1079 else 1080 if (DL != NULL) 1081 tputs(__tscroll(DL, -n, 0), 0, __cputchar); 1082 else 1083 if (dl != NULL) 1084 for (i = n; i < 0; i++) 1085 tputs(dl, 0, __cputchar); 1086 else 1087 if (sr != NULL && bot == __virtscr->maxy) 1088 for (i = n; i < 0; i++) 1089 tputs(sr, 0, __cputchar); 1090 else 1091 abort(); 1092 1093 /* Scroll the block down. */ 1094 __mvcur(bot + n + 1, 0, top, 0, 1); 1095 if (AL != NULL) 1096 tputs(__tscroll(AL, -n, 0), 0, __cputchar); 1097 else 1098 if (al != NULL) 1099 for (i = n; i < 0; i++) 1100 tputs(al, 0, __cputchar); 1101 else 1102 abort(); 1103 __mvcur(top, 0, oy, ox, 1); 1104 } 1105 } 1106 1107 /* 1108 * __unsetattr -- 1109 * Unset attributes on curscr. Leave standout, attribute and colour 1110 * modes if necessary (!MS). Always leave altcharset (xterm at least 1111 * ignores a cursor move if we don't). 1112 */ 1113 void /* ARGSUSED */ 1114 __unsetattr(int checkms) 1115 { 1116 int isms; 1117 1118 if (checkms) 1119 if (!MS) { 1120 isms = 1; 1121 } else { 1122 isms = 0; 1123 } 1124 else 1125 isms = 1; 1126 #ifdef DEBUG 1127 __CTRACE("__unsetattr: checkms = %d, MS = %s, wattr = %08x\n", 1128 checkms, MS ? "TRUE" : "FALSE", curscr->wattr); 1129 #endif 1130 1131 /* 1132 * Don't leave the screen in standout mode (check against MS). Check 1133 * to see if we also turn off underscore, attributes and colour. 1134 */ 1135 if (curscr->wattr & __STANDOUT && isms) { 1136 tputs(SE, 0, __cputchar); 1137 curscr->wattr &= __mask_SE; 1138 } 1139 /* 1140 * Don't leave the screen in underscore mode (check against MS). 1141 * Check to see if we also turn off attributes. Assume that we 1142 * also turn off colour. 1143 */ 1144 if (curscr->wattr & __UNDERSCORE && isms) { 1145 tputs(UE, 0, __cputchar); 1146 curscr->wattr &= __mask_UE; 1147 } 1148 /* 1149 * Don't leave the screen with attributes set (check against MS). 1150 * Assume that also turn off colour. 1151 */ 1152 if (curscr->wattr & __TERMATTR && isms) { 1153 tputs(ME, 0, __cputchar); 1154 curscr->wattr &= __mask_ME; 1155 } 1156 /* Don't leave the screen with altcharset set (don't check MS). */ 1157 if (curscr->wattr & __ALTCHARSET) { 1158 tputs(AE, 0, __cputchar); 1159 curscr->wattr &= ~__ALTCHARSET; 1160 } 1161 /* Don't leave the screen with colour set (check against MS). */ 1162 if (curscr->wattr & __COLOR && isms) { 1163 if (OC != NULL && CC == NULL) 1164 tputs(OC, 0, __cputchar); 1165 if (OP != NULL) { 1166 tputs(OP, 0, __cputchar); 1167 curscr->wattr &= __mask_OP; 1168 } 1169 } 1170 } 1171