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