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