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-1999 by Sun Microsystems, Inc.
24 * All rights reserved.
25 */
26
27 /* LINTLIBRARY */
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[] =
41 "$Header: /team/ps/sun_xcurses/archive/local_changes/xcurses/src/lib/"
42 "libxcurses/src/libc/xcurses/rcs/doupdate.c 1.22 1998/06/04 12:13:38 "
43 "cbates Exp $";
44 #endif
45 #endif
46
47 #include <sys/isa_defs.h>
48 #include <private.h>
49 #include <string.h>
50 #include <signal.h>
51
52 #undef SIGTSTP
53
54 /*
55 * This value is the ideal length for the cursor addressing sequence
56 * being four bytes long, ie. "<escape><cursor addressing code><row><col>".
57 * eg. VT52 - "\EYrc" or ADM3A - "\E=rc"
58 */
59 #define JUMP_SIZE 4
60
61 /*
62 * This value is the ideal length for the clear-to-eol sequence
63 * being two bytes long, ie "<escape><clear eol code>".
64 */
65 #define CEOL_SIZE 2
66
67 #define GOTO(r, c) ((void) __m_mvcur(curscr->_cury, curscr->_curx,\
68 r, c, __m_outc), curscr->_cury = r, curscr->_curx = c)
69
70 typedef struct cost_op {
71 short cost;
72 short op;
73 } lcost;
74
75 typedef void (*t_action)(int, int);
76
77
78 #define LC(i, j) (lc[(i) * (LINES + 1) + (j)])
79
80 static lcost *lc = NULL;
81 #if defined(_LP64)
82 static unsigned int *nhash = NULL;
83 #else
84 static unsigned long *nhash = NULL;
85 #endif
86 static t_action *del = NULL;
87 static t_action *ins_rep = NULL;
88
89 static WINDOW *newscr;
90
91 static void erase_bottom(int, int);
92 static void clear_bottom(int);
93 static void complex(void);
94 static int cost(int, int);
95 static void lines_delete(int, int);
96 static void lines_insert(int, int);
97 static void lines_replace(int, int);
98 static void script(int, int);
99 static int scroll_up(int);
100 static void simple(void);
101 static void text_replace(int);
102 #if 0
103 static int scroll_dn(int);
104 #endif
105
106
107 /*
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
__m_outc(int ch)114 __m_outc(int ch)
115 {
116 return (putc(ch, __m_screen->_of));
117 }
118
119 /*
120 * Allocate or grow doupdate() structures.
121 */
122 int
__m_doupdate_init(void)123 __m_doupdate_init(void)
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 = malloc((lines + 1) * (lines + 1) * sizeof (*lc));
135 if (new == NULL)
136 return (-1);
137 if (lc != NULL)
138 free(lc);
139 lc = (lcost *) new;
140
141 new = malloc((lines + lines) * sizeof (*del));
142 if (new == NULL)
143 return (-1);
144 if (del != NULL)
145 free(del);
146 del = (t_action *) new;
147 ins_rep = del + lines;
148
149 new = malloc(lines * sizeof (*nhash));
150 if (new == NULL)
151 return (-1);
152 if (nhash != NULL)
153 free(nhash);
154 #if defined(_LP64)
155 nhash = (unsigned int *) new;
156 #else
157 nhash = (unsigned long *) new;
158 #endif
159
160 nlines = lines;
161
162 return (0);
163 }
164
165 static void
erase_bottom(int start,int end)166 erase_bottom(int start, int end)
167 {
168 int i;
169
170 for (i = start; i < end; ++i) {
171 (void) __m_cc_erase(curscr, i, 0, i, curscr->_maxx - 1);
172 __m_cc_hash(curscr, __m_screen->_hash, i);
173 }
174 }
175
176 /*
177 * Clear from the start of the current row to bottom of screen.
178 */
179 static void
clear_bottom(int y)180 clear_bottom(int 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 != NULL) {
187 (void) TPUTS(clear_screen, 1, __m_outc);
188 } else {
189 (void) __m_mvcur(-1, -1, y, 0, __m_outc);
190 if (clr_eos != NULL) {
191 (void) TPUTS(clr_eos, 1, __m_outc);
192 } else if (clr_eol != NULL) {
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
208
209 /*
210 * Rewrite of text_replace() implementation by C. Bates of MKS
211 *
212 * This code creates a list of 'regions' for each test line which
213 * is to be replaced. Each region describes a portion of the line.
214 * Logic is performed on the list of regions, then the regions
215 * are used to generate output.
216 */
217 typedef struct LineRegion {
218 int col; /* Starting column of region */
219 int size; /* Size of region */
220 /* 0: Difference Region, 1: Common Region, 2: Delete Region */
221 int type;
222 } LineRegion;
223 #define REGION_DIFFERENT 0
224 #define REGION_COMMON 1
225 #define REGION_DELETE 2
226
227 #define DELETE_SEARCH_LIMIT 4
228 #define DELETE_THRESHOLD 10
229
230 static LineRegion regions[1024];
231 int nRegions = 0;
232
233 /*
234 * Return the first column of the completely blank End-of-line
235 */
236 static int
_find_blank_tail(int row)237 _find_blank_tail(int row)
238 {
239 cchar_t *nptr;
240 int tail = COLS;
241
242 if (!clr_eol)
243 return (COLS);
244 /*
245 * Find start of blank tail region.
246 */
247 nptr = &newscr->_line[row][COLS];
248 for (; 0 < tail; --tail) {
249 if (!__m_cc_compare(--nptr, &newscr->_bg, 1))
250 break;
251 }
252 return (tail);
253 }
254
255 /*
256 * Send all the characters in the region to the terminal
257 */
258 static void
_writeRegion(int row,LineRegion region)259 _writeRegion(int row, LineRegion region)
260 {
261 short npair;
262 attr_t nattr;
263 int i;
264 cchar_t *optr = &curscr->_line[row][region.col];
265 cchar_t *nptr = &newscr->_line[row][region.col];
266
267 for (i = 0; i < region.size; i++, nptr++, optr++) {
268 nattr = nptr->_at;
269 npair = nptr->_co;
270
271 /*
272 * Change attribute state.
273 */
274 if ((ATTR_STATE != nattr) || (optr->_at != nattr) ||
275 (cur_term->_co != npair)) {
276 (void) vid_puts(nattr, npair, NULL, __m_outc);
277 }
278 /*
279 * Don't display internal characters.
280 */
281 if (nptr->_f)
282 (void) __m_cc_write(nptr);
283
284 /*
285 * Update copy of screen image.
286 */
287 *optr = *nptr;
288 curscr->_curx = region.col + i + 1;
289 }
290 }
291
292 /*
293 * Delete some characters from the terminal for this region
294 */
295 static void
_deleteRegion(int row,LineRegion region)296 _deleteRegion(int row, LineRegion region)
297 {
298 int i;
299 cchar_t *optr = &curscr->_line[row][region.col];
300
301 if ((region.size <= 1) || !parm_dch) {
302 for (i = 0; i < region.size; i++)
303 (void) TPUTS(delete_character, 1, __m_outc);
304 } else {
305 (void) TPUTS(tparm(parm_dch, (long)region.size,
306 0, 0, 0, 0, 0, 0, 0, 0), region.size, __m_outc);
307 }
308 for (i = region.col; i < COLS - region.size; i++) {
309 /*
310 * Delete the chars in the image of the real screen
311 */
312 *optr = *(optr + region.size);
313 optr++;
314 }
315 }
316
317 /*
318 * Use clr_eol control if possible
319 */
320 static void
_clearToEOL(int row,int tail)321 _clearToEOL(int row, int tail)
322 {
323 if (tail < COLS) {
324 GOTO(row, tail);
325 /*
326 * Restore default color pair before area clear.
327 */
328 if (back_color_erase)
329 (void) vid_puts(WA_NORMAL, 0, NULL, __m_outc);
330
331 (void) TPUTS(clr_eol, 1, __m_outc);
332 (void) __m_cc_erase(curscr, row, tail, row, COLS - 1);
333 }
334 }
335
336 /*
337 * Delete leading common region
338 */
339 static void
_normalizeRegions1(void)340 _normalizeRegions1(void)
341 {
342 int iRegion;
343
344 /*
345 * Delete leading common region
346 */
347 if (regions[0].type == REGION_COMMON) {
348 nRegions--;
349 for (iRegion = 0; iRegion < nRegions; iRegion++) {
350 regions[iRegion] = regions[iRegion + 1];
351 }
352 }
353 }
354
355 /*
356 * Give each region a size, then delete all trailing common regions
357 */
358 static void
_normalizeRegions2(void)359 _normalizeRegions2(void)
360 {
361 int iRegion;
362
363 for (iRegion = 0; iRegion < nRegions - 1; iRegion++) {
364 regions[iRegion].size = regions[iRegion + 1].col -
365 regions[iRegion].col;
366 }
367 regions[nRegions - 1].size = COLS - regions[nRegions - 1].col;
368
369 /*
370 * Delete trailing common regions
371 */
372 while (regions[nRegions - 1].type == REGION_COMMON)
373 nRegions--;
374 }
375
376 /*
377 * Tiny common regions are merged into adjacent difference regions
378 */
379 static void
_mergeTinyRegions(void)380 _mergeTinyRegions(void)
381 {
382 int from;
383 int to;
384 for (from = 1, to = 1; from < nRegions; ) {
385 if ((regions[from].type == REGION_COMMON) &&
386 (regions[from].size < JUMP_SIZE)) {
387 /*
388 * Merge out tiny common regions
389 */
390 regions[to - 1].size += regions[from].size;
391 /*
392 * Now join adjacent non-common regions
393 */
394 if (++from < nRegions)
395 regions[to - 1].size += regions[from++].size;
396 } else {
397 regions[to++] = regions[from++];
398 }
399 }
400 nRegions = to;
401 }
402
403 /*
404 * Create the initial list of regions for this row
405 */
406 static int
_findRegions(int row)407 _findRegions(int row)
408 {
409 int cmp;
410 int old_cmp;
411 int col;
412 int bestDeleteCount;
413 cchar_t *nptr = &newscr->_line[row][0];
414 cchar_t *optr = &curscr->_line[row][0];
415
416 col = 0;
417 nRegions = 0;
418 bestDeleteCount = 0;
419 if ((__m_screen->_flags & S_INS_DEL_CHAR) &&
420 (parm_dch || delete_character)) {
421 int bestFit = 0;
422 int deletePoint;
423 int deleteCount;
424 int matches;
425
426 /*
427 * Skip to first difference
428 */
429 for (col = 0; col < COLS; col++) {
430 if (!__m_cc_compare(&optr[col], &nptr[col], 1))
431 break;
432 }
433 deletePoint = col;
434 for (deleteCount = 1; deleteCount < DELETE_SEARCH_LIMIT;
435 deleteCount++) {
436 matches = 0;
437 for (col = deletePoint; col < COLS - deleteCount;
438 col++) {
439 if (__m_cc_compare(&optr[col + deleteCount],
440 &nptr[col], 1))
441 matches++;
442 else
443 break;
444 }
445 if (matches > bestFit) {
446 bestFit = matches;
447 bestDeleteCount = deleteCount;
448 }
449 }
450 if (bestFit > DELETE_THRESHOLD) {
451 regions[nRegions].type = REGION_DELETE;
452 regions[nRegions].col = deletePoint;
453 regions[nRegions].size = bestDeleteCount;
454 nRegions++;
455 col = deletePoint + bestDeleteCount;
456 } else {
457 col = 0;
458 nRegions = 0;
459 /* Forget trying to use character delete */
460 bestDeleteCount = 0;
461 }
462 }
463 for (old_cmp = -1; col + bestDeleteCount < COLS; col++) {
464 cmp = __m_cc_compare(&optr[col + bestDeleteCount],
465 &nptr[col], 1);
466 if (cmp != old_cmp) {
467 regions[nRegions].type = cmp ? REGION_COMMON :
468 REGION_DIFFERENT;
469 regions[nRegions].col = col;
470 regions[nRegions].size = 0; /* Determine later */
471 nRegions++;
472 old_cmp = cmp;
473 }
474 }
475 if (bestDeleteCount) {
476 /*
477 * Force update of end-of-line if delete is to be used
478 */
479 regions[nRegions].type = REGION_DIFFERENT;
480 regions[nRegions].col = col;
481 regions[nRegions].size = 0; /* Determine later */
482 nRegions++;
483 }
484 _normalizeRegions1();
485 if (nRegions == 0)
486 return (0); /* No difference regions */
487
488 _normalizeRegions2();
489 return (1);
490 }
491
492 /*
493 * Determine if Clr-EOL optimization can be used, and
494 * adjust regions accordingly
495 */
496 static int
_ceolAdjustRegions(int row)497 _ceolAdjustRegions(int row)
498 {
499 int iRegion;
500 int blankEolStart = _find_blank_tail(row);
501
502 for (iRegion = 0; iRegion < nRegions; iRegion++) {
503 switch (regions[iRegion].type) {
504 case REGION_DIFFERENT:
505 if (regions[iRegion].col >= blankEolStart) {
506 /*
507 * Delete this and all following regions
508 */
509 nRegions = iRegion;
510 return (blankEolStart);
511 }
512 if (regions[iRegion].col + regions[iRegion].size >
513 blankEolStart) {
514 /*
515 * Truncate this region to end
516 * where blank EOL starts
517 */
518 regions[iRegion].size = blankEolStart -
519 regions[iRegion].col;
520 /*
521 * Delete all following regions
522 */
523 nRegions = iRegion + 1;
524 return (blankEolStart);
525 }
526 break;
527 case REGION_COMMON:
528 break;
529 case REGION_DELETE: /* Scrap the whole thing */
530 return (COLS);
531 }
532 }
533 return (COLS); /* Couldn't use Clear EOL optimization */
534 }
535
536 /*
537 * Generate output, based on region list
538 */
539 static void
_updateRegions(int row)540 _updateRegions(int row)
541 {
542 int ceolStart;
543 int iRegion;
544
545 ceolStart = _ceolAdjustRegions(row);
546
547 /*
548 * regions are guaranteed to start with a non-common region.
549 * tiny common regions have also been merged into
550 * bracketting common-regions.
551 */
552 if (nRegions) {
553 for (iRegion = 0; iRegion < nRegions; iRegion++) {
554 switch (regions[iRegion].type) {
555 case REGION_COMMON:
556 break;
557 case REGION_DELETE:
558 /*
559 * Start of non-common region
560 */
561 GOTO(row, regions[iRegion].col);
562 _deleteRegion(row, regions[iRegion]);
563 break;
564 case REGION_DIFFERENT:
565 /*
566 * Star of non-common region
567 */
568 GOTO(row, regions[iRegion].col);
569 _writeRegion(row, regions[iRegion]);
570 break;
571 }
572 }
573 }
574 if (ceolStart != COLS) {
575 _clearToEOL(row, ceolStart);
576 }
577 }
578
579 /*
580 * The new text_replace algorithm, which uses regions
581 */
582 static void
text_replace(int row)583 text_replace(int row)
584 {
585 if (!_findRegions(row))
586 return;
587 _mergeTinyRegions();
588 _updateRegions(row);
589
590 /*
591 * Line wrapping checks.
592 */
593 if (COLS <= curscr->_curx) {
594 --curscr->_curx;
595 if (auto_right_margin && (row < LINES - 1)) {
596 if (eat_newline_glitch) {
597 (void) __m_outc('\r');
598 (void) __m_outc('\n');
599 }
600 ++curscr->_cury;
601 curscr->_curx = 0;
602 }
603 }
604 }
605
606 /*
607 * Replace a block of lines.
608 * Only ever used for complex().
609 */
610 static void
lines_replace(int from,int to_1)611 lines_replace(int from, int to_1)
612 {
613 for (; from < to_1; ++from)
614 text_replace(from);
615 }
616
617 /*
618 * Delete a block of lines.
619 * Only ever used for complex().
620 */
621 static void
lines_delete(int from,int to_1)622 lines_delete(int from, int to_1)
623 {
624 int count = to_1 - from;
625
626 if (LINES <= to_1) {
627 erase_bottom(from, LINES);
628 clear_bottom(from);
629 } else {
630 GOTO(from, 0);
631 (void) winsdelln(curscr, -count);
632
633 if (parm_delete_line != NULL) {
634 /*
635 * Assume that the sequence to delete more than one
636 * line is faster than repeated single delete_lines.
637 */
638 (void) TPUTS(tparm(parm_delete_line, (long)count,
639 0, 0, 0, 0, 0, 0, 0, 0), count, __m_outc);
640 } else if (delete_line != NULL) {
641 while (from++ < to_1)
642 (void) TPUTS(delete_line, 1, __m_outc);
643 } else {
644 /* Error -- what to do. */
645 return;
646 }
647 }
648 }
649
650 /*
651 * Insert a block of lines.
652 * Only ever used for complex().
653 *
654 * We must assume that insert_line and parm_insert_line reset the
655 * cursor column to zero. Therefore it is text_replace() responsiblity
656 * to move the cursor to the correct column to begin the update.
657 */
658 static void
lines_insert(int from,int to_1)659 lines_insert(int from, int to_1)
660 {
661 int row;
662 int count = to_1 - from;
663
664 /*
665 * Position the cursor and insert a block of lines into the screen
666 * image now, insert lines into the physical screen, then draw the
667 * new screen lines.
668 */
669 GOTO(from, 0);
670 (void) winsdelln(curscr, count);
671
672 if (parm_insert_line != NULL) {
673 /*
674 * Assume that the sequence to insert more than one line is
675 * faster than repeated single insert_lines.
676 */
677 (void) TPUTS(tparm(parm_insert_line, (long)count,
678 0, 0, 0, 0, 0, 0, 0, 0), count, __m_outc);
679 } else if (insert_line != NULL) {
680 /*
681 * For the single line insert we use to iterate moving
682 * the cursor, inserting, and then drawing a line. That
683 * would appear to be slow but visually appealing. However,
684 * people on slow terminals want speed and those on fast
685 * terminal won't see it.
686 */
687 for (row = from; row < to_1; ++row)
688 (void) TPUTS(insert_line, 1, __m_outc);
689 } else {
690 /* Error -- what to do. */
691 return;
692 }
693
694 for (row = from; row < to_1; ++row)
695 text_replace(row);
696 }
697
698 static int
scroll_up(int n)699 scroll_up(int n)
700 {
701 int count = n;
702 int start, finish, to;
703
704 if (scroll_forward != NULL) {
705 GOTO(LINES-1, 0);
706 while (0 < n--)
707 (void) TPUTS(scroll_forward, 1, __m_outc);
708 } else if (parm_delete_line != NULL && 1 < n) {
709 GOTO(0, 0);
710 (void) TPUTS(tparm(parm_delete_line, (long)n,
711 0, 0, 0, 0, 0, 0, 0, 0), n, __m_outc);
712 } else if (delete_line != NULL) {
713 GOTO(0, 0);
714 while (0 < n--)
715 (void) TPUTS(delete_line, 1, __m_outc);
716 } else {
717 return (0);
718 }
719
720 /* Scroll recorded image. */
721 start = 0;
722 finish = count-1;
723 to = lines;
724
725 (void) __m_cc_erase(curscr, start, 0, finish, curscr->_maxx-1);
726 (void) __m_ptr_move((void **) curscr->_line,
727 curscr->_maxy, start, finish, to);
728
729 simple();
730
731 return (1);
732 }
733
734 #if 0
735 static int
736 scroll_dn(int n)
737 {
738 int count = n;
739 int start, finish, to;
740
741 if (LINES < n)
742 return (0);
743
744 if (scroll_reverse != NULL) {
745 GOTO(0, 0);
746 while (0 < n--)
747 (void) TPUTS(scroll_reverse, 1, __m_outc);
748 } else if (parm_insert_line != NULL && 1 < n) {
749 GOTO(0, 0);
750 (void) TPUTS(tparm(parm_insert_line, (long)n,
751 0, 0, 0, 0, 0, 0, 0, 0), n, __m_outc);
752 } else if (insert_line != NULL) {
753 GOTO(0, 0);
754 while (0 < n--)
755 (void) TPUTS(insert_line, 1, __m_outc);
756 } else {
757 return (0);
758 }
759
760 /* Scroll recorded image. */
761 start = lines - count;
762 finish = lines - 1;
763 to = 0;
764
765 (void) __m_cc_erase(curscr, start, 0, finish, curscr->_maxx-1);
766 (void) __m_ptr_move((void **) curscr->_line,
767 curscr->_maxy, start, finish, to);
768
769 simple();
770
771 return (1);
772 }
773 #endif
774
775 /*
776 * Dynamic programming algorithm for the string edit problem.
777 *
778 * This is a modified Gosling cost algorithm that takes into account
779 * null/move operations.
780 *
781 * Costs for move, delete, replace, and insert are 0, 1, 2, and 3
782 * repectively.
783 */
784 #define MOVE_COST 0
785 #define REPLACE_COST 10
786 #define INSERT_COST 12
787 #define DELETE_COST 1
788
789 static int
cost(int fr,int lr)790 cost(int fr, int lr)
791 {
792 lcost *lcp;
793 int or, nr, cc;
794 #if defined(_LP64)
795 unsigned int *ohash = __m_screen->_hash;
796 #else
797 unsigned long *ohash = __m_screen->_hash;
798 #endif
799
800 /*
801 * Prepare initial row and column of cost matrix.
802 *
803 * 0 3 6 9 ...
804 * 1
805 * 2
806 * 3
807 * :
808 */
809 LC(fr, fr).cost = MOVE_COST;
810 for (cc = 1, ++lr, nr = fr+1; nr <= lr; ++nr, ++cc) {
811 /* Top row is 3, 6, 9, ... */
812 LC(fr, nr).cost = cc * INSERT_COST;
813 LC(fr, nr).op = 'i';
814
815 /* Left column is 1, 2, 3, ... */
816 LC(nr, fr).cost = cc * DELETE_COST;
817 LC(nr, fr).op = 'd';
818 }
819
820 for (--lr, or = fr; or <= lr; ++or) {
821 for (nr = fr; nr <= lr; ++nr) {
822 lcp = &LC(or + 1, nr + 1);
823
824 /* Assume move op. */
825 lcp->cost = LC(or, nr).cost;
826 lcp->op = 'm';
827
828 if (ohash[or] != nhash[nr]) {
829 /* Lines are different, assume replace op. */
830 lcp->cost += REPLACE_COST;
831 lcp->op = 'r';
832 }
833
834 /* Compare insert op. */
835 if ((cc = LC(or + 1, nr).cost + INSERT_COST) <
836 lcp->cost) {
837 lcp->cost = cc;
838 lcp->op = 'i';
839 }
840
841 /* Compare delete op. */
842 if ((cc = LC(or, nr + 1).cost + DELETE_COST) <
843 lcp->cost) {
844 lcp->cost = cc;
845 lcp->op = 'd';
846 }
847 }
848 }
849
850 return (LC(lr + 1, lr + 1).cost);
851 }
852
853 /*
854 * Build edit script.
855 *
856 * Normally this would be a recursve routine doing the deletes, inserts,
857 * and replaces on individual lines. Instead we build the script so that
858 * we can later do the operations on a block basis. For terminals with
859 * parm_delete or parm_insert strings this will be better in terms of the
860 * number of characters sent to delete and insert a block of lines.
861 *
862 * Also we can optimize the script so that tail inserts become replaces.
863 * This saves unnecessary inserts operations when the tail can just be
864 * overwritten.
865 */
866 static void
script(int fr,int lr)867 script(int fr, int lr)
868 {
869 int i, j;
870 cchar_t *cp;
871
872 i = j = lr + 1;
873
874 (void) memset(del, 0, sizeof (*del) * LINES);
875 (void) memset(ins_rep, 0, sizeof (*ins_rep) * LINES);
876
877 do {
878 /*
879 * We don't have to bounds check i or j becuase row fr and
880 * column fr of lc have been preset in order to guarantee the
881 * correct motion.
882 */
883 switch (LC(i, j).op) {
884 case 'i':
885 --j;
886 ins_rep[j] = lines_insert;
887 break;
888 case 'd':
889 --i;
890 del[i] = lines_delete;
891 break;
892 case 'm':
893 --i;
894 --j;
895 break;
896 case 'r':
897 --i;
898 --j;
899 ins_rep[j] = lines_replace;
900 break;
901 }
902 } while (fr < i || fr < j);
903
904 /* Optimize Tail Inserts */
905 for (i = LINES-1; 0 <= i && ins_rep[i] == lines_insert; --i) {
906 /* Make each character in the screen line image invalid. */
907 for (cp = curscr->_line[i], j = 0; j < COLS; ++j, ++cp)
908 cp->_n = -1;
909 ins_rep[i] = lines_replace;
910 }
911 }
912
913 /*
914 * Complex update algorithm using insert/delete line operations.
915 *
916 * References:
917 * [MyM86] E.W. Myers & W. Miller, Row Replacement Algorithms for
918 * Screen Editors, TR 86-19, Dept. Computer Science, U. of Arizona
919 * [MyM87] E.W. Myers & W. Miller, A Simple Row Replacement Method,
920 * TR 86-28, Dept. Computer Science, U. of Arizona
921 * [Mil87] W. Miller, A Software Tools Sampler, Prentice-Hall, 1987
922 * [Gos81] James Gosling, A redisplay algorithm, Proceedings of the
923 * ACM Symposium on Text Manipulation, SIGPLAN Notices,
924 * 16(6) June 1981, pg 123-129
925 *
926 * All the above were reviewed and experimented with. Due to the nature of
927 * Curses' having to handling overlapping WINDOWs, the only suitable
928 * algorithum is [Gos81]. The others are better suited to editor type
929 * applications that have one window being the entire terminal screen.
930 *
931 */
932 static void
complex(void)933 complex(void)
934 {
935 int fr = -1;
936 int i, j, lr;
937 t_action func;
938
939 /* Find block of lines to change */
940 for (i = 0; i < LINES; ++i) {
941 if (newscr->_first[i] < newscr->_last[i]) {
942 /* Compute new hash. */
943 __m_cc_hash(newscr, nhash, i);
944 if (fr == -1)
945 fr = i;
946 lr = i;
947 } else {
948 /* Line not dirty so hash same as before. */
949 nhash[i] = __m_screen->_hash[i];
950 }
951 }
952
953 if (fr != -1) {
954 /* Gosling */
955 (void) cost(fr, lr);
956 script(fr, lr);
957
958 /* Do deletes first in reverse order. */
959 for (j = lr; fr <= j; --j) {
960 if (del[j] != (t_action) 0) {
961 for (i = j-1; fr <= i; --i)
962 if (del[i] == (t_action) 0)
963 break;
964
965 lines_delete(i+1, j+1);
966 j = i;
967 }
968 }
969
970 /* Do insert/replace in forward order. */
971 for (i = fr; i <= lr; ++i) {
972 if ((func = ins_rep[i]) != (t_action) 0) {
973 /* Find size of block */
974 for (j = i; j <= lr && ins_rep[j] == func; ++j)
975 ;
976 (*func)(i, j);
977 i = j - 1;
978 }
979 }
980 /*
981 * _line[], which contains pointers to screen lines,
982 * may be shuffled.
983 */
984 for (i = fr; i <= lr; ++i) {
985 /* Save new hash for next update. */
986 __m_screen->_hash[i] = nhash[i];
987
988 /* Mark line as untouched. */
989 newscr->_first[i] = newscr->_maxx;
990 newscr->_last[i] = -1;
991 }
992 }
993 }
994
995 /*
996 * Simple screen update algorithm
997 *
998 * We perform a simple incremental update of the terminal screen.
999 * Only the segment of a line that was touched is replaced on the
1000 * line.
1001 */
1002 static void
simple(void)1003 simple(void)
1004 {
1005 int row;
1006
1007 for (row = 0; row < newscr->_maxy; ++row) {
1008 if (newscr->_first[row] < newscr->_last[row]) {
1009 text_replace(row);
1010
1011 /* Mark line as untouched. */
1012 newscr->_first[row] = newscr->_maxx;
1013 newscr->_last[row] = -1;
1014 __m_cc_hash(curscr, __m_screen->_hash, row);
1015 }
1016 }
1017
1018 newscr->_flags &= ~W_REDRAW_WINDOW;
1019 }
1020
1021 void
wtouchln_hard(WINDOW * w,int y,int n)1022 wtouchln_hard(WINDOW *w, int y, int n)
1023 {
1024 int last;
1025
1026 last = w->_maxx;
1027
1028 for (; (y < w->_maxy) && (0 < n); ++y, --n) {
1029 /*
1030 * Force compare in doupdate to fail.
1031 * Touch should be unconditional
1032 */
1033 (void) memset(&__m_screen->_curscr->_line[w->_begy + y][w->_begx],
1034 0xff, last * sizeof (cchar_t));
1035 }
1036 }
1037 /*
1038 * Send all changes made to _newscr to the physical terminal.
1039 *
1040 * If idlok() is set TRUE then doupdate will try and use hardware insert
1041 * and delete line sequences in an effort to optimize output. idlok()
1042 * should really only be used in applications that want a proper scrolling
1043 * effect.
1044 *
1045 * Added scroll heuristic to handle special case where a full size window
1046 * with full size scroll region, will scroll the window and replace dirty
1047 * lines instead of performing usual cost/script operations.
1048 */
1049 int
doupdate(void)1050 doupdate(void)
1051 {
1052 #ifdef SIGTSTP
1053 int (*oldsig)(int) = signal(SIGTSTP, SIG_IGN);
1054 #endif
1055
1056 if (pollTypeahead()) {
1057 return (OK);
1058 }
1059 newscr = __m_screen->_newscr;
1060
1061 if (__m_screen->_flags & S_ENDWIN) {
1062 /* Return from temporary escape done with endwin(). */
1063 __m_screen->_flags &= ~S_ENDWIN;
1064
1065 (void) reset_prog_mode();
1066 if (enter_ca_mode != NULL)
1067 (void) TPUTS(enter_ca_mode, 1, __m_outc);
1068 if (keypad_xmit != NULL)
1069 (void) TPUTS(keypad_xmit, 1, __m_outc);
1070 if (ena_acs != NULL)
1071 (void) TPUTS(ena_acs, 1, __m_outc);
1072
1073 /* Force redraw of screen. */
1074 newscr->_flags |= W_CLEAR_WINDOW;
1075 }
1076 /*
1077 * When redrawwing a window, we not only assume that line
1078 * noise may have lost characters, but line noise may have
1079 * generated bogus characters on the screen outside the
1080 * the window in question, in which case redraw the entire
1081 * screen to be sure.
1082 */
1083 if ((newscr->_flags & (W_CLEAR_WINDOW | W_REDRAW_WINDOW)) ||
1084 (curscr->_flags & W_CLEAR_WINDOW)) {
1085 erase_bottom(0, newscr->_maxy);
1086 clear_bottom(0);
1087 (void) wtouchln(newscr, 0, newscr->_maxy, 1);
1088 newscr->_flags &= ~W_CLEAR_WINDOW;
1089 curscr->_flags &= ~W_CLEAR_WINDOW;
1090 }
1091
1092 /*
1093 * Scrolling heuristic should only be used if lines being
1094 * scrolled are clean because scrolling overrides updates
1095 *
1096 * Right now, the following code should always turn off
1097 * scrolling, because the internal scroll touches the
1098 * scrolled lines. This thing requires a lot more care
1099 * than I have right now...
1100 */
1101 if (newscr->_scroll) {
1102 int y;
1103 for (y = 0; y < newscr->_maxy; ++y) {
1104 if (0 <= newscr->_last[y]) {
1105 newscr->_scroll = 0;
1106 }
1107 }
1108 newscr->_scroll = 0; /* Just fudge it for now ... */
1109 }
1110 if (newscr->_flags & W_REDRAW_WINDOW) {
1111 simple();
1112 } else {
1113 if (newscr->_scroll == 0) {
1114 if (__m_screen->_flags & S_INS_DEL_LINE) {
1115 complex();
1116 } else {
1117 simple();
1118 }
1119 } else {
1120 if (!scroll_up(newscr->_scroll)) {
1121 if (__m_screen->_flags & S_INS_DEL_LINE) {
1122 complex();
1123 } else {
1124 simple();
1125 }
1126 }
1127 }
1128 }
1129
1130 if (!(newscr->_flags & W_LEAVE_CURSOR)) {
1131 GOTO(newscr->_cury, newscr->_curx);
1132 }
1133
1134 if (!(curscr->_flags & W_FLUSH)) {
1135 (void) fflush(__m_screen->_of);
1136 }
1137
1138 newscr->_scroll = curscr->_scroll = 0;
1139
1140 /* Send labels to terminal that supports them. */
1141 __m_slk_doupdate();
1142 #ifdef SIGTSTP
1143 signal(SIGTSTP, oldsig);
1144 #endif
1145
1146 return (OK);
1147 }
1148
1149 /*
1150 * If true, the implementation may use hardware insert and delete,
1151 * character features of the terminal. The window parameter
1152 * is ignored.
1153 */
1154 /* ARGSUSED */
1155 void
idcok(WINDOW * w,bool bf)1156 idcok(WINDOW *w, bool bf)
1157 {
1158 __m_screen->_flags &= ~S_INS_DEL_CHAR;
1159 if (bf)
1160 __m_screen->_flags |= S_INS_DEL_CHAR;
1161 }
1162
1163 /*
1164 * If true, the implementation may use hardware insert, delete,
1165 * and scroll line features of the terminal. The window parameter
1166 * is ignored.
1167 */
1168 /* ARGSUSED */
1169 int
idlok(WINDOW * w,bool bf)1170 idlok(WINDOW *w, bool bf)
1171 {
1172 __m_screen->_flags &= ~S_INS_DEL_LINE;
1173 if (bf && has_il())
1174 __m_screen->_flags |= S_INS_DEL_LINE;
1175
1176 return (OK);
1177 }
1178
1179 /*
1180 * Use the POSIX 32-bit CRC function to compute a hash value
1181 * for the window line.
1182 */
1183 void
1184 #if defined(_LP64)
__m_cc_hash(WINDOW * w,unsigned int * array,int y)1185 __m_cc_hash(WINDOW *w, unsigned int *array, int y)
1186 #else
1187 __m_cc_hash(WINDOW *w, unsigned long *array, int y)
1188 #endif
1189 {
1190 array[y] = 0;
1191 m_crcposix(&array[y], (unsigned char *) w->_line[y],
1192 (size_t)(w->_maxx * sizeof (**w->_line)));
1193 }
1194