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