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