1 /* $NetBSD: refresh.c,v 1.49 2002/12/05 17:22:13 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.49 2002/12/05 17:22:13 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 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 && 113 wy < screen->__virtscr->maxy - win->begy; wy++) { 114 wlp = win->lines[wy]; 115 #ifdef DEBUG 116 __CTRACE("wnoutrefresh: wy %d\tf: %d\tl:%d\tflags %x\n", wy, 117 *wlp->firstchp, *wlp->lastchp, wlp->flags); 118 #endif 119 if ((wlp->flags & __ISDIRTY) == 0) 120 continue; 121 vlp = screen->__virtscr->lines[wy + win->begy]; 122 123 if (*wlp->firstchp < win->maxx + win->ch_off && 124 *wlp->lastchp >= win->ch_off) { 125 /* Copy line from "win" to "__virtscr". */ 126 for (wx = *wlp->firstchp - win->ch_off, 127 x_off = win->begx + *wlp->firstchp - win->ch_off; 128 wx <= *wlp->lastchp && wx < win->maxx && 129 x_off < screen->__virtscr->maxx; wx++, x_off++) { 130 vlp->line[x_off].attr = wlp->line[wx].attr; 131 if (wlp->line[wx].attr & __COLOR) 132 vlp->line[x_off].attr |= 133 wlp->line[wx].battr & ~__COLOR; 134 else 135 vlp->line[x_off].attr |= 136 wlp->line[wx].battr; 137 if (wlp->line[wx].ch == ' ' && 138 wlp->line[wx].bch != ' ') 139 vlp->line[x_off].ch 140 = wlp->line[wx].bch; 141 else 142 vlp->line[x_off].ch 143 = wlp->line[wx].ch; 144 } 145 146 /* Set flags on "__virtscr" and unset on "win". */ 147 if (wlp->flags & __ISPASTEOL) 148 vlp->flags |= __ISPASTEOL; 149 else 150 vlp->flags &= ~__ISPASTEOL; 151 if (wlp->flags & __ISDIRTY) 152 vlp->flags |= __ISDIRTY; 153 154 #ifdef DEBUG 155 __CTRACE("win: firstch = %d, lastch = %d\n", 156 *wlp->firstchp, *wlp->lastchp); 157 #endif 158 /* Set change pointers on "__virtscr". */ 159 if (*vlp->firstchp > 160 *wlp->firstchp + win->begx - win->ch_off) 161 *vlp->firstchp = 162 *wlp->firstchp + win->begx - win->ch_off; 163 if (*vlp->lastchp < 164 *wlp->lastchp + win->begx - win->ch_off) 165 *vlp->lastchp = 166 *wlp->lastchp + win->begx - win->ch_off; 167 #ifdef DEBUG 168 __CTRACE("__virtscr: firstch = %d, lastch = %d\n", 169 *vlp->firstchp, *vlp->lastchp); 170 #endif 171 172 /* Set change pointers on "win". */ 173 if (*wlp->firstchp >= win->ch_off) 174 *wlp->firstchp = win->maxx + win->ch_off; 175 if (*wlp->lastchp < win->maxx + win->ch_off) 176 *wlp->lastchp = win->ch_off; 177 if (*wlp->lastchp < *wlp->firstchp) { 178 #ifdef DEBUG 179 __CTRACE("wnoutrefresh: line %d notdirty\n", 180 wy); 181 #endif 182 wlp->flags &= ~__ISDIRTY; 183 } 184 } 185 } 186 187 return (OK); 188 } 189 190 /* 191 * wrefresh -- 192 * Make the current screen look like "win" over the area coverd by 193 * win. 194 */ 195 int 196 wrefresh(WINDOW *win) 197 { 198 int retval; 199 200 _cursesi_screen->curwin = (win == _cursesi_screen->curscr); 201 if (!_cursesi_screen->curwin) 202 retval = _cursesi_wnoutrefresh(_cursesi_screen, win); 203 else 204 retval = OK; 205 if (retval == OK) { 206 retval = doupdate(); 207 if (!win->flags & __LEAVEOK) { 208 win->cury = max(0, curscr->cury - win->begy); 209 win->curx = max(0, curscr->curx - win->begx); 210 } 211 } 212 _cursesi_screen->curwin = 0; 213 return(retval); 214 } 215 216 /* 217 * doupdate -- 218 * Make the current screen look like the virtual window "__virtscr". 219 */ 220 int 221 doupdate(void) 222 { 223 WINDOW *win; 224 __LINE *wlp; 225 short wy; 226 int dnum; 227 228 /* Check if we need to restart ... */ 229 if (_cursesi_screen->endwin) 230 __restartwin(); 231 232 if (_cursesi_screen->curwin) 233 win = curscr; 234 else 235 win = _cursesi_screen->__virtscr; 236 237 /* Initialize loop parameters. */ 238 _cursesi_screen->ly = curscr->cury; 239 _cursesi_screen->lx = curscr->curx; 240 wy = 0; 241 242 if (!_cursesi_screen->curwin) 243 for (wy = 0; wy < win->maxy; wy++) { 244 wlp = win->lines[wy]; 245 if (wlp->flags & __ISDIRTY) 246 wlp->hash = __hash((char *)(void *)wlp->line, 247 (size_t) (win->maxx * __LDATASIZE)); 248 } 249 250 if ((win->flags & __CLEAROK) || (curscr->flags & __CLEAROK) || 251 _cursesi_screen->curwin) { 252 if (curscr->wattr & __COLOR) 253 __unsetattr(0); 254 tputs(__tc_cl, 0, __cputchar); 255 _cursesi_screen->ly = 0; 256 _cursesi_screen->lx = 0; 257 if (!_cursesi_screen->curwin) { 258 curscr->flags &= ~__CLEAROK; 259 curscr->cury = 0; 260 curscr->curx = 0; 261 werase(curscr); 262 } 263 __touchwin(win); 264 win->flags &= ~__CLEAROK; 265 } 266 if (!__CA) { 267 if (win->curx != 0) 268 __cputchar('\n'); 269 if (!_cursesi_screen->curwin) 270 werase(curscr); 271 } 272 #ifdef DEBUG 273 __CTRACE("doupdate: (%0.2o): curwin = %d\n", win, 274 _cursesi_screen->curwin); 275 __CTRACE("doupdate: \tfirstch\tlastch\n"); 276 #endif 277 278 if (!_cursesi_screen->curwin) { 279 /* 280 * Invoke quickch() only if more than a quarter of the lines 281 * in the window are dirty. 282 */ 283 for (wy = 0, dnum = 0; wy < win->maxy; wy++) 284 if (win->lines[wy]->flags & __ISDIRTY) 285 dnum++; 286 if (!__noqch && dnum > (int) win->maxy / 4) 287 quickch(); 288 } 289 290 #ifdef DEBUG 291 { 292 int i, j; 293 294 __CTRACE("#####################################\n"); 295 for (i = 0; i < curscr->maxy; i++) { 296 __CTRACE("C: %d:", i); 297 __CTRACE(" 0x%x \n", curscr->lines[i]->hash); 298 for (j = 0; j < curscr->maxx; j++) 299 __CTRACE("%c", curscr->lines[i]->line[j].ch); 300 __CTRACE("\n"); 301 __CTRACE(" attr:"); 302 for (j = 0; j < curscr->maxx; j++) 303 __CTRACE(" %x", 304 curscr->lines[i]->line[j].attr); 305 __CTRACE("\n"); 306 __CTRACE("W: %d:", i); 307 __CTRACE(" 0x%x \n", win->lines[i]->hash); 308 __CTRACE(" 0x%x ", win->lines[i]->flags); 309 for (j = 0; j < win->maxx; j++) 310 __CTRACE("%c", win->lines[i]->line[j].ch); 311 __CTRACE("\n"); 312 __CTRACE(" attr:"); 313 for (j = 0; j < win->maxx; j++) 314 __CTRACE(" %x", 315 win->lines[i]->line[j].attr); 316 __CTRACE("\n"); 317 } 318 } 319 #endif /* DEBUG */ 320 321 for (wy = 0; wy < win->maxy; wy++) { 322 wlp = win->lines[wy]; 323 /* XXX: remove this debug */ 324 #ifdef DEBUG 325 __CTRACE("doupdate: wy %d\tf: %d\tl:%d\tflags %x\n", wy, 326 *wlp->firstchp, *wlp->lastchp, wlp->flags); 327 #endif 328 if (!_cursesi_screen->curwin) 329 curscr->lines[wy]->hash = wlp->hash; 330 if (wlp->flags & __ISDIRTY) { 331 if (makech(wy) == ERR) 332 return (ERR); 333 else { 334 if (*wlp->firstchp >= 0) 335 *wlp->firstchp = win->maxx; 336 if (*wlp->lastchp < win->maxx) 337 *wlp->lastchp = 0; 338 if (*wlp->lastchp < *wlp->firstchp) { 339 #ifdef DEBUG 340 __CTRACE("doupdate: line %d notdirty\n", wy); 341 #endif 342 wlp->flags &= ~__ISDIRTY; 343 } 344 } 345 346 } 347 #ifdef DEBUG 348 __CTRACE("\t%d\t%d\n", *wlp->firstchp, *wlp->lastchp); 349 #endif 350 } 351 352 #ifdef DEBUG 353 __CTRACE("doupdate: ly=%d, lx=%d\n", _cursesi_screen->ly, 354 _cursesi_screen->lx); 355 #endif 356 357 if (_cursesi_screen->curwin) 358 domvcur(_cursesi_screen->ly, _cursesi_screen->lx, 359 (int) win->cury, (int) win->curx); 360 else { 361 if (win->flags & __LEAVEOK) { 362 curscr->cury = _cursesi_screen->ly; 363 curscr->curx = _cursesi_screen->lx; 364 } else { 365 domvcur(_cursesi_screen->ly, _cursesi_screen->lx, 366 win->cury, win->curx); 367 curscr->cury = win->cury; 368 curscr->curx = win->curx; 369 } 370 } 371 372 /* Don't leave the screen with attributes set. */ 373 __unsetattr(0); 374 (void) fflush(_cursesi_screen->outfd); 375 return (OK); 376 } 377 378 /* 379 * makech -- 380 * Make a change on the screen. 381 */ 382 static int 383 makech(wy) 384 int wy; 385 { 386 WINDOW *win; 387 static __LDATA blank = {' ', 0, ' ', 0}; 388 __LDATA *nsp, *csp, *cp, *cep; 389 int clsp, nlsp; /* Last space in lines. */ 390 int lch, wx; 391 char *ce; 392 attr_t lspc; /* Last space colour */ 393 attr_t off, on; 394 395 #ifdef __GNUC__ 396 nlsp = lspc = 0; /* XXX gcc -Wuninitialized */ 397 #endif 398 if (_cursesi_screen->curwin) 399 win = curscr; 400 else 401 win = __virtscr; 402 /* Is the cursor still on the end of the last line? */ 403 if (wy > 0 && curscr->lines[wy - 1]->flags & __ISPASTEOL) { 404 domvcur(_cursesi_screen->ly, _cursesi_screen->lx, 405 _cursesi_screen->ly + 1, 0); 406 _cursesi_screen->ly++; 407 _cursesi_screen->lx = 0; 408 } 409 wx = *win->lines[wy]->firstchp; 410 if (wx < 0) 411 wx = 0; 412 else 413 if (wx >= win->maxx) 414 return (OK); 415 lch = *win->lines[wy]->lastchp; 416 if (lch < 0) 417 return (OK); 418 else 419 if (lch >= (int) win->maxx) 420 lch = win->maxx - 1; 421 422 if (_cursesi_screen->curwin) 423 csp = ␣ 424 else 425 csp = &curscr->lines[wy]->line[wx]; 426 427 nsp = &win->lines[wy]->line[wx]; 428 if (__tc_ce && !_cursesi_screen->curwin) { 429 cp = &win->lines[wy]->line[win->maxx - 1]; 430 lspc = cp->attr & __COLOR; 431 while (cp->ch == ' ' && cp->attr == lspc) 432 if (cp-- <= win->lines[wy]->line) 433 break; 434 nlsp = cp - win->lines[wy]->line; 435 if (nlsp < 0) 436 nlsp = 0; 437 } 438 if (!_cursesi_screen->curwin) 439 ce = __tc_ce; 440 else 441 ce = NULL; 442 443 while (wx <= lch) { 444 if (memcmp(nsp, csp, sizeof(__LDATA)) == 0) { 445 if (wx <= lch) { 446 while (wx <= lch && 447 memcmp(nsp, csp, sizeof(__LDATA)) == 0) { 448 nsp++; 449 if (!_cursesi_screen->curwin) 450 ++csp; 451 ++wx; 452 } 453 continue; 454 } 455 break; 456 } 457 domvcur(_cursesi_screen->ly, _cursesi_screen->lx, wy, wx); 458 459 #ifdef DEBUG 460 __CTRACE("makech: 1: wx = %d, ly= %d, lx = %d, newy = %d, newx = %d\n", 461 wx, _cursesi_screen->ly, _cursesi_screen->lx, wy, wx); 462 #endif 463 _cursesi_screen->ly = wy; 464 _cursesi_screen->lx = wx; 465 while (memcmp(nsp, csp, sizeof(__LDATA)) != 0 && wx <= lch) { 466 if (ce != NULL && 467 wx >= nlsp && nsp->ch == ' ' && nsp->attr == lspc) { 468 /* Check for clear to end-of-line. */ 469 cep = &curscr->lines[wy]->line[win->maxx - 1]; 470 while (cep->ch == ' ' && cep->attr == lspc) 471 if (cep-- <= csp) 472 break; 473 clsp = cep - curscr->lines[wy]->line - 474 win->begx * __LDATASIZE; 475 #ifdef DEBUG 476 __CTRACE("makech: clsp = %d, nlsp = %d\n", 477 clsp, nlsp); 478 #endif 479 if (((clsp - nlsp >= strlen(__tc_ce) && 480 clsp < win->maxx * __LDATASIZE) || 481 wy == win->maxy - 1) && 482 (!(lspc & __COLOR) || 483 ((lspc & __COLOR) && __tc_ut))) { 484 __unsetattr(0); 485 if (__using_color && 486 ((lspc & __COLOR) != 487 (curscr->wattr & __COLOR))) 488 __set_color(curscr, lspc & 489 __COLOR); 490 tputs(__tc_ce, 0, __cputchar); 491 _cursesi_screen->lx = wx + win->begx; 492 while (wx++ <= clsp) { 493 csp->ch = ' '; 494 csp->attr = lspc; 495 csp++; 496 } 497 return (OK); 498 } 499 ce = NULL; 500 } 501 502 #ifdef DEBUG 503 __CTRACE("makech: have attributes %08x, need attributes %08x\n", curscr->wattr, nsp->attr); 504 #endif 505 506 off = ~nsp->attr & curscr->wattr; 507 508 /* 509 * Unset attributes as appropriate. Unset first 510 * so that the relevant attributes can be reset 511 * (because 'me' unsets 'mb', 'md', 'mh', 'mk', 512 * 'mp' and 'mr'). Check to see if we also turn off 513 * standout, attributes and colour. 514 */ 515 if (off & __TERMATTR && __tc_me != NULL) { 516 tputs(__tc_me, 0, __cputchar); 517 curscr->wattr &= __mask_me; 518 off &= __mask_me; 519 } 520 521 /* 522 * Exit underscore mode if appropriate. 523 * Check to see if we also turn off standout, 524 * attributes and colour. 525 */ 526 if (off & __UNDERSCORE && __tc_ue != NULL) { 527 tputs(__tc_ue, 0, __cputchar); 528 curscr->wattr &= __mask_ue; 529 off &= __mask_ue; 530 } 531 532 /* 533 * Exit standout mode as appropriate. 534 * Check to see if we also turn off underscore, 535 * attributes and colour. 536 * XXX 537 * Should use uc if so/se not available. 538 */ 539 if (off & __STANDOUT && __tc_se != NULL) { 540 tputs(__tc_se, 0, __cputchar); 541 curscr->wattr &= __mask_se; 542 off &= __mask_se; 543 } 544 545 if (off & __ALTCHARSET && __tc_ae != NULL) { 546 tputs(__tc_ae, 0, __cputchar); 547 curscr->wattr &= ~__ALTCHARSET; 548 } 549 550 /* Set/change colour as appropriate. */ 551 if (__using_color) 552 __set_color(curscr, nsp->attr & __COLOR); 553 554 on = nsp->attr & ~curscr->wattr; 555 556 /* 557 * Enter standout mode if appropriate. 558 */ 559 if (on & __STANDOUT && __tc_so != NULL && __tc_se 560 != NULL) { 561 tputs(__tc_so, 0, __cputchar); 562 curscr->wattr |= __STANDOUT; 563 } 564 565 /* 566 * Enter underscore mode if appropriate. 567 * XXX 568 * Should use uc if us/ue not available. 569 */ 570 if (on & __UNDERSCORE && __tc_us != NULL && 571 __tc_ue != NULL) { 572 tputs(__tc_us, 0, __cputchar); 573 curscr->wattr |= __UNDERSCORE; 574 } 575 576 /* 577 * Set other attributes as appropriate. 578 */ 579 if (__tc_me != NULL) { 580 if (on & __BLINK && __tc_mb != NULL) { 581 tputs(__tc_mb, 0, __cputchar); 582 curscr->wattr |= __BLINK; 583 } 584 if (on & __BOLD && __tc_md != NULL) { 585 tputs(__tc_md, 0, __cputchar); 586 curscr->wattr |= __BOLD; 587 } 588 if (on & __DIM && __tc_mh != NULL) { 589 tputs(__tc_mh, 0, __cputchar); 590 curscr->wattr |= __DIM; 591 } 592 if (on & __BLANK && __tc_mk != NULL) { 593 tputs(__tc_mk, 0, __cputchar); 594 curscr->wattr |= __BLANK; 595 } 596 if (on & __PROTECT && __tc_mp != NULL) { 597 tputs(__tc_mp, 0, __cputchar); 598 curscr->wattr |= __PROTECT; 599 } 600 if (on & __REVERSE && __tc_mr != NULL) { 601 tputs(__tc_mr, 0, __cputchar); 602 curscr->wattr |= __REVERSE; 603 } 604 } 605 606 /* Enter/exit altcharset mode as appropriate. */ 607 if (on & __ALTCHARSET && __tc_as != NULL && 608 __tc_ae != NULL) { 609 tputs(__tc_as, 0, __cputchar); 610 curscr->wattr |= __ALTCHARSET; 611 } 612 613 wx++; 614 if (wx >= win->maxx && 615 wy == win->maxy - 1 && !_cursesi_screen->curwin) 616 if (win->flags & __SCROLLOK) { 617 if (win->flags & __ENDLINE) 618 __unsetattr(1); 619 if (!(win->flags & __SCROLLWIN)) { 620 if (!_cursesi_screen->curwin) { 621 csp->attr = nsp->attr; 622 __cputchar((int) 623 (csp->ch = 624 nsp->ch)); 625 } else 626 __cputchar((int) nsp->ch); 627 } 628 if (wx < curscr->maxx) { 629 domvcur(_cursesi_screen->ly, wx, 630 (int) (win->maxy - 1), 631 (int) (win->maxx - 1)); 632 } 633 _cursesi_screen->ly = win->maxy - 1; 634 _cursesi_screen->lx = win->maxx - 1; 635 return (OK); 636 } 637 if (wx < win->maxx || wy < win->maxy - 1 || 638 !(win->flags & __SCROLLWIN)) { 639 if (!_cursesi_screen->curwin) { 640 csp->attr = nsp->attr; 641 __cputchar((int) (csp->ch = nsp->ch)); 642 csp++; 643 } else 644 __cputchar((int) nsp->ch); 645 } 646 #ifdef DEBUG 647 __CTRACE("makech: putchar(%c)\n", nsp->ch & 0177); 648 #endif 649 if (__tc_uc && ((nsp->attr & __STANDOUT) || 650 (nsp->attr & __UNDERSCORE))) { 651 __cputchar('\b'); 652 tputs(__tc_uc, 0, __cputchar); 653 } 654 nsp++; 655 #ifdef DEBUG 656 __CTRACE("makech: 2: wx = %d, lx = %d\n", wx, _cursesi_screen->lx); 657 #endif 658 } 659 if (_cursesi_screen->lx == wx) /* If no change. */ 660 break; 661 _cursesi_screen->lx = wx; 662 if (_cursesi_screen->lx >= COLS && __tc_am) 663 _cursesi_screen->lx = COLS - 1; 664 else 665 if (wx >= win->maxx) { 666 domvcur(_cursesi_screen->ly, 667 _cursesi_screen->lx, 668 _cursesi_screen->ly, 669 (int) (win->maxx - 1)); 670 _cursesi_screen->lx = win->maxx - 1; 671 } 672 #ifdef DEBUG 673 __CTRACE("makech: 3: wx = %d, lx = %d\n", wx, 674 _cursesi_screen->lx); 675 #endif 676 } 677 678 return (OK); 679 } 680 681 /* 682 * domvcur -- 683 * Do a mvcur, leaving attributes if necessary. 684 */ 685 static void 686 domvcur(oy, ox, ny, nx) 687 int oy, ox, ny, nx; 688 { 689 __unsetattr(1); 690 __mvcur(oy, ox, ny, nx, 1); 691 } 692 693 /* 694 * Quickch() attempts to detect a pattern in the change of the window 695 * in order to optimize the change, e.g., scroll n lines as opposed to 696 * repainting the screen line by line. 697 */ 698 699 static __LDATA buf[128]; 700 static u_int last_hash; 701 static size_t last_hash_len; 702 #define BLANKSIZE (sizeof(buf) / sizeof(buf[0])) 703 704 static void 705 quickch(void) 706 { 707 #define THRESH (int) __virtscr->maxy / 4 708 709 __LINE *clp, *tmp1, *tmp2; 710 int bsize, curs, curw, starts, startw, i, j; 711 int n, target, cur_period, bot, top, sc_region; 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 if (buf[0].ch != ' ') { 856 for (i = 0; i < BLANKSIZE; i++) { 857 buf[i].ch = ' '; 858 buf[i].bch = ' '; 859 buf[i].attr = 0; 860 buf[i].battr = 0; 861 } 862 } 863 864 if (__virtscr->maxx != last_hash_len) { 865 blank_hash = 0; 866 for (i = __virtscr->maxx; i > BLANKSIZE; i -= BLANKSIZE) { 867 blank_hash = __hash_more((char *)(void *)buf, sizeof(buf), 868 blank_hash); 869 } 870 blank_hash = __hash_more((char *)(void *)buf, 871 i * sizeof(buf[0]), blank_hash); 872 /* cache result in static data - screen width doesn't change often */ 873 last_hash_len = __virtscr->maxx; 874 last_hash = blank_hash; 875 } else 876 blank_hash = last_hash; 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 clp->line + 1, (__virtscr->maxx - 1) * 935 __LDATASIZE) || memcmp(clp->line, buf, 936 __LDATASIZE)) { 937 for (i = __virtscr->maxx; i > BLANKSIZE; 938 i -= BLANKSIZE) { 939 (void)memcpy(clp->line + i - 940 BLANKSIZE, buf, sizeof(buf)); 941 } 942 (void)memcpy(clp->line , buf, i * 943 sizeof(buf[0])); 944 #ifdef DEBUG 945 __CTRACE("-- blanked out: dirty\n"); 946 #endif 947 clp->hash = blank_hash; 948 __touchline(__virtscr, target, 0, (int) __virtscr->maxx - 1); 949 } else { 950 #ifdef DEBUG 951 __CTRACE(" -- blank line already: dirty\n"); 952 #endif 953 __touchline(__virtscr, target, 0, (int) __virtscr->maxx - 1); 954 } 955 } else { 956 #ifdef DEBUG 957 __CTRACE(" -- dirty\n"); 958 #endif 959 __touchline(__virtscr, target, 0, (int) __virtscr->maxx - 1); 960 } 961 if (target == cur_period) { 962 i = target + 1; 963 tmp1 = curscr->lines[i]; 964 cur_period = i; 965 } else { 966 tmp1 = tmp2; 967 i = target; 968 } 969 } 970 #ifdef DEBUG 971 __CTRACE("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n"); 972 for (i = 0; i < curscr->maxy; i++) { 973 __CTRACE("C: %d:", i); 974 for (j = 0; j < curscr->maxx; j++) 975 __CTRACE("%c", curscr->lines[i]->line[j].ch); 976 __CTRACE("\n"); 977 __CTRACE("W: %d:", i); 978 for (j = 0; j < __virtscr->maxx; j++) 979 __CTRACE("%c", __virtscr->lines[i]->line[j].ch); 980 __CTRACE("\n"); 981 } 982 #endif 983 if (n != 0) 984 scrolln(starts, startw, curs, bot, top); 985 } 986 987 /* 988 * scrolln -- 989 * Scroll n lines, where n is starts - startw. 990 */ 991 static void /* ARGSUSED */ 992 scrolln(starts, startw, curs, bot, top) 993 int starts, startw, curs, bot, top; 994 { 995 int i, oy, ox, n; 996 997 oy = curscr->cury; 998 ox = curscr->curx; 999 n = starts - startw; 1000 1001 /* 1002 * XXX 1003 * The initial tests that set __noqch don't let us reach here unless 1004 * we have either cs + ho + SF/sf/SR/sr, or AL + DL. SF/sf and SR/sr 1005 * scrolling can only shift the entire scrolling region, not just a 1006 * part of it, which means that the quickch() routine is going to be 1007 * sadly disappointed in us if we don't have cs as well. 1008 * 1009 * If cs, ho and SF/sf are set, can use the scrolling region. Because 1010 * the cursor position after cs is undefined, we need ho which gives us 1011 * the ability to move to somewhere without knowledge of the current 1012 * location of the cursor. Still call __mvcur() anyway, to update its 1013 * idea of where the cursor is. 1014 * 1015 * When the scrolling region has been set, the cursor has to be at the 1016 * last line of the region to make the scroll happen. 1017 * 1018 * Doing SF/SR or AL/DL appears faster on the screen than either sf/sr 1019 * or AL/DL, and, some terminals have AL/DL, sf/sr, and cs, but not 1020 * SF/SR. So, if we're scrolling almost all of the screen, try and use 1021 * AL/DL, otherwise use the scrolling region. The "almost all" is a 1022 * shameless hack for vi. 1023 */ 1024 if (n > 0) { 1025 if (__tc_cs != NULL && __tc_ho != NULL && (__tc_SF != NULL || 1026 ((__tc_AL == NULL || __tc_DL == NULL || 1027 top > 3 || bot + 3 < __virtscr->maxy) && 1028 __tc_sf != NULL))) { 1029 tputs(__tscroll(__tc_cs, top, bot + 1), 0, __cputchar); 1030 __mvcur(oy, ox, 0, 0, 1); 1031 tputs(__tc_ho, 0, __cputchar); 1032 __mvcur(0, 0, bot, 0, 1); 1033 if (__tc_SF != NULL) 1034 tputs(__tscroll(__tc_SF, n, 0), 0, __cputchar); 1035 else 1036 for (i = 0; i < n; i++) 1037 tputs(__tc_sf, 0, __cputchar); 1038 tputs(__tscroll(__tc_cs, 0, (int) __virtscr->maxy), 0, 1039 __cputchar); 1040 __mvcur(bot, 0, 0, 0, 1); 1041 tputs(__tc_ho, 0, __cputchar); 1042 __mvcur(0, 0, oy, ox, 1); 1043 return; 1044 } 1045 1046 /* Scroll up the block. */ 1047 if (__tc_SF != NULL && top == 0) { 1048 __mvcur(oy, ox, bot, 0, 1); 1049 tputs(__tscroll(__tc_SF, n, 0), 0, __cputchar); 1050 } else 1051 if (__tc_DL != NULL) { 1052 __mvcur(oy, ox, top, 0, 1); 1053 tputs(__tscroll(__tc_DL, n, 0), 0, __cputchar); 1054 } else 1055 if (__tc_dl != NULL) { 1056 __mvcur(oy, ox, top, 0, 1); 1057 for (i = 0; i < n; i++) 1058 tputs(__tc_dl, 0, __cputchar); 1059 } else 1060 if (__tc_sf != NULL && top == 0) { 1061 __mvcur(oy, ox, bot, 0, 1); 1062 for (i = 0; i < n; i++) 1063 tputs(__tc_sf, 0, 1064 __cputchar); 1065 } else 1066 abort(); 1067 1068 /* Push down the bottom region. */ 1069 __mvcur(top, 0, bot - n + 1, 0, 1); 1070 if (__tc_AL != NULL) 1071 tputs(__tscroll(__tc_AL, n, 0), 0, __cputchar); 1072 else 1073 if (__tc_al != NULL) 1074 for (i = 0; i < n; i++) 1075 tputs(__tc_al, 0, __cputchar); 1076 else 1077 abort(); 1078 __mvcur(bot - n + 1, 0, oy, ox, 1); 1079 } else { 1080 /* 1081 * !!! 1082 * n < 0 1083 * 1084 * If cs, ho and SR/sr are set, can use the scrolling region. 1085 * See the above comments for details. 1086 */ 1087 if (__tc_cs != NULL && __tc_ho != NULL && (__tc_SR != NULL || 1088 ((__tc_AL == NULL || __tc_DL == NULL || top > 3 || 1089 bot + 3 < __virtscr->maxy) && __tc_sr != NULL))) { 1090 tputs(__tscroll(__tc_cs, top, bot + 1), 0, __cputchar); 1091 __mvcur(oy, ox, 0, 0, 1); 1092 tputs(__tc_ho, 0, __cputchar); 1093 __mvcur(0, 0, top, 0, 1); 1094 1095 if (__tc_SR != NULL) 1096 tputs(__tscroll(__tc_SR, -n, 0), 0, __cputchar); 1097 else 1098 for (i = n; i < 0; i++) 1099 tputs(__tc_sr, 0, __cputchar); 1100 tputs(__tscroll(__tc_cs, 0, (int) __virtscr->maxy), 0, 1101 __cputchar); 1102 __mvcur(top, 0, 0, 0, 1); 1103 tputs(__tc_ho, 0, __cputchar); 1104 __mvcur(0, 0, oy, ox, 1); 1105 return; 1106 } 1107 1108 /* Preserve the bottom lines. */ 1109 __mvcur(oy, ox, bot + n + 1, 0, 1); 1110 if (__tc_SR != NULL && bot == __virtscr->maxy) 1111 tputs(__tscroll(__tc_SR, -n, 0), 0, __cputchar); 1112 else 1113 if (__tc_DL != NULL) 1114 tputs(__tscroll(__tc_DL, -n, 0), 0, __cputchar); 1115 else 1116 if (__tc_dl != NULL) 1117 for (i = n; i < 0; i++) 1118 tputs(__tc_dl, 0, __cputchar); 1119 else 1120 if (__tc_sr != NULL && 1121 bot == __virtscr->maxy) 1122 for (i = n; i < 0; i++) 1123 tputs(__tc_sr, 0, 1124 __cputchar); 1125 else 1126 abort(); 1127 1128 /* Scroll the block down. */ 1129 __mvcur(bot + n + 1, 0, top, 0, 1); 1130 if (__tc_AL != NULL) 1131 tputs(__tscroll(__tc_AL, -n, 0), 0, __cputchar); 1132 else 1133 if (__tc_al != NULL) 1134 for (i = n; i < 0; i++) 1135 tputs(__tc_al, 0, __cputchar); 1136 else 1137 abort(); 1138 __mvcur(top, 0, oy, ox, 1); 1139 } 1140 } 1141 1142 /* 1143 * __unsetattr -- 1144 * Unset attributes on curscr. Leave standout, attribute and colour 1145 * modes if necessary (!ms). Always leave altcharset (xterm at least 1146 * ignores a cursor move if we don't). 1147 */ 1148 void /* ARGSUSED */ 1149 __unsetattr(int checkms) 1150 { 1151 int isms; 1152 1153 if (checkms) 1154 if (!__tc_ms) { 1155 isms = 1; 1156 } else { 1157 isms = 0; 1158 } 1159 else 1160 isms = 1; 1161 #ifdef DEBUG 1162 __CTRACE("__unsetattr: checkms = %d, ms = %s, wattr = %08x\n", 1163 checkms, __tc_ms ? "TRUE" : "FALSE", curscr->wattr); 1164 #endif 1165 1166 /* 1167 * Don't leave the screen in standout mode (check against ms). Check 1168 * to see if we also turn off underscore, attributes and colour. 1169 */ 1170 if (curscr->wattr & __STANDOUT && isms) { 1171 tputs(__tc_se, 0, __cputchar); 1172 curscr->wattr &= __mask_se; 1173 } 1174 /* 1175 * Don't leave the screen in underscore mode (check against ms). 1176 * Check to see if we also turn off attributes. Assume that we 1177 * also turn off colour. 1178 */ 1179 if (curscr->wattr & __UNDERSCORE && isms) { 1180 tputs(__tc_ue, 0, __cputchar); 1181 curscr->wattr &= __mask_ue; 1182 } 1183 /* 1184 * Don't leave the screen with attributes set (check against ms). 1185 * Assume that also turn off colour. 1186 */ 1187 if (curscr->wattr & __TERMATTR && isms) { 1188 tputs(__tc_me, 0, __cputchar); 1189 curscr->wattr &= __mask_me; 1190 } 1191 /* Don't leave the screen with altcharset set (don't check ms). */ 1192 if (curscr->wattr & __ALTCHARSET) { 1193 tputs(__tc_ae, 0, __cputchar); 1194 curscr->wattr &= ~__ALTCHARSET; 1195 } 1196 /* Don't leave the screen with colour set (check against ms). */ 1197 if (__using_color && isms) 1198 __unset_color(curscr); 1199 } 1200