1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2015 Gary Mills 24 * Copyright (c) 1995, by Sun Microsystems, Inc. 25 * All rights reserved. 26 */ 27 28 /* 29 * doupdate.c 30 * 31 * XCurses Library 32 * 33 * Copyright 1990, 1995 by Mortice Kern Systems Inc. All rights reserved. 34 * 35 */ 36 37 #ifdef M_RCSID 38 #ifndef lint 39 static char const rcsID[] = "$Header: /rd/src/libc/xcurses/rcs/doupdate.c 1.9 1995/07/26 17:45:06 ant Exp $"; 40 #endif 41 #endif 42 43 #include <private.h> 44 #include <string.h> 45 #include <setjmp.h> 46 #include <signal.h> 47 48 #undef SIGTSTP 49 50 /* 51 * Disable typeahead trapping because it slow down updated dramatically 52 * on MPE/iX. 53 */ 54 #ifdef MPE_STUB 55 #undef M_CURSES_TYPEAHEAD 56 #endif 57 58 /* 59 * This value is the ideal length for the cursor addressing sequence 60 * being four bytes long, ie. "<escape><cursor addressing code><row><col>". 61 * eg. VT52 - "\EYrc" or ADM3A - "\E=rc" 62 */ 63 #define JUMP_SIZE 4 64 65 /* 66 * This value is the ideal length for the clear-to-eol sequence 67 * being two bytes long, ie "<escape><clear eol code>". 68 */ 69 #define CEOL_SIZE 2 70 71 #define GOTO(r,c) (__m_mvcur(curscr->_cury, curscr->_curx,r,c,__m_outc),\ 72 curscr->_cury = r, curscr->_curx = c) 73 74 typedef struct cost_op { 75 short cost; 76 short op; 77 } lcost; 78 79 typedef void (*t_action)(int, int); 80 81 static jmp_buf breakout; 82 83 #define LC(i,j) (lc[(i) * (LINES + 1) + (j)]) 84 85 static lcost *lc = (lcost *) 0; 86 static unsigned long *nhash = (unsigned long *) 0; 87 static t_action *del = (t_action *) 0; 88 static t_action *ins_rep = (t_action *) 0; 89 90 static WINDOW *newscr; 91 92 STATIC void erase_bottom(int); 93 STATIC void clear_bottom(int); 94 STATIC void complex(void); 95 STATIC int cost(int, int); 96 STATIC void lines_delete(int, int); 97 STATIC void lines_insert(int, int); 98 STATIC void lines_replace(int, int); 99 STATIC void script(int, int); 100 STATIC int scroll_dn(int); 101 STATIC int scroll_up(int); 102 STATIC void simple(void); 103 STATIC void text_replace(int); 104 STATIC void block_over(int, int, int); 105 106 /*f 107 * Wrapper that streams Curses output. 108 * 109 * All escape sequences going to the screen come through here. 110 * All ordinary characters go to the screen via the putc in doupdate.c 111 */ 112 int 113 __m_outc(ch) 114 int ch; 115 { 116 return putc(ch, __m_screen->_of); 117 } 118 119 /* 120 * Allocate or grow doupdate() structures. 121 */ 122 int 123 __m_doupdate_init() 124 { 125 void *new; 126 static short nlines = 0; 127 128 if (lines <= 0) 129 return -1; 130 131 if (lines <= nlines) 132 return 0; 133 134 new = m_malloc((lines+1) * (lines+1) * sizeof *lc); 135 if (new == (void *) 0) 136 return -1; 137 if (lc != (lcost *) 0) 138 free(lc); 139 lc = (lcost *) new; 140 141 new = m_malloc((lines + lines) * sizeof *del); 142 if (new == (void *) 0) 143 return -1; 144 if (del != (t_action *) 0) 145 free(del); 146 del = (t_action *) new; 147 ins_rep = del + lines; 148 149 new = m_malloc(lines * sizeof *nhash); 150 if (new == (void *) 0) 151 return -1; 152 if (nhash != (unsigned long *) 0) 153 free(nhash); 154 nhash = (unsigned long *) new; 155 156 nlines = lines; 157 158 return 0; 159 } 160 161 STATIC void 162 erase_bottom(y) 163 int y; 164 { 165 int i; 166 167 for (i = y; i < LINES; ++i) { 168 (void) __m_cc_erase(curscr, i, 0, i, curscr->_maxx-1); 169 __m_cc_hash(curscr, __m_screen->_hash, i); 170 } 171 } 172 173 /*f 174 * Clear from the start of the current row to bottom of screen. 175 */ 176 STATIC void 177 clear_bottom(y) 178 int y; 179 { 180 erase_bottom(y); 181 182 /* Restore default color pair before doing area clears. */ 183 if (back_color_erase) 184 (void) vid_puts(WA_NORMAL, 0, (void *) 0, __m_outc); 185 186 if (y == 0 && clear_screen != (char *) 0) { 187 (void) tputs(clear_screen, 1, __m_outc); 188 } else { 189 (void) __m_mvcur(-1, -1, y, 0, __m_outc); 190 if (clr_eos != (char *) 0) { 191 (void) tputs(clr_eos, 1, __m_outc); 192 } else if (clr_eol != (char *) 0) { 193 for (;;) { 194 (void) tputs(clr_eol, 1, __m_outc); 195 if (LINES <= y) 196 break; 197 (void) __m_mvcur(y, 0, y+1, 0, __m_outc); 198 ++y; 199 } 200 } 201 } 202 203 curscr->_cury = y; 204 curscr->_curx = 0; 205 } 206 207 /*f 208 * Replace a line of text. 209 * 210 * The principal scheme is to overwrite the region of a line between 211 * the first and last differing characters. A clear-eol is used to 212 * optimise an update region that consist largely of blanks. This can 213 * happen fairly often in the case of scrolled lines or full redraws. 214 * 215 * Miller's line redraw algorithm, used in the 'S' editor [Mil87], 216 * should be re-investigated to see if it is simple and fast enough for 217 * our needs, and if it can be adapted to handle the ceol_standout_glitch 218 * (HP 2392A terminals) and multibyte character sequences. 219 * 220 * Very early versions of this code applied a Gosling algorithm column 221 * wise in addition to the row-wise used in complex(). It was removed 222 * in favour of both computation and transmission speed. The assumption 223 * being that overwrites of a line region occured far more frequently 224 * than the need to insert/delete several isolated characters. 225 * 226 * References: 227 * [Mil87] W. Miller, A Software Tools Sampler, Prentice-Hall, 1987 228 */ 229 STATIC void 230 text_replace(row) 231 int row; 232 { 233 short npair; 234 attr_t cookie, nattr; 235 cchar_t *optr, *nptr; 236 int col, last, tail, jump, count; 237 238 #ifdef M_CURSES_TYPEAHEAD 239 /* Before replacing a line of text, check for type-ahead. */ 240 if (__m_screen->_flags & S_ISATTY) { 241 unsigned char cc; 242 243 if (read(__m_screen->_kfd, &cc, sizeof cc) == sizeof cc) { 244 (void) ungetch(cc); 245 longjmp(breakout, 1); 246 } 247 } 248 #endif /* M_CURSES_TYPEAHEAD */ 249 250 col = newscr->_first[row]; 251 if (col < 0) 252 col = 0; 253 254 last = newscr->_last[row]; 255 if (COLS < last) 256 last = COLS; 257 258 if (clr_eol != (char *) 0) { 259 /* Find start of blank tail region. */ 260 nptr = &newscr->_line[row][COLS]; 261 for (tail = COLS; 0 < tail; --tail) { 262 if (!__m_cc_compare(--nptr, &newscr->_bg, 1)) 263 break; 264 } 265 266 /* Only consider clear-to-end-of-line optimization if the 267 * blank tail falls within the end of the dirty region by 268 * more than ideal length of clear-to-end-of-line sequence. 269 * Else disable the check by forcing tail to be at the 270 * end-of-line. 271 */ 272 if (last < tail + CEOL_SIZE) 273 tail = COLS; 274 } 275 276 optr = &curscr->_line[row][col]; 277 nptr = &newscr->_line[row][col]; 278 279 for (jump = -1; col < last; ) { 280 /* Skip common regions. */ 281 for (count = 0; __m_cc_compare(optr, nptr, 1); ++count) { 282 /* Advance before possible goto. */ 283 ++optr; 284 ++nptr; 285 286 if (last <= ++col) 287 goto done; 288 } 289 290 /* Move the cursor by redrawing characters or using 291 * cursor motion commands. The first time that we 292 * address this row, jump equals -1, so that the cursor 293 * will be forced to the correct screen line. Once 294 * there, we should be able to track the cursor motion 295 * along the line and jump only when the cost of redrawing 296 * to column N is more expensive than a jump to column N. 297 */ 298 if (jump < count) { 299 /* First time addressing this row or cost of 300 * jumping cheaper than redrawing. 301 */ 302 jump = JUMP_SIZE; 303 GOTO(row, col); 304 count = 0; 305 306 /* If attributes at start of field are different 307 * force an attribute cookie to be dropped. 308 */ 309 if (ceol_standout_glitch 310 && (optr->_at != nptr->_at || optr->_co != nptr->_co)) 311 ATTR_STATE |= WA_COOKIE; 312 } else { 313 /* Redraw to move short distance. */ 314 optr -= count; 315 nptr -= count; 316 col -= count; 317 } 318 319 /* Write difference region. */ 320 while (col < last 321 && (!__m_cc_compare(optr, nptr, 1) || 0 < count--)) { 322 write_loop: 323 /* Check for clear-to-end-of-line optimization. */ 324 if (clr_eol != (char *) 0 && tail <= col) { 325 /* For HP terminals, only clear-to-end-of-line 326 * once the attributes have been turned off. 327 * Other terminals, we can proceed normally. 328 */ 329 if (!ceol_standout_glitch 330 || ATTR_STATE == WA_NORMAL) { 331 curscr->_curx = col; 332 goto done; 333 } 334 } 335 336 ++col; 337 338 /* Make sure we don't scroll the screen by writing 339 * to the bottom right corner. 340 */ 341 if (COLS <= col && LINES-1 <= row 342 && auto_right_margin && !eat_newline_glitch) { 343 /*** TODO 344 *** Insert character/auto_right_margin 345 *** hacks for writting into the last 346 *** column of the last line so as not 347 *** to scroll. 348 ***/ 349 curscr->_curx = col; 350 goto done; 351 } 352 353 /* Remember any existing attribute cookie. */ 354 cookie = optr->_at & WA_COOKIE; 355 356 nattr = nptr->_at; 357 npair = nptr->_co; 358 359 /* Change attribute state. On HP terminals we also 360 * have to check for attribute cookies that may need 361 * to be changed. 362 */ 363 if (ATTR_STATE != nattr 364 || optr->_at != nattr || optr->_co != npair) { 365 (void) vid_puts( 366 nattr, npair, (void *) 0, __m_outc 367 ); 368 369 /* Remember new or existing cookie. */ 370 cookie = WA_COOKIE; 371 } 372 373 /* Don't display internal characters. */ 374 if (nptr->_f) 375 (void) __m_cc_write(nptr); 376 377 /* Update copy of screen image. */ 378 *optr++ = *nptr++; 379 optr->_at |= cookie; 380 } 381 382 curscr->_curx = col; 383 384 /* Check the attributes at the end of the field with 385 * those of start of the next common region. If they 386 * differ, force another iteration of the write-loop 387 * that will change the attribute state. 388 */ 389 if (ceol_standout_glitch && col < COLS 390 && ATTR_STATE != (optr->_at & ~WA_COOKIE)) 391 goto write_loop; 392 } 393 done: 394 /* Before leaving this line, check if we have to turn off 395 * attributes and record a cookie. 396 */ 397 if (!move_standout_mode && ATTR_STATE != WA_NORMAL) { 398 /* ceol_standout_glitch, which affects HP terminals, 399 * drops hidden cookies on the screen where ever the 400 * cursor is, so disabling attributes before a cursor 401 * motion operation could disturb existing highlights. 402 */ 403 if (ceol_standout_glitch) 404 /* Attributes on an HP terminal do not cross lines. */ 405 ATTR_STATE = A_NORMAL; 406 else 407 (void) vid_puts(WA_NORMAL, 0, (void *) 0, __m_outc); 408 } 409 410 /* Re-check for clear to end-of-line optimization. */ 411 if (clr_eol != (char *) 0 && tail <= col && col < last) { 412 /* Is the tail of the current screen image non-blank? */ 413 for (tail = col; tail < COLS; ++tail, ++optr) 414 if (!__m_cc_compare(optr, &newscr->_bg, 1)) 415 break; 416 417 /* If tail didn't reach the right margin of 418 * the current screen image, then we will 419 * make it look like the new image with a 420 * clear to end-of-line. 421 */ 422 if (tail < COLS) { 423 /* Restore default color pair before area clear. */ 424 if (back_color_erase) 425 (void) vid_puts( 426 WA_NORMAL, 0, (void *) 0, __m_outc 427 ); 428 429 (void) tputs(clr_eol, 1, __m_outc); 430 __m_cc_erase(curscr, row, tail, row, COLS-1); 431 } 432 } 433 434 /* Line wrapping checks. */ 435 if (COLS <= curscr->_curx) { 436 --curscr->_curx; 437 if (auto_right_margin && row < LINES-1) { 438 if (eat_newline_glitch) { 439 __m_outc('\r'); 440 __m_outc('\n'); 441 } 442 ++curscr->_cury; 443 curscr->_curx = 0; 444 } 445 } 446 } 447 448 /*f 449 * Replace a block of lines. 450 * Only ever used for complex(). 451 */ 452 STATIC void 453 lines_replace(from, to_1) 454 int from, to_1; 455 { 456 for (; from < to_1; ++from) 457 text_replace(from); 458 } 459 460 /*f 461 * Delete a block of lines. 462 * Only ever used for complex(). 463 */ 464 STATIC void 465 lines_delete(from, to_1) 466 int from, to_1; 467 { 468 int count = to_1 - from; 469 470 if (LINES <= to_1) { 471 clear_bottom(from); 472 } else { 473 GOTO(from, 0); 474 (void) winsdelln(curscr, -count); 475 476 if (parm_delete_line != (char *) 0) { 477 /* Assume that the sequence to delete more than one 478 * line is faster than repeated single delete_lines. 479 */ 480 (void) tputs( 481 tparm( 482 parm_delete_line, (long) count, 483 0, 0, 0, 0, 0, 0, 0, 0 484 ), count, __m_outc 485 ); 486 } else if (delete_line != (char *) 0) { 487 while (from++ < to_1) 488 (void) tputs(delete_line, 1, __m_outc); 489 } else { 490 /* Error -- what to do. */ 491 return; 492 } 493 } 494 } 495 496 /*f 497 * Insert a block of lines. 498 * Only ever used for complex(). 499 * 500 * We must assume that insert_line and parm_insert_line reset the 501 * cursor column to zero. Therefore it is text_replace() responsiblity 502 * to move the cursor to the correct column to begin the update. 503 */ 504 STATIC void 505 lines_insert(from, to_1) 506 int from, to_1; 507 { 508 int row, count = to_1 - from; 509 510 /* Position the cursor and insert a block of lines into the screen 511 * image now, insert lines into the physical screen, then draw the 512 * new screen lines. 513 */ 514 GOTO(from, 0); 515 (void) winsdelln(curscr, count); 516 517 if (parm_insert_line != (char *) 0) { 518 /* Assume that the sequence to insert more than one line is 519 * faster than repeated single insert_lines. 520 */ 521 (void) tputs( 522 tparm( 523 parm_insert_line, (long) count, 524 0, 0, 0, 0, 0, 0, 0, 0 525 ), count, __m_outc 526 ); 527 } else if (insert_line != (char *) 0) { 528 /* For the single line insert we use to iterate moving 529 * the cursor, inserting, and then drawing a line. That 530 * would appear to be slow but visually appealing. However, 531 * people on slow terminals want speed and those on fast 532 * terminal won't see it. 533 */ 534 for (row = from; row < to_1; ++row) 535 (void) tputs(insert_line, 1, __m_outc); 536 } else { 537 /* Error -- what to do. */ 538 return; 539 } 540 541 for (row = from; row < to_1; ++row) 542 text_replace(row); 543 } 544 545 STATIC int 546 scroll_up(n) 547 int n; 548 { 549 int count = n; 550 int start, finish, to, row; 551 552 if (scroll_forward != (char *) 0) { 553 GOTO(LINES-1, 0); 554 while (0 < n--) 555 (void) tputs(scroll_forward, 1, __m_outc); 556 } else if (parm_delete_line != (char *) 0 && 1 < n) { 557 GOTO(0, 0); 558 (void) tputs( 559 tparm( 560 parm_delete_line, (long) n, 561 0, 0, 0, 0, 0, 0, 0, 0 562 ), n, __m_outc 563 ); 564 } else if (delete_line != (char *) 0) { 565 GOTO(0, 0); 566 while (0 < n--) 567 (void) tputs(delete_line, 1, __m_outc); 568 } else { 569 return 0; 570 } 571 572 /* Scroll recorded image. */ 573 start = 0; 574 finish = count-1; 575 to = lines; 576 577 (void) __m_cc_erase(curscr, start, 0, finish, curscr->_maxx-1); 578 (void) __m_ptr_move( 579 (void **) curscr->_line, curscr->_maxy, start, finish, to 580 ); 581 582 simple(); 583 584 return 1; 585 } 586 587 STATIC int 588 scroll_dn(n) 589 int n; 590 { 591 int count = n; 592 int start, finish, to, row; 593 594 if (LINES < n) 595 return 0; 596 597 if (scroll_reverse != (char *) 0) { 598 GOTO(0, 0); 599 while (0 < n--) 600 (void) tputs(scroll_reverse, 1, __m_outc); 601 } else if (parm_insert_line != (char *) 0 && 1 < n) { 602 GOTO(0, 0); 603 (void) tputs( 604 tparm( 605 parm_insert_line, (long) n, 606 0, 0, 0, 0, 0, 0, 0, 0 607 ), n, __m_outc 608 ); 609 } else if (insert_line != (char *) 0) { 610 GOTO(0, 0); 611 while (0 < n--) 612 (void) tputs(insert_line, 1, __m_outc); 613 } else { 614 return 0; 615 } 616 617 /* Scroll recorded image. */ 618 start = lines - count; 619 finish = lines - 1; 620 to = 0; 621 622 (void) __m_cc_erase(curscr, start, 0, finish, curscr->_maxx-1); 623 (void) __m_ptr_move( 624 (void **) curscr->_line, curscr->_maxy, start, finish, to 625 ); 626 627 simple(); 628 629 return 1; 630 } 631 632 #ifdef NEVER 633 STATIC int 634 is_same_line(old, new, count) 635 cchar_t *old, *new; 636 int count; 637 { 638 while (0 < count--) 639 if (!__m_cc_compare(old, new, 1)) 640 return 0; 641 642 return 1; 643 } 644 #endif /* NEVER */ 645 646 /*f 647 * Dynamic programming algorithm for the string edit problem. 648 * 649 * This is a modified Gosling cost algorithm that takes into account 650 * null/move operations. 651 * 652 * Costs for move, delete, replace, and insert are 0, 1, 2, and 3 653 * repectively. 654 */ 655 STATIC int 656 cost(fr, lr) 657 int fr, lr; 658 { 659 register lcost *lcp; 660 register int or, nr, cc; 661 register unsigned long *ohash = __m_screen->_hash; 662 cchar_t **oline = curscr->_line; 663 cchar_t **nline = newscr->_line; 664 int linesz = COLS * sizeof **oline; 665 666 /* Prepare initial row and column of cost matrix. 667 * 668 * 0 3 6 9 ... 669 * 1 670 * 2 671 * 3 672 * : 673 */ 674 LC(fr,fr).cost = 0; 675 for (cc = 1, ++lr, nr = fr+1; nr <= lr; ++nr, ++cc) { 676 /* Top row is 3, 6, 9, ... */ 677 LC(fr,nr).cost = cc * 3; 678 LC(fr,nr).op = 'i'; 679 680 /* Left column is 1, 2, 3, ... */ 681 LC(nr,fr).cost = cc; 682 LC(nr,fr).op = 'd'; 683 } 684 685 for (--lr, or = fr; or <= lr; ++or) { 686 for (nr = fr; nr <= lr; ++nr) { 687 lcp = &LC(or+1,nr+1); 688 689 /* Assume move op. */ 690 lcp->cost = LC(or,nr).cost; 691 lcp->op = 'm'; 692 693 if (ohash[or] != nhash[nr] 694 #ifdef NEVER 695 /* Should no longer require this code. Using the POSIX 32-bit CRC to 696 * generate a hash value should be sufficient now, since text_replace() 697 * will compare the contents of a line and output only the dirty regions. 698 */ 699 || !is_same_line(oline[or], nline[nr], linesz) 700 #endif 701 ) { 702 /* Lines are different, assume replace op. */ 703 lcp->cost += 2; 704 lcp->op = 'r'; 705 } 706 707 /* Compare insert op. */ 708 if ((cc = LC(or+1,nr).cost + 3) < lcp->cost) { 709 lcp->cost = cc; 710 lcp->op = 'i'; 711 } 712 713 /* Compare delete op. */ 714 if ((cc = LC(or,nr+1).cost + 1) < lcp->cost) { 715 lcp->cost = cc; 716 lcp->op = 'd'; 717 } 718 } 719 } 720 721 return LC(lr+1,lr+1).cost; 722 } 723 724 /*f 725 * Build edit script. 726 * 727 * Normally this would be a recursve routine doing the deletes, inserts, 728 * and replaces on individual lines. Instead we build the script so that 729 * we can later do the operations on a block basis. For terminals with 730 * parm_delete or parm_insert strings this will be better in terms of the 731 * number of characters sent to delete and insert a block of lines. 732 * 733 * Also we can optimize the script so that tail inserts become replaces. 734 * This saves unnecessary inserts operations when the tail can just be 735 * overwritten. 736 */ 737 STATIC void 738 script(fr, lr) 739 int fr, lr; 740 { 741 int i, j; 742 cchar_t *cp; 743 744 i = j = lr + 1; 745 746 memset(del, 0, sizeof *del * LINES); 747 memset(ins_rep, 0, sizeof *ins_rep * LINES); 748 749 do { 750 /* We don't have to bounds check i or j becuase row fr and 751 * column fr of lc have been preset in order to guarantee the 752 * correct motion. 753 */ 754 switch (LC(i,j).op) { 755 case 'i': 756 --j; 757 ins_rep[j] = lines_insert; 758 break; 759 case 'd': 760 --i; 761 del[i] = lines_delete; 762 break; 763 case 'm': 764 --i; 765 --j; 766 break; 767 case 'r': 768 --i; 769 --j; 770 ins_rep[j] = lines_replace; 771 break; 772 } 773 } while (fr < i || fr < j); 774 775 /* Optimize Tail Inserts */ 776 for (i = LINES-1; 0 <= i && ins_rep[i] == lines_insert; --i) { 777 /* Make each character in the screen line image invalid. */ 778 for (cp = curscr->_line[i], j = 0; j < COLS; ++j, ++cp) 779 cp->_n = -1; 780 ins_rep[i] = lines_replace; 781 } 782 } 783 784 /*f 785 * Complex update algorithm using insert/delete line operations. 786 * 787 * References: 788 * [MyM86] E.W. Myers & W. Miller, Row Replacement Algorithms for 789 * Screen Editors, TR 86-19, Dept. Computer Science, U. of Arizona 790 * [MyM87] E.W. Myers & W. Miller, A Simple Row Replacement Method, 791 * TR 86-28, Dept. Computer Science, U. of Arizona 792 * [Mil87] W. Miller, A Software Tools Sampler, Prentice-Hall, 1987 793 * [Gos81] James Gosling, A redisplay algorithm, Proceedings of the 794 * ACM Symposium on Text Manipulation, SIGPLAN Notices, 795 * 16(6) June 1981, pg 123-129 796 * 797 * All the above were reviewed and experimented with. Due to the nature of 798 * Curses' having to handling overlapping WINDOWs, the only suitable 799 * algorithum is [Gos81]. The others are better suited to editor type 800 * applications that have one window being the entire terminal screen. 801 * 802 */ 803 STATIC void 804 complex() 805 { 806 int fr = -1; 807 int i, j, lr; 808 t_action func; 809 810 /* Find block of lines to change */ 811 for (i = 0; i < LINES; ++i) { 812 if (newscr->_first[i] < newscr->_last[i]) { 813 /* Compute new hash. */ 814 __m_cc_hash(newscr, nhash, i); 815 if (fr == -1) 816 fr = i; 817 lr = i; 818 } else { 819 /* Line not dirty so hash same as before. */ 820 nhash[i] = __m_screen->_hash[i]; 821 } 822 } 823 824 if (fr != -1) { 825 /* Gosling */ 826 cost(fr, lr); 827 script(fr, lr); 828 829 /* Do deletes first in reverse order. */ 830 for (j = lr; fr <= j; --j) { 831 if (del[j] != (t_action) 0) { 832 for (i = j-1; fr <= i; --i) 833 if (del[i] == (t_action) 0) 834 break; 835 836 lines_delete(i+1, j+1); 837 j = i; 838 } 839 } 840 841 /* Do insert/replace in forward order. */ 842 for (i = fr; i <= lr; ++i) { 843 if ((func = ins_rep[i]) != (t_action) 0) { 844 /* Find size of block */ 845 for (j = i; j <= lr && ins_rep[j] == func; ++j) 846 ; 847 (*func)(i, j); 848 i = j-1; 849 } 850 } 851 record: 852 /* _line[], which contains pointers to screen lines, 853 * may be shuffled. 854 */ 855 for (i = fr; i <= lr; ++i) { 856 /* Save new hash for next update. */ 857 __m_screen->_hash[i] = nhash[i]; 858 859 /* Mark line as untouched. */ 860 newscr->_first[i] = newscr->_maxx; 861 newscr->_last[i] = -1; 862 } 863 } 864 } 865 866 /*f 867 * Simple screen update algorithm 868 * 869 * We perform a simple incremental update of the terminal screen. 870 * Only the segment of a line that was touched is replaced on the 871 * line. 872 */ 873 STATIC void 874 simple() 875 { 876 int row; 877 878 for (row = 0; row < LINES; ++row) { 879 if (newscr->_first[row] < newscr->_last[row]) { 880 text_replace(row); 881 882 /* Mark line as untouched. */ 883 newscr->_first[row] = newscr->_maxx; 884 newscr->_last[row] = -1; 885 886 if (__m_screen->_flags & S_INS_DEL_LINE) 887 __m_cc_hash(newscr, nhash, row); 888 } 889 } 890 891 newscr->_flags &= ~W_REDRAW_WINDOW; 892 } 893 894 /*f 895 * Send all changes made to _newscr to the physical terminal. 896 * 897 * If idlok() is set TRUE then doupdate will try and use hardware insert 898 * and delete line sequences in an effort to optimize output. idlok() 899 * should really only be used in applications that want a proper scrolling 900 * effect. 901 * 902 * Added scroll heuristic to handle special case where a full size window 903 * with full size scroll region, will scroll the window and replace dirty 904 * lines instead of performing usual cost/script operations. 905 */ 906 int 907 doupdate() 908 { 909 #ifdef SIGTSTP 910 int (*oldsig)(int) = signal(SIGTSTP, SIG_IGN); 911 #endif 912 913 #ifdef M_CURSES_TYPEAHEAD 914 unsigned char cc; 915 volatile int min, time, icanon; 916 917 if (__m_screen->_flags & S_ISATTY) { 918 /* Set up non-blocking input for typeahead trapping. */ 919 min = cur_term->_prog.c_cc[VMIN]; 920 time = cur_term->_prog.c_cc[VTIME]; 921 icanon = cur_term->_prog.c_lflag & ICANON; 922 923 cur_term->_prog.c_cc[VMIN] = 0; 924 cur_term->_prog.c_cc[VTIME] = 0; 925 cur_term->_prog.c_lflag &= ~ICANON; 926 927 (void) tcsetattr(__m_screen->_kfd, TCSANOW, &cur_term->_prog); 928 } 929 #endif /* M_CURSES_TYPEAHEAD */ 930 931 #ifdef M_CURSES_TRACE 932 __m_trace( 933 "doupdate(void) using %s algorithm.", 934 (__m_screen->_flags & S_INS_DEL_LINE) ? "complex" : "simple" 935 ); 936 #endif 937 938 newscr = __m_screen->_newscr; 939 940 if (__m_screen->_flags & S_ENDWIN) { 941 /* Return from temporary escape done with endwin(). */ 942 __m_screen->_flags &= ~S_ENDWIN; 943 944 (void) reset_prog_mode(); 945 if (enter_ca_mode != (char *) 0) 946 (void) tputs(enter_ca_mode, 1, __m_outc); 947 if (keypad_xmit != (char *) 0) 948 (void) tputs(keypad_xmit, 1, __m_outc); 949 if (ena_acs != (char *) 0) 950 (void) tputs(ena_acs, 1, __m_outc); 951 952 /* Force redraw of screen. */ 953 newscr->_flags |= W_CLEAR_WINDOW; 954 } 955 956 #ifdef M_CURSES_TYPEAHEAD 957 if (setjmp(breakout) == 0) { 958 if ((__m_screen->_flags & S_ISATTY) 959 && read(__m_screen->_kfd, &cc, sizeof cc) == sizeof cc) { 960 (void) ungetch(cc); 961 longjmp(breakout, 1); 962 } 963 #endif /* M_CURSES_TYPEAHEAD */ 964 965 /* When redrawwing a window, we not only assume that line 966 * noise may have lost characters, but line noise may have 967 * generated bogus characters on the screen outside the 968 * the window in question, in which case redraw the entire 969 * screen to be sure. 970 */ 971 if (newscr->_flags & (W_CLEAR_WINDOW | W_REDRAW_WINDOW)) { 972 clear_bottom(0); 973 newscr->_flags &= ~W_CLEAR_WINDOW; 974 (void) wtouchln(newscr, 0, newscr->_maxy, 1); 975 } 976 977 if (newscr->_flags & W_REDRAW_WINDOW) 978 simple(); 979 #if 0 /* This first expression, of undefined section, is useless 980 * since newscr->_scroll is unsigned and never LT zero. 981 */ 982 else if (newscr->_scroll < 0 && scroll_dn(-newscr->_scroll)) 983 #else 984 else if (scroll_dn(-newscr->_scroll)) 985 #endif 986 ; 987 else if (0 < newscr->_scroll && scroll_up(newscr->_scroll)) 988 ; 989 else if (__m_screen->_flags & S_INS_DEL_LINE) 990 complex(); 991 else 992 simple(); 993 994 if (!(newscr->_flags & W_LEAVE_CURSOR)) 995 GOTO(newscr->_cury, newscr->_curx); 996 997 if (!(curscr->_flags & W_FLUSH)) 998 (void) fflush(__m_screen->_of); 999 #ifdef M_CURSES_TYPEAHEAD 1000 } 1001 1002 if (__m_screen->_flags & S_ISATTY) { 1003 /* Restore previous input mode. */ 1004 cur_term->_prog.c_cc[VMIN] = min; 1005 cur_term->_prog.c_cc[VTIME] = time; 1006 cur_term->_prog.c_lflag |= icanon; 1007 1008 (void) tcsetattr(__m_screen->_kfd,TCSANOW,&cur_term->_prog); 1009 } 1010 #endif /* M_CURSES_TYPEAHEAD */ 1011 1012 newscr->_scroll = curscr->_scroll = 0; 1013 #ifdef SIGTSTP 1014 signal(SIGTSTP, oldsig); 1015 #endif 1016 1017 return __m_return_code("doupdate", OK); 1018 } 1019 1020 /* 1021 * If true, the implementation may use hardware insert and delete, 1022 * character features of the terminal. The window parameter 1023 * is ignored. 1024 */ 1025 void 1026 idcok(WINDOW *w, bool bf) 1027 { 1028 #ifdef M_CURSES_TRACE 1029 __m_trace("idcok(%p, %d)", w, bf); 1030 #endif 1031 1032 __m_screen->_flags &= ~S_INS_DEL_CHAR; 1033 if (bf) 1034 __m_screen->_flags |= S_INS_DEL_CHAR; 1035 1036 __m_return_void("idcok"); 1037 } 1038 1039 /* 1040 * If true, the implementation may use hardware insert, delete, 1041 * and scroll line features of the terminal. The window parameter 1042 * is ignored. 1043 */ 1044 int 1045 idlok(WINDOW *w, bool bf) 1046 { 1047 #ifdef M_CURSES_TRACE 1048 __m_trace("idlok(%p, %d)", w, bf); 1049 #endif 1050 1051 __m_screen->_flags &= ~S_INS_DEL_LINE; 1052 if (bf && has_il()) 1053 __m_screen->_flags |= S_INS_DEL_LINE; 1054 1055 return __m_return_code("idlok", OK); 1056 } 1057 1058 /* 1059 * Use the POSIX 32-bit CRC function to compute a hash value 1060 * for the window line. 1061 */ 1062 void 1063 __m_cc_hash(w, array, y) 1064 WINDOW *w; 1065 unsigned long *array; 1066 int y; 1067 { 1068 array[y] = 0; 1069 m_crcposix( 1070 &array[y], (unsigned char *) w->_line[y], 1071 (size_t) (w->_maxx * sizeof **w->_line) 1072 ); 1073 } 1074 1075 1076