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