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