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