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