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.17 (Berkeley) 10/20/92"; 10 #endif /* not lint */ 11 12 #include <curses.h> 13 #include <string.h> 14 15 /* Equality of characters in terms of standout */ 16 #define __SOEQ(a, b) !(((a) & __STANDOUT) ^ ((b) & __STANDOUT)) 17 18 static int curwin; 19 static short ly, lx; 20 21 int doqch = 1; 22 23 WINDOW *_win; 24 25 static void domvcur __P((int, int, int, int)); 26 static int makech __P((WINDOW *, int)); 27 static void quickch __P((WINDOW *)); 28 static void scrolln __P((WINDOW *, int, int, int, int, int)); 29 /* 30 * wrefresh -- 31 * Make the current screen look like "win" over the area coverd by 32 * win. 33 */ 34 int 35 wrefresh(win) 36 register WINDOW *win; 37 { 38 register LINE *wlp; 39 register int retval; 40 register short wy; 41 42 /* Make sure were in visual state. */ 43 if (__endwin) { 44 tputs(VS, 0, __cputchar); 45 tputs(TI, 0, __cputchar); 46 __endwin = 0; 47 } 48 49 /* Initialize loop parameters. */ 50 51 ly = curscr->cury; 52 lx = curscr->curx; 53 wy = 0; 54 _win = win; 55 curwin = (win == curscr); 56 57 if (!curwin) 58 for (wy = 0; wy < win->maxy; wy++) { 59 wlp = win->lines[wy]; 60 if (wlp->flags & __ISDIRTY) 61 /* need standout characteristics as well */ 62 wlp->hash = __hash(wlp->line, 2 * win->maxx); 63 } 64 65 if (win->flags & __CLEAROK || curscr->flags & __CLEAROK || curwin) { 66 if ((win->flags & __FULLWIN) || curscr->flags & __CLEAROK) { 67 tputs(CL, 0, __cputchar); 68 ly = 0; 69 lx = 0; 70 if (!curwin) { 71 curscr->flags &= ~__CLEAROK; 72 curscr->cury = 0; 73 curscr->curx = 0; 74 werase(curscr); 75 } 76 touchwin(win); 77 } 78 win->flags &= ~__CLEAROK; 79 } 80 if (!CA) { 81 if (win->curx != 0) 82 putchar('\n'); 83 if (!curwin) 84 werase(curscr); 85 } 86 #ifdef DEBUG 87 __TRACE("wrefresh: (%0.2o): curwin = %d\n", win, curwin); 88 __TRACE("wrefresh: \tfirstch\tlastch\n"); 89 #endif 90 91 #ifndef NOQCH 92 if (!__noqch && (win->flags & __FULLWIN) && !curwin) 93 quickch(win); 94 #endif 95 for (wy = 0; wy < win->maxy; wy++) { 96 #ifdef DEBUG 97 __TRACE("%d\t%d\t%d\n", 98 wy, win->lines[wy]->firstch, win->lines[wy]->lastch); 99 #endif 100 if (!curwin) 101 curscr->lines[wy]->hash = win->lines[wy]->hash; 102 if (win->lines[wy]->flags & __ISDIRTY) 103 if (makech(win, wy) == ERR) 104 return (ERR); 105 else { 106 if (win->lines[wy]->firstch >= win->ch_off) 107 win->lines[wy]->firstch = win->maxx + 108 win->ch_off; 109 if (win->lines[wy]->lastch < win->maxx + 110 win->ch_off) 111 win->lines[wy]->lastch = win->ch_off; 112 if (win->lines[wy]->lastch < 113 win->lines[wy]->firstch) 114 win->lines[wy]->flags &= ~__ISDIRTY; 115 } 116 #ifdef DEBUG 117 __TRACE("\t%d\t%d\n", win->lines[wy]->firstch, 118 win->lines[wy]->lastch); 119 #endif 120 } 121 122 #ifdef DEBUG 123 __TRACE("refresh: ly=%d, lx=%d\n", ly, lx); 124 #endif 125 126 if (win == curscr) 127 domvcur(ly, lx, win->cury, win->curx); 128 else { 129 if (win->flags & __LEAVEOK) { 130 curscr->cury = ly; 131 curscr->curx = lx; 132 ly -= win->begy; 133 lx -= win->begx; 134 if (ly >= 0 && ly < win->maxy && lx >= 0 && 135 lx < win->maxx) { 136 win->cury = ly; 137 win->curx = lx; 138 } else 139 win->cury = win->curx = 0; 140 } else { 141 domvcur(ly, lx, win->cury + win->begy, 142 win->curx + win->begx); 143 curscr->cury = win->cury + win->begy; 144 curscr->curx = win->curx + win->begx; 145 } 146 } 147 retval = OK; 148 149 _win = NULL; 150 (void)fflush(stdout); 151 return (retval); 152 } 153 154 /* 155 * makech -- 156 * Make a change on the screen. 157 */ 158 static int 159 makech(win, wy) 160 register WINDOW *win; 161 int wy; 162 { 163 register int nlsp, clsp; /* Last space in lines. */ 164 register short wx, lch, y; 165 register char *nsp, *csp, *ce, *nsop, *csop; 166 char zero, soeq; 167 168 zero = '\0'; 169 /* Is the cursor still on the end of the last line? */ 170 if (wy > 0 && win->lines[wy - 1]->flags & __ISPASTEOL) { 171 win->lines[wy - 1]->flags &= ~__ISPASTEOL; 172 domvcur(ly, lx, ly + 1, 0); 173 ly++; 174 lx = 0; 175 } 176 if (!(win->lines[wy]->flags & __ISDIRTY)) 177 return (OK); 178 wx = win->lines[wy]->firstch - win->ch_off; 179 if (wx >= win->maxx) 180 return (OK); 181 else if (wx < 0) 182 wx = 0; 183 lch = win->lines[wy]->lastch - win->ch_off; 184 if (lch < 0) 185 return (OK); 186 else if (lch >= win->maxx) 187 lch = win->maxx - 1; 188 y = wy + win->begy; 189 190 if (curwin) 191 csp = " "; 192 else 193 csp = &curscr->lines[wy + win->begy]->line[wx + win->begx]; 194 195 nsp = &win->lines[wy]->line[wx]; 196 if (CE && !curwin) { 197 for (ce = &win->lines[wy]->line[win->maxx - 1]; 198 *ce == ' ' && !(*(ce + win->maxx) & __STANDOUT); ce--) 199 if (ce <= win->lines[wy]->line) 200 break; 201 nlsp = ce - win->lines[wy]->line; 202 } 203 if (!curwin) 204 ce = CE; 205 else 206 ce = NULL; 207 208 while (wx <= lch) { 209 if (*nsp == *csp && 210 __SOEQ(*(nsp + win->maxx), *(csp + win->maxx))){ 211 if (wx <= lch) { 212 while (*nsp == *csp && 213 __SOEQ(*(nsp + win->maxx), 214 *(csp + win->maxx)) && 215 wx <= lch) { 216 nsp++; 217 if (!curwin) 218 csp++; 219 ++wx; 220 } 221 continue; 222 } 223 break; 224 } 225 domvcur(ly, lx, y, wx + win->begx); 226 227 #ifdef DEBUG 228 __TRACE("makech: 1: wx = %d, ly= %d, lx = %d, newy = %d, newx = %d\n", 229 wx, ly, lx, y, wx + win->begx); 230 #endif 231 ly = y; 232 lx = wx + win->begx; 233 nsop = nsp + win->maxx; 234 if (!curwin) 235 csop = csp + win->maxx; 236 else 237 csop = &zero; 238 while ((*nsp != *csp || !__SOEQ(*nsop, *csop)) && wx <= lch) { 239 #ifdef notdef 240 /* XXX 241 * The problem with this code is that we can't count on 242 * terminals wrapping around after the 243 * last character on the previous line has been output 244 * In effect, what then could happen is that the CE 245 * clear the previous line and do nothing to the 246 * next line. 247 */ 248 if (ce != NULL && wx >= nlsp && *nsp == ' ') { 249 /* Check for clear to end-of-line. */ 250 ce = &curscr->lines[wy]->line[COLS - 1]; 251 while (*ce == ' ') 252 if (ce-- <= csp) 253 break; 254 clsp = ce - curscr->lines[wy]->line - 255 win->begx; 256 #ifdef DEBUG 257 __TRACE("makech: clsp = %d, nlsp = %d\n", clsp, nlsp); 258 #endif 259 if (clsp - nlsp >= strlen(CE) && 260 clsp < win->maxx) { 261 #ifdef DEBUG 262 __TRACE("makech: using CE\n"); 263 #endif 264 tputs(CE, 0, __cputchar); 265 lx = wx + win->begx; 266 while (wx++ <= clsp) { 267 *csp++ = ' '; 268 *csop++ &= ~__STANDOUT; 269 } 270 return (OK); 271 } 272 ce = NULL; 273 } 274 #endif 275 276 /* Enter/exit standout mode as appropriate. */ 277 if (SO && (*nsop & __STANDOUT) != 278 (curscr->flags & __WSTANDOUT)) { 279 if (*nsop & __STANDOUT) { 280 tputs(SO, 0, __cputchar); 281 curscr->flags |= __WSTANDOUT; 282 } else { 283 tputs(SE, 0, __cputchar); 284 curscr->flags &= ~__WSTANDOUT; 285 } 286 } 287 288 wx++; 289 if (wx >= win->maxx && wy == win->maxy - 1 && !curwin) 290 if (win->flags & __SCROLLOK) { 291 if (curscr->flags & __WSTANDOUT 292 && win->flags & __ENDLINE) 293 if (!MS) { 294 tputs(SE, 0, 295 __cputchar); 296 curscr->flags &= 297 ~__WSTANDOUT; 298 } 299 if (!curwin) { 300 *csop = *nsop; 301 putchar(*csp = *nsp); 302 } else 303 putchar(*nsp); 304 #ifdef notdef /* why is this here? */ 305 if (win->flags & __FULLWIN && !curwin) 306 scroll(curscr); 307 #endif 308 if (wx + win->begx < curscr->maxx) { 309 domvcur(ly, wx + win->begx, 310 win->begy + win->maxy - 1, 311 win->begx + win->maxx - 1); 312 } 313 ly = win->begy + win->maxy - 1; 314 lx = win->begx + win->maxx - 1; 315 return (OK); 316 } else 317 if (win->flags & __SCROLLWIN) { 318 lx = --wx; 319 return (ERR); 320 } 321 if (!curwin) { 322 *csop++ = *nsop; 323 putchar(*csp++ = *nsp); 324 } else 325 putchar(*nsp); 326 327 #ifdef DEBUG 328 __TRACE("makech: putchar(%c)\n", *nsp & 0177); 329 #endif 330 if (UC && (*nsop & __STANDOUT)) { 331 putchar('\b'); 332 tputs(UC, 0, __cputchar); 333 } 334 nsp++; 335 nsop++; 336 } 337 #ifdef DEBUG 338 __TRACE("makech: 2: wx = %d, lx = %d\n", wx, lx); 339 #endif 340 if (lx == wx + win->begx) /* If no change. */ 341 break; 342 lx = wx + win->begx; 343 if (lx >= COLS && AM) { 344 /* 345 * xn glitch: chomps a newline after auto-wrap. 346 * we just feed it now and forget about it. 347 */ 348 if (XN) { 349 lx = 0; 350 ly++; 351 putchar('\n'); 352 putchar('\r'); 353 } else { 354 if (wy != LINES) 355 win->lines[wy]->flags |= __ISPASTEOL; 356 lx = COLS - 1; 357 } 358 } else if (wx >= win->maxx) { 359 if (wy != win->maxy) 360 win->lines[wy]->flags |= __ISPASTEOL; 361 domvcur(ly, lx, ly, win->maxx + win->begx - 1); 362 lx = win->maxx + win->begx - 1; 363 } 364 365 #ifdef DEBUG 366 __TRACE("makech: 3: wx = %d, lx = %d\n", wx, lx); 367 #endif 368 } 369 return (OK); 370 } 371 372 /* 373 * domvcur -- 374 * Do a mvcur, leaving standout mode if necessary. 375 */ 376 static void 377 domvcur(oy, ox, ny, nx) 378 int oy, ox, ny, nx; 379 { 380 if (curscr->flags & __WSTANDOUT && !MS) { 381 tputs(SE, 0, __cputchar); 382 curscr->flags &= ~__WSTANDOUT; 383 } 384 385 mvcur(oy, ox, ny, nx); 386 } 387 388 /* 389 * Quickch() attempts to detect a pattern in the change of the window 390 * in order to optimize the change, e.g., scroll n lines as opposed to 391 * repainting the screen line by line. 392 */ 393 394 static void 395 quickch(win) 396 WINDOW *win; 397 { 398 #define THRESH win->maxy / 4 399 400 register LINE *clp, *tmp1, *tmp2; 401 register int bsize, curs, curw, starts, startw, i, j; 402 int n, target, remember, bot, top; 403 char buf[1024]; 404 u_int blank_hash; 405 406 /* 407 * Search for the largest block of text not changed. 408 */ 409 for (bsize = win->maxy; bsize >= THRESH; bsize--) 410 for (startw = 0; startw <= win->maxy - bsize; startw++) 411 for (starts = 0; starts <= win->maxy - bsize; 412 starts++) { 413 for (curw = startw, curs = starts; 414 curs < starts + bsize; curw++, curs++) 415 if (win->lines[curw]->hash != 416 curscr->lines[curs]->hash || 417 bcmp(&win->lines[curw], 418 &curscr->lines[curs], 419 2 * win->maxx) != 0) 420 break; 421 if (curs == starts + bsize) 422 goto done; 423 } 424 done: 425 /* Did not find anything or block is in correct place already. */ 426 if (bsize < THRESH || starts == startw) 427 return; 428 429 /* 430 * Find how many lines from the top of the screen are unchanged. 431 */ 432 if (starts != 0) { 433 for (top = 0; top < win->maxy; top++) 434 if (win->lines[top]->hash != curscr->lines[top]->hash 435 || bcmp(&win->lines[top], &curscr->lines[top], 436 2 * win->maxx) != 0) 437 break; 438 } else 439 top = 0; 440 441 /* 442 * Find how many lines from bottom of screen are unchanged. 443 */ 444 if (curs != win->maxy) { 445 for (bot = win->maxy - 1; bot >= 0; bot--) 446 if (win->lines[bot]->hash != curscr->lines[bot]->hash 447 || bcmp(&win->lines[bot], &curscr->lines[bot], 448 2 * win->maxx) != 0) 449 break; 450 } else 451 bot = win->maxy - 1; 452 453 #ifdef DEBUG 454 __TRACE("quickch:bsize=%d,starts=%d,startw=%d,curw=%d,curs=%d,top=%d,bot=%d\n", 455 bsize, starts, startw, curw, curs, top, bot); 456 #endif 457 458 /* 459 * Make sure that there is no overlap between the bottom and top 460 * regions and the middle scrolled block. 461 */ 462 if (bot < curw) 463 bot = curw - 1; 464 if (top > startw) 465 top = startw; 466 467 scrolln(win, starts, startw, curs, top, bot); 468 469 n = startw - starts; 470 471 /* So we don't have to call __hash() each time */ 472 (void)memset(buf, ' ', win->maxx); 473 (void)memset(buf + win->maxx, '\0', win->maxx); 474 blank_hash = __hash(buf, 2 * win->maxx); 475 476 /* 477 * Perform the rotation to maintain the consistency of curscr. 478 */ 479 i = 0; 480 tmp1 = curscr->lines[0]; 481 remember = 0; 482 for (j = 0; j < win->maxy; j++) { 483 target = (i + n + win->maxy) % win->maxy; 484 tmp2 = curscr->lines[target]; 485 curscr->lines[target] = tmp1; 486 /* Mark block as clean and blank out scrolled lines. */ 487 clp = curscr->lines[target]; 488 #ifdef DEBUG 489 __TRACE("quickch: n=%d startw=%d curw=%d i = %d target=%d ", 490 n, startw, curw, i, target); 491 #endif 492 if (target >= startw && target < curw || target < top || 493 target > bot) { 494 #ifdef DEBUG 495 __TRACE("-- notdirty"); 496 #endif 497 win->lines[target]->flags &= ~__ISDIRTY; 498 } else if ((n < 0 && target >= win->maxy + n) || 499 (n > 0 && target < n)) { 500 if (clp->hash != blank_hash || 501 bcmp(clp->line, buf, 2 * win->maxx) != 0) { 502 (void)bcopy(clp->line, buf, 2 * win->maxx); 503 #ifdef DEBUG 504 __TRACE("-- bcopy "); 505 #endif 506 clp->hash = blank_hash; 507 } else 508 #ifdef DEBUG 509 __TRACE(" -- no bcopy"); 510 #endif 511 touchline(win, target, 0, win->maxx - 1); 512 } else { 513 #ifdef DEBUG 514 __TRACE(" -- just dirty"); 515 #endif 516 touchline(win, target, 0, win->maxx - 1); 517 } 518 #ifdef DEBUG 519 __TRACE("\n"); 520 #endif 521 if (target == remember) { 522 i = target + 1; 523 tmp1 = curscr->lines[i]; 524 remember = i; 525 } else { 526 tmp1 = tmp2; 527 i = target; 528 } 529 } 530 #ifdef DEBUG 531 __TRACE("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n"); 532 for (i = 0; i < curscr->maxy; i++) 533 __TRACE("Q: %d: %.70s\n", i, 534 curscr->lines[i]->line); 535 #endif 536 } 537 538 static void 539 scrolln(win, starts, startw, curs, top, bot) 540 WINDOW *win; 541 int starts, startw, curs, top, bot; 542 { 543 int i, oy, ox, n; 544 545 oy = curscr->cury; 546 ox = curscr->curx; 547 n = starts - startw; 548 549 if (n > 0) { 550 mvcur(oy, ox, top, 0); 551 /* Scroll up the block */ 552 if (DL) 553 tputs(tscroll(DL, n), 0, __cputchar); 554 else 555 for(i = 0; i < n; i++) 556 tputs(dl, 0, __cputchar); 557 /* Push back down the bottom region */ 558 if (bot < win->maxy - 1) { 559 mvcur(top, 0, bot - n + 1, 0); 560 if (AL) 561 tputs(tscroll(AL, n), 0, __cputchar); 562 else 563 for(i = 0; i < n; i++) 564 tputs(al, 0, __cputchar); 565 mvcur(bot - n + 1, 0, oy, ox); 566 } else 567 mvcur(top, 0, oy, ox); 568 } else { 569 /* Preserve the bottom lines. (Pull them up) */ 570 if (bot < win->maxy - 1) { 571 mvcur(oy, ox, curs, 0); 572 if (DL) 573 tputs(tscroll(DL, -n), 0, __cputchar); 574 else 575 for(i = n; i < 0; i++) 576 tputs(dl, 0, __cputchar); 577 mvcur(curs, 0, starts, 0); 578 } else 579 mvcur(oy, ox, starts, 0); 580 581 /* Scroll the block down */ 582 if (AL) 583 tputs(tscroll(AL, -n), 0, __cputchar); 584 else 585 for(i = n; i < 0; i++) 586 tputs(al, 0, __cputchar); 587 mvcur(starts, 0, oy, ox); 588 } 589 } 590 591 592