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