1 /* $OpenBSD: tty_update.c,v 1.7 2000/03/10 01:35:05 millert Exp $ */ 2 3 /**************************************************************************** 4 * Copyright (c) 1998,1999,2000 Free Software Foundation, Inc. * 5 * * 6 * Permission is hereby granted, free of charge, to any person obtaining a * 7 * copy of this software and associated documentation files (the * 8 * "Software"), to deal in the Software without restriction, including * 9 * without limitation the rights to use, copy, modify, merge, publish, * 10 * distribute, distribute with modifications, sublicense, and/or sell * 11 * copies of the Software, and to permit persons to whom the Software is * 12 * furnished to do so, subject to the following conditions: * 13 * * 14 * The above copyright notice and this permission notice shall be included * 15 * in all copies or substantial portions of the Software. * 16 * * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 20 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 21 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 22 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 23 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 24 * * 25 * Except as contained in this notice, the name(s) of the above copyright * 26 * holders shall not be used in advertising or otherwise to promote the * 27 * sale, use or other dealings in this Software without prior written * 28 * authorization. * 29 ****************************************************************************/ 30 31 /**************************************************************************** 32 * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 * 33 * and: Eric S. Raymond <esr@snark.thyrsus.com> * 34 ****************************************************************************/ 35 36 /*----------------------------------------------------------------- 37 * 38 * lib_doupdate.c 39 * 40 * The routine doupdate() and its dependents. Also _nc_outstr(), 41 * so all physical output is concentrated here (except _nc_outch() 42 * in lib_tputs.c). 43 * 44 *-----------------------------------------------------------------*/ 45 46 #ifdef __BEOS__ 47 #include <OS.h> 48 #endif 49 50 #include <curses.priv.h> 51 52 #if defined(TRACE) && HAVE_SYS_TIMES_H && HAVE_TIMES 53 #define USE_TRACE_TIMES 1 54 #else 55 #define USE_TRACE_TIMES 0 56 #endif 57 58 #if HAVE_SYS_TIME_H && HAVE_SYS_TIME_SELECT 59 #include <sys/time.h> 60 #endif 61 62 #if USE_TRACE_TIMES 63 #include <sys/times.h> 64 #endif 65 66 #if USE_FUNC_POLL 67 #elif HAVE_SELECT 68 #if HAVE_SYS_SELECT_H 69 #include <sys/select.h> 70 #endif 71 #endif 72 73 #include <term.h> 74 75 MODULE_ID("$From: tty_update.c,v 1.131 2000/02/26 23:22:11 tom Exp $") 76 77 /* 78 * This define controls the line-breakout optimization. Every once in a 79 * while during screen refresh, we want to check for input and abort the 80 * update if there's some waiting. CHECK_INTERVAL controls the number of 81 * changed lines to be emitted between input checks. 82 * 83 * Note: Input-check-and-abort is no longer done if the screen is being 84 * updated from scratch. This is a feature, not a bug. 85 */ 86 #define CHECK_INTERVAL 5 87 88 #define FILL_BCE() (SP->_coloron && !SP->_default_color && !back_color_erase) 89 90 /* 91 * Enable checking to see if doupdate and friends are tracking the true 92 * cursor position correctly. NOTE: this is a debugging hack which will 93 * work ONLY on ANSI-compatible terminals! 94 */ 95 /* #define POSITION_DEBUG */ 96 97 static inline chtype ClrBlank(WINDOW *win); 98 static int ClrBottom(int total); 99 static void ClearScreen(chtype blank); 100 static void ClrUpdate(void); 101 static void DelChar(int count); 102 static void InsStr(chtype * line, int count); 103 static void TransformLine(int const lineno); 104 105 #ifdef POSITION_DEBUG 106 /**************************************************************************** 107 * 108 * Debugging code. Only works on ANSI-standard terminals. 109 * 110 ****************************************************************************/ 111 112 static void 113 position_check(int expected_y, int expected_x, char *legend) 114 /* check to see if the real cursor position matches the virtual */ 115 { 116 char buf[20]; 117 int y, x; 118 119 if (!_nc_tracing || (expected_y < 0 && expected_x < 0)) 120 return; 121 122 memset(buf, '\0', sizeof(buf)); 123 putp("\033[6n"); /* only works on ANSI-compatibles */ 124 _nc_flush(); 125 (void) read(0, buf, sizeof(buf) - 1); 126 _tracef("probe returned %s", _nc_visbuf(buf)); 127 128 /* try to interpret as a position report */ 129 if (sscanf(buf, "\033[%d;%dR", &y, &x) != 2) { 130 _tracef("position probe failed in %s", legend); 131 } else { 132 if (expected_x < 0) 133 expected_x = x - 1; 134 if (expected_y < 0) 135 expected_y = y - 1; 136 if (y - 1 != expected_y || x - 1 != expected_x) { 137 beep(); 138 _tracef("position seen (%d, %d) doesn't match expected one (%d, %d) in %s", 139 y - 1, x - 1, expected_y, expected_x, legend); 140 } else { 141 _tracef("position matches OK in %s", legend); 142 } 143 } 144 } 145 #else 146 #define position_check(expected_y, expected_x, legend) /* nothing */ 147 #endif /* POSITION_DEBUG */ 148 149 /**************************************************************************** 150 * 151 * Optimized update code 152 * 153 ****************************************************************************/ 154 155 static inline void 156 GoTo(int const row, int const col) 157 { 158 chtype oldattr = SP->_current_attr; 159 160 TR(TRACE_MOVE, ("GoTo(%d, %d) from (%d, %d)", 161 row, col, SP->_cursrow, SP->_curscol)); 162 163 position_check(SP->_cursrow, SP->_curscol, "GoTo"); 164 165 /* 166 * Force restore even if msgr is on when we're in an alternate 167 * character set -- these have a strong tendency to screw up the 168 * CR & LF used for local character motions! 169 */ 170 if ((oldattr & A_ALTCHARSET) 171 || (oldattr && !move_standout_mode)) { 172 TR(TRACE_CHARPUT, ("turning off (%#lx) %s before move", 173 oldattr, _traceattr(oldattr))); 174 vidattr(A_NORMAL); 175 } 176 177 mvcur(SP->_cursrow, SP->_curscol, row, col); 178 SP->_cursrow = row; 179 SP->_curscol = col; 180 position_check(SP->_cursrow, SP->_curscol, "GoTo2"); 181 } 182 183 static inline void 184 PutAttrChar(chtype ch) 185 { 186 if (tilde_glitch && (TextOf(ch) == '~')) 187 ch = ('`' | AttrOf(ch)); 188 189 TR(TRACE_CHARPUT, ("PutAttrChar(%s) at (%d, %d)", 190 _tracechtype(ch), 191 SP->_cursrow, SP->_curscol)); 192 UpdateAttrs(ch); 193 if (SP->_cleanup) { 194 _nc_outch((int) TextOf(ch)); 195 } else { 196 putc((int) TextOf(ch), SP->_ofp); /* macro's fastest... */ 197 #ifdef TRACE 198 _nc_outchars++; 199 #endif /* TRACE */ 200 } 201 SP->_curscol++; 202 if (char_padding) { 203 TPUTS_TRACE("char_padding"); 204 putp(char_padding); 205 } 206 } 207 208 static bool 209 check_pending(void) 210 /* check for pending input */ 211 { 212 bool have_pending = FALSE; 213 214 /* 215 * Only carry out this check when the flag is zero, otherwise we'll 216 * have the refreshing slow down drastically (or stop) if there's an 217 * unread character available. 218 */ 219 if (SP->_fifohold != 0) 220 return FALSE; 221 222 if (SP->_checkfd >= 0) { 223 #if USE_FUNC_POLL 224 struct pollfd fds[1]; 225 fds[0].fd = SP->_checkfd; 226 fds[0].events = POLLIN; 227 if (poll(fds, 1, 0) > 0) { 228 have_pending = TRUE; 229 } 230 #elif defined(__BEOS__) 231 /* 232 * BeOS's select() is declared in socket.h, so the configure script does 233 * not see it. That's just as well, since that function works only for 234 * sockets. This (using snooze and ioctl) was distilled from Be's patch 235 * for ncurses which uses a separate thread to simulate select(). 236 * 237 * FIXME: the return values from the ioctl aren't very clear if we get 238 * interrupted. 239 */ 240 int n = 0; 241 int howmany = ioctl(0, 'ichr', &n); 242 if (howmany >= 0 && n > 0) { 243 have_pending = TRUE; 244 } 245 #elif HAVE_SELECT 246 fd_set fdset; 247 struct timeval ktimeout; 248 249 ktimeout.tv_sec = 250 ktimeout.tv_usec = 0; 251 252 FD_ZERO(&fdset); 253 FD_SET(SP->_checkfd, &fdset); 254 if (select(SP->_checkfd + 1, &fdset, NULL, NULL, &ktimeout) != 0) { 255 have_pending = TRUE; 256 } 257 #endif 258 } 259 if (have_pending) { 260 SP->_fifohold = 5; 261 _nc_flush(); 262 } 263 return FALSE; 264 } 265 266 /* 267 * No one supports recursive inline functions. However, gcc is quieter if we 268 * instantiate the recursive part separately. 269 */ 270 #if CC_HAS_INLINE_FUNCS 271 static void callPutChar(chtype const); 272 #else 273 #define callPutChar(ch) PutChar(ch) 274 #endif 275 276 static inline void PutChar(chtype const ch); /* forward declaration */ 277 278 /* put char at lower right corner */ 279 static void 280 PutCharLR(chtype const ch) 281 { 282 if (!auto_right_margin) { 283 /* we can put the char directly */ 284 PutAttrChar(ch); 285 } else if (enter_am_mode && exit_am_mode) { 286 /* we can suppress automargin */ 287 TPUTS_TRACE("exit_am_mode"); 288 putp(exit_am_mode); 289 290 PutAttrChar(ch); 291 SP->_curscol--; 292 position_check(SP->_cursrow, SP->_curscol, "exit_am_mode"); 293 294 TPUTS_TRACE("enter_am_mode"); 295 putp(enter_am_mode); 296 } else if ((enter_insert_mode && exit_insert_mode) 297 || insert_character || parm_ich) { 298 GoTo(screen_lines - 1, screen_columns - 2); 299 callPutChar(ch); 300 GoTo(screen_lines - 1, screen_columns - 2); 301 InsStr(newscr->_line[screen_lines - 1].text + screen_columns - 2, 1); 302 } 303 } 304 305 static void 306 wrap_cursor(void) 307 { 308 if (eat_newline_glitch) { 309 /* 310 * xenl can manifest two different ways. The vt100 311 * way is that, when you'd expect the cursor to wrap, 312 * it stays hung at the right margin (on top of the 313 * character just emitted) and doesn't wrap until the 314 * *next* graphic char is emitted. The c100 way is 315 * to ignore LF received just after an am wrap. 316 * 317 * An aggressive way to handle this would be to 318 * emit CR/LF after the char and then assume the wrap 319 * is done, you're on the first position of the next 320 * line, and the terminal out of its weird state. 321 * Here it's safe to just tell the code that the 322 * cursor is in hyperspace and let the next mvcur() 323 * call straighten things out. 324 */ 325 SP->_curscol = -1; 326 SP->_cursrow = -1; 327 } else if (auto_right_margin) { 328 SP->_curscol = 0; 329 SP->_cursrow++; 330 } else { 331 SP->_curscol--; 332 } 333 position_check(SP->_cursrow, SP->_curscol, "wrap_cursor"); 334 } 335 336 static inline void 337 PutChar(chtype const ch) 338 /* insert character, handling automargin stuff */ 339 { 340 if (SP->_cursrow == screen_lines - 1 && SP->_curscol == screen_columns - 1) 341 PutCharLR(ch); 342 else 343 PutAttrChar(ch); 344 345 if (SP->_curscol >= screen_columns) 346 wrap_cursor(); 347 348 position_check(SP->_cursrow, SP->_curscol, "PutChar"); 349 } 350 351 /* 352 * Check whether the given character can be output by clearing commands. This 353 * includes test for being a space and not including any 'bad' attributes, such 354 * as A_REVERSE. All attribute flags which don't affect appearance of a space 355 * or can be output by clearing (A_COLOR in case of bce-terminal) are excluded. 356 */ 357 static inline bool 358 can_clear_with(chtype ch) 359 { 360 if (!back_color_erase && SP->_coloron) { 361 if (ch & A_COLOR) 362 return FALSE; 363 if (!SP->_default_color) 364 return FALSE; 365 if (SP->_default_fg != C_MASK || SP->_default_bg != C_MASK) 366 return FALSE; 367 } 368 return ((ch & ~(NONBLANK_ATTR | A_COLOR)) == BLANK); 369 } 370 371 /* 372 * Issue a given span of characters from an array. 373 * Must be functionally equivalent to: 374 * for (i = 0; i < num; i++) 375 * PutChar(ntext[i]); 376 * but can leave the cursor positioned at the middle of the interval. 377 * 378 * Returns: 0 - cursor is at the end of interval 379 * 1 - cursor is somewhere in the middle 380 * 381 * This code is optimized using ech and rep. 382 */ 383 static int 384 EmitRange(const chtype * ntext, int num) 385 { 386 int i; 387 388 if (erase_chars || repeat_char) { 389 while (num > 0) { 390 int runcount; 391 chtype ntext0; 392 393 while (num > 1 && ntext[0] != ntext[1]) { 394 PutChar(ntext[0]); 395 ntext++; 396 num--; 397 } 398 ntext0 = ntext[0]; 399 if (num == 1) { 400 PutChar(ntext0); 401 return 0; 402 } 403 runcount = 2; 404 405 while (runcount < num && ntext[runcount] == ntext0) 406 runcount++; 407 408 /* 409 * The cost expression in the middle isn't exactly right. 410 * _cup_cost is an upper bound on the cost for moving to the 411 * end of the erased area, but not the cost itself (which we 412 * can't compute without emitting the move). This may result 413 * in erase_chars not getting used in some situations for 414 * which it would be marginally advantageous. 415 */ 416 if (erase_chars 417 && runcount > SP->_ech_cost + SP->_cup_cost 418 && can_clear_with(ntext0)) { 419 UpdateAttrs(ntext0); 420 putp(tparm(erase_chars, runcount)); 421 422 /* 423 * If this is the last part of the given interval, 424 * don't bother moving cursor, since it can be the 425 * last update on the line. 426 */ 427 if (runcount < num) 428 GoTo(SP->_cursrow, SP->_curscol + runcount); 429 else 430 return 1; /* cursor stays in the middle */ 431 } else if (repeat_char && runcount > SP->_rep_cost) { 432 bool wrap_possible = (SP->_curscol + runcount >= screen_columns); 433 int rep_count = runcount; 434 435 if (wrap_possible) 436 rep_count--; 437 438 UpdateAttrs(ntext0); 439 putp(tparm(repeat_char, TextOf(ntext0), rep_count)); 440 SP->_curscol += rep_count; 441 442 if (wrap_possible) 443 PutChar(ntext0); 444 } else { 445 for (i = 0; i < runcount; i++) 446 PutChar(ntext[i]); 447 } 448 ntext += runcount; 449 num -= runcount; 450 } 451 return 0; 452 } 453 454 for (i = 0; i < num; i++) 455 PutChar(ntext[i]); 456 return 0; 457 } 458 459 /* 460 * Output the line in the given range [first .. last] 461 * 462 * If there's a run of identical characters that's long enough to justify 463 * cursor movement, use that also. 464 * 465 * Returns: same as EmitRange 466 */ 467 static int 468 PutRange( 469 const chtype * otext, 470 const chtype * ntext, 471 int row, 472 int first, int last) 473 { 474 int j, run; 475 int cost = min(SP->_cup_ch_cost, SP->_hpa_ch_cost); 476 477 TR(TRACE_CHARPUT, ("PutRange(%p, %p, %d, %d, %d)", 478 otext, ntext, row, first, last)); 479 480 if (otext != ntext 481 && (last - first + 1) > cost) { 482 for (j = first, run = 0; j <= last; j++) { 483 if (otext[j] == ntext[j]) { 484 run++; 485 } else { 486 if (run > cost) { 487 int before_run = (j - run); 488 EmitRange(ntext + first, before_run - first); 489 GoTo(row, first = j); 490 } 491 run = 0; 492 } 493 } 494 } 495 return EmitRange(ntext + first, last - first + 1); 496 } 497 498 #if CC_HAS_INLINE_FUNCS 499 static void 500 callPutChar(chtype const ch) 501 { 502 PutChar(ch); 503 } 504 #endif 505 506 /* leave unbracketed here so 'indent' works */ 507 #define MARK_NOCHANGE(win,row) \ 508 win->_line[row].firstchar = _NOCHANGE; \ 509 win->_line[row].lastchar = _NOCHANGE; \ 510 if_USE_SCROLL_HINTS(win->_line[row].oldindex = row) 511 512 int 513 doupdate(void) 514 { 515 int i; 516 int nonempty; 517 #if USE_TRACE_TIMES 518 struct tms before, after; 519 #endif /* USE_TRACE_TIMES */ 520 521 T((T_CALLED("doupdate()"))); 522 523 #ifdef TRACE 524 if (_nc_tracing & TRACE_UPDATE) { 525 if (curscr->_clear) 526 _tracef("curscr is clear"); 527 else 528 _tracedump("curscr", curscr); 529 _tracedump("newscr", newscr); 530 } 531 #endif /* TRACE */ 532 533 _nc_signal_handler(FALSE); 534 535 if (SP->_fifohold) 536 SP->_fifohold--; 537 538 #if USE_SIZECHANGE 539 if (SP->_endwin || SP->_sig_winch) { 540 /* 541 * This is a transparent extension: XSI does not address it, 542 * and applications need not know that ncurses can do it. 543 * 544 * Check if the terminal size has changed while curses was off 545 * (this can happen in an xterm, for example), and resize the 546 * ncurses data structures accordingly. 547 */ 548 _nc_update_screensize(); 549 } 550 #endif 551 552 if (SP->_endwin) { 553 554 T(("coming back from shell mode")); 555 reset_prog_mode(); 556 557 _nc_mvcur_resume(); 558 _nc_screen_resume(); 559 SP->_mouse_resume(SP); 560 561 SP->_endwin = FALSE; 562 } 563 #if USE_TRACE_TIMES 564 /* zero the metering machinery */ 565 _nc_outchars = 0; 566 (void) times(&before); 567 #endif /* USE_TRACE_TIMES */ 568 569 /* 570 * This is the support for magic-cookie terminals. The 571 * theory: we scan the virtual screen looking for attribute 572 * turnons. Where we find one, check to make sure it's 573 * realizable by seeing if the required number of 574 * un-attributed blanks are present before and after the 575 * attributed range; try to shift the range boundaries over 576 * blanks (not changing the screen display) so this becomes 577 * true. If it is, shift the beginning attribute change 578 * appropriately (the end one, if we've gotten this far, is 579 * guaranteed room for its cookie). If not, nuke the added 580 * attributes out of the span. 581 */ 582 #if USE_XMC_SUPPORT 583 if (magic_cookie_glitch > 0) { 584 int j, k; 585 attr_t rattr = A_NORMAL; 586 587 for (i = 0; i < screen_lines; i++) { 588 for (j = 0; j < screen_columns; j++) { 589 bool failed = FALSE; 590 chtype turnon = AttrOf(newscr->_line[i].text[j]) & ~rattr; 591 592 /* is an attribute turned on here? */ 593 if (turnon == 0) { 594 rattr = AttrOf(newscr->_line[i].text[j]); 595 continue; 596 } 597 598 T(("At (%d, %d): from %s...", i, j, _traceattr(rattr))); 599 T(("...to %s", _traceattr(turnon))); 600 601 /* 602 * If the attribute change location is a blank with a 603 * "safe" attribute, undo the attribute turnon. This may 604 * ensure there's enough room to set the attribute before 605 * the first non-blank in the run. 606 */ 607 #define SAFE(a) (!((a) & (chtype)~NONBLANK_ATTR)) 608 if (TextOf(newscr->_line[i].text[j]) == ' ' && SAFE(turnon)) { 609 newscr->_line[i].text[j] &= ~turnon; 610 continue; 611 } 612 613 /* check that there's enough room at start of span */ 614 for (k = 1; k <= magic_cookie_glitch; k++) { 615 if (j - k < 0 616 || TextOf(newscr->_line[i].text[j - k]) != ' ' 617 || !SAFE(AttrOf(newscr->_line[i].text[j - k]))) 618 failed = TRUE; 619 } 620 if (!failed) { 621 bool end_onscreen = FALSE; 622 int m, n = j; 623 624 /* find end of span, if it's onscreen */ 625 for (m = i; m < screen_lines; m++) { 626 for (; n < screen_columns; n++) { 627 if (AttrOf(newscr->_line[m].text[n]) == rattr) { 628 end_onscreen = TRUE; 629 T(("Range attributed with %s ends at (%d, %d)", 630 _traceattr(turnon), m, n)); 631 goto foundit; 632 } 633 } 634 n = 0; 635 } 636 T(("Range attributed with %s ends offscreen", 637 _traceattr(turnon))); 638 foundit:; 639 640 if (end_onscreen) { 641 chtype *lastline = newscr->_line[m].text; 642 643 /* 644 * If there are safely-attributed blanks at the 645 * end of the range, shorten the range. This will 646 * help ensure that there is enough room at end 647 * of span. 648 */ 649 while (n >= 0 650 && TextOf(lastline[n]) == ' ' 651 && SAFE(AttrOf(lastline[n]))) 652 lastline[n--] &= ~turnon; 653 654 /* check that there's enough room at end of span */ 655 for (k = 1; k <= magic_cookie_glitch; k++) 656 if (n + k >= screen_columns 657 || TextOf(lastline[n + k]) != ' ' 658 || !SAFE(AttrOf(lastline[n + k]))) 659 failed = TRUE; 660 } 661 } 662 663 if (failed) { 664 int p, q = j; 665 666 T(("Clearing %s beginning at (%d, %d)", 667 _traceattr(turnon), i, j)); 668 669 /* turn off new attributes over span */ 670 for (p = i; p < screen_lines; p++) { 671 for (; q < screen_columns; q++) { 672 if (AttrOf(newscr->_line[p].text[q]) == rattr) 673 goto foundend; 674 newscr->_line[p].text[q] &= ~turnon; 675 } 676 q = 0; 677 } 678 foundend:; 679 } else { 680 T(("Cookie space for %s found before (%d, %d)", 681 _traceattr(turnon), i, j)); 682 683 /* 684 * back up the start of range so there's room 685 * for cookies before the first nonblank character 686 */ 687 for (k = 1; k <= magic_cookie_glitch; k++) 688 newscr->_line[i].text[j - k] |= turnon; 689 } 690 691 rattr = AttrOf(newscr->_line[i].text[j]); 692 } 693 } 694 695 #ifdef TRACE 696 /* show altered highlights after magic-cookie check */ 697 if (_nc_tracing & TRACE_UPDATE) { 698 _tracef("After magic-cookie check..."); 699 _tracedump("newscr", newscr); 700 } 701 #endif /* TRACE */ 702 } 703 #endif /* USE_XMC_SUPPORT */ 704 705 nonempty = 0; 706 if (curscr->_clear || newscr->_clear) { /* force refresh ? */ 707 T(("clearing and updating from scratch")); 708 ClrUpdate(); 709 curscr->_clear = FALSE; /* reset flag */ 710 newscr->_clear = FALSE; /* reset flag */ 711 } else { 712 int changedlines = CHECK_INTERVAL; 713 714 if (check_pending()) 715 goto cleanup; 716 717 nonempty = min(screen_lines, newscr->_maxy + 1); 718 719 if (SP->_scrolling) { 720 _nc_scroll_optimize(); 721 } 722 723 nonempty = ClrBottom(nonempty); 724 725 T(("Transforming lines, nonempty %d", nonempty)); 726 for (i = 0; i < nonempty; i++) { 727 /* 728 * Here is our line-breakout optimization. 729 */ 730 if (changedlines == CHECK_INTERVAL) { 731 if (check_pending()) 732 goto cleanup; 733 changedlines = 0; 734 } 735 736 /* 737 * newscr->line[i].firstchar is normally set 738 * by wnoutrefresh. curscr->line[i].firstchar 739 * is normally set by _nc_scroll_window in the 740 * vertical-movement optimization code, 741 */ 742 if (newscr->_line[i].firstchar != _NOCHANGE 743 || curscr->_line[i].firstchar != _NOCHANGE) { 744 TransformLine(i); 745 changedlines++; 746 } 747 748 /* mark line changed successfully */ 749 if (i <= newscr->_maxy) { 750 MARK_NOCHANGE(newscr, i) 751 } 752 if (i <= curscr->_maxy) { 753 MARK_NOCHANGE(curscr, i) 754 } 755 } 756 } 757 758 /* put everything back in sync */ 759 for (i = nonempty; i <= newscr->_maxy; i++) { 760 MARK_NOCHANGE(newscr, i) 761 } 762 for (i = nonempty; i <= curscr->_maxy; i++) { 763 MARK_NOCHANGE(curscr, i) 764 } 765 766 if (!newscr->_leaveok) { 767 curscr->_curx = newscr->_curx; 768 curscr->_cury = newscr->_cury; 769 770 GoTo(curscr->_cury, curscr->_curx); 771 } 772 773 cleanup: 774 /* 775 * Keep the physical screen in normal mode in case we get other 776 * processes writing to the screen. 777 */ 778 UpdateAttrs(A_NORMAL); 779 780 _nc_flush(); 781 curscr->_attrs = newscr->_attrs; 782 783 #if USE_TRACE_TIMES 784 (void) times(&after); 785 TR(TRACE_TIMES, 786 ("Update cost: %ld chars, %ld clocks system time, %ld clocks user time", 787 _nc_outchars, 788 after.tms_stime - before.tms_stime, 789 after.tms_utime - before.tms_utime)); 790 #endif /* USE_TRACE_TIMES */ 791 792 _nc_signal_handler(TRUE); 793 794 returnCode(OK); 795 } 796 797 /* 798 * ClrBlank(win) 799 * 800 * Returns the attributed character that corresponds to the "cleared" 801 * screen. If the terminal has the back-color-erase feature, this will be 802 * colored according to the wbkgd() call. 803 * 804 * We treat 'curscr' specially because it isn't supposed to be set directly 805 * in the wbkgd() call. Assume 'stdscr' for this case. 806 */ 807 #define BCE_ATTRS (A_NORMAL|A_COLOR) 808 #define BCE_BKGD(win) (((win) == curscr ? stdscr : (win))->_bkgd) 809 810 static inline chtype 811 ClrBlank(WINDOW *win) 812 { 813 chtype blank = BLANK; 814 if (back_color_erase) 815 blank |= (BCE_BKGD(win) & BCE_ATTRS); 816 return blank; 817 } 818 819 /* 820 ** ClrUpdate() 821 ** 822 ** Update by clearing and redrawing the entire screen. 823 ** 824 */ 825 826 static void 827 ClrUpdate(void) 828 { 829 int i; 830 chtype blank = ClrBlank(stdscr); 831 int nonempty = min(screen_lines, newscr->_maxy + 1); 832 833 T(("ClrUpdate() called")); 834 835 ClearScreen(blank); 836 837 T(("updating screen from scratch")); 838 839 nonempty = ClrBottom(nonempty); 840 841 for (i = 0; i < nonempty; i++) 842 TransformLine(i); 843 } 844 845 /* 846 ** ClrToEOL(blank) 847 ** 848 ** Clear to end of current line, starting at the cursor position 849 */ 850 851 static void 852 ClrToEOL(chtype blank) 853 { 854 int j; 855 bool needclear = FALSE; 856 857 for (j = SP->_curscol; j < screen_columns; j++) { 858 chtype *cp = &(curscr->_line[SP->_cursrow].text[j]); 859 860 if (*cp != blank) { 861 *cp = blank; 862 needclear = TRUE; 863 } 864 } 865 866 if (needclear) { 867 UpdateAttrs(blank); 868 TPUTS_TRACE("clr_eol"); 869 if (SP->_el_cost > (screen_columns - SP->_curscol)) { 870 int count = (screen_columns - SP->_curscol); 871 while (count-- > 0) 872 PutChar(blank); 873 } else 874 putp(clr_eol); 875 } 876 } 877 878 /* 879 ** ClrToEOS(blank) 880 ** 881 ** Clear to end of screen, starting at the cursor position 882 */ 883 884 static void 885 ClrToEOS(chtype blank) 886 { 887 int row, col; 888 889 row = SP->_cursrow; 890 col = SP->_curscol; 891 892 { 893 UpdateAttrs(blank); 894 TPUTS_TRACE("clr_eos"); 895 tputs(clr_eos, screen_lines - row, _nc_outch); 896 } 897 898 while (col < screen_columns) 899 curscr->_line[row].text[col++] = blank; 900 901 for (row++; row < screen_lines; row++) { 902 for (col = 0; col < screen_columns; col++) 903 curscr->_line[row].text[col] = blank; 904 } 905 } 906 907 /* 908 * ClrBottom(total) 909 * 910 * Test if clearing the end of the screen would satisfy part of the 911 * screen-update. Do this by scanning backwards through the lines in the 912 * screen, checking if each is blank, and one or more are changed. 913 */ 914 static int 915 ClrBottom(int total) 916 { 917 static chtype *tstLine; 918 static size_t lenLine; 919 920 int row; 921 size_t col; 922 int top = total; 923 int last = min(screen_columns, newscr->_maxx + 1); 924 size_t length = sizeof(chtype) * last; 925 chtype blank = newscr->_line[total - 1].text[last - 1]; /* lower right char */ 926 927 if (!clr_eos || !can_clear_with(blank)) 928 return total; 929 930 if ((tstLine == 0) || (last > (int) lenLine)) { 931 tstLine = typeRealloc(chtype, last, tstLine); 932 if (tstLine == 0) 933 return total; 934 lenLine = last; 935 tstLine[0] = ~blank; /* force the fill below */ 936 } 937 if (tstLine[0] != blank) { 938 for (col = 0; col < lenLine; col++) 939 tstLine[col] = blank; 940 } 941 942 for (row = total - 1; row >= 0; row--) { 943 if (memcmp(tstLine, newscr->_line[row].text, length)) 944 break; 945 if (memcmp(tstLine, curscr->_line[row].text, length)) 946 top = row; 947 } 948 949 /* don't use clr_eos for just one line if clr_eol available */ 950 if (top < total - 1 || (top < total && !clr_eol && !clr_bol)) { 951 GoTo(top, 0); 952 ClrToEOS(blank); 953 total = top; 954 if (SP->oldhash && SP->newhash) { 955 for (row = top; row < screen_lines; row++) 956 SP->oldhash[row] = SP->newhash[row]; 957 } 958 } 959 #if NO_LEAKS 960 if (tstLine != 0) { 961 FreeAndNull(tstLine); 962 } 963 #endif 964 return total; 965 } 966 967 /* 968 ** TransformLine(lineno) 969 ** 970 ** Transform the given line in curscr to the one in newscr, using 971 ** Insert/Delete Character if _nc_idcok && has_ic(). 972 ** 973 ** firstChar = position of first different character in line 974 ** oLastChar = position of last different character in old line 975 ** nLastChar = position of last different character in new line 976 ** 977 ** move to firstChar 978 ** overwrite chars up to min(oLastChar, nLastChar) 979 ** if oLastChar < nLastChar 980 ** insert newLine[oLastChar+1..nLastChar] 981 ** else 982 ** delete oLastChar - nLastChar spaces 983 */ 984 985 static void 986 TransformLine(int const lineno) 987 { 988 int firstChar, oLastChar, nLastChar; 989 chtype *newLine = newscr->_line[lineno].text; 990 chtype *oldLine = curscr->_line[lineno].text; 991 int n; 992 bool attrchanged = FALSE; 993 994 T(("TransformLine(%d) called", lineno)); 995 996 /* copy new hash value to old one */ 997 if (SP->oldhash && SP->newhash) 998 SP->oldhash[lineno] = SP->newhash[lineno]; 999 1000 if (ceol_standout_glitch && clr_eol) { 1001 firstChar = 0; 1002 while (firstChar < screen_columns) { 1003 if (AttrOf(newLine[firstChar]) != AttrOf(oldLine[firstChar])) 1004 attrchanged = TRUE; 1005 firstChar++; 1006 } 1007 } 1008 1009 firstChar = 0; 1010 1011 if (attrchanged) { /* we may have to disregard the whole line */ 1012 GoTo(lineno, firstChar); 1013 ClrToEOL(ClrBlank(curscr)); 1014 PutRange(oldLine, newLine, lineno, 0, (screen_columns - 1)); 1015 #if USE_XMC_SUPPORT 1016 1017 #define NEW(r,c) newscr->_line[r].text[c] 1018 #define xmc_turn_on(a,b) ((((a)^(b)) & ~(a) & SP->_xmc_triggers) != 0) 1019 #define xmc_turn_off(a,b) xmc_turn_on(b,a) 1020 1021 /* 1022 * This is a very simple loop to paint characters which may have the 1023 * magic cookie glitch embedded. It doesn't know much about video 1024 * attributes which are continued from one line to the next. It 1025 * assumes that we have filtered out requests for attribute changes 1026 * that do not get mapped to blank positions. 1027 * 1028 * FIXME: we are not keeping track of where we put the cookies, so this 1029 * will work properly only once, since we may overwrite a cookie in a 1030 * following operation. 1031 */ 1032 } else if (magic_cookie_glitch > 0) { 1033 GoTo(lineno, firstChar); 1034 for (n = 0; n < screen_columns; n++) { 1035 int m = n + magic_cookie_glitch; 1036 1037 /* check for turn-on: 1038 * If we are writing an attributed blank, where the 1039 * previous cell is not attributed. 1040 */ 1041 if (TextOf(newLine[n]) == ' ' 1042 && ((n > 0 1043 && xmc_turn_on(newLine[n - 1], newLine[n])) 1044 || (n == 0 1045 && lineno > 0 1046 && xmc_turn_on(NEW(lineno - 1, screen_columns - 1), 1047 newLine[n])))) { 1048 n = m; 1049 } 1050 1051 PutChar(newLine[n]); 1052 1053 /* check for turn-off: 1054 * If we are writing an attributed non-blank, where the 1055 * next cell is blank, and not attributed. 1056 */ 1057 if (TextOf(newLine[n]) != ' ' 1058 && ((n + 1 < screen_columns 1059 && xmc_turn_off(newLine[n], newLine[n + 1])) 1060 || (n + 1 >= screen_columns 1061 && lineno + 1 < screen_lines 1062 && xmc_turn_off(newLine[n], NEW(lineno + 1, 0))))) { 1063 n = m; 1064 } 1065 1066 } 1067 #undef NEW 1068 #endif 1069 } else { 1070 chtype blank; 1071 1072 /* find the first differing character */ 1073 while (firstChar < screen_columns && 1074 newLine[firstChar] == oldLine[firstChar]) 1075 firstChar++; 1076 1077 /* if there wasn't one, we're done */ 1078 if (firstChar >= screen_columns) 1079 return; 1080 1081 /* it may be cheap to clear leading whitespace with clr_bol */ 1082 if (clr_bol && can_clear_with(blank = newLine[0])) { 1083 int oFirstChar, nFirstChar; 1084 1085 for (oFirstChar = 0; oFirstChar < screen_columns; oFirstChar++) 1086 if (oldLine[oFirstChar] != blank) 1087 break; 1088 for (nFirstChar = 0; nFirstChar < screen_columns; nFirstChar++) 1089 if (newLine[nFirstChar] != blank) 1090 break; 1091 1092 if (nFirstChar > oFirstChar + SP->_el1_cost) { 1093 if (nFirstChar >= screen_columns && SP->_el_cost <= SP->_el1_cost) { 1094 GoTo(lineno, 0); 1095 UpdateAttrs(blank); 1096 TPUTS_TRACE("clr_eol"); 1097 putp(clr_eol); 1098 } else { 1099 GoTo(lineno, nFirstChar - 1); 1100 UpdateAttrs(blank); 1101 TPUTS_TRACE("clr_bol"); 1102 putp(clr_bol); 1103 } 1104 1105 while (firstChar < nFirstChar) 1106 oldLine[firstChar++] = blank; 1107 1108 if (firstChar >= screen_columns) 1109 return; 1110 } 1111 } 1112 1113 blank = newLine[screen_columns - 1]; 1114 1115 if (!can_clear_with(blank)) { 1116 /* find the last differing character */ 1117 nLastChar = screen_columns - 1; 1118 1119 while (nLastChar > firstChar 1120 && newLine[nLastChar] == oldLine[nLastChar]) 1121 nLastChar--; 1122 1123 if (nLastChar >= firstChar) { 1124 GoTo(lineno, firstChar); 1125 PutRange(oldLine, newLine, lineno, firstChar, nLastChar); 1126 memcpy(oldLine + firstChar, 1127 newLine + firstChar, 1128 (nLastChar - firstChar + 1) * sizeof(chtype)); 1129 } 1130 return; 1131 } 1132 1133 /* find last non-blank character on old line */ 1134 oLastChar = screen_columns - 1; 1135 while (oLastChar > firstChar && oldLine[oLastChar] == blank) 1136 oLastChar--; 1137 1138 /* find last non-blank character on new line */ 1139 nLastChar = screen_columns - 1; 1140 while (nLastChar > firstChar && newLine[nLastChar] == blank) 1141 nLastChar--; 1142 1143 if ((nLastChar == firstChar) 1144 && (SP->_el_cost < (oLastChar - nLastChar))) { 1145 GoTo(lineno, firstChar); 1146 if (newLine[firstChar] != blank) 1147 PutChar(newLine[firstChar]); 1148 ClrToEOL(blank); 1149 } else if ((nLastChar != oLastChar) 1150 && (newLine[nLastChar] != oldLine[oLastChar] 1151 || !(_nc_idcok && has_ic()))) { 1152 GoTo(lineno, firstChar); 1153 if ((oLastChar - nLastChar) > SP->_el_cost) { 1154 if (PutRange(oldLine, newLine, lineno, firstChar, nLastChar)) 1155 GoTo(lineno, nLastChar + 1); 1156 ClrToEOL(blank); 1157 } else { 1158 n = max(nLastChar, oLastChar); 1159 PutRange(oldLine, newLine, lineno, firstChar, n); 1160 } 1161 } else { 1162 int nLastNonblank = nLastChar; 1163 int oLastNonblank = oLastChar; 1164 1165 /* find the last characters that really differ */ 1166 while (newLine[nLastChar] == oldLine[oLastChar]) { 1167 if (nLastChar != 0 1168 && oLastChar != 0) { 1169 nLastChar--; 1170 oLastChar--; 1171 } else { 1172 break; 1173 } 1174 } 1175 1176 n = min(oLastChar, nLastChar); 1177 if (n >= firstChar) { 1178 GoTo(lineno, firstChar); 1179 PutRange(oldLine, newLine, lineno, firstChar, n); 1180 } 1181 1182 if (oLastChar < nLastChar) { 1183 int m = max(nLastNonblank, oLastNonblank); 1184 GoTo(lineno, n + 1); 1185 if (InsCharCost(nLastChar - oLastChar) 1186 > (m - n)) { 1187 PutRange(oldLine, newLine, lineno, n + 1, m); 1188 } else { 1189 InsStr(&newLine[n + 1], nLastChar - oLastChar); 1190 } 1191 } else if (oLastChar > nLastChar) { 1192 GoTo(lineno, n + 1); 1193 if (DelCharCost(oLastChar - nLastChar) 1194 > SP->_el_cost + nLastNonblank - (n + 1)) { 1195 if (PutRange(oldLine, newLine, lineno, 1196 n + 1, nLastNonblank)) 1197 GoTo(lineno, nLastNonblank + 1); 1198 ClrToEOL(blank); 1199 } else { 1200 /* 1201 * The delete-char sequence will 1202 * effectively shift in blanks from the 1203 * right margin of the screen. Ensure 1204 * that they are the right color by 1205 * setting the video attributes from 1206 * the last character on the row. 1207 */ 1208 UpdateAttrs(blank); 1209 DelChar(oLastChar - nLastChar); 1210 } 1211 } 1212 } 1213 } 1214 1215 /* update the code's internal representation */ 1216 if (screen_columns > firstChar) 1217 memcpy(oldLine + firstChar, 1218 newLine + firstChar, 1219 (screen_columns - firstChar) * sizeof(chtype)); 1220 } 1221 1222 /* 1223 ** ClearScreen(blank) 1224 ** 1225 ** Clear the physical screen and put cursor at home 1226 ** 1227 */ 1228 1229 static void 1230 ClearScreen(chtype blank) 1231 { 1232 int i, j; 1233 bool fast_clear = (clear_screen || clr_eos || clr_eol); 1234 1235 T(("ClearScreen() called")); 1236 1237 #ifdef NCURSES_EXT_FUNCS 1238 if (SP->_coloron 1239 && !SP->_default_color) { 1240 _nc_do_color(0, FALSE, _nc_outch); 1241 if (!back_color_erase) { 1242 fast_clear = FALSE; 1243 } 1244 } 1245 #endif 1246 1247 if (fast_clear) { 1248 if (clear_screen) { 1249 UpdateAttrs(blank); 1250 TPUTS_TRACE("clear_screen"); 1251 putp(clear_screen); 1252 SP->_cursrow = SP->_curscol = 0; 1253 position_check(SP->_cursrow, SP->_curscol, "ClearScreen"); 1254 } else if (clr_eos) { 1255 SP->_cursrow = SP->_curscol = -1; 1256 GoTo(0, 0); 1257 1258 UpdateAttrs(blank); 1259 TPUTS_TRACE("clr_eos"); 1260 putp(clr_eos); 1261 } else if (clr_eol) { 1262 SP->_cursrow = SP->_curscol = -1; 1263 1264 for (i = 0; i < screen_lines; i++) { 1265 GoTo(i, 0); 1266 UpdateAttrs(blank); 1267 TPUTS_TRACE("clr_eol"); 1268 putp(clr_eol); 1269 } 1270 GoTo(0, 0); 1271 } 1272 } else { 1273 for (i = 0; i < screen_lines; i++) { 1274 GoTo(i, 0); 1275 UpdateAttrs(blank); 1276 for (j = 0; j < screen_columns; j++) 1277 PutChar(blank); 1278 } 1279 GoTo(0, 0); 1280 } 1281 1282 for (i = 0; i < screen_lines; i++) { 1283 for (j = 0; j < screen_columns; j++) 1284 curscr->_line[i].text[j] = blank; 1285 } 1286 1287 T(("screen cleared")); 1288 } 1289 1290 /* 1291 ** InsStr(line, count) 1292 ** 1293 ** Insert the count characters pointed to by line. 1294 ** 1295 */ 1296 1297 static void 1298 InsStr(chtype * line, int count) 1299 { 1300 T(("InsStr(%p,%d) called", line, count)); 1301 1302 /* Prefer parm_ich as it has the smallest cost - no need to shift 1303 * the whole line on each character. */ 1304 /* The order must match that of InsCharCost. */ 1305 if (parm_ich) { 1306 TPUTS_TRACE("parm_ich"); 1307 tputs(tparm(parm_ich, count), count, _nc_outch); 1308 while (count) { 1309 PutAttrChar(*line); 1310 line++; 1311 count--; 1312 } 1313 } else if (enter_insert_mode && exit_insert_mode) { 1314 TPUTS_TRACE("enter_insert_mode"); 1315 putp(enter_insert_mode); 1316 while (count) { 1317 PutAttrChar(*line); 1318 if (insert_padding) { 1319 TPUTS_TRACE("insert_padding"); 1320 putp(insert_padding); 1321 } 1322 line++; 1323 count--; 1324 } 1325 TPUTS_TRACE("exit_insert_mode"); 1326 putp(exit_insert_mode); 1327 } else { 1328 while (count) { 1329 TPUTS_TRACE("insert_character"); 1330 putp(insert_character); 1331 PutAttrChar(*line); 1332 if (insert_padding) { 1333 TPUTS_TRACE("insert_padding"); 1334 putp(insert_padding); 1335 } 1336 line++; 1337 count--; 1338 } 1339 } 1340 position_check(SP->_cursrow, SP->_curscol, "InsStr"); 1341 } 1342 1343 /* 1344 ** DelChar(count) 1345 ** 1346 ** Delete count characters at current position 1347 ** 1348 */ 1349 1350 static void 1351 DelChar(int count) 1352 { 1353 int n; 1354 1355 T(("DelChar(%d) called, position = (%d,%d)", count, newscr->_cury, newscr->_curx)); 1356 1357 if (parm_dch) { 1358 TPUTS_TRACE("parm_dch"); 1359 tputs(tparm(parm_dch, count), count, _nc_outch); 1360 } else { 1361 for (n = 0; n < count; n++) { 1362 TPUTS_TRACE("delete_character"); 1363 putp(delete_character); 1364 } 1365 } 1366 } 1367 1368 /* 1369 ** _nc_outstr(char *str) 1370 ** 1371 ** Emit a string without waiting for update. 1372 */ 1373 1374 void 1375 _nc_outstr(const char *str) 1376 { 1377 (void) putp(str); 1378 _nc_flush(); 1379 } 1380 1381 /* 1382 * Physical-scrolling support 1383 * 1384 * This code was adapted from Keith Bostic's hardware scrolling 1385 * support for 4.4BSD curses. I (esr) translated it to use terminfo 1386 * capabilities, narrowed the call interface slightly, and cleaned 1387 * up some convoluted tests. I also added support for the memory_above 1388 * memory_below, and non_dest_scroll_region capabilities. 1389 * 1390 * For this code to work, we must have either 1391 * change_scroll_region and scroll forward/reverse commands, or 1392 * insert and delete line capabilities. 1393 * When the scrolling region has been set, the cursor has to 1394 * be at the last line of the region to make the scroll up 1395 * happen, or on the first line of region to scroll down. 1396 * 1397 * This code makes one aesthetic decision in the opposite way from 1398 * BSD curses. BSD curses preferred pairs of il/dl operations 1399 * over scrolls, allegedly because il/dl looked faster. We, on 1400 * the other hand, prefer scrolls because (a) they're just as fast 1401 * on many terminals and (b) using them avoids bouncing an 1402 * unchanged bottom section of the screen up and down, which is 1403 * visually nasty. 1404 * 1405 * (lav): added more cases, used dl/il when bot==maxy and in csr case. 1406 * 1407 * I used assumption that capabilities il/il1/dl/dl1 work inside 1408 * changed scroll region not shifting screen contents outside of it. 1409 * If there are any terminals behaving different way, it would be 1410 * necessary to add some conditions to scroll_csr_forward/backward. 1411 */ 1412 1413 /* Try to scroll up assuming given csr (miny, maxy). Returns ERR on failure */ 1414 static int 1415 scroll_csr_forward(int n, int top, int bot, int miny, int maxy, chtype blank) 1416 { 1417 int i, j; 1418 1419 if (n == 1 && scroll_forward && top == miny && bot == maxy) { 1420 GoTo(bot, 0); 1421 UpdateAttrs(blank); 1422 TPUTS_TRACE("scroll_forward"); 1423 tputs(scroll_forward, 0, _nc_outch); 1424 } else if (n == 1 && delete_line && bot == maxy) { 1425 GoTo(top, 0); 1426 UpdateAttrs(blank); 1427 TPUTS_TRACE("delete_line"); 1428 tputs(delete_line, 0, _nc_outch); 1429 } else if (parm_index && top == miny && bot == maxy) { 1430 GoTo(bot, 0); 1431 UpdateAttrs(blank); 1432 TPUTS_TRACE("parm_index"); 1433 tputs(tparm(parm_index, n, 0), n, _nc_outch); 1434 } else if (parm_delete_line && bot == maxy) { 1435 GoTo(top, 0); 1436 UpdateAttrs(blank); 1437 TPUTS_TRACE("parm_delete_line"); 1438 tputs(tparm(parm_delete_line, n, 0), n, _nc_outch); 1439 } else if (scroll_forward && top == miny && bot == maxy) { 1440 GoTo(bot, 0); 1441 UpdateAttrs(blank); 1442 for (i = 0; i < n; i++) { 1443 TPUTS_TRACE("scroll_forward"); 1444 tputs(scroll_forward, 0, _nc_outch); 1445 } 1446 } else if (delete_line && bot == maxy) { 1447 GoTo(top, 0); 1448 UpdateAttrs(blank); 1449 for (i = 0; i < n; i++) { 1450 TPUTS_TRACE("delete_line"); 1451 tputs(delete_line, 0, _nc_outch); 1452 } 1453 } else 1454 return ERR; 1455 1456 #ifdef NCURSES_EXT_FUNCS 1457 if (FILL_BCE()) { 1458 for (i = 0; i < n; i++) { 1459 GoTo(bot-i, 0); 1460 for (j = 0; j < screen_columns; j++) 1461 PutChar(blank); 1462 } 1463 } 1464 #endif 1465 return OK; 1466 } 1467 1468 /* Try to scroll down assuming given csr (miny, maxy). Returns ERR on failure */ 1469 /* n > 0 */ 1470 static int 1471 scroll_csr_backward(int n, int top, int bot, int miny, int maxy, chtype blank) 1472 { 1473 int i, j; 1474 1475 if (n == 1 && scroll_reverse && top == miny && bot == maxy) { 1476 GoTo(top, 0); 1477 UpdateAttrs(blank); 1478 TPUTS_TRACE("scroll_reverse"); 1479 tputs(scroll_reverse, 0, _nc_outch); 1480 } else if (n == 1 && insert_line && bot == maxy) { 1481 GoTo(top, 0); 1482 UpdateAttrs(blank); 1483 TPUTS_TRACE("insert_line"); 1484 tputs(insert_line, 0, _nc_outch); 1485 } else if (parm_rindex && top == miny && bot == maxy) { 1486 GoTo(top, 0); 1487 UpdateAttrs(blank); 1488 TPUTS_TRACE("parm_rindex"); 1489 tputs(tparm(parm_rindex, n, 0), n, _nc_outch); 1490 } else if (parm_insert_line && bot == maxy) { 1491 GoTo(top, 0); 1492 UpdateAttrs(blank); 1493 TPUTS_TRACE("parm_insert_line"); 1494 tputs(tparm(parm_insert_line, n, 0), n, _nc_outch); 1495 } else if (scroll_reverse && top == miny && bot == maxy) { 1496 GoTo(top, 0); 1497 UpdateAttrs(blank); 1498 for (i = 0; i < n; i++) { 1499 TPUTS_TRACE("scroll_reverse"); 1500 tputs(scroll_reverse, 0, _nc_outch); 1501 } 1502 } else if (insert_line && bot == maxy) { 1503 GoTo(top, 0); 1504 UpdateAttrs(blank); 1505 for (i = 0; i < n; i++) { 1506 TPUTS_TRACE("insert_line"); 1507 tputs(insert_line, 0, _nc_outch); 1508 } 1509 } else 1510 return ERR; 1511 1512 #ifdef NCURSES_EXT_FUNCS 1513 if (FILL_BCE()) { 1514 for (i = 0; i < n; i++) { 1515 GoTo(top+i, 0); 1516 for (j = 0; j < screen_columns; j++) 1517 PutChar(blank); 1518 } 1519 } 1520 #endif 1521 return OK; 1522 } 1523 1524 /* scroll by using delete_line at del and insert_line at ins */ 1525 /* n > 0 */ 1526 static int 1527 scroll_idl(int n, int del, int ins, chtype blank) 1528 { 1529 int i; 1530 1531 if (!((parm_delete_line || delete_line) && (parm_insert_line || insert_line))) 1532 return ERR; 1533 1534 GoTo(del, 0); 1535 UpdateAttrs(blank); 1536 if (n == 1 && delete_line) { 1537 TPUTS_TRACE("delete_line"); 1538 tputs(delete_line, 0, _nc_outch); 1539 } else if (parm_delete_line) { 1540 TPUTS_TRACE("parm_delete_line"); 1541 tputs(tparm(parm_delete_line, n, 0), n, _nc_outch); 1542 } else { /* if (delete_line) */ 1543 for (i = 0; i < n; i++) { 1544 TPUTS_TRACE("delete_line"); 1545 tputs(delete_line, 0, _nc_outch); 1546 } 1547 } 1548 1549 GoTo(ins, 0); 1550 UpdateAttrs(blank); 1551 if (n == 1 && insert_line) { 1552 TPUTS_TRACE("insert_line"); 1553 tputs(insert_line, 0, _nc_outch); 1554 } else if (parm_insert_line) { 1555 TPUTS_TRACE("parm_insert_line"); 1556 tputs(tparm(parm_insert_line, n, 0), n, _nc_outch); 1557 } else { /* if (insert_line) */ 1558 for (i = 0; i < n; i++) { 1559 TPUTS_TRACE("insert_line"); 1560 tputs(insert_line, 0, _nc_outch); 1561 } 1562 } 1563 1564 return OK; 1565 } 1566 1567 int 1568 _nc_scrolln(int n, int top, int bot, int maxy) 1569 /* scroll region from top to bot by n lines */ 1570 { 1571 chtype blank = ClrBlank(stdscr); 1572 int i; 1573 bool cursor_saved = FALSE; 1574 int res; 1575 1576 TR(TRACE_MOVE, ("mvcur_scrolln(%d, %d, %d, %d)", n, top, bot, maxy)); 1577 1578 #if USE_XMC_SUPPORT 1579 /* 1580 * If we scroll, we might remove a cookie. 1581 */ 1582 if (magic_cookie_glitch > 0) { 1583 return (ERR); 1584 } 1585 #endif 1586 1587 if (n > 0) { /* scroll up (forward) */ 1588 /* 1589 * Explicitly clear if stuff pushed off top of region might 1590 * be saved by the terminal. 1591 */ 1592 if (non_dest_scroll_region || (memory_above && top == 0)) { 1593 for (i = 0; i < n; i++) { 1594 GoTo(i, 0); 1595 ClrToEOL(BLANK); 1596 } 1597 } 1598 1599 res = scroll_csr_forward(n, top, bot, 0, maxy, blank); 1600 1601 if (res == ERR && change_scroll_region) { 1602 if ((((n == 1 && scroll_forward) || parm_index) 1603 && (SP->_cursrow == bot || SP->_cursrow == bot - 1)) 1604 && save_cursor && restore_cursor) { 1605 cursor_saved = TRUE; 1606 TPUTS_TRACE("save_cursor"); 1607 tputs(save_cursor, 0, _nc_outch); 1608 } 1609 TPUTS_TRACE("change_scroll_region"); 1610 tputs(tparm(change_scroll_region, top, bot), 0, _nc_outch); 1611 if (cursor_saved) { 1612 TPUTS_TRACE("restore_cursor"); 1613 tputs(restore_cursor, 0, _nc_outch); 1614 } else { 1615 SP->_cursrow = SP->_curscol = -1; 1616 } 1617 1618 res = scroll_csr_forward(n, top, bot, top, bot, blank); 1619 1620 TPUTS_TRACE("change_scroll_region"); 1621 tputs(tparm(change_scroll_region, 0, maxy), 0, _nc_outch); 1622 SP->_cursrow = SP->_curscol = -1; 1623 } 1624 1625 if (res == ERR && _nc_idlok) 1626 res = scroll_idl(n, top, bot - n + 1, blank); 1627 } else { /* (n < 0) - scroll down (backward) */ 1628 /* 1629 * Do explicit clear to end of region if it's possible that the 1630 * terminal might hold on to stuff we push off the end. 1631 */ 1632 if (non_dest_scroll_region || (memory_below && bot == maxy)) { 1633 if (bot == maxy && clr_eos) { 1634 GoTo(maxy + n, 0); 1635 ClrToEOS(BLANK); 1636 } else if (clr_eol) { 1637 for (i = 0; i < -n; i++) { 1638 GoTo(maxy + n + i, 0); 1639 ClrToEOL(BLANK); 1640 } 1641 } 1642 } 1643 1644 res = scroll_csr_backward(-n, top, bot, 0, maxy, blank); 1645 1646 if (res == ERR && change_scroll_region) { 1647 if (top != 0 && (SP->_cursrow == top || SP->_cursrow == top - 1) 1648 && save_cursor && restore_cursor) { 1649 cursor_saved = TRUE; 1650 TPUTS_TRACE("save_cursor"); 1651 tputs(save_cursor, 0, _nc_outch); 1652 } 1653 TPUTS_TRACE("change_scroll_region"); 1654 tputs(tparm(change_scroll_region, top, bot), 0, _nc_outch); 1655 if (cursor_saved) { 1656 TPUTS_TRACE("restore_cursor"); 1657 tputs(restore_cursor, 0, _nc_outch); 1658 } else { 1659 SP->_cursrow = SP->_curscol = -1; 1660 } 1661 1662 res = scroll_csr_backward(-n, top, bot, top, bot, blank); 1663 1664 TPUTS_TRACE("change_scroll_region"); 1665 tputs(tparm(change_scroll_region, 0, maxy), 0, _nc_outch); 1666 SP->_cursrow = SP->_curscol = -1; 1667 } 1668 1669 if (res == ERR && _nc_idlok) 1670 res = scroll_idl(-n, bot + n + 1, top, blank); 1671 } 1672 1673 if (res == ERR) 1674 return (ERR); 1675 1676 _nc_scroll_window(curscr, n, top, bot, blank); 1677 1678 /* shift hash values too - they can be reused */ 1679 _nc_scroll_oldhash(n, top, bot); 1680 1681 return (OK); 1682 } 1683 1684 void 1685 _nc_screen_resume(void) 1686 { 1687 /* make sure terminal is in a sane known state */ 1688 SP->_current_attr = A_NORMAL; 1689 newscr->_clear = TRUE; 1690 1691 if (SP->_coloron == TRUE && orig_pair) 1692 putp(orig_pair); 1693 if (exit_attribute_mode) 1694 putp(exit_attribute_mode); 1695 else { 1696 /* turn off attributes */ 1697 if (exit_alt_charset_mode) 1698 putp(exit_alt_charset_mode); 1699 if (exit_standout_mode) 1700 putp(exit_standout_mode); 1701 if (exit_underline_mode) 1702 putp(exit_underline_mode); 1703 } 1704 if (exit_insert_mode) 1705 putp(exit_insert_mode); 1706 if (enter_am_mode && exit_am_mode) 1707 putp(auto_right_margin ? enter_am_mode : exit_am_mode); 1708 } 1709 1710 void 1711 _nc_screen_init(void) 1712 { 1713 _nc_screen_resume(); 1714 } 1715 1716 /* wrap up screen handling */ 1717 void 1718 _nc_screen_wrap(void) 1719 { 1720 UpdateAttrs(A_NORMAL); 1721 #ifdef NCURSES_EXT_FUNCS 1722 if (SP->_coloron 1723 && !SP->_default_color) { 1724 SP->_default_color = TRUE; 1725 _nc_do_color(0, FALSE, _nc_outch); 1726 SP->_default_color = FALSE; 1727 } 1728 #endif 1729 } 1730 1731 #if USE_XMC_SUPPORT 1732 void 1733 _nc_do_xmc_glitch(attr_t previous) 1734 { 1735 attr_t chg = XMC_CHANGES(previous ^ SP->_current_attr); 1736 1737 while (chg != 0) { 1738 if (chg & 1) { 1739 SP->_curscol += magic_cookie_glitch; 1740 if (SP->_curscol >= SP->_columns) 1741 wrap_cursor(); 1742 T(("bumped to %d,%d after cookie", SP->_cursrow, SP->_curscol)); 1743 } 1744 chg >>= 1; 1745 } 1746 } 1747 #endif /* USE_XMC_SUPPORT */ 1748