Annotation of src/usr.bin/mg/display.c, Revision 1.49
1.49 ! jmc 1: /* $OpenBSD: display.c,v 1.48 2017/07/06 19:27:37 schwarze Exp $ */
1.22 kjell 2:
3: /* This file is in the public domain. */
1.4 niklas 4:
1.1 deraadt 5: /*
6: * The functions in this file handle redisplay. The
7: * redisplay system knows almost nothing about the editing
8: * process; the editing functions do, however, set some
9: * hints to eliminate a lot of the grinding. There is more
10: * that can be done; the "vtputc" interface is a real
1.24 deraadt 11: * pig.
1.1 deraadt 12: */
1.45 bcallah 13:
14: #include <sys/queue.h>
15: #include <ctype.h>
16: #include <signal.h>
17: #include <stdio.h>
18: #include <stdlib.h>
19: #include <string.h>
1.47 bcallah 20: #include <term.h>
1.45 bcallah 21:
1.21 db 22: #include "def.h"
23: #include "kbd.h"
1.16 vincent 24:
1.1 deraadt 25: /*
26: * A video structure always holds
27: * an array of characters whose length is equal to
1.7 art 28: * the longest line possible. v_text is allocated
29: * dynamically to fit the screen width.
1.1 deraadt 30: */
1.23 deraadt 31: struct video {
1.6 mickey 32: short v_hash; /* Hash code, for compares. */
33: short v_flag; /* Flag word. */
34: short v_color; /* Color of the line. */
1.47 bcallah 35: int v_cost; /* Cost of display. */
1.7 art 36: char *v_text; /* The actual characters. */
1.23 deraadt 37: };
1.3 millert 38:
39: #define VFCHG 0x0001 /* Changed. */
40: #define VFHBAD 0x0002 /* Hash and cost are bad. */
1.49 ! jmc 41: #define VFEXT 0x0004 /* extended line (beyond ncol) */
1.1 deraadt 42:
43: /*
44: * SCORE structures hold the optimal
45: * trace trajectory, and the cost of redisplay, when
46: * the dynamic programming redisplay code is used.
47: */
1.23 deraadt 48: struct score {
1.47 bcallah 49: int s_itrace; /* "i" index for track back. */
50: int s_jtrace; /* "j" index for trace back. */
51: int s_cost; /* Display cost. */
1.23 deraadt 52: };
1.3 millert 53:
1.10 millert 54: void vtmove(int, int);
55: void vtputc(int);
56: void vtpute(int);
1.11 vincent 57: int vtputs(const char *);
1.10 millert 58: void vteeol(void);
59: void updext(int, int);
1.41 lum 60: void modeline(struct mgwin *, int);
1.10 millert 61: void setscores(int, int);
62: void traceback(int, int, int, int);
1.23 deraadt 63: void ucopy(struct video *, struct video *);
64: void uline(int, struct video *, struct video *);
65: void hash(struct video *);
1.6 mickey 66:
67:
68: int sgarbf = TRUE; /* TRUE if screen is garbage. */
1.7 art 69: int vtrow = HUGE; /* Virtual cursor row. */
70: int vtcol = HUGE; /* Virtual cursor column. */
1.6 mickey 71: int tthue = CNONE; /* Current color. */
72: int ttrow = HUGE; /* Physical cursor row. */
73: int ttcol = HUGE; /* Physical cursor column. */
74: int tttop = HUGE; /* Top of scroll region. */
75: int ttbot = HUGE; /* Bottom of scroll region. */
1.21 db 76: int lbound = 0; /* leftmost bound of the current */
77: /* line being displayed */
1.3 millert 78:
1.23 deraadt 79: struct video **vscreen; /* Edge vector, virtual. */
80: struct video **pscreen; /* Edge vector, physical. */
81: struct video *video; /* Actual screen data. */
82: struct video blanks; /* Blank line image. */
1.1 deraadt 83:
84: /*
85: * This matrix is written as an array because
86: * we do funny things in the "setscores" routine, which
87: * is very compute intensive, to make the subscripts go away.
88: * It would be "SCORE score[NROW][NROW]" in old speak.
89: * Look at "setscores" to understand what is up.
90: */
1.23 deraadt 91: struct score *score; /* [NROW * NROW] */
1.7 art 92:
1.46 bcallah 93: static int linenos = TRUE;
1.39 florian 94: static int colnos = FALSE;
1.29 kjell 95:
1.34 kjell 96: /* Is macro recording enabled? */
97: extern int macrodef;
1.35 kjell 98: /* Is working directory global? */
99: extern int globalwd;
1.34 kjell 100:
1.29 kjell 101: /*
1.35 kjell 102: * Since we don't have variables (we probably should) these are command
103: * processors for changing the values of mode flags.
1.29 kjell 104: */
105: /* ARGSUSED */
106: int
107: linenotoggle(int f, int n)
108: {
109: if (f & FFARG)
110: linenos = n > 0;
111: else
112: linenos = !linenos;
113:
1.31 deraadt 114: sgarbf = TRUE;
1.29 kjell 115:
116: return (TRUE);
117: }
118:
1.39 florian 119: /* ARGSUSED */
120: int
121: colnotoggle(int f, int n)
122: {
123: if (f & FFARG)
124: colnos = n > 0;
125: else
126: colnos = !colnos;
127:
128: sgarbf = TRUE;
129:
130: return (TRUE);
131: }
132:
1.7 art 133: /*
134: * Reinit the display data structures, this is called when the terminal
135: * size changes.
136: */
137: int
138: vtresize(int force, int newrow, int newcol)
139: {
1.21 db 140: int i;
141: int rowchanged, colchanged;
142: static int first_run = 1;
1.23 deraadt 143: struct video *vp;
1.7 art 144:
1.15 vincent 145: if (newrow < 1 || newcol < 1)
146: return (FALSE);
1.7 art 147:
148: rowchanged = (newrow != nrow);
149: colchanged = (newcol != ncol);
150:
151: #define TRYREALLOC(a, n) do { \
152: void *tmp; \
153: if ((tmp = realloc((a), (n))) == NULL) { \
154: panic("out of memory in display code"); \
1.15 vincent 155: } \
1.7 art 156: (a) = tmp; \
157: } while (0)
158:
1.42 deraadt 159: #define TRYREALLOCARRAY(a, n, m) do { \
160: void *tmp; \
161: if ((tmp = reallocarray((a), (n), (m))) == NULL) {\
162: panic("out of memory in display code"); \
163: } \
164: (a) = tmp; \
165: } while (0)
166:
1.7 art 167: /* No update needed */
1.21 db 168: if (!first_run && !force && !rowchanged && !colchanged)
1.15 vincent 169: return (TRUE);
1.7 art 170:
1.21 db 171: if (first_run)
1.7 art 172: memset(&blanks, 0, sizeof(blanks));
1.9 deraadt 173:
1.7 art 174: if (rowchanged || first_run) {
175: int vidstart;
176:
177: /*
178: * This is not pretty.
179: */
180: if (nrow == 0)
181: vidstart = 0;
182: else
183: vidstart = 2 * (nrow - 1);
184:
185: /*
1.21 db 186: * We're shrinking, free some internal data.
1.7 art 187: */
188: if (newrow < nrow) {
189: for (i = 2 * (newrow - 1); i < 2 * (nrow - 1); i++) {
190: free(video[i].v_text);
191: video[i].v_text = NULL;
192: }
193: }
194:
1.44 tedu 195: TRYREALLOCARRAY(score, newrow, newrow * sizeof(struct score));
1.42 deraadt 196: TRYREALLOCARRAY(vscreen, (newrow - 1), sizeof(struct video *));
197: TRYREALLOCARRAY(pscreen, (newrow - 1), sizeof(struct video *));
1.44 tedu 198: TRYREALLOCARRAY(video, (newrow - 1), 2 * sizeof(struct video));
1.7 art 199:
200: /*
1.21 db 201: * Zero-out the entries we just allocated.
1.7 art 202: */
1.15 vincent 203: for (i = vidstart; i < 2 * (newrow - 1); i++)
1.23 deraadt 204: memset(&video[i], 0, sizeof(struct video));
1.7 art 205:
206: /*
207: * Reinitialize vscreen and pscreen arrays completely.
208: */
209: vp = &video[0];
210: for (i = 0; i < newrow - 1; ++i) {
211: vscreen[i] = vp;
212: ++vp;
213: pscreen[i] = vp;
214: ++vp;
215: }
216: }
217: if (rowchanged || colchanged || first_run) {
1.15 vincent 218: for (i = 0; i < 2 * (newrow - 1); i++)
1.42 deraadt 219: TRYREALLOC(video[i].v_text, newcol);
220: TRYREALLOC(blanks.v_text, newcol);
1.7 art 221: }
222:
223: nrow = newrow;
224: ncol = newcol;
1.9 deraadt 225:
1.7 art 226: if (ttrow > nrow)
227: ttrow = nrow;
228: if (ttcol > ncol)
229: ttcol = ncol;
230:
1.9 deraadt 231: first_run = 0;
1.15 vincent 232: return (TRUE);
1.7 art 233: }
234:
235: #undef TRYREALLOC
1.43 tedu 236: #undef TRYREALLOCARRAY
1.1 deraadt 237:
238: /*
239: * Initialize the data structures used
240: * by the display code. The edge vectors used
241: * to access the screens are set up. The operating
242: * system's terminal I/O channel is set up. Fill the
243: * "blanks" array with ASCII blanks. The rest is done
244: * at compile time. The original window is marked
245: * as needing full update, and the physical screen
246: * is marked as garbage, so all the right stuff happens
247: * on the first call to redisplay.
248: */
1.5 art 249: void
1.11 vincent 250: vtinit(void)
1.3 millert 251: {
1.6 mickey 252: int i;
1.1 deraadt 253:
254: ttopen();
255: ttinit();
1.9 deraadt 256:
1.7 art 257: /*
258: * ttinit called ttresize(), which called vtresize(), so our data
259: * structures are setup correctly.
260: */
261:
1.1 deraadt 262: blanks.v_color = CTEXT;
1.7 art 263: for (i = 0; i < ncol; ++i)
1.1 deraadt 264: blanks.v_text[i] = ' ';
265: }
266:
267: /*
268: * Tidy up the virtual display system
269: * in anticipation of a return back to the host
270: * operating system. Right now all we do is position
271: * the cursor to the last line, erase the line, and
272: * close the terminal channel.
273: */
1.5 art 274: void
1.11 vincent 275: vttidy(void)
1.3 millert 276: {
1.1 deraadt 277: ttcolor(CTEXT);
1.3 millert 278: ttnowindow(); /* No scroll window. */
279: ttmove(nrow - 1, 0); /* Echo line. */
1.1 deraadt 280: tteeol();
281: tttidy();
282: ttflush();
283: ttclose();
284: }
285:
286: /*
287: * Move the virtual cursor to an origin
288: * 0 spot on the virtual display screen. I could
289: * store the column as a character pointer to the spot
290: * on the line, which would make "vtputc" a little bit
291: * more efficient. No checking for errors.
292: */
1.5 art 293: void
1.11 vincent 294: vtmove(int row, int col)
1.3 millert 295: {
1.1 deraadt 296: vtrow = row;
297: vtcol = col;
298: }
299:
300: /*
301: * Write a character to the virtual display,
302: * dealing with long lines and the display of unprintable
303: * things like control characters. Also expand tabs every 8
304: * columns. This code only puts printing characters into
305: * the virtual display image. Special care must be taken when
306: * expanding tabs. On a screen whose width is not a multiple
307: * of 8, it is possible for the virtual cursor to hit the
308: * right margin before the next tab stop is reached. This
309: * makes the tab code loop if you are not careful.
310: * Three guesses how we found this.
311: */
1.5 art 312: void
1.11 vincent 313: vtputc(int c)
1.3 millert 314: {
1.23 deraadt 315: struct video *vp;
1.1 deraadt 316:
1.8 vincent 317: c &= 0xff;
1.9 deraadt 318:
1.1 deraadt 319: vp = vscreen[vtrow];
320: if (vtcol >= ncol)
1.3 millert 321: vp->v_text[ncol - 1] = '$';
1.1 deraadt 322: else if (c == '\t'
323: #ifdef NOTAB
1.17 deraadt 324: && !(curbp->b_flag & BFNOTAB)
1.1 deraadt 325: #endif
1.17 deraadt 326: ) {
1.1 deraadt 327: do {
328: vtputc(' ');
1.3 millert 329: } while (vtcol < ncol && (vtcol & 0x07) != 0);
1.1 deraadt 330: } else if (ISCTRL(c)) {
331: vtputc('^');
332: vtputc(CCHR(c));
1.16 vincent 333: } else if (isprint(c))
1.1 deraadt 334: vp->v_text[vtcol++] = c;
1.16 vincent 335: else {
336: char bf[5];
1.17 deraadt 337:
1.21 db 338: snprintf(bf, sizeof(bf), "\\%o", c);
1.16 vincent 339: vtputs(bf);
340: }
1.1 deraadt 341: }
342:
1.3 millert 343: /*
344: * Put a character to the virtual screen in an extended line. If we are not
345: * yet on left edge, don't print it yet. Check for overflow on the right
346: * margin.
1.1 deraadt 347: */
1.5 art 348: void
1.11 vincent 349: vtpute(int c)
1.1 deraadt 350: {
1.23 deraadt 351: struct video *vp;
1.1 deraadt 352:
1.8 vincent 353: c &= 0xff;
1.9 deraadt 354:
1.3 millert 355: vp = vscreen[vtrow];
356: if (vtcol >= ncol)
357: vp->v_text[ncol - 1] = '$';
358: else if (c == '\t'
1.1 deraadt 359: #ifdef NOTAB
1.17 deraadt 360: && !(curbp->b_flag & BFNOTAB)
1.1 deraadt 361: #endif
1.17 deraadt 362: ) {
1.3 millert 363: do {
364: vtpute(' ');
1.8 vincent 365: } while (((vtcol + lbound) & 0x07) != 0 && vtcol < ncol);
1.3 millert 366: } else if (ISCTRL(c) != FALSE) {
367: vtpute('^');
368: vtpute(CCHR(c));
1.48 schwarze 369: } else if (isprint(c)) {
1.3 millert 370: if (vtcol >= 0)
371: vp->v_text[vtcol] = c;
372: ++vtcol;
1.48 schwarze 373: } else {
374: char bf[5], *cp;
375:
376: snprintf(bf, sizeof(bf), "\\%o", c);
377: for (cp = bf; *cp != '\0'; cp++)
378: vtpute(*cp);
1.3 millert 379: }
380: }
1.20 vincent 381:
1.3 millert 382: /*
383: * Erase from the end of the software cursor to the end of the line on which
384: * the software cursor is located. The display routines will decide if a
385: * hardware erase to end of line command should be used to display this.
1.1 deraadt 386: */
1.5 art 387: void
1.11 vincent 388: vteeol(void)
1.3 millert 389: {
1.23 deraadt 390: struct video *vp;
1.1 deraadt 391:
392: vp = vscreen[vtrow];
393: while (vtcol < ncol)
394: vp->v_text[vtcol++] = ' ';
395: }
396:
397: /*
398: * Make sure that the display is
399: * right. This is a three part process. First,
400: * scan through all of the windows looking for dirty
401: * ones. Check the framing, and refresh the screen.
402: * Second, make sure that "currow" and "curcol" are
403: * correct for the current window. Third, make the
404: * virtual and physical screens the same.
405: */
1.5 art 406: void
1.41 lum 407: update(int modelinecolor)
1.3 millert 408: {
1.23 deraadt 409: struct line *lp;
410: struct mgwin *wp;
411: struct video *vp1;
412: struct video *vp2;
1.21 db 413: int c, i, j;
414: int hflag;
415: int currow, curcol;
416: int offs, size;
1.1 deraadt 417:
1.26 kjell 418: if (charswaiting())
1.3 millert 419: return;
420: if (sgarbf) { /* must update everything */
1.1 deraadt 421: wp = wheadp;
1.3 millert 422: while (wp != NULL) {
1.37 kjell 423: wp->w_rflag |= WFMODE | WFFULL;
1.1 deraadt 424: wp = wp->w_wndp;
425: }
426: }
1.39 florian 427: if (linenos || colnos) {
1.29 kjell 428: wp = wheadp;
429: while (wp != NULL) {
1.37 kjell 430: wp->w_rflag |= WFMODE;
1.29 kjell 431: wp = wp->w_wndp;
432: }
433: }
1.21 db 434: hflag = FALSE; /* Not hard. */
1.8 vincent 435: for (wp = wheadp; wp != NULL; wp = wp->w_wndp) {
436: /*
437: * Nothing to be done.
438: */
1.37 kjell 439: if (wp->w_rflag == 0)
1.8 vincent 440: continue;
1.9 deraadt 441:
1.37 kjell 442: if ((wp->w_rflag & WFFRAME) == 0) {
1.8 vincent 443: lp = wp->w_linep;
444: for (i = 0; i < wp->w_ntrows; ++i) {
445: if (lp == wp->w_dotp)
446: goto out;
1.30 kjell 447: if (lp == wp->w_bufp->b_headp)
1.8 vincent 448: break;
449: lp = lforw(lp);
1.1 deraadt 450: }
1.8 vincent 451: }
452: /*
453: * Put the middle-line in place.
454: */
1.28 kjell 455: i = wp->w_frame;
1.8 vincent 456: if (i > 0) {
457: --i;
458: if (i >= wp->w_ntrows)
459: i = wp->w_ntrows - 1;
460: } else if (i < 0) {
461: i += wp->w_ntrows;
462: if (i < 0)
463: i = 0;
464: } else
1.9 deraadt 465: i = wp->w_ntrows / 2; /* current center, no change */
466:
1.8 vincent 467: /*
1.21 db 468: * Find the line.
1.8 vincent 469: */
470: lp = wp->w_dotp;
1.30 kjell 471: while (i != 0 && lback(lp) != wp->w_bufp->b_headp) {
1.8 vincent 472: --i;
473: lp = lback(lp);
474: }
475: wp->w_linep = lp;
1.37 kjell 476: wp->w_rflag |= WFFULL; /* Force full. */
1.8 vincent 477: out:
478: lp = wp->w_linep; /* Try reduced update. */
479: i = wp->w_toprow;
1.37 kjell 480: if ((wp->w_rflag & ~WFMODE) == WFEDIT) {
1.8 vincent 481: while (lp != wp->w_dotp) {
482: ++i;
483: lp = lforw(lp);
1.1 deraadt 484: }
1.8 vincent 485: vscreen[i]->v_color = CTEXT;
486: vscreen[i]->v_flag |= (VFCHG | VFHBAD);
487: vtmove(i, 0);
488: for (j = 0; j < llength(lp); ++j)
489: vtputc(lgetc(lp, j));
490: vteeol();
1.37 kjell 491: } else if ((wp->w_rflag & (WFEDIT | WFFULL)) != 0) {
1.8 vincent 492: hflag = TRUE;
493: while (i < wp->w_toprow + wp->w_ntrows) {
1.1 deraadt 494: vscreen[i]->v_color = CTEXT;
1.3 millert 495: vscreen[i]->v_flag |= (VFCHG | VFHBAD);
1.1 deraadt 496: vtmove(i, 0);
1.30 kjell 497: if (lp != wp->w_bufp->b_headp) {
1.8 vincent 498: for (j = 0; j < llength(lp); ++j)
499: vtputc(lgetc(lp, j));
500: lp = lforw(lp);
501: }
1.1 deraadt 502: vteeol();
1.8 vincent 503: ++i;
1.1 deraadt 504: }
505: }
1.37 kjell 506: if ((wp->w_rflag & WFMODE) != 0)
1.41 lum 507: modeline(wp, modelinecolor);
1.37 kjell 508: wp->w_rflag = 0;
1.28 kjell 509: wp->w_frame = 0;
1.1 deraadt 510: }
1.21 db 511: lp = curwp->w_linep; /* Cursor location. */
1.1 deraadt 512: currow = curwp->w_toprow;
513: while (lp != curwp->w_dotp) {
514: ++currow;
515: lp = lforw(lp);
516: }
517: curcol = 0;
518: i = 0;
519: while (i < curwp->w_doto) {
520: c = lgetc(lp, i++);
521: if (c == '\t'
522: #ifdef NOTAB
1.3 millert 523: && !(curbp->b_flag & BFNOTAB)
1.1 deraadt 524: #endif
1.18 vincent 525: ) {
1.3 millert 526: curcol |= 0x07;
1.18 vincent 527: curcol++;
528: } else if (ISCTRL(c) != FALSE)
529: curcol += 2;
530: else if (isprint(c))
531: curcol++;
532: else {
533: char bf[5];
534:
1.21 db 535: snprintf(bf, sizeof(bf), "\\%o", c);
1.18 vincent 536: curcol += strlen(bf);
537: }
1.1 deraadt 538: }
1.3 millert 539: if (curcol >= ncol - 1) { /* extended line. */
540: /* flag we are extended and changed */
1.1 deraadt 541: vscreen[currow]->v_flag |= VFEXT | VFCHG;
1.3 millert 542: updext(currow, curcol); /* and output extended line */
543: } else
544: lbound = 0; /* not extended line */
1.1 deraadt 545:
1.3 millert 546: /*
1.21 db 547: * Make sure no lines need to be de-extended because the cursor is no
548: * longer on them.
1.3 millert 549: */
1.1 deraadt 550: wp = wheadp;
551: while (wp != NULL) {
1.3 millert 552: lp = wp->w_linep;
553: i = wp->w_toprow;
554: while (i < wp->w_toprow + wp->w_ntrows) {
555: if (vscreen[i]->v_flag & VFEXT) {
556: /* always flag extended lines as changed */
557: vscreen[i]->v_flag |= VFCHG;
558: if ((wp != curwp) || (lp != wp->w_dotp) ||
559: (curcol < ncol - 1)) {
560: vtmove(i, 0);
561: for (j = 0; j < llength(lp); ++j)
562: vtputc(lgetc(lp, j));
563: vteeol();
564: /* this line no longer is extended */
565: vscreen[i]->v_flag &= ~VFEXT;
566: }
567: }
568: lp = lforw(lp);
569: ++i;
1.1 deraadt 570: }
1.3 millert 571: /* if garbaged then fix up mode lines */
572: if (sgarbf != FALSE)
573: vscreen[i]->v_flag |= VFCHG;
574: /* and onward to the next window */
575: wp = wp->w_wndp;
1.1 deraadt 576: }
577:
1.3 millert 578: if (sgarbf != FALSE) { /* Screen is garbage. */
1.21 db 579: sgarbf = FALSE; /* Erase-page clears. */
580: epresf = FALSE; /* The message area. */
581: tttop = HUGE; /* Forget where you set. */
1.3 millert 582: ttbot = HUGE; /* scroll region. */
583: tthue = CNONE; /* Color unknown. */
1.1 deraadt 584: ttmove(0, 0);
585: tteeop();
1.3 millert 586: for (i = 0; i < nrow - 1; ++i) {
1.1 deraadt 587: uline(i, vscreen[i], &blanks);
588: ucopy(vscreen[i], pscreen[i]);
589: }
590: ttmove(currow, curcol - lbound);
591: ttflush();
592: return;
593: }
594: if (hflag != FALSE) { /* Hard update? */
1.3 millert 595: for (i = 0; i < nrow - 1; ++i) {/* Compute hash data. */
1.1 deraadt 596: hash(vscreen[i]);
597: hash(pscreen[i]);
598: }
599: offs = 0; /* Get top match. */
1.3 millert 600: while (offs != nrow - 1) {
1.1 deraadt 601: vp1 = vscreen[offs];
602: vp2 = pscreen[offs];
603: if (vp1->v_color != vp2->v_color
1.3 millert 604: || vp1->v_hash != vp2->v_hash)
1.1 deraadt 605: break;
606: uline(offs, vp1, vp2);
607: ucopy(vp1, vp2);
608: ++offs;
609: }
1.3 millert 610: if (offs == nrow - 1) { /* Might get it all. */
1.1 deraadt 611: ttmove(currow, curcol - lbound);
612: ttflush();
613: return;
614: }
1.3 millert 615: size = nrow - 1; /* Get bottom match. */
1.1 deraadt 616: while (size != offs) {
1.3 millert 617: vp1 = vscreen[size - 1];
618: vp2 = pscreen[size - 1];
1.1 deraadt 619: if (vp1->v_color != vp2->v_color
1.3 millert 620: || vp1->v_hash != vp2->v_hash)
1.1 deraadt 621: break;
1.3 millert 622: uline(size - 1, vp1, vp2);
1.1 deraadt 623: ucopy(vp1, vp2);
624: --size;
625: }
626: if ((size -= offs) == 0) /* Get screen size. */
627: panic("Illegal screen size in update");
628: setscores(offs, size); /* Do hard update. */
629: traceback(offs, size, size, size);
1.3 millert 630: for (i = 0; i < size; ++i)
631: ucopy(vscreen[offs + i], pscreen[offs + i]);
1.1 deraadt 632: ttmove(currow, curcol - lbound);
633: ttflush();
634: return;
635: }
1.3 millert 636: for (i = 0; i < nrow - 1; ++i) { /* Easy update. */
1.1 deraadt 637: vp1 = vscreen[i];
638: vp2 = pscreen[i];
1.3 millert 639: if ((vp1->v_flag & VFCHG) != 0) {
1.1 deraadt 640: uline(i, vp1, vp2);
641: ucopy(vp1, vp2);
642: }
643: }
644: ttmove(currow, curcol - lbound);
645: ttflush();
646: }
647:
648: /*
649: * Update a saved copy of a line,
1.23 deraadt 650: * kept in a video structure. The "vvp" is
1.1 deraadt 651: * the one in the "vscreen". The "pvp" is the one
652: * in the "pscreen". This is called to make the
653: * virtual and physical screens the same when
654: * display has done an update.
655: */
1.5 art 656: void
1.23 deraadt 657: ucopy(struct video *vvp, struct video *pvp)
1.3 millert 658: {
659: vvp->v_flag &= ~VFCHG; /* Changes done. */
660: pvp->v_flag = vvp->v_flag; /* Update model. */
661: pvp->v_hash = vvp->v_hash;
662: pvp->v_cost = vvp->v_cost;
1.1 deraadt 663: pvp->v_color = vvp->v_color;
664: bcopy(vvp->v_text, pvp->v_text, ncol);
665: }
666:
1.3 millert 667: /*
668: * updext: update the extended line which the cursor is currently on at a
669: * column greater than the terminal width. The line will be scrolled right or
1.21 db 670: * left to let the user see where the cursor is.
1.1 deraadt 671: */
1.5 art 672: void
1.11 vincent 673: updext(int currow, int curcol)
1.1 deraadt 674: {
1.23 deraadt 675: struct line *lp; /* pointer to current line */
1.21 db 676: int j; /* index into line */
1.1 deraadt 677:
1.13 millert 678: if (ncol < 2)
679: return;
680:
1.3 millert 681: /*
682: * calculate what column the left bound should be
683: * (force cursor into middle half of screen)
684: */
685: lbound = curcol - (curcol % (ncol >> 1)) - (ncol >> 2);
1.13 millert 686:
1.3 millert 687: /*
1.49 ! jmc 688: * scan through the line outputting characters to the virtual screen
1.3 millert 689: * once we reach the left edge
690: */
691: vtmove(currow, -lbound); /* start scanning offscreen */
692: lp = curwp->w_dotp; /* line to output */
693: for (j = 0; j < llength(lp); ++j) /* until the end-of-line */
694: vtpute(lgetc(lp, j));
1.21 db 695: vteeol(); /* truncate the virtual line */
1.3 millert 696: vscreen[currow]->v_text[0] = '$'; /* and put a '$' in column 1 */
1.1 deraadt 697: }
698:
699: /*
700: * Update a single line. This routine only
701: * uses basic functionality (no insert and delete character,
1.23 deraadt 702: * but erase to end of line). The "vvp" points at the video
1.1 deraadt 703: * structure for the line on the virtual screen, and the "pvp"
704: * is the same for the physical screen. Avoid erase to end of
705: * line when updating CMODE color lines, because of the way that
706: * reverse video works on most terminals.
707: */
1.5 art 708: void
1.23 deraadt 709: uline(int row, struct video *vvp, struct video *pvp)
1.3 millert 710: {
711: char *cp1;
712: char *cp2;
713: char *cp3;
714: char *cp4;
715: char *cp5;
1.21 db 716: int nbflag;
1.1 deraadt 717:
1.3 millert 718: if (vvp->v_color != pvp->v_color) { /* Wrong color, do a */
719: ttmove(row, 0); /* full redraw. */
1.1 deraadt 720: #ifdef STANDOUT_GLITCH
1.2 millert 721: if (pvp->v_color != CTEXT && magic_cookie_glitch >= 0)
722: tteeol();
1.1 deraadt 723: #endif
724: ttcolor(vvp->v_color);
725: #ifdef STANDOUT_GLITCH
1.2 millert 726: cp1 = &vvp->v_text[magic_cookie_glitch > 0 ? magic_cookie_glitch : 0];
1.3 millert 727: /*
1.21 db 728: * The odd code for magic_cookie_glitch==0 is to avoid
729: * putting the invisible glitch character on the next line.
1.1 deraadt 730: * (Hazeltine executive 80 model 30)
731: */
1.24 deraadt 732: cp2 = &vvp->v_text[ncol - (magic_cookie_glitch >= 0 ?
733: (magic_cookie_glitch != 0 ? magic_cookie_glitch : 1) : 0)];
1.1 deraadt 734: #else
735: cp1 = &vvp->v_text[0];
736: cp2 = &vvp->v_text[ncol];
737: #endif
738: while (cp1 != cp2) {
739: ttputc(*cp1++);
740: ++ttcol;
741: }
742: ttcolor(CTEXT);
743: return;
744: }
1.21 db 745: cp1 = &vvp->v_text[0]; /* Compute left match. */
1.1 deraadt 746: cp2 = &pvp->v_text[0];
1.3 millert 747: while (cp1 != &vvp->v_text[ncol] && cp1[0] == cp2[0]) {
1.1 deraadt 748: ++cp1;
749: ++cp2;
750: }
1.3 millert 751: if (cp1 == &vvp->v_text[ncol]) /* All equal. */
1.1 deraadt 752: return;
753: nbflag = FALSE;
1.21 db 754: cp3 = &vvp->v_text[ncol]; /* Compute right match. */
1.1 deraadt 755: cp4 = &pvp->v_text[ncol];
756: while (cp3[-1] == cp4[-1]) {
757: --cp3;
758: --cp4;
1.3 millert 759: if (cp3[0] != ' ') /* Note non-blanks in */
760: nbflag = TRUE; /* the right match. */
1.1 deraadt 761: }
1.3 millert 762: cp5 = cp3; /* Is erase good? */
763: if (nbflag == FALSE && vvp->v_color == CTEXT) {
764: while (cp5 != cp1 && cp5[-1] == ' ')
1.1 deraadt 765: --cp5;
766: /* Alcyon hack */
1.3 millert 767: if ((int) (cp3 - cp5) <= tceeol)
1.1 deraadt 768: cp5 = cp3;
769: }
770: /* Alcyon hack */
1.3 millert 771: ttmove(row, (int) (cp1 - &vvp->v_text[0]));
1.1 deraadt 772: #ifdef STANDOUT_GLITCH
1.2 millert 773: if (vvp->v_color != CTEXT && magic_cookie_glitch > 0) {
1.3 millert 774: if (cp1 < &vvp->v_text[magic_cookie_glitch])
775: cp1 = &vvp->v_text[magic_cookie_glitch];
776: if (cp5 > &vvp->v_text[ncol - magic_cookie_glitch])
777: cp5 = &vvp->v_text[ncol - magic_cookie_glitch];
1.2 millert 778: } else if (magic_cookie_glitch < 0)
1.1 deraadt 779: #endif
780: ttcolor(vvp->v_color);
781: while (cp1 != cp5) {
782: ttputc(*cp1++);
783: ++ttcol;
784: }
1.3 millert 785: if (cp5 != cp3) /* Do erase. */
1.1 deraadt 786: tteeol();
787: }
788:
789: /*
1.3 millert 790: * Redisplay the mode line for the window pointed to by the "wp".
1.21 db 791: * This is the only routine that has any idea of how the mode line is
1.3 millert 792: * formatted. You can change the modeline format by hacking at this
793: * routine. Called by "update" any time there is a dirty window. Note
794: * that if STANDOUT_GLITCH is defined, first and last magic_cookie_glitch
795: * characters may never be seen.
796: */
1.5 art 797: void
1.41 lum 798: modeline(struct mgwin *wp, int modelinecolor)
1.3 millert 799: {
1.25 kjell 800: int n, md;
1.23 deraadt 801: struct buffer *bp;
1.29 kjell 802: char sl[21]; /* Overkill. Space for 2^64 in base 10. */
803: int len;
1.3 millert 804:
805: n = wp->w_toprow + wp->w_ntrows; /* Location. */
1.41 lum 806: vscreen[n]->v_color = modelinecolor; /* Mode line color. */
1.3 millert 807: vscreen[n]->v_flag |= (VFCHG | VFHBAD); /* Recompute, display. */
808: vtmove(n, 0); /* Seek to right line. */
1.1 deraadt 809: bp = wp->w_bufp;
1.3 millert 810: vtputc('-');
811: vtputc('-');
1.17 deraadt 812: if ((bp->b_flag & BFREADONLY) != 0) {
1.12 vincent 813: vtputc('%');
1.14 vincent 814: if ((bp->b_flag & BFCHG) != 0)
815: vtputc('*');
816: else
817: vtputc('%');
1.12 vincent 818: } else if ((bp->b_flag & BFCHG) != 0) { /* "*" if changed. */
1.3 millert 819: vtputc('*');
820: vtputc('*');
1.17 deraadt 821: } else {
1.3 millert 822: vtputc('-');
823: vtputc('-');
1.1 deraadt 824: }
825: vtputc('-');
1.3 millert 826: n = 5;
1.1 deraadt 827: n += vtputs("Mg: ");
828: if (bp->b_bname[0] != '\0')
829: n += vtputs(&(bp->b_bname[0]));
1.21 db 830: while (n < 42) { /* Pad out with blanks. */
1.1 deraadt 831: vtputc(' ');
832: ++n;
833: }
834: vtputc('(');
835: ++n;
1.25 kjell 836: for (md = 0; ; ) {
837: n += vtputs(bp->b_modes[md]->p_name);
838: if (++md > bp->b_nmodes)
1.3 millert 839: break;
840: vtputc('-');
841: ++n;
1.1 deraadt 842: }
1.35 kjell 843: /* XXX These should eventually move to a real mode */
1.34 kjell 844: if (macrodef == TRUE)
845: n += vtputs("-def");
1.35 kjell 846: if (globalwd == TRUE)
847: n += vtputs("-gwd");
1.1 deraadt 848: vtputc(')');
849: ++n;
1.29 kjell 850:
1.39 florian 851: if (linenos && colnos)
852: len = snprintf(sl, sizeof(sl), "--L%d--C%d", wp->w_dotline,
1.40 florian 853: getcolpos(wp));
1.39 florian 854: else if (linenos)
1.38 deraadt 855: len = snprintf(sl, sizeof(sl), "--L%d", wp->w_dotline);
1.39 florian 856: else if (colnos)
1.40 florian 857: len = snprintf(sl, sizeof(sl), "--C%d", getcolpos(wp));
1.39 florian 858: if ((linenos || colnos) && len < sizeof(sl) && len != -1)
859: n += vtputs(sl);
1.29 kjell 860:
1.21 db 861: while (n < ncol) { /* Pad out. */
1.1 deraadt 862: vtputc('-');
863: ++n;
864: }
865: }
1.21 db 866:
1.1 deraadt 867: /*
1.21 db 868: * Output a string to the mode line, report how long it was.
1.1 deraadt 869: */
1.3 millert 870: int
1.11 vincent 871: vtputs(const char *s)
1.3 millert 872: {
1.11 vincent 873: int n = 0;
1.1 deraadt 874:
875: while (*s != '\0') {
876: vtputc(*s++);
877: ++n;
878: }
1.21 db 879: return (n);
1.1 deraadt 880: }
1.3 millert 881:
1.1 deraadt 882: /*
1.3 millert 883: * Compute the hash code for the line pointed to by the "vp".
884: * Recompute it if necessary. Also set the approximate redisplay
885: * cost. The validity of the hash code is marked by a flag bit.
886: * The cost understand the advantages of erase to end of line.
887: * Tuned for the VAX by Bob McNamara; better than it used to be on
1.1 deraadt 888: * just about any machine.
889: */
1.5 art 890: void
1.23 deraadt 891: hash(struct video *vp)
1.3 millert 892: {
1.21 db 893: int i, n;
894: char *s;
1.3 millert 895:
896: if ((vp->v_flag & VFHBAD) != 0) { /* Hash bad. */
897: s = &vp->v_text[ncol - 1];
898: for (i = ncol; i != 0; --i, --s)
1.1 deraadt 899: if (*s != ' ')
900: break;
1.3 millert 901: n = ncol - i; /* Erase cheaper? */
1.1 deraadt 902: if (n > tceeol)
903: n = tceeol;
1.3 millert 904: vp->v_cost = i + n; /* Bytes + blanks. */
905: for (n = 0; i != 0; --i, --s)
906: n = (n << 5) + n + *s;
907: vp->v_hash = n; /* Hash code. */
908: vp->v_flag &= ~VFHBAD; /* Flag as all done. */
1.1 deraadt 909: }
910: }
911:
912: /*
913: * Compute the Insert-Delete
914: * cost matrix. The dynamic programming algorithm
915: * described by James Gosling is used. This code assumes
916: * that the line above the echo line is the last line involved
917: * in the scroll region. This is easy to arrange on the VT100
918: * because of the scrolling region. The "offs" is the origin 0
919: * offset of the first row in the virtual/physical screen that
920: * is being updated; the "size" is the length of the chunk of
921: * screen being updated. For a full screen update, use offs=0
922: * and size=nrow-1.
923: *
924: * Older versions of this code implemented the score matrix by
925: * a two dimensional array of SCORE nodes. This put all kinds of
926: * multiply instructions in the code! This version is written to
927: * use a linear array and pointers, and contains no multiplication
928: * at all. The code has been carefully looked at on the VAX, with
929: * only marginal checking on other machines for efficiency. In
930: * fact, this has been tuned twice! Bob McNamara tuned it even
931: * more for the VAX, which is a big issue for him because of
932: * the 66 line X displays.
933: *
934: * On some machines, replacing the "for (i=1; i<=size; ++i)" with
935: * i = 1; do { } while (++i <=size)" will make the code quite a
936: * bit better; but it looks ugly.
937: */
1.5 art 938: void
1.11 vincent 939: setscores(int offs, int size)
1.3 millert 940: {
1.23 deraadt 941: struct score *sp;
942: struct score *sp1;
943: struct video **vp, **pp;
944: struct video **vbase, **pbase;
1.21 db 945: int tempcost;
946: int bestcost;
947: int j, i;
1.3 millert 948:
949: vbase = &vscreen[offs - 1]; /* By hand CSE's. */
950: pbase = &pscreen[offs - 1];
951: score[0].s_itrace = 0; /* [0, 0] */
1.1 deraadt 952: score[0].s_jtrace = 0;
1.3 millert 953: score[0].s_cost = 0;
954: sp = &score[1]; /* Row 0, inserts. */
1.1 deraadt 955: tempcost = 0;
956: vp = &vbase[1];
1.3 millert 957: for (j = 1; j <= size; ++j) {
1.1 deraadt 958: sp->s_itrace = 0;
1.3 millert 959: sp->s_jtrace = j - 1;
1.1 deraadt 960: tempcost += tcinsl;
961: tempcost += (*vp)->v_cost;
962: sp->s_cost = tempcost;
963: ++vp;
964: ++sp;
965: }
1.7 art 966: sp = &score[nrow]; /* Column 0, deletes. */
1.1 deraadt 967: tempcost = 0;
1.3 millert 968: for (i = 1; i <= size; ++i) {
969: sp->s_itrace = i - 1;
1.1 deraadt 970: sp->s_jtrace = 0;
1.3 millert 971: tempcost += tcdell;
1.1 deraadt 972: sp->s_cost = tempcost;
1.7 art 973: sp += nrow;
1.1 deraadt 974: }
1.7 art 975: sp1 = &score[nrow + 1]; /* [1, 1]. */
1.1 deraadt 976: pp = &pbase[1];
1.3 millert 977: for (i = 1; i <= size; ++i) {
1.1 deraadt 978: sp = sp1;
979: vp = &vbase[1];
1.3 millert 980: for (j = 1; j <= size; ++j) {
981: sp->s_itrace = i - 1;
1.1 deraadt 982: sp->s_jtrace = j;
1.7 art 983: bestcost = (sp - nrow)->s_cost;
1.3 millert 984: if (j != size) /* Cd(A[i])=0 @ Dis. */
1.1 deraadt 985: bestcost += tcdell;
1.3 millert 986: tempcost = (sp - 1)->s_cost;
1.1 deraadt 987: tempcost += (*vp)->v_cost;
1.3 millert 988: if (i != size) /* Ci(B[j])=0 @ Dsj. */
1.1 deraadt 989: tempcost += tcinsl;
990: if (tempcost < bestcost) {
991: sp->s_itrace = i;
1.3 millert 992: sp->s_jtrace = j - 1;
1.1 deraadt 993: bestcost = tempcost;
994: }
1.7 art 995: tempcost = (sp - nrow - 1)->s_cost;
1.1 deraadt 996: if ((*pp)->v_color != (*vp)->v_color
1.3 millert 997: || (*pp)->v_hash != (*vp)->v_hash)
1.1 deraadt 998: tempcost += (*vp)->v_cost;
999: if (tempcost < bestcost) {
1.3 millert 1000: sp->s_itrace = i - 1;
1001: sp->s_jtrace = j - 1;
1.1 deraadt 1002: bestcost = tempcost;
1003: }
1004: sp->s_cost = bestcost;
1.3 millert 1005: ++sp; /* Next column. */
1.1 deraadt 1006: ++vp;
1007: }
1008: ++pp;
1.7 art 1009: sp1 += nrow; /* Next row. */
1.1 deraadt 1010: }
1011: }
1012:
1013: /*
1014: * Trace back through the dynamic programming cost
1015: * matrix, and update the screen using an optimal sequence
1016: * of redraws, insert lines, and delete lines. The "offs" is
1017: * the origin 0 offset of the chunk of the screen we are about to
1018: * update. The "i" and "j" are always started in the lower right
1019: * corner of the matrix, and imply the size of the screen.
1020: * A full screen traceback is called with offs=0 and i=j=nrow-1.
1021: * There is some do-it-yourself double subscripting here,
1022: * which is acceptable because this routine is much less compute
1023: * intensive then the code that builds the score matrix!
1024: */
1.5 art 1025: void
1.11 vincent 1026: traceback(int offs, int size, int i, int j)
1.6 mickey 1027: {
1.21 db 1028: int itrace, jtrace;
1.6 mickey 1029: int k;
1.21 db 1030: int ninsl, ndraw, ndell;
1.1 deraadt 1031:
1.3 millert 1032: if (i == 0 && j == 0) /* End of update. */
1.1 deraadt 1033: return;
1.7 art 1034: itrace = score[(nrow * i) + j].s_itrace;
1035: jtrace = score[(nrow * i) + j].s_jtrace;
1.3 millert 1036: if (itrace == i) { /* [i, j-1] */
1037: ninsl = 0; /* Collect inserts. */
1.1 deraadt 1038: if (i != size)
1039: ninsl = 1;
1040: ndraw = 1;
1.3 millert 1041: while (itrace != 0 || jtrace != 0) {
1.7 art 1042: if (score[(nrow * itrace) + jtrace].s_itrace != itrace)
1.1 deraadt 1043: break;
1.7 art 1044: jtrace = score[(nrow * itrace) + jtrace].s_jtrace;
1.1 deraadt 1045: if (i != size)
1046: ++ninsl;
1047: ++ndraw;
1048: }
1049: traceback(offs, size, itrace, jtrace);
1050: if (ninsl != 0) {
1051: ttcolor(CTEXT);
1.3 millert 1052: ttinsl(offs + j - ninsl, offs + size - 1, ninsl);
1.1 deraadt 1053: }
1.3 millert 1054: do { /* B[j], A[j] blank. */
1055: k = offs + j - ndraw;
1.1 deraadt 1056: uline(k, vscreen[k], &blanks);
1057: } while (--ndraw);
1058: return;
1059: }
1.3 millert 1060: if (jtrace == j) { /* [i-1, j] */
1061: ndell = 0; /* Collect deletes. */
1.1 deraadt 1062: if (j != size)
1063: ndell = 1;
1.3 millert 1064: while (itrace != 0 || jtrace != 0) {
1.7 art 1065: if (score[(nrow * itrace) + jtrace].s_jtrace != jtrace)
1.1 deraadt 1066: break;
1.7 art 1067: itrace = score[(nrow * itrace) + jtrace].s_itrace;
1.1 deraadt 1068: if (j != size)
1069: ++ndell;
1070: }
1071: if (ndell != 0) {
1072: ttcolor(CTEXT);
1.3 millert 1073: ttdell(offs + i - ndell, offs + size - 1, ndell);
1.1 deraadt 1074: }
1075: traceback(offs, size, itrace, jtrace);
1076: return;
1077: }
1078: traceback(offs, size, itrace, jtrace);
1.3 millert 1079: k = offs + j - 1;
1080: uline(k, vscreen[k], pscreen[offs + i - 1]);
1.1 deraadt 1081: }