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.36 (Berkeley) 03/02/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 short 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 wx = *win->lines[wy]->firstchp - win->ch_off; 185 if (wx >= win->maxx) 186 return (OK); 187 else if (wx < 0) 188 wx = 0; 189 lch = *win->lines[wy]->lastchp - win->ch_off; 190 if (lch < 0) 191 return (OK); 192 else if (lch >= win->maxx) 193 lch = win->maxx - 1; 194 y = wy + win->begy; 195 196 if (curwin) 197 csp = ␣ 198 else 199 csp = &curscr->lines[wy + win->begy]->line[wx + win->begx]; 200 201 nsp = &win->lines[wy]->line[wx]; 202 force = win->lines[wy]->flags & __FORCEPAINT; 203 win->lines[wy]->flags &= ~__FORCEPAINT; 204 if (CE && !curwin) { 205 for (cp = &win->lines[wy]->line[win->maxx - 1]; 206 cp->ch == ' ' && cp->attr == 0; cp--) 207 if (cp <= win->lines[wy]->line) 208 break; 209 nlsp = cp - win->lines[wy]->line; 210 } 211 if (!curwin) 212 ce = CE; 213 else 214 ce = NULL; 215 216 if (force) { 217 if (CM) 218 tputs(tgoto(CM, lx, ly), 0, __cputchar); 219 else { 220 tputs(HO, 0, __cputchar); 221 mvcur(0, 0, ly, lx); 222 } 223 } 224 while (wx <= lch) { 225 if (!force && memcmp(nsp, csp, sizeof(__LDATA)) == 0) { 226 if (wx <= lch) { 227 while (memcmp(nsp, csp, sizeof(__LDATA)) == 0 && 228 wx <= lch) { 229 nsp++; 230 if (!curwin) 231 csp++; 232 ++wx; 233 } 234 continue; 235 } 236 break; 237 } 238 domvcur(ly, lx, y, wx + win->begx); 239 240 #ifdef DEBUG 241 __TRACE("makech: 1: wx = %d, ly= %d, lx = %d, newy = %d, newx = %d, force =%d\n", 242 wx, ly, lx, y, wx + win->begx, force); 243 #endif 244 ly = y; 245 lx = wx + win->begx; 246 while ((force || memcmp(nsp, csp, sizeof(__LDATA)) != 0) 247 && wx <= lch) { 248 249 /* Enter/exit standout mode as appropriate. */ 250 if (SO && (nsp->attr & __STANDOUT) != 251 (curscr->flags & __WSTANDOUT)) { 252 if (nsp->attr & __STANDOUT) { 253 tputs(SO, 0, __cputchar); 254 curscr->flags |= __WSTANDOUT; 255 } else { 256 tputs(SE, 0, __cputchar); 257 curscr->flags &= ~__WSTANDOUT; 258 } 259 } 260 261 wx++; 262 if (wx >= win->maxx && wy == win->maxy - 1 && !curwin) 263 if (win->flags & __SCROLLOK) { 264 if (curscr->flags & __WSTANDOUT 265 && win->flags & __ENDLINE) 266 if (!MS) { 267 tputs(SE, 0, 268 __cputchar); 269 curscr->flags &= 270 ~__WSTANDOUT; 271 } 272 if (!curwin) { 273 csp->attr = nsp->attr; 274 putchar(csp->ch = nsp->ch); 275 } else 276 putchar(nsp->ch); 277 278 if (wx + win->begx < curscr->maxx) { 279 domvcur(ly, wx + win->begx, 280 win->begy + win->maxy - 1, 281 win->begx + win->maxx - 1); 282 } 283 ly = win->begy + win->maxy - 1; 284 lx = win->begx + win->maxx - 1; 285 return (OK); 286 } else 287 if (win->flags & __SCROLLWIN) { 288 lx = --wx; 289 return (ERR); 290 } 291 if (!curwin) { 292 csp->attr = nsp->attr; 293 putchar(csp->ch = nsp->ch); 294 csp++; 295 } else 296 putchar(nsp->ch); 297 298 #ifdef DEBUG 299 __TRACE("makech: putchar(%c)\n", nsp->ch & 0177); 300 #endif 301 if (UC && (nsp->attr & __STANDOUT)) { 302 putchar('\b'); 303 tputs(UC, 0, __cputchar); 304 } 305 nsp++; 306 } 307 #ifdef DEBUG 308 __TRACE("makech: 2: wx = %d, lx = %d\n", wx, lx); 309 #endif 310 if (lx == wx + win->begx) /* If no change. */ 311 break; 312 lx = wx + win->begx; 313 if (lx >= COLS && AM) { 314 if (wy != LINES) 315 win->lines[wy]->flags |= __ISPASTEOL; 316 lx = COLS - 1; 317 } else if (wx >= win->maxx) { 318 if (wy != win->maxy) 319 win->lines[wy]->flags |= __ISPASTEOL; 320 domvcur(ly, lx, ly, win->maxx + win->begx - 1); 321 lx = win->maxx + win->begx - 1; 322 } 323 324 #ifdef DEBUG 325 __TRACE("makech: 3: wx = %d, lx = %d\n", wx, lx); 326 #endif 327 } 328 return (OK); 329 } 330 331 /* 332 * domvcur -- 333 * Do a mvcur, leaving standout mode if necessary. 334 */ 335 static void 336 domvcur(oy, ox, ny, nx) 337 int oy, ox, ny, nx; 338 { 339 if (curscr->flags & __WSTANDOUT && !MS) { 340 tputs(SE, 0, __cputchar); 341 curscr->flags &= ~__WSTANDOUT; 342 } 343 344 mvcur(oy, ox, ny, nx); 345 } 346 347 /* 348 * Quickch() attempts to detect a pattern in the change of the window 349 * in order to optimize the change, e.g., scroll n lines as opposed to 350 * repainting the screen line by line. 351 */ 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 /* 392 * Search for the largest block of text not changed. 393 * Invariants of the loop: 394 * - Startw is the index of the beginning of the examined block in win. 395 * - Starts is the index of the beginning of the examined block in 396 * curscr. 397 * - Curs is the index of one past the end of the exmined block in win. 398 * - Curw is the index of one past the end of the exmined block in 399 * curscr. 400 * - bsize is the current size of the examined block. 401 */ 402 for (bsize = bot - top; bsize >= THRESH; bsize--) { 403 for (startw = top; startw <= bot - bsize; startw++) 404 for (starts = top; starts <= bot - bsize; 405 starts++) { 406 for (curw = startw, curs = starts; 407 curs < starts + bsize; curw++, curs++) 408 if (win->lines[curw]->flags & 409 __FORCEPAINT || 410 (win->lines[curw]->hash != 411 curscr->lines[curs]->hash || 412 memcmp(win->lines[curw]->line, 413 curscr->lines[curs]->line, 414 win->maxx * __LDATASIZE) != 0)) 415 break; 416 if (curs == starts + bsize) 417 goto done; 418 } 419 } 420 done: 421 /* Did not find anything */ 422 if (bsize < THRESH) 423 return; 424 425 #ifdef DEBUG 426 __TRACE("quickch:bsize=%d,starts=%d,startw=%d,curw=%d,curs=%d,top=%d,bot=%d\n", 427 bsize, starts, startw, curw, curs, top, bot); 428 #endif 429 430 /* 431 * Make sure that there is no overlap between the bottom and top 432 * regions and the middle scrolled block. 433 */ 434 if (bot < curs) 435 bot = curs - 1; 436 if (top > starts) 437 top = starts; 438 439 n = startw - starts; 440 441 #ifdef DEBUG 442 __TRACE("#####################################\n"); 443 for (i = 0; i < curscr->maxy; i++) { 444 __TRACE("C: %d:", i); 445 __TRACE(" 0x%x \n", curscr->lines[i]->hash); 446 for (j = 0; j < curscr->maxx; j++) 447 __TRACE("%c", 448 curscr->lines[i]->line[j].ch); 449 __TRACE("\n"); 450 for (j = 0; j < curscr->maxx; j++) 451 __TRACE("%x", 452 curscr->lines[i]->line[j].attr); 453 __TRACE("\n"); 454 __TRACE("W: %d:", i); 455 __TRACE(" 0x%x \n", win->lines[i]->hash); 456 __TRACE(" 0x%x ", win->lines[i]->flags); 457 for (j = 0; j < win->maxx; j++) 458 __TRACE("%c", 459 win->lines[i]->line[j].ch); 460 __TRACE("\n"); 461 for (j = 0; j < win->maxx; j++) 462 __TRACE("%x", 463 win->lines[i]->line[j].attr); 464 __TRACE("\n"); 465 } 466 #endif 467 if (n != 0) 468 scrolln(win, starts, startw, curs, bot, top); 469 470 /* So we don't have to call __hash() each time */ 471 for (i = 0; i < win->maxx; i++) { 472 buf[i].ch = ' '; 473 buf[i].attr = 0; 474 } 475 blank_hash = __hash((char *) buf, win->maxx * __LDATASIZE); 476 477 /* 478 * Perform the rotation to maintain the consistency of curscr. 479 * This is hairy since we are doing an *in place* rotation. 480 * Invariants of the loop: 481 * - I is the index of the current line. 482 * - Target is the index of the target of line i. 483 * - Tmp1 points to current line (i). 484 * - Tmp2 and points to target line (target); 485 * - Cur_period is the index of the end of the current period. 486 * (see below). 487 * 488 * There are 2 major issues here that make this rotation non-trivial: 489 * 1. Scrolling in a scrolling region bounded by the top 490 * and bottom regions determined (whose size is sc_region). 491 * 2. As a result of the use of the mod function, there may be a 492 * period introduced, i.e., 2 maps to 4, 4 to 6, n-2 to 0, and 493 * 0 to 2, which then causes all odd lines not to be rotated. 494 * To remedy this, an index of the end ( = beginning) of the 495 * current 'period' is kept, cur_period, and when it is reached, 496 * the next period is started from cur_period + 1 which is 497 * guaranteed not to have been reached since that would mean that 498 * all records would have been reached. (think about it...). 499 * 500 * Lines in the rotation can have 3 attributes which are marked on the 501 * line so that curscr is consistent with the visual screen. 502 * 1. Not dirty -- lines inside the scrolled block, top region or 503 * bottom region. 504 * 2. Blank lines -- lines in the differential of the scrolling 505 * region adjacent to top and bot regions 506 * depending on scrolling direction. 507 * 3. Dirty line -- all other lines are marked dirty. 508 */ 509 sc_region = bot - top + 1; 510 i = top; 511 tmp1 = curscr->lines[top]; 512 cur_period = top; 513 for (j = top; j <= bot; j++) { 514 target = (i - top + n + sc_region) % sc_region + top; 515 tmp2 = curscr->lines[target]; 516 curscr->lines[target] = tmp1; 517 /* Mark block as clean and blank out scrolled lines. */ 518 clp = curscr->lines[target]; 519 #ifdef DEBUG 520 __TRACE("quickch: n=%d startw=%d curw=%d i = %d target=%d ", 521 n, startw, curw, i, target); 522 #endif 523 if ((target >= startw && target < curw) || target < top 524 || target > bot) { 525 #ifdef DEBUG 526 __TRACE("-- notdirty"); 527 #endif 528 win->lines[target]->flags &= ~__ISDIRTY; 529 } else if ((n > 0 && target >= top && target < top + n) || 530 (n < 0 && target <= bot && target > bot + n)) { 531 if (clp->hash != blank_hash || memcmp(clp->line, 532 buf, win->maxx * __LDATASIZE) !=0) { 533 (void)memcpy(clp->line, buf, 534 win->maxx * __LDATASIZE); 535 #ifdef DEBUG 536 __TRACE("-- blanked out: dirty"); 537 #endif 538 clp->hash = blank_hash; 539 __touchline(win, target, 0, win->maxx - 1, 0); 540 } else { 541 __touchline(win, target, 0, win->maxx - 1, 0); 542 #ifdef DEBUG 543 __TRACE(" -- blank line already: dirty"); 544 #endif 545 } 546 } else { 547 #ifdef DEBUG 548 __TRACE(" -- dirty"); 549 #endif 550 __touchline(win, target, 0, win->maxx - 1, 0); 551 } 552 #ifdef DEBUG 553 __TRACE("\n"); 554 #endif 555 if (target == cur_period) { 556 i = target + 1; 557 tmp1 = curscr->lines[i]; 558 cur_period = i; 559 } else { 560 tmp1 = tmp2; 561 i = target; 562 } 563 } 564 #ifdef DEBUG 565 __TRACE("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n"); 566 for (i = 0; i < curscr->maxy; i++) { 567 __TRACE("C: %d:", i); 568 for (j = 0; j < curscr->maxx; j++) 569 __TRACE("%c", 570 curscr->lines[i]->line[j].ch); 571 __TRACE("\n"); 572 __TRACE("W: %d:", i); 573 for (j = 0; j < win->maxx; j++) 574 __TRACE("%c", 575 win->lines[i]->line[j].ch); 576 __TRACE("\n"); 577 } 578 #endif 579 } 580 581 /* 582 * Scrolln performs the scroll by n lines, where n is starts - startw. 583 */ 584 static void 585 scrolln(win, starts, startw, curs, bot, top) 586 WINDOW *win; 587 int starts, startw, curs, bot, top; 588 { 589 int i, oy, ox, n; 590 591 oy = curscr->cury; 592 ox = curscr->curx; 593 n = starts - startw; 594 595 if (n > 0) { 596 mvcur(oy, ox, top, 0); 597 /* Scroll up the block */ 598 if (DL) 599 tputs(__tscroll(DL, n), 0, __cputchar); 600 else 601 for(i = 0; i < n; i++) 602 tputs(dl, 0, __cputchar); 603 604 /* 605 * Push down the bottom region. 606 */ 607 mvcur(top, 0, bot - n + 1, 0); 608 if (AL) 609 tputs(__tscroll(AL, n), 0, __cputchar); 610 else 611 for(i = 0; i < n; i++) 612 tputs(al, 0, __cputchar); 613 mvcur(bot - n + 1, 0, oy, ox); 614 } else { 615 /* Preserve the bottom lines */ 616 mvcur(oy, ox, bot + n + 1, 0); /* n < 0 */ 617 if (DL) 618 tputs(__tscroll(DL, -n), 0, __cputchar); 619 else 620 for(i = n; i < 0; i++) 621 tputs(dl, 0, __cputchar); 622 mvcur(bot + n + 1, 0, top, 0); 623 624 /* Scroll the block down */ 625 if (AL) 626 tputs(__tscroll(AL, -n), 0, __cputchar); 627 else 628 for(i = n; i < 0; i++) 629 tputs(al, 0, __cputchar); 630 mvcur(top, 0, oy, ox); 631 } 632 } 633 634 635