1 /* $OpenBSD: tty_update.c,v 1.9 2000/06/19 03:53:55 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.136 2000/05/20 23:28:00 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_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_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 int cost = min(SP->_cup_ch_cost, SP->_hpa_ch_cost); 481 482 TR(TRACE_CHARPUT, ("PutRange(%p, %p, %d, %d, %d)", 483 otext, ntext, row, first, last)); 484 485 if (otext != ntext 486 && (last - first + 1) > cost) { 487 for (j = first, run = 0; j <= last; j++) { 488 if (otext[j] == ntext[j]) { 489 run++; 490 } else { 491 if (run > cost) { 492 int before_run = (j - run); 493 EmitRange(ntext + first, before_run - first); 494 GoTo(row, first = j); 495 } 496 run = 0; 497 } 498 } 499 } 500 return EmitRange(ntext + first, last - first + 1); 501 } 502 503 #if CC_HAS_INLINE_FUNCS 504 static void 505 callPutChar(chtype const ch) 506 { 507 PutChar(ch); 508 } 509 #endif 510 511 /* leave unbracketed here so 'indent' works */ 512 #define MARK_NOCHANGE(win,row) \ 513 win->_line[row].firstchar = _NOCHANGE; \ 514 win->_line[row].lastchar = _NOCHANGE; \ 515 if_USE_SCROLL_HINTS(win->_line[row].oldindex = row) 516 517 int 518 doupdate(void) 519 { 520 int i; 521 int nonempty; 522 #if USE_TRACE_TIMES 523 struct tms before, after; 524 #endif /* USE_TRACE_TIMES */ 525 526 T((T_CALLED("doupdate()"))); 527 528 #ifdef TRACE 529 if (_nc_tracing & TRACE_UPDATE) { 530 if (curscr->_clear) 531 _tracef("curscr is clear"); 532 else 533 _tracedump("curscr", curscr); 534 _tracedump("newscr", newscr); 535 } 536 #endif /* TRACE */ 537 538 _nc_signal_handler(FALSE); 539 540 if (SP->_fifohold) 541 SP->_fifohold--; 542 543 #if USE_SIZECHANGE 544 if (SP->_endwin || SP->_sig_winch) { 545 /* 546 * This is a transparent extension: XSI does not address it, 547 * and applications need not know that ncurses can do it. 548 * 549 * Check if the terminal size has changed while curses was off 550 * (this can happen in an xterm, for example), and resize the 551 * ncurses data structures accordingly. 552 */ 553 _nc_update_screensize(); 554 } 555 #endif 556 557 if (SP->_endwin) { 558 559 T(("coming back from shell mode")); 560 reset_prog_mode(); 561 562 _nc_mvcur_resume(); 563 _nc_screen_resume(); 564 SP->_mouse_resume(SP); 565 566 SP->_endwin = FALSE; 567 } 568 #if USE_TRACE_TIMES 569 /* zero the metering machinery */ 570 _nc_outchars = 0; 571 (void) times(&before); 572 #endif /* USE_TRACE_TIMES */ 573 574 /* 575 * This is the support for magic-cookie terminals. The 576 * theory: we scan the virtual screen looking for attribute 577 * turnons. Where we find one, check to make sure it's 578 * realizable by seeing if the required number of 579 * un-attributed blanks are present before and after the 580 * attributed range; try to shift the range boundaries over 581 * blanks (not changing the screen display) so this becomes 582 * true. If it is, shift the beginning attribute change 583 * appropriately (the end one, if we've gotten this far, is 584 * guaranteed room for its cookie). If not, nuke the added 585 * attributes out of the span. 586 */ 587 #if USE_XMC_SUPPORT 588 if (magic_cookie_glitch > 0) { 589 int j, k; 590 attr_t rattr = A_NORMAL; 591 592 for (i = 0; i < screen_lines; i++) { 593 for (j = 0; j < screen_columns; j++) { 594 bool failed = FALSE; 595 chtype turnon = AttrOf(newscr->_line[i].text[j]) & ~rattr; 596 597 /* is an attribute turned on here? */ 598 if (turnon == 0) { 599 rattr = AttrOf(newscr->_line[i].text[j]); 600 continue; 601 } 602 603 T(("At (%d, %d): from %s...", i, j, _traceattr(rattr))); 604 T(("...to %s", _traceattr(turnon))); 605 606 /* 607 * If the attribute change location is a blank with a 608 * "safe" attribute, undo the attribute turnon. This may 609 * ensure there's enough room to set the attribute before 610 * the first non-blank in the run. 611 */ 612 #define SAFE(a) (!((a) & (chtype)~NONBLANK_ATTR)) 613 if (TextOf(newscr->_line[i].text[j]) == ' ' && SAFE(turnon)) { 614 newscr->_line[i].text[j] &= ~turnon; 615 continue; 616 } 617 618 /* check that there's enough room at start of span */ 619 for (k = 1; k <= magic_cookie_glitch; k++) { 620 if (j - k < 0 621 || TextOf(newscr->_line[i].text[j - k]) != ' ' 622 || !SAFE(AttrOf(newscr->_line[i].text[j - k]))) 623 failed = TRUE; 624 } 625 if (!failed) { 626 bool end_onscreen = FALSE; 627 int m, n = j; 628 629 /* find end of span, if it's onscreen */ 630 for (m = i; m < screen_lines; m++) { 631 for (; n < screen_columns; n++) { 632 if (AttrOf(newscr->_line[m].text[n]) == rattr) { 633 end_onscreen = TRUE; 634 T(("Range attributed with %s ends at (%d, %d)", 635 _traceattr(turnon), m, n)); 636 goto foundit; 637 } 638 } 639 n = 0; 640 } 641 T(("Range attributed with %s ends offscreen", 642 _traceattr(turnon))); 643 foundit:; 644 645 if (end_onscreen) { 646 chtype *lastline = newscr->_line[m].text; 647 648 /* 649 * If there are safely-attributed blanks at the 650 * end of the range, shorten the range. This will 651 * help ensure that there is enough room at end 652 * of span. 653 */ 654 while (n >= 0 655 && TextOf(lastline[n]) == ' ' 656 && SAFE(AttrOf(lastline[n]))) 657 lastline[n--] &= ~turnon; 658 659 /* check that there's enough room at end of span */ 660 for (k = 1; k <= magic_cookie_glitch; k++) 661 if (n + k >= screen_columns 662 || TextOf(lastline[n + k]) != ' ' 663 || !SAFE(AttrOf(lastline[n + k]))) 664 failed = TRUE; 665 } 666 } 667 668 if (failed) { 669 int p, q = j; 670 671 T(("Clearing %s beginning at (%d, %d)", 672 _traceattr(turnon), i, j)); 673 674 /* turn off new attributes over span */ 675 for (p = i; p < screen_lines; p++) { 676 for (; q < screen_columns; q++) { 677 if (AttrOf(newscr->_line[p].text[q]) == rattr) 678 goto foundend; 679 newscr->_line[p].text[q] &= ~turnon; 680 } 681 q = 0; 682 } 683 foundend:; 684 } else { 685 T(("Cookie space for %s found before (%d, %d)", 686 _traceattr(turnon), i, j)); 687 688 /* 689 * back up the start of range so there's room 690 * for cookies before the first nonblank character 691 */ 692 for (k = 1; k <= magic_cookie_glitch; k++) 693 newscr->_line[i].text[j - k] |= turnon; 694 } 695 696 rattr = AttrOf(newscr->_line[i].text[j]); 697 } 698 } 699 700 #ifdef TRACE 701 /* show altered highlights after magic-cookie check */ 702 if (_nc_tracing & TRACE_UPDATE) { 703 _tracef("After magic-cookie check..."); 704 _tracedump("newscr", newscr); 705 } 706 #endif /* TRACE */ 707 } 708 #endif /* USE_XMC_SUPPORT */ 709 710 nonempty = 0; 711 if (curscr->_clear || newscr->_clear) { /* force refresh ? */ 712 T(("clearing and updating from scratch")); 713 ClrUpdate(); 714 curscr->_clear = FALSE; /* reset flag */ 715 newscr->_clear = FALSE; /* reset flag */ 716 } else { 717 int changedlines = CHECK_INTERVAL; 718 719 if (check_pending()) 720 goto cleanup; 721 722 nonempty = min(screen_lines, newscr->_maxy + 1); 723 724 if (SP->_scrolling) { 725 _nc_scroll_optimize(); 726 } 727 728 nonempty = ClrBottom(nonempty); 729 730 T(("Transforming lines, nonempty %d", nonempty)); 731 for (i = 0; i < nonempty; i++) { 732 /* 733 * Here is our line-breakout optimization. 734 */ 735 if (changedlines == CHECK_INTERVAL) { 736 if (check_pending()) 737 goto cleanup; 738 changedlines = 0; 739 } 740 741 /* 742 * newscr->line[i].firstchar is normally set 743 * by wnoutrefresh. curscr->line[i].firstchar 744 * is normally set by _nc_scroll_window in the 745 * vertical-movement optimization code, 746 */ 747 if (newscr->_line[i].firstchar != _NOCHANGE 748 || curscr->_line[i].firstchar != _NOCHANGE) { 749 TransformLine(i); 750 changedlines++; 751 } 752 753 /* mark line changed successfully */ 754 if (i <= newscr->_maxy) { 755 MARK_NOCHANGE(newscr, i) 756 } 757 if (i <= curscr->_maxy) { 758 MARK_NOCHANGE(curscr, i) 759 } 760 } 761 } 762 763 /* put everything back in sync */ 764 for (i = nonempty; i <= newscr->_maxy; i++) { 765 MARK_NOCHANGE(newscr, i) 766 } 767 for (i = nonempty; i <= curscr->_maxy; i++) { 768 MARK_NOCHANGE(curscr, i) 769 } 770 771 if (!newscr->_leaveok) { 772 curscr->_curx = newscr->_curx; 773 curscr->_cury = newscr->_cury; 774 775 GoTo(curscr->_cury, curscr->_curx); 776 } 777 778 cleanup: 779 /* 780 * Keep the physical screen in normal mode in case we get other 781 * processes writing to the screen. 782 */ 783 UpdateAttrs(A_NORMAL); 784 785 _nc_flush(); 786 curscr->_attrs = newscr->_attrs; 787 788 #if USE_TRACE_TIMES 789 (void) times(&after); 790 TR(TRACE_TIMES, 791 ("Update cost: %ld chars, %ld clocks system time, %ld clocks user time", 792 _nc_outchars, 793 after.tms_stime - before.tms_stime, 794 after.tms_utime - before.tms_utime)); 795 #endif /* USE_TRACE_TIMES */ 796 797 _nc_signal_handler(TRUE); 798 799 returnCode(OK); 800 } 801 802 /* 803 * ClrBlank(win) 804 * 805 * Returns the attributed character that corresponds to the "cleared" 806 * screen. If the terminal has the back-color-erase feature, this will be 807 * colored according to the wbkgd() call. 808 * 809 * We treat 'curscr' specially because it isn't supposed to be set directly 810 * in the wbkgd() call. Assume 'stdscr' for this case. 811 */ 812 #define BCE_ATTRS (A_NORMAL|A_COLOR) 813 #define BCE_BKGD(win) (((win) == curscr ? stdscr : (win))->_bkgd) 814 815 static inline chtype 816 ClrBlank(WINDOW *win) 817 { 818 chtype blank = BLANK; 819 if (back_color_erase) 820 blank |= (BCE_BKGD(win) & BCE_ATTRS); 821 return blank; 822 } 823 824 /* 825 ** ClrUpdate() 826 ** 827 ** Update by clearing and redrawing the entire screen. 828 ** 829 */ 830 831 static void 832 ClrUpdate(void) 833 { 834 int i; 835 chtype blank = ClrBlank(stdscr); 836 int nonempty = min(screen_lines, newscr->_maxy + 1); 837 838 T(("ClrUpdate() called")); 839 840 ClearScreen(blank); 841 842 T(("updating screen from scratch")); 843 844 nonempty = ClrBottom(nonempty); 845 846 for (i = 0; i < nonempty; i++) 847 TransformLine(i); 848 } 849 850 /* 851 ** ClrToEOL(blank) 852 ** 853 ** Clear to end of current line, starting at the cursor position 854 */ 855 856 static void 857 ClrToEOL(chtype blank, bool needclear) 858 { 859 int j; 860 861 if (curscr != 0 862 && SP->_cursrow >= 0 863 && SP->_curscol >= 0) { 864 for (j = SP->_curscol; j < screen_columns; j++) { 865 chtype *cp = &(curscr->_line[SP->_cursrow].text[j]); 866 867 if (*cp != blank) { 868 *cp = blank; 869 needclear = TRUE; 870 } 871 } 872 } else { 873 needclear = TRUE; 874 } 875 876 if (needclear) { 877 UpdateAttrs(blank); 878 TPUTS_TRACE("clr_eol"); 879 if (SP->_el_cost > (screen_columns - SP->_curscol)) { 880 int count = (screen_columns - SP->_curscol); 881 while (count-- > 0) 882 PutChar(blank); 883 } else { 884 putp(clr_eol); 885 } 886 } 887 } 888 889 /* 890 ** ClrToEOS(blank) 891 ** 892 ** Clear to end of screen, starting at the cursor position 893 */ 894 895 static void 896 ClrToEOS(chtype blank) 897 { 898 int row, col; 899 900 row = SP->_cursrow; 901 col = SP->_curscol; 902 903 { 904 UpdateAttrs(blank); 905 TPUTS_TRACE("clr_eos"); 906 tputs(clr_eos, screen_lines - row, _nc_outch); 907 } 908 909 while (col < screen_columns) 910 curscr->_line[row].text[col++] = blank; 911 912 for (row++; row < screen_lines; row++) { 913 for (col = 0; col < screen_columns; col++) 914 curscr->_line[row].text[col] = blank; 915 } 916 } 917 918 /* 919 * ClrBottom(total) 920 * 921 * Test if clearing the end of the screen would satisfy part of the 922 * screen-update. Do this by scanning backwards through the lines in the 923 * screen, checking if each is blank, and one or more are changed. 924 */ 925 static int 926 ClrBottom(int total) 927 { 928 static chtype *tstLine; 929 static size_t lenLine; 930 931 int row; 932 size_t col; 933 int top = total; 934 int last = min(screen_columns, newscr->_maxx + 1); 935 size_t length = sizeof(chtype) * last; 936 chtype blank = newscr->_line[total - 1].text[last - 1]; /* lower right char */ 937 938 if (!clr_eos || !can_clear_with(blank)) 939 return total; 940 941 if ((tstLine == 0) || (last > (int) lenLine)) { 942 tstLine = typeRealloc(chtype, last, tstLine); 943 if (tstLine == 0) 944 return total; 945 lenLine = last; 946 tstLine[0] = ~blank; /* force the fill below */ 947 } 948 if (tstLine[0] != blank) { 949 for (col = 0; col < lenLine; col++) 950 tstLine[col] = blank; 951 } 952 953 for (row = total - 1; row >= 0; row--) { 954 if (memcmp(tstLine, newscr->_line[row].text, length)) 955 break; 956 if (memcmp(tstLine, curscr->_line[row].text, length)) 957 top = row; 958 } 959 960 /* don't use clr_eos for just one line if clr_eol available */ 961 if (top < total - 1 || (top < total && !clr_eol && !clr_bol)) { 962 GoTo(top, 0); 963 ClrToEOS(blank); 964 total = top; 965 if (SP->oldhash && SP->newhash) { 966 for (row = top; row < screen_lines; row++) 967 SP->oldhash[row] = SP->newhash[row]; 968 } 969 } 970 #if NO_LEAKS 971 if (tstLine != 0) { 972 FreeAndNull(tstLine); 973 } 974 #endif 975 return total; 976 } 977 978 /* 979 ** TransformLine(lineno) 980 ** 981 ** Transform the given line in curscr to the one in newscr, using 982 ** Insert/Delete Character if _nc_idcok && has_ic(). 983 ** 984 ** firstChar = position of first different character in line 985 ** oLastChar = position of last different character in old line 986 ** nLastChar = position of last different character in new line 987 ** 988 ** move to firstChar 989 ** overwrite chars up to min(oLastChar, nLastChar) 990 ** if oLastChar < nLastChar 991 ** insert newLine[oLastChar+1..nLastChar] 992 ** else 993 ** delete oLastChar - nLastChar spaces 994 */ 995 996 static void 997 TransformLine(int const lineno) 998 { 999 int firstChar, oLastChar, nLastChar; 1000 chtype *newLine = newscr->_line[lineno].text; 1001 chtype *oldLine = curscr->_line[lineno].text; 1002 int n; 1003 bool attrchanged = FALSE; 1004 1005 T(("TransformLine(%d) called", lineno)); 1006 1007 /* copy new hash value to old one */ 1008 if (SP->oldhash && SP->newhash) 1009 SP->oldhash[lineno] = SP->newhash[lineno]; 1010 1011 if (ceol_standout_glitch && clr_eol) { 1012 firstChar = 0; 1013 while (firstChar < screen_columns) { 1014 if (AttrOf(newLine[firstChar]) != AttrOf(oldLine[firstChar])) 1015 attrchanged = TRUE; 1016 firstChar++; 1017 } 1018 } 1019 1020 firstChar = 0; 1021 1022 if (attrchanged) { /* we may have to disregard the whole line */ 1023 GoTo(lineno, firstChar); 1024 ClrToEOL(ClrBlank(curscr), FALSE); 1025 PutRange(oldLine, newLine, lineno, 0, (screen_columns - 1)); 1026 #if USE_XMC_SUPPORT 1027 1028 #define NEW(r,c) newscr->_line[r].text[c] 1029 #define xmc_turn_on(a,b) ((((a)^(b)) & ~(a) & SP->_xmc_triggers) != 0) 1030 #define xmc_turn_off(a,b) xmc_turn_on(b,a) 1031 1032 /* 1033 * This is a very simple loop to paint characters which may have the 1034 * magic cookie glitch embedded. It doesn't know much about video 1035 * attributes which are continued from one line to the next. It 1036 * assumes that we have filtered out requests for attribute changes 1037 * that do not get mapped to blank positions. 1038 * 1039 * FIXME: we are not keeping track of where we put the cookies, so this 1040 * will work properly only once, since we may overwrite a cookie in a 1041 * following operation. 1042 */ 1043 } else if (magic_cookie_glitch > 0) { 1044 GoTo(lineno, firstChar); 1045 for (n = 0; n < screen_columns; n++) { 1046 int m = n + magic_cookie_glitch; 1047 1048 /* check for turn-on: 1049 * If we are writing an attributed blank, where the 1050 * previous cell is not attributed. 1051 */ 1052 if (TextOf(newLine[n]) == ' ' 1053 && ((n > 0 1054 && xmc_turn_on(newLine[n - 1], newLine[n])) 1055 || (n == 0 1056 && lineno > 0 1057 && xmc_turn_on(NEW(lineno - 1, screen_columns - 1), 1058 newLine[n])))) { 1059 n = m; 1060 } 1061 1062 PutChar(newLine[n]); 1063 1064 /* check for turn-off: 1065 * If we are writing an attributed non-blank, where the 1066 * next cell is blank, and not attributed. 1067 */ 1068 if (TextOf(newLine[n]) != ' ' 1069 && ((n + 1 < screen_columns 1070 && xmc_turn_off(newLine[n], newLine[n + 1])) 1071 || (n + 1 >= screen_columns 1072 && lineno + 1 < screen_lines 1073 && xmc_turn_off(newLine[n], NEW(lineno + 1, 0))))) { 1074 n = m; 1075 } 1076 1077 } 1078 #undef NEW 1079 #endif 1080 } else { 1081 chtype blank; 1082 1083 /* find the first differing character */ 1084 while (firstChar < screen_columns && 1085 newLine[firstChar] == oldLine[firstChar]) 1086 firstChar++; 1087 1088 /* if there wasn't one, we're done */ 1089 if (firstChar >= screen_columns) 1090 return; 1091 1092 /* it may be cheap to clear leading whitespace with clr_bol */ 1093 if (clr_bol && can_clear_with(blank = newLine[0])) { 1094 int oFirstChar, nFirstChar; 1095 1096 for (oFirstChar = 0; oFirstChar < screen_columns; oFirstChar++) 1097 if (oldLine[oFirstChar] != blank) 1098 break; 1099 for (nFirstChar = 0; nFirstChar < screen_columns; nFirstChar++) 1100 if (newLine[nFirstChar] != blank) 1101 break; 1102 1103 if (nFirstChar > oFirstChar + SP->_el1_cost) { 1104 if (nFirstChar >= screen_columns && SP->_el_cost <= SP->_el1_cost) { 1105 GoTo(lineno, 0); 1106 UpdateAttrs(blank); 1107 TPUTS_TRACE("clr_eol"); 1108 putp(clr_eol); 1109 } else { 1110 GoTo(lineno, nFirstChar - 1); 1111 UpdateAttrs(blank); 1112 TPUTS_TRACE("clr_bol"); 1113 putp(clr_bol); 1114 } 1115 1116 while (firstChar < nFirstChar) 1117 oldLine[firstChar++] = blank; 1118 1119 if (firstChar >= screen_columns) 1120 return; 1121 } 1122 } 1123 1124 blank = newLine[screen_columns - 1]; 1125 1126 if (!can_clear_with(blank)) { 1127 /* find the last differing character */ 1128 nLastChar = screen_columns - 1; 1129 1130 while (nLastChar > firstChar 1131 && newLine[nLastChar] == oldLine[nLastChar]) 1132 nLastChar--; 1133 1134 if (nLastChar >= firstChar) { 1135 GoTo(lineno, firstChar); 1136 PutRange(oldLine, newLine, lineno, firstChar, nLastChar); 1137 memcpy(oldLine + firstChar, 1138 newLine + firstChar, 1139 (nLastChar - firstChar + 1) * sizeof(chtype)); 1140 } 1141 return; 1142 } 1143 1144 /* find last non-blank character on old line */ 1145 oLastChar = screen_columns - 1; 1146 while (oLastChar > firstChar && oldLine[oLastChar] == blank) 1147 oLastChar--; 1148 1149 /* find last non-blank character on new line */ 1150 nLastChar = screen_columns - 1; 1151 while (nLastChar > firstChar && newLine[nLastChar] == blank) 1152 nLastChar--; 1153 1154 if ((nLastChar == firstChar) 1155 && (SP->_el_cost < (oLastChar - nLastChar))) { 1156 GoTo(lineno, firstChar); 1157 if (newLine[firstChar] != blank) 1158 PutChar(newLine[firstChar]); 1159 ClrToEOL(blank, FALSE); 1160 } else if ((nLastChar != oLastChar) 1161 && (newLine[nLastChar] != oldLine[oLastChar] 1162 || !(_nc_idcok && has_ic()))) { 1163 GoTo(lineno, firstChar); 1164 if ((oLastChar - nLastChar) > SP->_el_cost) { 1165 if (PutRange(oldLine, newLine, lineno, firstChar, nLastChar)) 1166 GoTo(lineno, nLastChar + 1); 1167 ClrToEOL(blank, FALSE); 1168 } else { 1169 n = max(nLastChar, oLastChar); 1170 PutRange(oldLine, newLine, lineno, firstChar, n); 1171 } 1172 } else { 1173 int nLastNonblank = nLastChar; 1174 int oLastNonblank = oLastChar; 1175 1176 /* find the last characters that really differ */ 1177 while (newLine[nLastChar] == oldLine[oLastChar]) { 1178 if (nLastChar != 0 1179 && oLastChar != 0) { 1180 nLastChar--; 1181 oLastChar--; 1182 } else { 1183 break; 1184 } 1185 } 1186 1187 n = min(oLastChar, nLastChar); 1188 if (n >= firstChar) { 1189 GoTo(lineno, firstChar); 1190 PutRange(oldLine, newLine, lineno, firstChar, n); 1191 } 1192 1193 if (oLastChar < nLastChar) { 1194 int m = max(nLastNonblank, oLastNonblank); 1195 GoTo(lineno, n + 1); 1196 if (InsCharCost(nLastChar - oLastChar) 1197 > (m - n)) { 1198 PutRange(oldLine, newLine, lineno, n + 1, m); 1199 } else { 1200 InsStr(&newLine[n + 1], nLastChar - oLastChar); 1201 } 1202 } else if (oLastChar > nLastChar) { 1203 GoTo(lineno, n + 1); 1204 if (DelCharCost(oLastChar - nLastChar) 1205 > SP->_el_cost + nLastNonblank - (n + 1)) { 1206 if (PutRange(oldLine, newLine, lineno, 1207 n + 1, nLastNonblank)) 1208 GoTo(lineno, nLastNonblank + 1); 1209 ClrToEOL(blank, FALSE); 1210 } else { 1211 /* 1212 * The delete-char sequence will 1213 * effectively shift in blanks from the 1214 * right margin of the screen. Ensure 1215 * that they are the right color by 1216 * setting the video attributes from 1217 * the last character on the row. 1218 */ 1219 UpdateAttrs(blank); 1220 DelChar(oLastChar - nLastChar); 1221 } 1222 } 1223 } 1224 } 1225 1226 /* update the code's internal representation */ 1227 if (screen_columns > firstChar) 1228 memcpy(oldLine + firstChar, 1229 newLine + firstChar, 1230 (screen_columns - firstChar) * sizeof(chtype)); 1231 } 1232 1233 /* 1234 ** ClearScreen(blank) 1235 ** 1236 ** Clear the physical screen and put cursor at home 1237 ** 1238 */ 1239 1240 static void 1241 ClearScreen(chtype blank) 1242 { 1243 int i, j; 1244 bool fast_clear = (clear_screen || clr_eos || clr_eol); 1245 1246 T(("ClearScreen() called")); 1247 1248 #ifdef NCURSES_EXT_FUNCS 1249 if (SP->_coloron 1250 && !SP->_default_color) { 1251 _nc_do_color(COLOR_PAIR(SP->_current_attr), 0, FALSE, _nc_outch); 1252 if (!back_color_erase) { 1253 fast_clear = FALSE; 1254 } 1255 } 1256 #endif 1257 1258 if (fast_clear) { 1259 if (clear_screen) { 1260 UpdateAttrs(blank); 1261 TPUTS_TRACE("clear_screen"); 1262 putp(clear_screen); 1263 SP->_cursrow = SP->_curscol = 0; 1264 position_check(SP->_cursrow, SP->_curscol, "ClearScreen"); 1265 } else if (clr_eos) { 1266 SP->_cursrow = SP->_curscol = -1; 1267 GoTo(0, 0); 1268 1269 UpdateAttrs(blank); 1270 TPUTS_TRACE("clr_eos"); 1271 putp(clr_eos); 1272 } else if (clr_eol) { 1273 SP->_cursrow = SP->_curscol = -1; 1274 1275 for (i = 0; i < screen_lines; i++) { 1276 GoTo(i, 0); 1277 UpdateAttrs(blank); 1278 TPUTS_TRACE("clr_eol"); 1279 putp(clr_eol); 1280 } 1281 GoTo(0, 0); 1282 } 1283 } else { 1284 for (i = 0; i < screen_lines; i++) { 1285 GoTo(i, 0); 1286 UpdateAttrs(blank); 1287 for (j = 0; j < screen_columns; j++) 1288 PutChar(blank); 1289 } 1290 GoTo(0, 0); 1291 } 1292 1293 for (i = 0; i < screen_lines; i++) { 1294 for (j = 0; j < screen_columns; j++) 1295 curscr->_line[i].text[j] = blank; 1296 } 1297 1298 T(("screen cleared")); 1299 } 1300 1301 /* 1302 ** InsStr(line, count) 1303 ** 1304 ** Insert the count characters pointed to by line. 1305 ** 1306 */ 1307 1308 static void 1309 InsStr(chtype * line, int count) 1310 { 1311 T(("InsStr(%p,%d) called", line, count)); 1312 1313 /* Prefer parm_ich as it has the smallest cost - no need to shift 1314 * the whole line on each character. */ 1315 /* The order must match that of InsCharCost. */ 1316 if (parm_ich) { 1317 TPUTS_TRACE("parm_ich"); 1318 tputs(tparm(parm_ich, count), count, _nc_outch); 1319 while (count) { 1320 PutAttrChar(*line); 1321 line++; 1322 count--; 1323 } 1324 } else if (enter_insert_mode && exit_insert_mode) { 1325 TPUTS_TRACE("enter_insert_mode"); 1326 putp(enter_insert_mode); 1327 while (count) { 1328 PutAttrChar(*line); 1329 if (insert_padding) { 1330 TPUTS_TRACE("insert_padding"); 1331 putp(insert_padding); 1332 } 1333 line++; 1334 count--; 1335 } 1336 TPUTS_TRACE("exit_insert_mode"); 1337 putp(exit_insert_mode); 1338 } else { 1339 while (count) { 1340 TPUTS_TRACE("insert_character"); 1341 putp(insert_character); 1342 PutAttrChar(*line); 1343 if (insert_padding) { 1344 TPUTS_TRACE("insert_padding"); 1345 putp(insert_padding); 1346 } 1347 line++; 1348 count--; 1349 } 1350 } 1351 position_check(SP->_cursrow, SP->_curscol, "InsStr"); 1352 } 1353 1354 /* 1355 ** DelChar(count) 1356 ** 1357 ** Delete count characters at current position 1358 ** 1359 */ 1360 1361 static void 1362 DelChar(int count) 1363 { 1364 int n; 1365 1366 T(("DelChar(%d) called, position = (%d,%d)", count, newscr->_cury, newscr->_curx)); 1367 1368 if (parm_dch) { 1369 TPUTS_TRACE("parm_dch"); 1370 tputs(tparm(parm_dch, count), count, _nc_outch); 1371 } else { 1372 for (n = 0; n < count; n++) { 1373 TPUTS_TRACE("delete_character"); 1374 putp(delete_character); 1375 } 1376 } 1377 } 1378 1379 /* 1380 ** _nc_outstr(char *str) 1381 ** 1382 ** Emit a string without waiting for update. 1383 */ 1384 1385 void 1386 _nc_outstr(const char *str) 1387 { 1388 (void) putp(str); 1389 _nc_flush(); 1390 } 1391 1392 /* 1393 * Physical-scrolling support 1394 * 1395 * This code was adapted from Keith Bostic's hardware scrolling 1396 * support for 4.4BSD curses. I (esr) translated it to use terminfo 1397 * capabilities, narrowed the call interface slightly, and cleaned 1398 * up some convoluted tests. I also added support for the memory_above 1399 * memory_below, and non_dest_scroll_region capabilities. 1400 * 1401 * For this code to work, we must have either 1402 * change_scroll_region and scroll forward/reverse commands, or 1403 * insert and delete line capabilities. 1404 * When the scrolling region has been set, the cursor has to 1405 * be at the last line of the region to make the scroll up 1406 * happen, or on the first line of region to scroll down. 1407 * 1408 * This code makes one aesthetic decision in the opposite way from 1409 * BSD curses. BSD curses preferred pairs of il/dl operations 1410 * over scrolls, allegedly because il/dl looked faster. We, on 1411 * the other hand, prefer scrolls because (a) they're just as fast 1412 * on many terminals and (b) using them avoids bouncing an 1413 * unchanged bottom section of the screen up and down, which is 1414 * visually nasty. 1415 * 1416 * (lav): added more cases, used dl/il when bot==maxy and in csr case. 1417 * 1418 * I used assumption that capabilities il/il1/dl/dl1 work inside 1419 * changed scroll region not shifting screen contents outside of it. 1420 * If there are any terminals behaving different way, it would be 1421 * necessary to add some conditions to scroll_csr_forward/backward. 1422 */ 1423 1424 /* Try to scroll up assuming given csr (miny, maxy). Returns ERR on failure */ 1425 static int 1426 scroll_csr_forward(int n, int top, int bot, int miny, int maxy, chtype blank) 1427 { 1428 int i, j; 1429 1430 if (n == 1 && scroll_forward && top == miny && bot == maxy) { 1431 GoTo(bot, 0); 1432 UpdateAttrs(blank); 1433 TPUTS_TRACE("scroll_forward"); 1434 tputs(scroll_forward, 0, _nc_outch); 1435 } else if (n == 1 && delete_line && bot == maxy) { 1436 GoTo(top, 0); 1437 UpdateAttrs(blank); 1438 TPUTS_TRACE("delete_line"); 1439 tputs(delete_line, 0, _nc_outch); 1440 } else if (parm_index && top == miny && bot == maxy) { 1441 GoTo(bot, 0); 1442 UpdateAttrs(blank); 1443 TPUTS_TRACE("parm_index"); 1444 tputs(tparm(parm_index, n, 0), n, _nc_outch); 1445 } else if (parm_delete_line && bot == maxy) { 1446 GoTo(top, 0); 1447 UpdateAttrs(blank); 1448 TPUTS_TRACE("parm_delete_line"); 1449 tputs(tparm(parm_delete_line, n, 0), n, _nc_outch); 1450 } else if (scroll_forward && top == miny && bot == maxy) { 1451 GoTo(bot, 0); 1452 UpdateAttrs(blank); 1453 for (i = 0; i < n; i++) { 1454 TPUTS_TRACE("scroll_forward"); 1455 tputs(scroll_forward, 0, _nc_outch); 1456 } 1457 } else if (delete_line && bot == maxy) { 1458 GoTo(top, 0); 1459 UpdateAttrs(blank); 1460 for (i = 0; i < n; i++) { 1461 TPUTS_TRACE("delete_line"); 1462 tputs(delete_line, 0, _nc_outch); 1463 } 1464 } else 1465 return ERR; 1466 1467 #ifdef NCURSES_EXT_FUNCS 1468 if (FILL_BCE()) { 1469 for (i = 0; i < n; i++) { 1470 GoTo(bot - i, 0); 1471 for (j = 0; j < screen_columns; j++) 1472 PutChar(blank); 1473 } 1474 } 1475 #endif 1476 return OK; 1477 } 1478 1479 /* Try to scroll down assuming given csr (miny, maxy). Returns ERR on failure */ 1480 /* n > 0 */ 1481 static int 1482 scroll_csr_backward(int n, int top, int bot, int miny, int maxy, chtype blank) 1483 { 1484 int i, j; 1485 1486 if (n == 1 && scroll_reverse && top == miny && bot == maxy) { 1487 GoTo(top, 0); 1488 UpdateAttrs(blank); 1489 TPUTS_TRACE("scroll_reverse"); 1490 tputs(scroll_reverse, 0, _nc_outch); 1491 } else if (n == 1 && insert_line && bot == maxy) { 1492 GoTo(top, 0); 1493 UpdateAttrs(blank); 1494 TPUTS_TRACE("insert_line"); 1495 tputs(insert_line, 0, _nc_outch); 1496 } else if (parm_rindex && top == miny && bot == maxy) { 1497 GoTo(top, 0); 1498 UpdateAttrs(blank); 1499 TPUTS_TRACE("parm_rindex"); 1500 tputs(tparm(parm_rindex, n, 0), n, _nc_outch); 1501 } else if (parm_insert_line && bot == maxy) { 1502 GoTo(top, 0); 1503 UpdateAttrs(blank); 1504 TPUTS_TRACE("parm_insert_line"); 1505 tputs(tparm(parm_insert_line, n, 0), n, _nc_outch); 1506 } else if (scroll_reverse && top == miny && bot == maxy) { 1507 GoTo(top, 0); 1508 UpdateAttrs(blank); 1509 for (i = 0; i < n; i++) { 1510 TPUTS_TRACE("scroll_reverse"); 1511 tputs(scroll_reverse, 0, _nc_outch); 1512 } 1513 } else if (insert_line && bot == maxy) { 1514 GoTo(top, 0); 1515 UpdateAttrs(blank); 1516 for (i = 0; i < n; i++) { 1517 TPUTS_TRACE("insert_line"); 1518 tputs(insert_line, 0, _nc_outch); 1519 } 1520 } else 1521 return ERR; 1522 1523 #ifdef NCURSES_EXT_FUNCS 1524 if (FILL_BCE()) { 1525 for (i = 0; i < n; i++) { 1526 GoTo(top + i, 0); 1527 for (j = 0; j < screen_columns; j++) 1528 PutChar(blank); 1529 } 1530 } 1531 #endif 1532 return OK; 1533 } 1534 1535 /* scroll by using delete_line at del and insert_line at ins */ 1536 /* n > 0 */ 1537 static int 1538 scroll_idl(int n, int del, int ins, chtype blank) 1539 { 1540 int i; 1541 1542 if (!((parm_delete_line || delete_line) && (parm_insert_line || insert_line))) 1543 return ERR; 1544 1545 GoTo(del, 0); 1546 UpdateAttrs(blank); 1547 if (n == 1 && delete_line) { 1548 TPUTS_TRACE("delete_line"); 1549 tputs(delete_line, 0, _nc_outch); 1550 } else if (parm_delete_line) { 1551 TPUTS_TRACE("parm_delete_line"); 1552 tputs(tparm(parm_delete_line, n, 0), n, _nc_outch); 1553 } else { /* if (delete_line) */ 1554 for (i = 0; i < n; i++) { 1555 TPUTS_TRACE("delete_line"); 1556 tputs(delete_line, 0, _nc_outch); 1557 } 1558 } 1559 1560 GoTo(ins, 0); 1561 UpdateAttrs(blank); 1562 if (n == 1 && insert_line) { 1563 TPUTS_TRACE("insert_line"); 1564 tputs(insert_line, 0, _nc_outch); 1565 } else if (parm_insert_line) { 1566 TPUTS_TRACE("parm_insert_line"); 1567 tputs(tparm(parm_insert_line, n, 0), n, _nc_outch); 1568 } else { /* if (insert_line) */ 1569 for (i = 0; i < n; i++) { 1570 TPUTS_TRACE("insert_line"); 1571 tputs(insert_line, 0, _nc_outch); 1572 } 1573 } 1574 1575 return OK; 1576 } 1577 1578 int 1579 _nc_scrolln(int n, int top, int bot, int maxy) 1580 /* scroll region from top to bot by n lines */ 1581 { 1582 chtype blank = ClrBlank(stdscr); 1583 int i; 1584 bool cursor_saved = FALSE; 1585 int res; 1586 1587 TR(TRACE_MOVE, ("mvcur_scrolln(%d, %d, %d, %d)", n, top, bot, maxy)); 1588 1589 #if USE_XMC_SUPPORT 1590 /* 1591 * If we scroll, we might remove a cookie. 1592 */ 1593 if (magic_cookie_glitch > 0) { 1594 return (ERR); 1595 } 1596 #endif 1597 1598 if (n > 0) { /* scroll up (forward) */ 1599 /* 1600 * Explicitly clear if stuff pushed off top of region might 1601 * be saved by the terminal. 1602 */ 1603 if (non_dest_scroll_region || (memory_above && top == 0)) { 1604 for (i = 0; i < n; i++) { 1605 GoTo(i, 0); 1606 ClrToEOL(BLANK, FALSE); 1607 } 1608 } 1609 1610 res = scroll_csr_forward(n, top, bot, 0, maxy, blank); 1611 1612 if (res == ERR && change_scroll_region) { 1613 if ((((n == 1 && scroll_forward) || parm_index) 1614 && (SP->_cursrow == bot || SP->_cursrow == bot - 1)) 1615 && save_cursor && restore_cursor) { 1616 cursor_saved = TRUE; 1617 TPUTS_TRACE("save_cursor"); 1618 tputs(save_cursor, 0, _nc_outch); 1619 } 1620 TPUTS_TRACE("change_scroll_region"); 1621 tputs(tparm(change_scroll_region, top, bot), 0, _nc_outch); 1622 if (cursor_saved) { 1623 TPUTS_TRACE("restore_cursor"); 1624 tputs(restore_cursor, 0, _nc_outch); 1625 } else { 1626 SP->_cursrow = SP->_curscol = -1; 1627 } 1628 1629 res = scroll_csr_forward(n, top, bot, top, bot, blank); 1630 1631 TPUTS_TRACE("change_scroll_region"); 1632 tputs(tparm(change_scroll_region, 0, maxy), 0, _nc_outch); 1633 SP->_cursrow = SP->_curscol = -1; 1634 } 1635 1636 if (res == ERR && _nc_idlok) 1637 res = scroll_idl(n, top, bot - n + 1, blank); 1638 } else { /* (n < 0) - scroll down (backward) */ 1639 /* 1640 * Do explicit clear to end of region if it's possible that the 1641 * terminal might hold on to stuff we push off the end. 1642 */ 1643 if (non_dest_scroll_region || (memory_below && bot == maxy)) { 1644 if (bot == maxy && clr_eos) { 1645 GoTo(maxy + n, 0); 1646 ClrToEOS(BLANK); 1647 } else if (clr_eol) { 1648 for (i = 0; i < -n; i++) { 1649 GoTo(maxy + n + i, 0); 1650 ClrToEOL(BLANK, FALSE); 1651 } 1652 } 1653 } 1654 1655 res = scroll_csr_backward(-n, top, bot, 0, maxy, blank); 1656 1657 if (res == ERR && change_scroll_region) { 1658 if (top != 0 && (SP->_cursrow == top || SP->_cursrow == top - 1) 1659 && save_cursor && restore_cursor) { 1660 cursor_saved = TRUE; 1661 TPUTS_TRACE("save_cursor"); 1662 tputs(save_cursor, 0, _nc_outch); 1663 } 1664 TPUTS_TRACE("change_scroll_region"); 1665 tputs(tparm(change_scroll_region, top, bot), 0, _nc_outch); 1666 if (cursor_saved) { 1667 TPUTS_TRACE("restore_cursor"); 1668 tputs(restore_cursor, 0, _nc_outch); 1669 } else { 1670 SP->_cursrow = SP->_curscol = -1; 1671 } 1672 1673 res = scroll_csr_backward(-n, top, bot, top, bot, blank); 1674 1675 TPUTS_TRACE("change_scroll_region"); 1676 tputs(tparm(change_scroll_region, 0, maxy), 0, _nc_outch); 1677 SP->_cursrow = SP->_curscol = -1; 1678 } 1679 1680 if (res == ERR && _nc_idlok) 1681 res = scroll_idl(-n, bot + n + 1, top, blank); 1682 } 1683 1684 if (res == ERR) 1685 return (ERR); 1686 1687 _nc_scroll_window(curscr, n, top, bot, blank); 1688 1689 /* shift hash values too - they can be reused */ 1690 _nc_scroll_oldhash(n, top, bot); 1691 1692 return (OK); 1693 } 1694 1695 void 1696 _nc_screen_resume(void) 1697 { 1698 /* make sure terminal is in a sane known state */ 1699 SP->_current_attr = A_NORMAL; 1700 newscr->_clear = TRUE; 1701 1702 if (SP->_coloron == TRUE && orig_pair) 1703 putp(orig_pair); 1704 if (exit_attribute_mode) 1705 putp(exit_attribute_mode); 1706 else { 1707 /* turn off attributes */ 1708 if (exit_alt_charset_mode) 1709 putp(exit_alt_charset_mode); 1710 if (exit_standout_mode) 1711 putp(exit_standout_mode); 1712 if (exit_underline_mode) 1713 putp(exit_underline_mode); 1714 } 1715 if (exit_insert_mode) 1716 putp(exit_insert_mode); 1717 if (enter_am_mode && exit_am_mode) 1718 putp(auto_right_margin ? enter_am_mode : exit_am_mode); 1719 } 1720 1721 void 1722 _nc_screen_init(void) 1723 { 1724 _nc_screen_resume(); 1725 } 1726 1727 /* wrap up screen handling */ 1728 void 1729 _nc_screen_wrap(void) 1730 { 1731 UpdateAttrs(A_NORMAL); 1732 #ifdef NCURSES_EXT_FUNCS 1733 if (SP->_coloron 1734 && !SP->_default_color) { 1735 SP->_default_color = TRUE; 1736 _nc_do_color(-1, 0, FALSE, _nc_outch); 1737 SP->_default_color = FALSE; 1738 1739 mvcur(SP->_cursrow, SP->_curscol, screen_lines - 1, 0); 1740 SP->_cursrow = screen_lines - 1; 1741 SP->_curscol = 0; 1742 1743 ClrToEOL(BLANK, TRUE); 1744 } 1745 #endif 1746 } 1747 1748 #if USE_XMC_SUPPORT 1749 void 1750 _nc_do_xmc_glitch(attr_t previous) 1751 { 1752 attr_t chg = XMC_CHANGES(previous ^ SP->_current_attr); 1753 1754 while (chg != 0) { 1755 if (chg & 1) { 1756 SP->_curscol += magic_cookie_glitch; 1757 if (SP->_curscol >= SP->_columns) 1758 wrap_cursor(); 1759 T(("bumped to %d,%d after cookie", SP->_cursrow, SP->_curscol)); 1760 } 1761 chg >>= 1; 1762 } 1763 } 1764 #endif /* USE_XMC_SUPPORT */ 1765