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