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