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