Update
[uemacs.git] / src / display.c
blob7dd942bad1f173ef759d89d4a1d7e7f7e058b5a7
1 /* display.c
3 * The functions in this file handle redisplay. There are two halves, the
4 * ones that update the virtual display screen, and the ones that make the
5 * physical display screen the same as the virtual display screen. These
6 * functions use hints that are left in the windows by the commands.
8 * modified by Petri Kutvonen
9 */
11 #include <stdio.h>
12 #include <stdarg.h>
13 #include <unistd.h>
14 #include <errno.h>
16 #include "estruct.h"
17 #include "edef.h"
18 #include "efunc.h"
19 #include "version.h"
21 struct video {
22 int v_flag; /* Flags */
23 #if COLOR
24 int v_fcolor; /* current forground color */
25 int v_bcolor; /* current background color */
26 int v_rfcolor; /* requested forground color */
27 int v_rbcolor; /* requested background color */
28 #endif
29 char v_text[1]; /* Screen data. */
32 #define VFCHG 0x0001 /* Changed flag */
33 #define VFEXT 0x0002 /* extended (beyond column 80) */
34 #define VFREV 0x0004 /* reverse video status */
35 #define VFREQ 0x0008 /* reverse video request */
36 #define VFCOL 0x0010 /* color change requested */
38 static struct video **vscreen; /* Virtual screen. */
39 #if MEMMAP == 0 || SCROLLCODE
40 static struct video **pscreen; /* Physical screen. */
41 #endif
43 static int displaying = TRUE;
44 #if UNIX
45 #include <signal.h>
46 #endif
47 #ifdef SIGWINCH
48 #include <sys/ioctl.h>
49 /* for window size changes */
50 int chg_width, chg_height;
51 #endif
53 static int reframe(struct window *wp);
54 static void updone(struct window *wp);
55 static void updall(struct window *wp);
56 static int scrolls(int inserts);
57 static void scrscroll(int from, int to, int count);
58 static int texttest(int vrow, int prow);
59 static int endofline(char *s, int n);
60 static void updext(void);
61 static int updateline(int row, struct video *vp1, struct video *vp2);
62 static void modeline(struct window *wp);
63 static void mlputi(int i, int r);
64 static void mlputli(long l, int r);
65 static void mlputf(int s);
66 static int newscreensize(int h, int w);
68 #if RAINBOW
69 static void putline(int row, int col, char *buf);
70 #endif
73 * Initialize the data structures used by the display code. The edge vectors
74 * used to access the screens are set up. The operating system's terminal I/O
75 * channel is set up. All the other things get initialized at compile time.
76 * The original window has "WFCHG" set, so that it will get completely
77 * redrawn on the first call to "update".
79 void vtinit(void)
81 int i;
82 struct video *vp;
84 TTopen(); /* open the screen */
85 TTkopen(); /* open the keyboard */
86 TTrev(FALSE);
87 vscreen = (struct video **) malloc(term.t_mrow * sizeof(struct video *));
89 if (vscreen == NULL)
90 exit(1);
92 #if MEMMAP == 0 || SCROLLCODE
93 pscreen = (struct video **) malloc(term.t_mrow * sizeof(struct video *));
95 if (pscreen == NULL)
96 exit(1);
97 #endif
99 for (i = 0; i < term.t_mrow; ++i) {
100 vp = (struct video *)malloc(sizeof(struct video) + term.t_mcol);
102 if (vp == NULL)
103 exit(1);
105 vp->v_flag = 0;
106 #if COLOR
107 vp->v_rfcolor = 7;
108 vp->v_rbcolor = 0;
109 #endif
110 vscreen[i] = vp;
111 #if MEMMAP == 0 || SCROLLCODE
112 vp = (struct video *)malloc(sizeof(struct video) + term.t_mcol);
114 if (vp == NULL)
115 exit(1);
117 vp->v_flag = 0;
118 pscreen[i] = vp;
119 #endif
123 #if CLEAN
124 /* free up all the dynamically allocated video structures */
126 void vtfree(void)
128 int i;
129 for (i = 0; i < term.t_mrow; ++i) {
130 free(vscreen[i]);
131 #if MEMMAP == 0 || SCROLLCODE
132 free(pscreen[i]);
133 #endif
135 free(vscreen);
136 #if MEMMAP == 0 || SCROLLCODE
137 free(pscreen);
138 #endif
140 #endif
143 * Clean up the virtual terminal system, in anticipation for a return to the
144 * operating system. Move down to the last line and clear it out (the next
145 * system prompt will be written in the line). Shut down the channel to the
146 * terminal.
148 void vttidy(void)
150 mlerase();
151 movecursor(term.t_nrow, 0);
152 TTflush();
153 TTclose();
154 TTkclose();
155 #ifdef PKCODE
156 fwrite("\r", 1, 1, stdout);
157 #endif
161 * Set the virtual cursor to the specified row and column on the virtual
162 * screen. There is no checking for nonsense values; this might be a good
163 * idea during the early stages.
165 void vtmove(int row, int col)
167 vtrow = row;
168 vtcol = col;
172 * Write a character to the virtual screen. The virtual row and
173 * column are updated. If we are not yet on left edge, don't print
174 * it yet. If the line is too long put a "$" in the last column.
176 * This routine only puts printing characters into the virtual
177 * terminal buffers. Only column overflow is checked.
179 static void vtputc(unsigned char c)
181 struct video *vp; /* ptr to line being updated */
183 vp = vscreen[vtrow];
185 if (vtcol >= term.t_ncol) {
186 ++vtcol;
187 vp->v_text[term.t_ncol - 1] = '$';
188 return;
191 if (c == '\t') {
192 do {
193 vtputc(' ');
194 } while (((vtcol + taboff) & tabmask) != 0);
195 return;
198 if (c < 0x20) {
199 vtputc('^');
200 vtputc(c ^ 0x40);
201 return;
204 if (c == 0x7f) {
205 vtputc('^');
206 vtputc('?');
207 return;
210 if (c >= 0x80 && c < 0xA0) {
211 static const char hex[] = "0123456789abcdef";
212 vtputc('\\');
213 vtputc(hex[c >> 4]);
214 vtputc(hex[c & 15]);
215 return;
218 if (vtcol >= 0)
219 vp->v_text[vtcol] = c;
220 ++vtcol;
224 * Erase from the end of the software cursor to the end of the line on which
225 * the software cursor is located.
227 static void vteeol(void)
229 /* struct video *vp; */
230 char *vcp = vscreen[vtrow]->v_text;
232 /* vp = vscreen[vtrow]; */
233 while (vtcol < term.t_ncol)
234 /* vp->v_text[vtcol++] = ' '; */
235 vcp[vtcol++] = ' ';
239 * upscreen:
240 * user routine to force a screen update
241 * always finishes complete update
243 int upscreen(int f, int n)
245 update(TRUE);
246 return TRUE;
249 #if SCROLLCODE
250 static int scrflags;
251 #endif
254 * Make sure that the display is right. This is a three part process. First,
255 * scan through all of the windows looking for dirty ones. Check the framing,
256 * and refresh the screen. Second, make sure that "currow" and "curcol" are
257 * correct for the current window. Third, make the virtual and physical
258 * screens the same.
260 * int force; force update past type ahead?
262 int update(int force)
264 struct window *wp;
266 #if TYPEAH && ! PKCODE
267 if (force == FALSE && typahead())
268 return TRUE;
269 #endif
270 #if VISMAC == 0
271 if (force == FALSE && kbdmode == PLAY)
272 return TRUE;
273 #endif
275 displaying = TRUE;
277 #if SCROLLCODE
279 /* first, propagate mode line changes to all instances of
280 a buffer displayed in more than one window */
281 wp = wheadp;
282 while (wp != NULL) {
283 if (wp->w_flag & WFMODE) {
284 if (wp->w_bufp->b_nwnd > 1) {
285 /* make sure all previous windows have this */
286 struct window *owp;
287 owp = wheadp;
288 while (owp != NULL) {
289 if (owp->w_bufp == wp->w_bufp)
290 owp->w_flag |= WFMODE;
291 owp = owp->w_wndp;
295 wp = wp->w_wndp;
298 #endif
300 /* update any windows that need refreshing */
301 wp = wheadp;
302 while (wp != NULL) {
303 if (wp->w_flag) {
304 /* if the window has changed, service it */
305 reframe(wp); /* check the framing */
306 #if SCROLLCODE
307 if (wp->w_flag & (WFKILLS | WFINS)) {
308 scrflags |=
309 (wp->w_flag & (WFINS | WFKILLS));
310 wp->w_flag &= ~(WFKILLS | WFINS);
312 #endif
313 if ((wp->w_flag & ~WFMODE) == WFEDIT)
314 updone(wp); /* update EDITed line */
315 else if (wp->w_flag & ~WFMOVE)
316 updall(wp); /* update all lines */
317 #if SCROLLCODE
318 if (scrflags || (wp->w_flag & WFMODE))
319 #else
320 if (wp->w_flag & WFMODE)
321 #endif
322 modeline(wp); /* update modeline */
323 wp->w_flag = 0;
324 wp->w_force = 0;
326 /* on to the next window */
327 wp = wp->w_wndp;
330 /* recalc the current hardware cursor location */
331 updpos();
333 #if MEMMAP && ! SCROLLCODE
334 /* update the cursor and flush the buffers */
335 movecursor(currow, curcol - lbound);
336 #endif
338 /* check for lines to de-extend */
339 upddex();
341 /* if screen is garbage, re-plot it */
342 if (sgarbf != FALSE)
343 updgar();
345 /* update the virtual screen to the physical screen */
346 updupd(force);
348 /* update the cursor and flush the buffers */
349 movecursor(currow, curcol - lbound);
350 TTflush();
351 displaying = FALSE;
352 #if SIGWINCH
353 while (chg_width || chg_height)
354 newscreensize(chg_height, chg_width);
355 #endif
356 return TRUE;
360 * reframe:
361 * check to see if the cursor is on in the window
362 * and re-frame it if needed or wanted
364 static int reframe(struct window *wp)
366 struct line *lp, *lp0;
367 int i = 0;
369 /* if not a requested reframe, check for a needed one */
370 if ((wp->w_flag & WFFORCE) == 0) {
371 #if SCROLLCODE
372 /* loop from one line above the window to one line after */
373 lp = wp->w_linep;
374 lp0 = lback(lp);
375 if (lp0 == wp->w_bufp->b_linep)
376 i = 0;
377 else {
378 i = -1;
379 lp = lp0;
381 for (; i <= (int) (wp->w_ntrows); i++)
382 #else
383 lp = wp->w_linep;
384 for (i = 0; i < wp->w_ntrows; i++)
385 #endif
387 /* if the line is in the window, no reframe */
388 if (lp == wp->w_dotp) {
389 #if SCROLLCODE
390 /* if not _quite_ in, we'll reframe gently */
391 if (i < 0 || i == wp->w_ntrows) {
392 /* if the terminal can't help, then
393 we're simply outside */
394 if (term.t_scroll == NULL)
395 i = wp->w_force;
396 break;
398 #endif
399 return TRUE;
402 /* if we are at the end of the file, reframe */
403 if (lp == wp->w_bufp->b_linep)
404 break;
406 /* on to the next line */
407 lp = lforw(lp);
410 #if SCROLLCODE
411 if (i == -1) { /* we're just above the window */
412 i = scrollcount; /* put dot at first line */
413 scrflags |= WFINS;
414 } else if (i == wp->w_ntrows) { /* we're just below the window */
415 i = -scrollcount; /* put dot at last line */
416 scrflags |= WFKILLS;
417 } else /* put dot where requested */
418 #endif
419 i = wp->w_force; /* (is 0, unless reposition() was called) */
421 wp->w_flag |= WFMODE;
423 /* how far back to reframe? */
424 if (i > 0) { /* only one screen worth of lines max */
425 if (--i >= wp->w_ntrows)
426 i = wp->w_ntrows - 1;
427 } else if (i < 0) { /* negative update???? */
428 i += wp->w_ntrows;
429 if (i < 0)
430 i = 0;
431 } else
432 i = wp->w_ntrows / 2;
434 /* backup to new line at top of window */
435 lp = wp->w_dotp;
436 while (i != 0 && lback(lp) != wp->w_bufp->b_linep) {
437 --i;
438 lp = lback(lp);
441 /* and reset the current line at top of window */
442 wp->w_linep = lp;
443 wp->w_flag |= WFHARD;
444 wp->w_flag &= ~WFFORCE;
445 return TRUE;
449 * updone:
450 * update the current line to the virtual screen
452 * struct window *wp; window to update current line in
454 static void updone(struct window *wp)
456 struct line *lp; /* line to update */
457 int sline; /* physical screen line to update */
458 int i;
460 /* search down the line we want */
461 lp = wp->w_linep;
462 sline = wp->w_toprow;
463 while (lp != wp->w_dotp) {
464 ++sline;
465 lp = lforw(lp);
468 /* and update the virtual line */
469 vscreen[sline]->v_flag |= VFCHG;
470 vscreen[sline]->v_flag &= ~VFREQ;
471 vtmove(sline, 0);
472 for (i = 0; i < llength(lp); ++i)
473 vtputc(lgetc(lp, i));
474 #if COLOR
475 vscreen[sline]->v_rfcolor = wp->w_fcolor;
476 vscreen[sline]->v_rbcolor = wp->w_bcolor;
477 #endif
478 vteeol();
482 * updall:
483 * update all the lines in a window on the virtual screen
485 * struct window *wp; window to update lines in
487 static void updall(struct window *wp)
489 struct line *lp; /* line to update */
490 int sline; /* physical screen line to update */
491 int i;
493 /* search down the lines, updating them */
494 lp = wp->w_linep;
495 sline = wp->w_toprow;
496 while (sline < wp->w_toprow + wp->w_ntrows) {
498 /* and update the virtual line */
499 vscreen[sline]->v_flag |= VFCHG;
500 vscreen[sline]->v_flag &= ~VFREQ;
501 vtmove(sline, 0);
502 if (lp != wp->w_bufp->b_linep) {
503 /* if we are not at the end */
504 for (i = 0; i < llength(lp); ++i)
505 vtputc(lgetc(lp, i));
506 lp = lforw(lp);
509 /* on to the next one */
510 #if COLOR
511 vscreen[sline]->v_rfcolor = wp->w_fcolor;
512 vscreen[sline]->v_rbcolor = wp->w_bcolor;
513 #endif
514 vteeol();
515 ++sline;
521 * updpos:
522 * update the position of the hardware cursor and handle extended
523 * lines. This is the only update for simple moves.
525 void updpos(void)
527 struct line *lp;
528 int c;
529 int i;
531 /* find the current row */
532 lp = curwp->w_linep;
533 currow = curwp->w_toprow;
534 while (lp != curwp->w_dotp) {
535 ++currow;
536 lp = lforw(lp);
539 /* find the current column */
540 curcol = 0;
541 i = 0;
542 while (i < curwp->w_doto) {
543 c = lgetc(lp, i++);
544 if (c == '\t')
545 curcol |= tabmask;
546 else if (c < 0x20 || c == 0x7f)
547 ++curcol;
548 else if (c >= 0x80 && c <= 0xa0)
549 curcol+=2;
551 ++curcol;
554 /* if extended, flag so and update the virtual line image */
555 if (curcol >= term.t_ncol - 1) {
556 vscreen[currow]->v_flag |= (VFEXT | VFCHG);
557 updext();
558 } else
559 lbound = 0;
563 * upddex:
564 * de-extend any line that derserves it
566 void upddex(void)
568 struct window *wp;
569 struct line *lp;
570 int i, j;
572 wp = wheadp;
574 while (wp != NULL) {
575 lp = wp->w_linep;
576 i = wp->w_toprow;
578 while (i < wp->w_toprow + wp->w_ntrows) {
579 if (vscreen[i]->v_flag & VFEXT) {
580 if ((wp != curwp) || (lp != wp->w_dotp) ||
581 (curcol < term.t_ncol - 1)) {
582 vtmove(i, 0);
583 for (j = 0; j < llength(lp); ++j)
584 vtputc(lgetc(lp, j));
585 vteeol();
587 /* this line no longer is extended */
588 vscreen[i]->v_flag &= ~VFEXT;
589 vscreen[i]->v_flag |= VFCHG;
592 lp = lforw(lp);
593 ++i;
595 /* and onward to the next window */
596 wp = wp->w_wndp;
601 * updgar:
602 * if the screen is garbage, clear the physical screen and
603 * the virtual screen and force a full update
605 void updgar(void)
607 char *txt;
608 int i, j;
610 for (i = 0; i < term.t_nrow; ++i) {
611 vscreen[i]->v_flag |= VFCHG;
612 #if REVSTA
613 vscreen[i]->v_flag &= ~VFREV;
614 #endif
615 #if COLOR
616 vscreen[i]->v_fcolor = gfcolor;
617 vscreen[i]->v_bcolor = gbcolor;
618 #endif
619 #if MEMMAP == 0 || SCROLLCODE
620 txt = pscreen[i]->v_text;
621 for (j = 0; j < term.t_ncol; ++j)
622 txt[j] = ' ';
623 #endif
626 movecursor(0, 0); /* Erase the screen. */
627 (*term.t_eeop) ();
628 sgarbf = FALSE; /* Erase-page clears */
629 mpresf = FALSE; /* the message area. */
630 #if COLOR
631 mlerase(); /* needs to be cleared if colored */
632 #endif
636 * updupd:
637 * update the physical screen from the virtual screen
639 * int force; forced update flag
641 int updupd(int force)
643 struct video *vp1;
644 int i;
646 #if SCROLLCODE
647 if (scrflags & WFKILLS)
648 scrolls(FALSE);
649 if (scrflags & WFINS)
650 scrolls(TRUE);
651 scrflags = 0;
652 #endif
654 for (i = 0; i < term.t_nrow; ++i) {
655 vp1 = vscreen[i];
657 /* for each line that needs to be updated */
658 if ((vp1->v_flag & VFCHG) != 0) {
659 #if TYPEAH && ! PKCODE
660 if (force == FALSE && typahead())
661 return TRUE;
662 #endif
663 #if MEMMAP && ! SCROLLCODE
664 updateline(i, vp1);
665 #else
666 updateline(i, vp1, pscreen[i]);
667 #endif
670 return TRUE;
673 #if SCROLLCODE
676 * optimize out scrolls (line breaks, and newlines)
677 * arg. chooses between looking for inserts or deletes
679 static int scrolls(int inserts)
680 { /* returns true if it does something */
681 struct video *vpv; /* virtual screen image */
682 struct video *vpp; /* physical screen image */
683 int i, j, k;
684 int rows, cols;
685 int first, match, count, target, end;
686 int longmatch, longcount;
687 int from, to;
689 if (!term.t_scroll) /* no way to scroll */
690 return FALSE;
692 rows = term.t_nrow;
693 cols = term.t_ncol;
695 first = -1;
696 for (i = 0; i < rows; i++) { /* find first wrong line */
697 if (!texttest(i, i)) {
698 first = i;
699 break;
703 if (first < 0)
704 return FALSE; /* no text changes */
706 vpv = vscreen[first];
707 vpp = pscreen[first];
709 if (inserts) {
710 /* determine types of potential scrolls */
711 end = endofline(vpv->v_text, cols);
712 if (end == 0)
713 target = first; /* newlines */
714 else if (strncmp(vpp->v_text, vpv->v_text, end) == 0)
715 target = first + 1; /* broken line newlines */
716 else
717 target = first;
718 } else {
719 target = first + 1;
722 /* find the matching shifted area */
723 match = -1;
724 longmatch = -1;
725 longcount = 0;
726 from = target;
727 for (i = from + 1; i < rows - longcount /* P.K. */ ; i++) {
728 if (inserts ? texttest(i, from) : texttest(from, i)) {
729 match = i;
730 count = 1;
731 for (j = match + 1, k = from + 1;
732 j < rows && k < rows; j++, k++) {
733 if (inserts ? texttest(j, k) :
734 texttest(k, j))
735 count++;
736 else
737 break;
739 if (longcount < count) {
740 longcount = count;
741 longmatch = match;
745 match = longmatch;
746 count = longcount;
748 if (!inserts) {
749 /* full kill case? */
750 if (match > 0 && texttest(first, match - 1)) {
751 target--;
752 match--;
753 count++;
757 /* do the scroll */
758 if (match > 0 && count > 2) { /* got a scroll */
759 /* move the count lines starting at target to match */
760 if (inserts) {
761 from = target;
762 to = match;
763 } else {
764 from = match;
765 to = target;
767 #if 0
769 char line[NLINE];
770 sprintf(line,
771 "scrolls: move the %d lines starting at %d to %d, first %d, scrolls %d",
772 count, from, to, first,
773 2 * count >= abs(from - to));
774 mlwrite(line);
776 #endif
777 if (2 * count < abs(from - to))
778 return FALSE;
779 scrscroll(from, to, count);
780 for (i = 0; i < count; i++) {
781 vpp = pscreen[to + i];
782 vpv = vscreen[to + i];
783 strncpy(vpp->v_text, vpv->v_text, cols);
784 vpp->v_flag = vpv->v_flag; /* XXX */
785 if (vpp->v_flag & VFREV) {
786 vpp->v_flag &= ~VFREV;
787 vpp->v_flag |= ~VFREQ;
789 #if MEMMAP
790 vscreen[to + i]->v_flag &= ~VFCHG;
791 #endif
793 if (inserts) {
794 from = target;
795 to = match;
796 } else {
797 from = target + count;
798 to = match + count;
800 #if MEMMAP == 0
801 for (i = from; i < to; i++) {
802 char *txt;
803 txt = pscreen[i]->v_text;
804 for (j = 0; j < term.t_ncol; ++j)
805 txt[j] = ' ';
806 vscreen[i]->v_flag |= VFCHG;
808 #endif
809 return TRUE;
811 return FALSE;
814 /* move the "count" lines starting at "from" to "to" */
815 static void scrscroll(int from, int to, int count)
817 ttrow = ttcol = -1;
818 (*term.t_scroll) (from, to, count);
822 * return TRUE on text match
824 * int vrow, prow; virtual, physical rows
826 static int texttest(int vrow, int prow)
828 struct video *vpv = vscreen[vrow]; /* virtual screen image */
829 struct video *vpp = pscreen[prow]; /* physical screen image */
831 return !memcmp(vpv->v_text, vpp->v_text, term.t_ncol);
835 * return the index of the first blank of trailing whitespace
837 static int endofline(char *s, int n)
839 int i;
840 for (i = n - 1; i >= 0; i--)
841 if (s[i] != ' ')
842 return i + 1;
843 return 0;
846 #endif /* SCROLLCODE */
849 * updext:
850 * update the extended line which the cursor is currently
851 * on at a column greater than the terminal width. The line
852 * will be scrolled right or left to let the user see where
853 * the cursor is
855 static void updext(void)
857 int rcursor; /* real cursor location */
858 struct line *lp; /* pointer to current line */
859 int j; /* index into line */
861 /* calculate what column the real cursor will end up in */
862 rcursor = ((curcol - term.t_ncol) % term.t_scrsiz) + term.t_margin;
863 taboff = lbound = curcol - rcursor + 1;
865 /* scan through the line outputing characters to the virtual screen */
866 /* once we reach the left edge */
867 vtmove(currow, -lbound); /* start scanning offscreen */
868 lp = curwp->w_dotp; /* line to output */
869 for (j = 0; j < llength(lp); ++j) /* until the end-of-line */
870 vtputc(lgetc(lp, j));
872 /* truncate the virtual line, restore tab offset */
873 vteeol();
874 taboff = 0;
876 /* and put a '$' in column 1 */
877 vscreen[currow]->v_text[0] = '$';
881 * Update a single line. This does not know how to use insert or delete
882 * character sequences; we are using VT52 functionality. Update the physical
883 * row and column variables. It does try an exploit erase to end of line. The
884 * RAINBOW version of this routine uses fast video.
886 #if MEMMAP
887 /* UPDATELINE specific code for the IBM-PC and other compatables */
889 static int updateline(int row, struct video *vp1, struct video *vp2)
891 #if SCROLLCODE
892 char *cp1;
893 char *cp2;
894 int nch;
896 cp1 = &vp1->v_text[0];
897 cp2 = &vp2->v_text[0];
898 nch = term.t_ncol;
899 do {
900 *cp2 = *cp1;
901 ++cp2;
902 ++cp1;
904 while (--nch);
905 #endif
906 #if COLOR
907 scwrite(row, vp1->v_text, vp1->v_rfcolor, vp1->v_rbcolor);
908 vp1->v_fcolor = vp1->v_rfcolor;
909 vp1->v_bcolor = vp1->v_rbcolor;
910 #else
911 if (vp1->v_flag & VFREQ)
912 scwrite(row, vp1->v_text, 0, 7);
913 else
914 scwrite(row, vp1->v_text, 7, 0);
915 #endif
916 vp1->v_flag &= ~(VFCHG | VFCOL); /* flag this line as changed */
920 #else
923 * updateline()
925 * int row; row of screen to update
926 * struct video *vp1; virtual screen image
927 * struct video *vp2; physical screen image
929 static int updateline(int row, struct video *vp1, struct video *vp2)
931 #if RAINBOW
932 /* UPDATELINE specific code for the DEC rainbow 100 micro */
934 char *cp1;
935 char *cp2;
936 int nch;
938 /* since we don't know how to make the rainbow do this, turn it off */
939 flags &= (~VFREV & ~VFREQ);
941 cp1 = &vp1->v_text[0]; /* Use fast video. */
942 cp2 = &vp2->v_text[0];
943 putline(row + 1, 1, cp1);
944 nch = term.t_ncol;
946 do {
947 *cp2 = *cp1;
948 ++cp2;
949 ++cp1;
951 while (--nch);
952 *flags &= ~VFCHG;
953 #else
954 /* UPDATELINE code for all other versions */
956 char *cp1;
957 char *cp2;
958 char *cp3;
959 char *cp4;
960 char *cp5;
961 int nbflag; /* non-blanks to the right flag? */
962 int rev; /* reverse video flag */
963 int req; /* reverse video request flag */
966 /* set up pointers to virtual and physical lines */
967 cp1 = &vp1->v_text[0];
968 cp2 = &vp2->v_text[0];
970 #if COLOR
971 TTforg(vp1->v_rfcolor);
972 TTbacg(vp1->v_rbcolor);
973 #endif
975 #if REVSTA | COLOR
976 /* if we need to change the reverse video status of the
977 current line, we need to re-write the entire line */
978 rev = (vp1->v_flag & VFREV) == VFREV;
979 req = (vp1->v_flag & VFREQ) == VFREQ;
980 if ((rev != req)
981 #if COLOR
982 || (vp1->v_fcolor != vp1->v_rfcolor)
983 || (vp1->v_bcolor != vp1->v_rbcolor)
984 #endif
986 movecursor(row, 0); /* Go to start of line. */
987 /* set rev video if needed */
988 if (rev != req)
989 (*term.t_rev) (req);
991 /* scan through the line and dump it to the screen and
992 the virtual screen array */
993 cp3 = &vp1->v_text[term.t_ncol];
994 while (cp1 < cp3) {
995 TTputc(*cp1);
996 ++ttcol;
997 *cp2++ = *cp1++;
999 /* turn rev video off */
1000 if (rev != req)
1001 (*term.t_rev) (FALSE);
1003 /* update the needed flags */
1004 vp1->v_flag &= ~VFCHG;
1005 if (req)
1006 vp1->v_flag |= VFREV;
1007 else
1008 vp1->v_flag &= ~VFREV;
1009 #if COLOR
1010 vp1->v_fcolor = vp1->v_rfcolor;
1011 vp1->v_bcolor = vp1->v_rbcolor;
1012 #endif
1013 return TRUE;
1015 #endif
1017 /* advance past any common chars at the left */
1018 while (cp1 != &vp1->v_text[term.t_ncol] && cp1[0] == cp2[0]) {
1019 ++cp1;
1020 ++cp2;
1023 /* This can still happen, even though we only call this routine on changed
1024 * lines. A hard update is always done when a line splits, a massive
1025 * change is done, or a buffer is displayed twice. This optimizes out most
1026 * of the excess updating. A lot of computes are used, but these tend to
1027 * be hard operations that do a lot of update, so I don't really care.
1029 /* if both lines are the same, no update needs to be done */
1030 if (cp1 == &vp1->v_text[term.t_ncol]) {
1031 vp1->v_flag &= ~VFCHG; /* flag this line is changed */
1032 return TRUE;
1035 /* find out if there is a match on the right */
1036 nbflag = FALSE;
1037 cp3 = &vp1->v_text[term.t_ncol];
1038 cp4 = &vp2->v_text[term.t_ncol];
1040 while (cp3[-1] == cp4[-1]) {
1041 --cp3;
1042 --cp4;
1043 if (cp3[0] != ' ') /* Note if any nonblank */
1044 nbflag = TRUE; /* in right match. */
1047 cp5 = cp3;
1049 /* Erase to EOL ? */
1050 if (nbflag == FALSE && eolexist == TRUE && (req != TRUE)) {
1051 while (cp5 != cp1 && cp5[-1] == ' ')
1052 --cp5;
1054 if (cp3 - cp5 <= 3) /* Use only if erase is */
1055 cp5 = cp3; /* fewer characters. */
1058 movecursor(row, cp1 - &vp1->v_text[0]); /* Go to start of line. */
1059 #if REVSTA
1060 TTrev(rev);
1061 #endif
1063 while (cp1 != cp5) { /* Ordinary. */
1064 TTputc(*cp1);
1065 ++ttcol;
1066 *cp2++ = *cp1++;
1069 if (cp5 != cp3) { /* Erase. */
1070 TTeeol();
1071 while (cp1 != cp3)
1072 *cp2++ = *cp1++;
1074 #if REVSTA
1075 TTrev(FALSE);
1076 #endif
1077 vp1->v_flag &= ~VFCHG; /* flag this line as updated */
1078 return TRUE;
1079 #endif
1081 #endif
1084 * Redisplay the mode line for the window pointed to by the "wp". This is the
1085 * only routine that has any idea of how the modeline is formatted. You can
1086 * change the modeline format by hacking at this routine. Called by "update"
1087 * any time there is a dirty window.
1089 static void modeline(struct window *wp)
1091 char *cp;
1092 int c;
1093 int n; /* cursor position count */
1094 struct buffer *bp;
1095 int i; /* loop index */
1096 int lchar; /* character to draw line in buffer with */
1097 int firstm; /* is this the first mode? */
1098 char tline[NLINE]; /* buffer for part of mode line */
1100 n = wp->w_toprow + wp->w_ntrows; /* Location. */
1101 vscreen[n]->v_flag |= VFCHG | VFREQ | VFCOL; /* Redraw next time. */
1102 #if COLOR
1103 vscreen[n]->v_rfcolor = 0; /* black on */
1104 vscreen[n]->v_rbcolor = 7; /* white..... */
1105 #endif
1106 vtmove(n, 0); /* Seek to right line. */
1107 if (wp == curwp) /* mark the current buffer */
1108 #if PKCODE
1109 lchar = '-';
1110 #else
1111 lchar = '=';
1112 #endif
1113 else
1114 #if REVSTA
1115 if (revexist)
1116 lchar = ' ';
1117 else
1118 #endif
1119 lchar = '-';
1121 bp = wp->w_bufp;
1122 #if PKCODE == 0
1123 if ((bp->b_flag & BFTRUNC) != 0)
1124 vtputc('#');
1125 else
1126 #endif
1127 vtputc(lchar);
1129 if ((bp->b_flag & BFCHG) != 0) /* "*" if changed. */
1130 vtputc('*');
1131 else
1132 vtputc(lchar);
1134 n = 2;
1136 strcpy(tline, " ");
1137 strcat(tline, PROGRAM_NAME_LONG);
1138 strcat(tline, " ");
1139 strcat(tline, VERSION);
1140 strcat(tline, ": ");
1141 cp = &tline[0];
1142 while ((c = *cp++) != 0) {
1143 vtputc(c);
1144 ++n;
1147 cp = &bp->b_bname[0];
1148 while ((c = *cp++) != 0) {
1149 vtputc(c);
1150 ++n;
1153 strcpy(tline, " (");
1155 /* display the modes */
1157 firstm = TRUE;
1158 if ((bp->b_flag & BFTRUNC) != 0) {
1159 firstm = FALSE;
1160 strcat(tline, "Truncated");
1162 for (i = 0; i < NUMMODES; i++) /* add in the mode flags */
1163 if (wp->w_bufp->b_mode & (1 << i)) {
1164 if (firstm != TRUE)
1165 strcat(tline, " ");
1166 firstm = FALSE;
1167 strcat(tline, mode2name[i]);
1169 strcat(tline, ") ");
1171 cp = &tline[0];
1172 while ((c = *cp++) != 0) {
1173 vtputc(c);
1174 ++n;
1177 #if 0
1178 vtputc(lchar);
1179 vtputc((wp->w_flag & WFCOLR) != 0 ? 'C' : lchar);
1180 vtputc((wp->w_flag & WFMODE) != 0 ? 'M' : lchar);
1181 vtputc((wp->w_flag & WFHARD) != 0 ? 'H' : lchar);
1182 vtputc((wp->w_flag & WFEDIT) != 0 ? 'E' : lchar);
1183 vtputc((wp->w_flag & WFMOVE) != 0 ? 'V' : lchar);
1184 vtputc((wp->w_flag & WFFORCE) != 0 ? 'F' : lchar);
1185 vtputc(lchar);
1186 n += 8;
1187 #endif
1189 #if PKCODE
1190 if (bp->b_fname[0] != 0 && strcmp(bp->b_bname, bp->b_fname) != 0)
1191 #else
1192 if (bp->b_fname[0] != 0) /* File name. */
1193 #endif
1195 #if PKCODE == 0
1196 cp = "File: ";
1198 while ((c = *cp++) != 0) {
1199 vtputc(c);
1200 ++n;
1202 #endif
1204 cp = &bp->b_fname[0];
1206 while ((c = *cp++) != 0) {
1207 vtputc(c);
1208 ++n;
1211 vtputc(' ');
1212 ++n;
1215 while (n < term.t_ncol) { /* Pad to full width. */
1216 vtputc(lchar);
1217 ++n;
1220 { /* determine if top line, bottom line, or both are visible */
1221 struct line *lp = wp->w_linep;
1222 int rows = wp->w_ntrows;
1223 char *msg = NULL;
1225 vtcol = n - 7; /* strlen(" top ") plus a couple */
1226 while (rows--) {
1227 lp = lforw(lp);
1228 if (lp == wp->w_bufp->b_linep) {
1229 msg = " Bot ";
1230 break;
1233 if (lback(wp->w_linep) == wp->w_bufp->b_linep) {
1234 if (msg) {
1235 if (wp->w_linep == wp->w_bufp->b_linep)
1236 msg = " Emp ";
1237 else
1238 msg = " All ";
1239 } else {
1240 msg = " Top ";
1243 if (!msg) {
1244 struct line *lp;
1245 int numlines, predlines, ratio;
1247 lp = lforw(bp->b_linep);
1248 numlines = 0;
1249 predlines = 0;
1250 while (lp != bp->b_linep) {
1251 if (lp == wp->w_linep) {
1252 predlines = numlines;
1254 ++numlines;
1255 lp = lforw(lp);
1257 if (wp->w_dotp == bp->b_linep) {
1258 msg = " Bot ";
1259 } else {
1260 ratio = 0;
1261 if (numlines != 0)
1262 ratio =
1263 (100L * predlines) / numlines;
1264 if (ratio > 99)
1265 ratio = 99;
1266 sprintf(tline, " %2d%% ", ratio);
1267 msg = tline;
1271 cp = msg;
1272 while ((c = *cp++) != 0) {
1273 vtputc(c);
1274 ++n;
1279 void upmode(void)
1280 { /* update all the mode lines */
1281 struct window *wp;
1283 wp = wheadp;
1284 while (wp != NULL) {
1285 wp->w_flag |= WFMODE;
1286 wp = wp->w_wndp;
1291 * Send a command to the terminal to move the hardware cursor to row "row"
1292 * and column "col". The row and column arguments are origin 0. Optimize out
1293 * random calls. Update "ttrow" and "ttcol".
1295 void movecursor(int row, int col)
1297 if (row != ttrow || col != ttcol) {
1298 ttrow = row;
1299 ttcol = col;
1300 TTmove(row, col);
1305 * Erase the message line. This is a special routine because the message line
1306 * is not considered to be part of the virtual screen. It always works
1307 * immediately; the terminal buffer is flushed via a call to the flusher.
1309 void mlerase(void)
1311 int i;
1313 movecursor(term.t_nrow, 0);
1314 if (discmd == FALSE)
1315 return;
1317 #if COLOR
1318 TTforg(7);
1319 TTbacg(0);
1320 #endif
1321 if (eolexist == TRUE)
1322 TTeeol();
1323 else {
1324 for (i = 0; i < term.t_ncol - 1; i++)
1325 TTputc(' ');
1326 movecursor(term.t_nrow, 1); /* force the move! */
1327 movecursor(term.t_nrow, 0);
1329 TTflush();
1330 mpresf = FALSE;
1334 * Write a message into the message line. Keep track of the physical cursor
1335 * position. A small class of printf like format items is handled. Assumes the
1336 * stack grows down; this assumption is made by the "++" in the argument scan
1337 * loop. Set the "message line" flag TRUE.
1339 * char *fmt; format string for output
1340 * char *arg; pointer to first argument to print
1342 void mlwrite(const char *fmt, ...)
1344 int c; /* current char in format string */
1345 va_list ap;
1347 /* if we are not currently echoing on the command line, abort this */
1348 if (discmd == FALSE) {
1349 movecursor(term.t_nrow, 0);
1350 return;
1352 #if COLOR
1353 /* set up the proper colors for the command line */
1354 TTforg(7);
1355 TTbacg(0);
1356 #endif
1358 /* if we can not erase to end-of-line, do it manually */
1359 if (eolexist == FALSE) {
1360 mlerase();
1361 TTflush();
1364 movecursor(term.t_nrow, 0);
1365 va_start(ap, fmt);
1366 while ((c = *fmt++) != 0) {
1367 if (c != '%') {
1368 TTputc(c);
1369 ++ttcol;
1370 } else {
1371 c = *fmt++;
1372 switch (c) {
1373 case 'd':
1374 mlputi(va_arg(ap, int), 10);
1375 break;
1377 case 'o':
1378 mlputi(va_arg(ap, int), 8);
1379 break;
1381 case 'x':
1382 mlputi(va_arg(ap, int), 16);
1383 break;
1385 case 'D':
1386 mlputli(va_arg(ap, long), 10);
1387 break;
1389 case 's':
1390 mlputs(va_arg(ap, char *));
1391 break;
1393 case 'f':
1394 mlputf(va_arg(ap, int));
1395 break;
1397 default:
1398 TTputc(c);
1399 ++ttcol;
1403 va_end(ap);
1405 /* if we can, erase to the end of screen */
1406 if (eolexist == TRUE)
1407 TTeeol();
1408 TTflush();
1409 mpresf = TRUE;
1413 * Force a string out to the message line regardless of the
1414 * current $discmd setting. This is needed when $debug is TRUE
1415 * and for the write-message and clear-message-line commands
1417 * char *s; string to force out
1419 void mlforce(char *s)
1421 int oldcmd; /* original command display flag */
1423 oldcmd = discmd; /* save the discmd value */
1424 discmd = TRUE; /* and turn display on */
1425 mlwrite(s); /* write the string out */
1426 discmd = oldcmd; /* and restore the original setting */
1430 * Write out a string. Update the physical cursor position. This assumes that
1431 * the characters in the string all have width "1"; if this is not the case
1432 * things will get screwed up a little.
1434 void mlputs(char *s)
1436 int c;
1438 while ((c = *s++) != 0) {
1439 TTputc(c);
1440 ++ttcol;
1445 * Write out an integer, in the specified radix. Update the physical cursor
1446 * position.
1448 static void mlputi(int i, int r)
1450 int q;
1451 static char hexdigits[] = "0123456789ABCDEF";
1453 if (i < 0) {
1454 i = -i;
1455 TTputc('-');
1458 q = i / r;
1460 if (q != 0)
1461 mlputi(q, r);
1463 TTputc(hexdigits[i % r]);
1464 ++ttcol;
1468 * do the same except as a long integer.
1470 static void mlputli(long l, int r)
1472 long q;
1474 if (l < 0) {
1475 l = -l;
1476 TTputc('-');
1479 q = l / r;
1481 if (q != 0)
1482 mlputli(q, r);
1484 TTputc((int) (l % r) + '0');
1485 ++ttcol;
1489 * write out a scaled integer with two decimal places
1491 * int s; scaled integer to output
1493 static void mlputf(int s)
1495 int i; /* integer portion of number */
1496 int f; /* fractional portion of number */
1498 /* break it up */
1499 i = s / 100;
1500 f = s % 100;
1502 /* send out the integer portion */
1503 mlputi(i, 10);
1504 TTputc('.');
1505 TTputc((f / 10) + '0');
1506 TTputc((f % 10) + '0');
1507 ttcol += 3;
1510 #if RAINBOW
1512 static void putline(int row, int col, char *buf)
1514 int n;
1516 n = strlen(buf);
1517 if (col + n - 1 > term.t_ncol)
1518 n = term.t_ncol - col + 1;
1519 Put_Data(row, col, n, buf);
1521 #endif
1523 /* Get terminal size from system.
1524 Store number of lines into *heightp and width into *widthp.
1525 If zero or a negative number is stored, the value is not valid. */
1527 void getscreensize(int *widthp, int *heightp)
1529 #ifdef TIOCGWINSZ
1530 struct winsize size;
1531 *widthp = 0;
1532 *heightp = 0;
1533 if (ioctl(0, TIOCGWINSZ, &size) < 0)
1534 return;
1535 *widthp = size.ws_col;
1536 *heightp = size.ws_row;
1537 #else
1538 *widthp = 0;
1539 *heightp = 0;
1540 #endif
1543 #ifdef SIGWINCH
1544 void sizesignal(int signr)
1546 int w, h;
1547 int old_errno = errno;
1549 getscreensize(&w, &h);
1551 if (h && w && (h - 1 != term.t_nrow || w != term.t_ncol))
1552 newscreensize(h, w);
1554 signal(SIGWINCH, sizesignal);
1555 errno = old_errno;
1558 static int newscreensize(int h, int w)
1560 /* do the change later */
1561 if (displaying) {
1562 chg_width = w;
1563 chg_height = h;
1564 return FALSE;
1566 chg_width = chg_height = 0;
1567 if (h - 1 < term.t_mrow)
1568 newsize(TRUE, h);
1569 if (w < term.t_mcol)
1570 newwidth(TRUE, w);
1572 update(TRUE);
1573 return TRUE;
1576 #endif