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