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