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