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