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