1 /* 2 * Copyright (c) 1981, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 static char sccsid[] = "@(#)refresh.c 8.2 (Berkeley) 05/04/94"; 10 #endif /* not lint */ 11 12 #include <string.h> 13 14 #include "curses.h" 15 16 static int curwin; 17 static short ly, lx; 18 19 static void domvcur __P((int, int, int, int)); 20 static int makech __P((WINDOW *, int)); 21 static void quickch __P((WINDOW *)); 22 static void scrolln __P((WINDOW *, int, int, int, int, int)); 23 24 /* 25 * wrefresh -- 26 * Make the current screen look like "win" over the area coverd by 27 * win. 28 */ 29 int 30 wrefresh(win) 31 register WINDOW *win; 32 { 33 register __LINE *wlp; 34 register int retval; 35 register short wy; 36 int dnum; 37 38 /* Initialize loop parameters. */ 39 ly = curscr->cury; 40 lx = curscr->curx; 41 wy = 0; 42 curwin = (win == curscr); 43 44 if (!curwin) 45 for (wy = 0; wy < win->maxy; wy++) { 46 wlp = win->lines[wy]; 47 if (wlp->flags & __ISDIRTY) 48 wlp->hash = 49 __hash((char *) wlp->line, win->maxx * __LDATASIZE); 50 } 51 52 if (win->flags & __CLEAROK || curscr->flags & __CLEAROK || curwin) { 53 if ((win->flags & __FULLWIN) || curscr->flags & __CLEAROK) { 54 tputs(CL, 0, __cputchar); 55 ly = 0; 56 lx = 0; 57 if (!curwin) { 58 curscr->flags &= ~__CLEAROK; 59 curscr->cury = 0; 60 curscr->curx = 0; 61 werase(curscr); 62 } 63 __touchwin(win); 64 } 65 win->flags &= ~__CLEAROK; 66 } 67 if (!CA) { 68 if (win->curx != 0) 69 putchar('\n'); 70 if (!curwin) 71 werase(curscr); 72 } 73 #ifdef DEBUG 74 __CTRACE("wrefresh: (%0.2o): curwin = %d\n", win, curwin); 75 __CTRACE("wrefresh: \tfirstch\tlastch\n"); 76 #endif 77 78 #ifndef NOQCH 79 if ((win->flags & __FULLWIN) && !curwin) { 80 /* 81 * Invoke quickch() only if more than a quarter of the lines 82 * in the window are dirty. 83 */ 84 for (wy = 0, dnum = 0; wy < win->maxy; wy++) 85 if (win->lines[wy]->flags & (__ISDIRTY | __FORCEPAINT)) 86 dnum++; 87 if (!__noqch && dnum > (int) win->maxy / 4) 88 quickch(win); 89 } 90 #endif 91 92 #ifdef DEBUG 93 { int i, j; 94 __CTRACE("#####################################\n"); 95 for (i = 0; i < curscr->maxy; i++) { 96 __CTRACE("C: %d:", i); 97 __CTRACE(" 0x%x \n", curscr->lines[i]->hash); 98 for (j = 0; j < curscr->maxx; j++) 99 __CTRACE("%c", 100 curscr->lines[i]->line[j].ch); 101 __CTRACE("\n"); 102 for (j = 0; j < curscr->maxx; j++) 103 __CTRACE("%x", 104 curscr->lines[i]->line[j].attr); 105 __CTRACE("\n"); 106 __CTRACE("W: %d:", i); 107 __CTRACE(" 0x%x \n", win->lines[i]->hash); 108 __CTRACE(" 0x%x ", win->lines[i]->flags); 109 for (j = 0; j < win->maxx; j++) 110 __CTRACE("%c", 111 win->lines[i]->line[j].ch); 112 __CTRACE("\n"); 113 for (j = 0; j < win->maxx; j++) 114 __CTRACE("%x", 115 win->lines[i]->line[j].attr); 116 __CTRACE("\n"); 117 } 118 } 119 #endif /* DEBUG */ 120 121 for (wy = 0; wy < win->maxy; wy++) { 122 #ifdef DEBUG 123 __CTRACE("%d\t%d\t%d\n", 124 wy, *win->lines[wy]->firstchp, *win->lines[wy]->lastchp); 125 #endif 126 if (!curwin) 127 curscr->lines[wy]->hash = win->lines[wy]->hash; 128 if (win->lines[wy]->flags & (__ISDIRTY | __FORCEPAINT)) { 129 if (makech(win, wy) == ERR) 130 return (ERR); 131 else { 132 if (*win->lines[wy]->firstchp >= win->ch_off) 133 *win->lines[wy]->firstchp = win->maxx + 134 win->ch_off; 135 if (*win->lines[wy]->lastchp < win->maxx + 136 win->ch_off) 137 *win->lines[wy]->lastchp = win->ch_off; 138 if (*win->lines[wy]->lastchp < 139 *win->lines[wy]->firstchp) { 140 #ifdef DEBUG 141 __CTRACE("wrefresh: line %d notdirty \n", wy); 142 #endif 143 win->lines[wy]->flags &= ~__ISDIRTY; 144 } 145 } 146 147 } 148 #ifdef DEBUG 149 __CTRACE("\t%d\t%d\n", *win->lines[wy]->firstchp, 150 *win->lines[wy]->lastchp); 151 #endif 152 } 153 154 #ifdef DEBUG 155 __CTRACE("refresh: ly=%d, lx=%d\n", ly, lx); 156 #endif 157 158 if (win == curscr) 159 domvcur(ly, lx, win->cury, win->curx); 160 else { 161 if (win->flags & __LEAVEOK) { 162 curscr->cury = ly; 163 curscr->curx = lx; 164 ly -= win->begy; 165 lx -= win->begx; 166 if (ly >= 0 && ly < win->maxy && lx >= 0 && 167 lx < win->maxx) { 168 win->cury = ly; 169 win->curx = lx; 170 } else 171 win->cury = win->curx = 0; 172 } else { 173 domvcur(ly, lx, win->cury + win->begy, 174 win->curx + win->begx); 175 curscr->cury = win->cury + win->begy; 176 curscr->curx = win->curx + win->begx; 177 } 178 } 179 retval = OK; 180 181 (void)fflush(stdout); 182 return (retval); 183 } 184 185 /* 186 * makech -- 187 * Make a change on the screen. 188 */ 189 static int 190 makech(win, wy) 191 register WINDOW *win; 192 int wy; 193 { 194 static __LDATA blank = {' ', 0}; 195 register int nlsp, clsp; /* Last space in lines. */ 196 register int wx, lch, y; 197 register __LDATA *nsp, *csp, *cp, *cep; 198 u_int force; 199 char *ce; 200 201 /* Is the cursor still on the end of the last line? */ 202 if (wy > 0 && win->lines[wy - 1]->flags & __ISPASTEOL) { 203 domvcur(ly, lx, ly + 1, 0); 204 ly++; 205 lx = 0; 206 } 207 wx = *win->lines[wy]->firstchp - win->ch_off; 208 if (wx < 0) 209 wx = 0; 210 else if (wx >= win->maxx) 211 return (OK); 212 lch = *win->lines[wy]->lastchp - win->ch_off; 213 if (lch < 0) 214 return (OK); 215 else if (lch >= (int) win->maxx) 216 lch = win->maxx - 1; 217 y = wy + win->begy; 218 219 if (curwin) 220 csp = ␣ 221 else 222 csp = &curscr->lines[wy + win->begy]->line[wx + win->begx]; 223 224 nsp = &win->lines[wy]->line[wx]; 225 force = win->lines[wy]->flags & __FORCEPAINT; 226 win->lines[wy]->flags &= ~__FORCEPAINT; 227 if (CE && !curwin) { 228 for (cp = &win->lines[wy]->line[win->maxx - 1]; 229 cp->ch == ' ' && cp->attr == 0; cp--) 230 if (cp <= win->lines[wy]->line) 231 break; 232 nlsp = cp - win->lines[wy]->line; 233 } 234 if (!curwin) 235 ce = CE; 236 else 237 ce = NULL; 238 239 if (force) { 240 if (CM) 241 tputs(tgoto(CM, lx, ly), 0, __cputchar); 242 else { 243 tputs(HO, 0, __cputchar); 244 __mvcur(0, 0, ly, lx, 1); 245 } 246 } 247 while (wx <= lch) { 248 if (!force && memcmp(nsp, csp, sizeof(__LDATA)) == 0) { 249 if (wx <= lch) { 250 while (wx <= lch && 251 memcmp(nsp, csp, sizeof(__LDATA)) == 0) { 252 nsp++; 253 if (!curwin) 254 csp++; 255 ++wx; 256 } 257 continue; 258 } 259 break; 260 } 261 domvcur(ly, lx, y, wx + win->begx); 262 263 #ifdef DEBUG 264 __CTRACE("makech: 1: wx = %d, ly= %d, lx = %d, newy = %d, newx = %d, force =%d\n", 265 wx, ly, lx, y, wx + win->begx, force); 266 #endif 267 ly = y; 268 lx = wx + win->begx; 269 while ((force || memcmp(nsp, csp, sizeof(__LDATA)) != 0) 270 && wx <= lch) { 271 272 if (ce != NULL && win->maxx + win->begx == 273 curscr->maxx && wx >= nlsp && nsp->ch == ' ') { 274 /* Check for clear to end-of-line. */ 275 cep = &curscr->lines[wy]->line[win->maxx - 1]; 276 while (cep->ch == ' ' && cep->attr == 0) 277 if (cep-- <= csp) 278 break; 279 clsp = cep - curscr->lines[wy]->line - 280 win->begx * __LDATASIZE; 281 #ifdef DEBUG 282 __CTRACE("makech: clsp = %d, nlsp = %d\n", clsp, nlsp); 283 #endif 284 if ((clsp - nlsp >= strlen(CE) 285 && clsp < win->maxx * __LDATASIZE) || 286 wy == win->maxy - 1) { 287 #ifdef DEBUG 288 __CTRACE("makech: using CE\n"); 289 #endif 290 tputs(CE, 0, __cputchar); 291 lx = wx + win->begx; 292 while (wx++ <= clsp) { 293 csp->ch = ' '; 294 csp->attr = 0; 295 csp++; 296 } 297 return (OK); 298 } 299 ce = NULL; 300 } 301 302 /* Enter/exit standout mode as appropriate. */ 303 if (SO && (nsp->attr & __STANDOUT) != 304 (curscr->flags & __WSTANDOUT)) { 305 if (nsp->attr & __STANDOUT) { 306 tputs(SO, 0, __cputchar); 307 curscr->flags |= __WSTANDOUT; 308 } else { 309 tputs(SE, 0, __cputchar); 310 curscr->flags &= ~__WSTANDOUT; 311 } 312 } 313 314 wx++; 315 if (wx >= win->maxx && wy == win->maxy - 1 && !curwin) 316 if (win->flags & __SCROLLOK) { 317 if (curscr->flags & __WSTANDOUT 318 && win->flags & __ENDLINE) 319 if (!MS) { 320 tputs(SE, 0, 321 __cputchar); 322 curscr->flags &= 323 ~__WSTANDOUT; 324 } 325 if (!(win->flags & __SCROLLWIN)) { 326 if (!curwin) { 327 csp->attr = nsp->attr; 328 putchar(csp->ch = nsp->ch); 329 } else 330 putchar(nsp->ch); 331 } 332 if (wx + win->begx < curscr->maxx) { 333 domvcur(ly, wx + win->begx, 334 win->begy + win->maxy - 1, 335 win->begx + win->maxx - 1); 336 } 337 ly = win->begy + win->maxy - 1; 338 lx = win->begx + win->maxx - 1; 339 return (OK); 340 } 341 if (wx < win->maxx || wy < win->maxy - 1 || 342 !(win->flags & __SCROLLWIN)) { 343 if (!curwin) { 344 csp->attr = nsp->attr; 345 putchar(csp->ch = nsp->ch); 346 csp++; 347 } else 348 putchar(nsp->ch); 349 } 350 #ifdef DEBUG 351 __CTRACE("makech: putchar(%c)\n", nsp->ch & 0177); 352 #endif 353 if (UC && (nsp->attr & __STANDOUT)) { 354 putchar('\b'); 355 tputs(UC, 0, __cputchar); 356 } 357 nsp++; 358 #ifdef DEBUG 359 __CTRACE("makech: 2: wx = %d, lx = %d\n", wx, lx); 360 #endif 361 } 362 if (lx == wx + win->begx) /* If no change. */ 363 break; 364 lx = wx + win->begx; 365 if (lx >= COLS && AM) 366 lx = COLS - 1; 367 else if (wx >= win->maxx) { 368 domvcur(ly, lx, ly, win->maxx + win->begx - 1); 369 lx = win->maxx + win->begx - 1; 370 } 371 372 #ifdef DEBUG 373 __CTRACE("makech: 3: wx = %d, lx = %d\n", wx, lx); 374 #endif 375 } 376 return (OK); 377 } 378 379 /* 380 * domvcur -- 381 * Do a mvcur, leaving standout mode if necessary. 382 */ 383 static void 384 domvcur(oy, ox, ny, nx) 385 int oy, ox, ny, nx; 386 { 387 if (curscr->flags & __WSTANDOUT && !MS) { 388 tputs(SE, 0, __cputchar); 389 curscr->flags &= ~__WSTANDOUT; 390 } 391 392 __mvcur(oy, ox, ny, nx, 1); 393 } 394 395 /* 396 * Quickch() attempts to detect a pattern in the change of the window 397 * in order to optimize the change, e.g., scroll n lines as opposed to 398 * repainting the screen line by line. 399 */ 400 401 static void 402 quickch(win) 403 WINDOW *win; 404 { 405 #define THRESH (int) win->maxy / 4 406 407 register __LINE *clp, *tmp1, *tmp2; 408 register int bsize, curs, curw, starts, startw, i, j; 409 int n, target, cur_period, bot, top, sc_region; 410 __LDATA buf[1024]; 411 u_int blank_hash; 412 413 /* 414 * Find how many lines from the top of the screen are unchanged. 415 */ 416 for (top = 0; top < win->maxy; top++) 417 if (win->lines[top]->flags & __FORCEPAINT || 418 win->lines[top]->hash != curscr->lines[top]->hash 419 || memcmp(win->lines[top]->line, 420 curscr->lines[top]->line, 421 win->maxx * __LDATASIZE) != 0) 422 break; 423 else 424 win->lines[top]->flags &= ~__ISDIRTY; 425 /* 426 * Find how many lines from bottom of screen are unchanged. 427 */ 428 for (bot = win->maxy - 1; bot >= 0; bot--) 429 if (win->lines[bot]->flags & __FORCEPAINT || 430 win->lines[bot]->hash != curscr->lines[bot]->hash 431 || memcmp(win->lines[bot]->line, 432 curscr->lines[bot]->line, 433 win->maxx * __LDATASIZE) != 0) 434 break; 435 else 436 win->lines[bot]->flags &= ~__ISDIRTY; 437 438 #ifdef NO_JERKINESS 439 /* 440 * If we have a bottom unchanged region return. Scrolling the 441 * bottom region up and then back down causes a screen jitter. 442 * This will increase the number of characters sent to the screen 443 * but it looks better. 444 */ 445 if (bot < win->maxy - 1) 446 return; 447 #endif /* NO_JERKINESS */ 448 449 /* 450 * Search for the largest block of text not changed. 451 * Invariants of the loop: 452 * - Startw is the index of the beginning of the examined block in win. 453 * - Starts is the index of the beginning of the examined block in 454 * curscr. 455 * - Curs is the index of one past the end of the exmined block in win. 456 * - Curw is the index of one past the end of the exmined block in 457 * curscr. 458 * - bsize is the current size of the examined block. 459 */ 460 for (bsize = bot - top; bsize >= THRESH; bsize--) { 461 for (startw = top; startw <= bot - bsize; startw++) 462 for (starts = top; starts <= bot - bsize; 463 starts++) { 464 for (curw = startw, curs = starts; 465 curs < starts + bsize; curw++, curs++) 466 if (win->lines[curw]->flags & 467 __FORCEPAINT || 468 (win->lines[curw]->hash != 469 curscr->lines[curs]->hash || 470 memcmp(win->lines[curw]->line, 471 curscr->lines[curs]->line, 472 win->maxx * __LDATASIZE) != 0)) 473 break; 474 if (curs == starts + bsize) 475 goto done; 476 } 477 } 478 done: 479 /* Did not find anything */ 480 if (bsize < THRESH) 481 return; 482 483 #ifdef DEBUG 484 __CTRACE("quickch:bsize=%d,starts=%d,startw=%d,curw=%d,curs=%d,top=%d,bot=%d\n", 485 bsize, starts, startw, curw, curs, top, bot); 486 #endif 487 488 /* 489 * Make sure that there is no overlap between the bottom and top 490 * regions and the middle scrolled block. 491 */ 492 if (bot < curs) 493 bot = curs - 1; 494 if (top > starts) 495 top = starts; 496 497 n = startw - starts; 498 499 #ifdef DEBUG 500 __CTRACE("#####################################\n"); 501 for (i = 0; i < curscr->maxy; i++) { 502 __CTRACE("C: %d:", i); 503 __CTRACE(" 0x%x \n", curscr->lines[i]->hash); 504 for (j = 0; j < curscr->maxx; j++) 505 __CTRACE("%c", 506 curscr->lines[i]->line[j].ch); 507 __CTRACE("\n"); 508 for (j = 0; j < curscr->maxx; j++) 509 __CTRACE("%x", 510 curscr->lines[i]->line[j].attr); 511 __CTRACE("\n"); 512 __CTRACE("W: %d:", i); 513 __CTRACE(" 0x%x \n", win->lines[i]->hash); 514 __CTRACE(" 0x%x ", win->lines[i]->flags); 515 for (j = 0; j < win->maxx; j++) 516 __CTRACE("%c", 517 win->lines[i]->line[j].ch); 518 __CTRACE("\n"); 519 for (j = 0; j < win->maxx; j++) 520 __CTRACE("%x", 521 win->lines[i]->line[j].attr); 522 __CTRACE("\n"); 523 } 524 #endif 525 526 /* So we don't have to call __hash() each time */ 527 for (i = 0; i < win->maxx; i++) { 528 buf[i].ch = ' '; 529 buf[i].attr = 0; 530 } 531 blank_hash = __hash((char *) buf, win->maxx * __LDATASIZE); 532 533 /* 534 * Perform the rotation to maintain the consistency of curscr. 535 * This is hairy since we are doing an *in place* rotation. 536 * Invariants of the loop: 537 * - I is the index of the current line. 538 * - Target is the index of the target of line i. 539 * - Tmp1 points to current line (i). 540 * - Tmp2 and points to target line (target); 541 * - Cur_period is the index of the end of the current period. 542 * (see below). 543 * 544 * There are 2 major issues here that make this rotation non-trivial: 545 * 1. Scrolling in a scrolling region bounded by the top 546 * and bottom regions determined (whose size is sc_region). 547 * 2. As a result of the use of the mod function, there may be a 548 * period introduced, i.e., 2 maps to 4, 4 to 6, n-2 to 0, and 549 * 0 to 2, which then causes all odd lines not to be rotated. 550 * To remedy this, an index of the end ( = beginning) of the 551 * current 'period' is kept, cur_period, and when it is reached, 552 * the next period is started from cur_period + 1 which is 553 * guaranteed not to have been reached since that would mean that 554 * all records would have been reached. (think about it...). 555 * 556 * Lines in the rotation can have 3 attributes which are marked on the 557 * line so that curscr is consistent with the visual screen. 558 * 1. Not dirty -- lines inside the scrolled block, top region or 559 * bottom region. 560 * 2. Blank lines -- lines in the differential of the scrolling 561 * region adjacent to top and bot regions 562 * depending on scrolling direction. 563 * 3. Dirty line -- all other lines are marked dirty. 564 */ 565 sc_region = bot - top + 1; 566 i = top; 567 tmp1 = curscr->lines[top]; 568 cur_period = top; 569 for (j = top; j <= bot; j++) { 570 target = (i - top + n + sc_region) % sc_region + top; 571 tmp2 = curscr->lines[target]; 572 curscr->lines[target] = tmp1; 573 /* Mark block as clean and blank out scrolled lines. */ 574 clp = curscr->lines[target]; 575 #ifdef DEBUG 576 __CTRACE("quickch: n=%d startw=%d curw=%d i = %d target=%d ", 577 n, startw, curw, i, target); 578 #endif 579 if ((target >= startw && target < curw) || target < top 580 || target > bot) { 581 #ifdef DEBUG 582 __CTRACE("-- notdirty"); 583 #endif 584 win->lines[target]->flags &= ~__ISDIRTY; 585 } else if ((n > 0 && target >= top && target < top + n) || 586 (n < 0 && target <= bot && target > bot + n)) { 587 if (clp->hash != blank_hash || memcmp(clp->line, 588 buf, win->maxx * __LDATASIZE) !=0) { 589 (void)memcpy(clp->line, buf, 590 win->maxx * __LDATASIZE); 591 #ifdef DEBUG 592 __CTRACE("-- blanked out: dirty"); 593 #endif 594 clp->hash = blank_hash; 595 __touchline(win, target, 0, win->maxx - 1, 0); 596 } else { 597 __touchline(win, target, 0, win->maxx - 1, 0); 598 #ifdef DEBUG 599 __CTRACE(" -- blank line already: dirty"); 600 #endif 601 } 602 } else { 603 #ifdef DEBUG 604 __CTRACE(" -- dirty"); 605 #endif 606 __touchline(win, target, 0, win->maxx - 1, 0); 607 } 608 #ifdef DEBUG 609 __CTRACE("\n"); 610 #endif 611 if (target == cur_period) { 612 i = target + 1; 613 tmp1 = curscr->lines[i]; 614 cur_period = i; 615 } else { 616 tmp1 = tmp2; 617 i = target; 618 } 619 } 620 #ifdef DEBUG 621 __CTRACE("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n"); 622 for (i = 0; i < curscr->maxy; i++) { 623 __CTRACE("C: %d:", i); 624 for (j = 0; j < curscr->maxx; j++) 625 __CTRACE("%c", 626 curscr->lines[i]->line[j].ch); 627 __CTRACE("\n"); 628 __CTRACE("W: %d:", i); 629 for (j = 0; j < win->maxx; j++) 630 __CTRACE("%c", 631 win->lines[i]->line[j].ch); 632 __CTRACE("\n"); 633 } 634 #endif 635 if (n != 0) { 636 WINDOW *wp; 637 scrolln(win, starts, startw, curs, bot, top); 638 /* 639 * Need to repoint any subwindow lines to the rotated 640 * line structured. 641 */ 642 for (wp = win->nextp; wp != win; wp = wp->nextp) 643 __set_subwin(win, wp); 644 } 645 } 646 647 /* 648 * Scrolln performs the scroll by n lines, where n is starts - startw. 649 */ 650 static void 651 scrolln(win, starts, startw, curs, bot, top) 652 WINDOW *win; 653 int starts, startw, curs, bot, top; 654 { 655 int i, oy, ox, n; 656 657 oy = curscr->cury; 658 ox = curscr->curx; 659 n = starts - startw; 660 661 if (n > 0) { 662 __mvcur(oy, ox, top, 0, 1); 663 /* Scroll up the block */ 664 if (DL) 665 tputs(__tscroll(DL, n), 0, __cputchar); 666 else 667 for(i = 0; i < n; i++) 668 tputs(dl, 0, __cputchar); 669 670 /* 671 * Push down the bottom region. 672 */ 673 __mvcur(top, 0, bot - n + 1, 0, 1); 674 if (AL) 675 tputs(__tscroll(AL, n), 0, __cputchar); 676 else 677 for(i = 0; i < n; i++) 678 tputs(al, 0, __cputchar); 679 __mvcur(bot - n + 1, 0, oy, ox, 1); 680 } else { 681 /* Preserve the bottom lines */ 682 __mvcur(oy, ox, bot + n + 1, 0, 1); /* n < 0 */ 683 if (DL) 684 tputs(__tscroll(DL, -n), 0, __cputchar); 685 else 686 for(i = n; i < 0; i++) 687 tputs(dl, 0, __cputchar); 688 __mvcur(bot + n + 1, 0, top, 0, 1); 689 690 /* Scroll the block down */ 691 if (AL) 692 tputs(__tscroll(AL, -n), 0, __cputchar); 693 else 694 for(i = n; i < 0; i++) 695 tputs(al, 0, __cputchar); 696 __mvcur(top, 0, oy, ox, 1); 697 } 698 } 699