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