|
|
1.1 root 1: /*
2: * The functions in this file handle redisplay.
3: * There are two halves, the ones that update the virtual display screen,
4: * and the ones that make the physical display screen the same as the virtual
5: * display screen. These functions use hints that are left in the windows by
6: * the commands.
7: */
8: #include <stdio.h>
9: #include "ed.h"
10:
11: #define WFDEBUG 0 /* Window flag debug. */
12: #define FASTHACK 1 /* Slightly faster update code */
13:
14: typedef struct VIDEO {
15: short v_flag; /* Flags */
16: uchar v_text[]; /* Screen data. */
17: } VIDEO;
18:
19: #define VFCHG 0x0001 /* Changed. */
20: #define VFSTD 0x0002 /* Standout. */
21:
22: int sgarbf = TRUE; /* TRUE if screen is garbage */
23: int mpresf = FALSE; /* TRUE if message in last line */
24: int vtrow = 0; /* Row location of SW cursor */
25: int vtcol = 0; /* Column location of SW cursor */
26: int ttrow = HUGE; /* Row location of HW cursor */
27: int ttcol = HUGE; /* Column location of HW cursor */
28: VIDEO **vscreen; /* Virtual screen. */
29: VIDEO **pscreen; /* Physical screen. */
30:
31: /*
32: * Initialize the data structures used by the display code.
33: * The edge vectors used to access the screens are set up.
34: * The operating system's terminal I/O channel is set up.
35: * All the other things get initialized at compile time.
36: * The original window has "WFCHG" set, so that it will get
37: * completely redrawn on the first call to "update".
38: */
39: vtinit()
40: {
41: register int i;
42: register VIDEO *vp;
43:
44: vscreen = (VIDEO **) malloc(term.t_nrow*sizeof(VIDEO *));
45: if (vscreen == NULL)
46: abort();
47: pscreen = (VIDEO **) malloc(term.t_nrow*sizeof(VIDEO *));
48: if (pscreen == NULL)
49: abort();
50: for (i=0; i<term.t_nrow; ++i) {
51: vp = (VIDEO *) malloc(sizeof(VIDEO)+term.t_ncol);
52: if (vp == NULL)
53: abort();
54: vscreen[i] = vp;
55: vp = (VIDEO *) malloc(sizeof(VIDEO)+term.t_ncol);
56: if (vp == NULL)
57: abort();
58: pscreen[i] = vp;
59: }
60: }
61:
62: /*
63: * Clean up the virtual terminal system, in anticipation for a return to the
64: * operating system. Move down to the last line and clear it out (the next
65: * system prompt will be written in the line). Shut down the channel to the
66: * terminal.
67: */
68: vttidy()
69: {
70: movecursor(term.t_nrow, 0);
71: teeol();
72: tstand(0);
73: tclose();
74: }
75:
76: /*
77: * Set the virtual cursor to the specified row and column on the
78: * virtual screen. There is no checking for nonsense values; this might
79: * be a good idea during the early stages.
80: */
81: vtmove(row, col)
82: {
83: vtrow = row;
84: vtcol = col;
85: }
86:
87: /*
88: * If bind.dispmode == 1 chars < ' ' are displayed directly
89: * else they are displayed whit preceeding ^ and ^= '@'
90: */
91: displaymod()
92: {
93: bind.dispmode ^= 1;
94: }
95:
96: /*
97: * Return 2 if char is tab 1 if it needs ^ for ansi emulation.
98: */
99: dblchr(c)
100: register unsigned c;
101: {
102: if (bind.dispmode) {
103: switch(c) {
104: case '\n':
105: case '\f':
106: case '\r':
107: case 0x1b:
108: return (1);
109: case '\t':
110: return (2);
111: }
112: return (0);
113: }
114: return (((c < ' ') || (c == 0x7f)) ? ((c == '\t') ? 2 : 1) : 0);
115: }
116:
117: /*
118: * Write a character to the virtual screen. The virtual row and
119: * column are updated. If the line is too long put a "$" in the last column.
120: * This routine only puts printing characters into the virtual terminal buffers.
121: * Only column overflow is checked.
122: */
123: vtputc(c)
124: unsigned c;
125: {
126: register VIDEO *vp;
127:
128: vp = vscreen[vtrow];
129: switch (dblchr(c)) {
130: case 0: /* normal character */
131: if (vtcol >= term.t_ncol)
132: vp->v_text[term.t_ncol - 1] = '$';
133: else
134: vp->v_text[vtcol++] = c;
135: break;
136: case 2: /* tab */
137: do {
138: if (vtcol >= term.t_ncol) {
139: vp->v_text[term.t_ncol - 1] = '$';
140: break;
141: } else
142: vp->v_text[vtcol++] = ' ';
143: } while (vtcol % bind.tabsiz);
144: break;
145: case 1: /* needs ^ */
146: vtputc('^');
147: vtputc(c ^ 0x40);
148: break;
149: }
150: }
151:
152: /*
153: * Erase from the end of the software cursor to the end of the line on which
154: * the software cursor is located.
155: */
156: vteeol()
157: {
158: register VIDEO *vp;
159:
160: vp = vscreen[vtrow];
161: while (vtcol < term.t_ncol)
162: vp->v_text[vtcol++] = ' ';
163: }
164:
165: /*
166: * Make sure that the display is right. This is a three part process.
167: * First, scan through all of the windows looking for dirty ones.
168: * Check the framing, and refresh the screen.
169: * Second, make sure that "currow" and "curcol" are correct for the current
170: * window.
171: * Third, make the virtual and physical screens the same.
172: */
173: update()
174: {
175: register LINE *lp;
176: LINE *xlp;
177: register WINDOW *wp;
178: register VIDEO *vp1;
179: register VIDEO *vp2;
180: register int i;
181: register int j;
182:
183: wp = wheadp;
184: while (wp != NULL) {
185: /* Look at any window with update flags set on. */
186: if (wp->w_flag != 0) {
187: /* If not force reframe, check the framing. */
188: if ((wp->w_flag&WFFORCE) == 0) {
189: lp = wp->w_linep;
190: for (i=0; i<wp->w_ntrows; ++i) {
191: if (lp == wp->w_dotp)
192: goto out;
193: if (lp == wp->w_bufp->b_linep)
194: break;
195: lp = lforw(lp);
196: }
197: }
198: /* Not acceptable, better compute a new value */
199: /* for the line at the top of the window. Then */
200: /* set the "WFHARD" flag to force full redraw. */
201: i = wp->w_force;
202: if (i > 0) {
203: --i;
204: if (i >= wp->w_ntrows)
205: i = wp->w_ntrows-1;
206: } else if (i < 0) {
207: i += wp->w_ntrows;
208: if (i < 0)
209: i = 0;
210: } else
211: i = wp->w_ntrows/2;
212: lp = wp->w_dotp;
213: while (i!=0 && lback(lp)!=wp->w_bufp->b_linep) {
214: --i;
215: lp = lback(lp);
216: }
217: wp->w_linep = lp;
218: wp->w_flag |= WFHARD; /* Force full. */
219: out:
220: /* Try to use reduced update. Mode line update */
221: /* has its own special flag. The fast update is */
222: /* used if the only thing to do is within the */
223: /* line editing. */
224: lp = wp->w_linep;
225: i = wp->w_toprow;
226: if ((wp->w_flag&~WFMODE) == WFEDIT) {
227: while (lp != wp->w_dotp) {
228: ++i;
229: lp = lforw(lp);
230: }
231: #ifdef FASTHACK
232: vscreen[i]->v_flag = VFCHG;
233: #else
234: vscreen[i]->v_flag |= VFCHG;
235: vscreen[i]->v_flag &= ~VFSTD;
236: #endif
237: vtmove(i, 0);
238: for (j=0; j<llength(lp); ++j)
239: vtputc(lgetc(lp, j));
240: vteeol();
241: } else if ((wp->w_flag&(WFEDIT|WFHARD)) != 0) {
242: while (i < wp->w_toprow+wp->w_ntrows) {
243: #ifdef FASTHACK
244: vscreen[i]->v_flag = VFCHG;
245: #else
246: vscreen[i]->v_flag |= VFCHG;
247: vscreen[i]->v_flag &= ~VFSTD;
248: #endif
249: vtmove(i, 0);
250: if (lp != wp->w_bufp->b_linep) {
251: for (j=0; j<llength(lp); ++j)
252: vtputc(lgetc(lp, j));
253: lp = lforw(lp);
254: }
255: vteeol();
256: ++i;
257: }
258: }
259: #if !WFDEBUG
260: if ((wp->w_flag&WFMODE) != 0)
261: modeline(wp);
262: wp->w_flag = 0;
263: wp->w_force = 0;
264: #endif
265: }
266: #if WFDEBUG
267: modeline(wp);
268: wp->w_flag = 0;
269: wp->w_force = 0;
270: #endif
271: /* Set standout mode on status line... */
272: vscreen[wp->w_toprow+wp->w_ntrows]->v_flag |= VFSTD;
273: wp = wp->w_wndp;
274: }
275: /* Always recompute the row and column number of the hardware */
276: /* cursor. This is the only update for simple moves. */
277: xlp = lp = curwp->w_linep;
278: currow = curwp->w_toprow;
279: while (lp != curwp->w_dotp) {
280: ++currow;
281: if (xlp == (lp = lforw(lp))) { /* Fix infinite loop problem */
282: currow = curwp->w_toprow;
283: curwp->w_dotp = lp = xlp;
284: curwp->w_doto = 0;
285: break;
286: }
287: }
288: curcol = truecol(lp, curwp->w_doto);
289:
290: #if GEM && NATIVE
291: /* Special preparation for screen update on ATARI ST native screen */
292: /* We shut off the cursor -- This speeds up the writing of text */
293: /* quite a bit. */
294:
295: astcursor(0); /* Turn cursor off for update */
296: #endif
297:
298: /* Special hacking if the screen is garbage. Clear the hardware */
299: /* screen, and update your copy to agree with it. Set all the */
300: /* virtual screen change bits, to force a full update. */
301:
302: if (sgarbf != FALSE) {
303: for (i=0; i<term.t_nrow; ++i) {
304: vscreen[i]->v_flag |= VFCHG;
305: vp1 = pscreen[i];
306: for (j=0; j<term.t_ncol; ++j)
307: vp1->v_text[j] = ' ';
308: }
309: teeop(); /* Erase the screen. */
310: sgarbf = FALSE; /* Erase-page clears */
311: mpresf = FALSE; /* the message area. */
312: }
313:
314: /* Make sure that the physical and virtual displays agree. */
315: /* Unlike before, the "updateline" code is only called with a */
316: /* line that has been updated for sure. */
317:
318: for (i=0; i<term.t_nrow; ++i) {
319: vp1 = vscreen[i];
320: if ((vp1->v_flag&VFCHG) != 0) {
321: vp2 = pscreen[i];
322: if ((vp1->v_flag&VFSTD) != 0) { /* Standout mode */
323: #ifndef FASTHACK
324: vp1->v_flag &= ~VFSTD;
325: #endif
326: tstand(1);
327: updateline(i, &vp1->v_text[0], &vp2->v_text[0]);
328: tstand(0);
329: } else
330: updateline(i, &vp1->v_text[0], &vp2->v_text[0]);
331: #ifdef FASTHACK
332: vp1->v_flag = 0;
333: #else
334: vp1->v_flag &= ~VFCHG;
335: #endif
336: }
337: }
338: /* Finally, update the hardware cursor and flush out buffers. */
339: bracketmode(currow, curcol);
340: #if GEM && NATIVE
341: astcursor(1); /* Turn cursor back on... */
342: #endif
343: tflush();
344: }
345:
346: /*
347: * Update a single line. This does not know how to use insert or delete
348: * character sequences; we are using VT52 functionality. Update the physical
349: * row and column variables. It does try and exploit erase to end of line.
350: * The RAINBOW version of this routine uses fast video.
351: */
352: updateline(row, vline, pline)
353: uchar vline[];
354: uchar pline[];
355: {
356: #if RAINBOW|IBM
357: register uchar *cp1;
358: register uchar *cp2;
359: register int nch;
360:
361: cp1 = &vline[0]; /* Use fast video. */
362: cp2 = &pline[0];
363: putline(row+1, 1, cp1);
364: nch = term.t_ncol;
365: do {
366: *cp2 = *cp1;
367: ++cp2;
368: ++cp1;
369: } while (--nch);
370: #else
371: register uchar *cp1;
372: register uchar *cp2;
373: register uchar *cp3;
374: register uchar *cp4;
375: register uchar *cp5;
376: register int nbflag;
377:
378: cp1 = &vline[0]; /* Compute left match. */
379: cp2 = &pline[0];
380: while (cp1!=&vline[term.t_ncol] && cp1[0]==cp2[0]) {
381: ++cp1;
382: ++cp2;
383: }
384: /* This can still happen, even though we only call this routine */
385: /* on changed lines. A hard update is always done when a line */
386: /* splits, a massive change is done, or a buffer is displayed */
387: /* twice. This optimizes out most of the excess updating. A lot */
388: /* of computes are used, but these tend to be hard operations */
389: /* that do a lot of update, so I don't really care. */
390: if (cp1 == &vline[term.t_ncol]) /* All equal. */
391: return;
392: nbflag = FALSE;
393: cp3 = &vline[term.t_ncol]; /* Compute right match. */
394: cp4 = &pline[term.t_ncol];
395: while (cp3[-1] == cp4[-1]) {
396: --cp3;
397: --cp4;
398: if (cp3[0] != ' ') /* Note if any nonblank */
399: nbflag = TRUE; /* in right match. */
400: }
401: cp5 = cp3;
402: if (nbflag == FALSE) { /* Erase to EOL ? */
403: while (cp5!=cp1 && cp5[-1]==' ')
404: --cp5;
405: if (cp3-cp5 <= 3) /* Use only if erase is */
406: cp5 = cp3; /* fewer characters. */
407: }
408: movecursor(row, (int)(cp1-&vline[0])); /* Go to start of line. */
409: while (cp1 != cp5) { /* Ordinary. */
410: tputc(*cp1);
411: ++ttcol;
412: *cp2++ = *cp1++;
413: }
414: if (cp5 != cp3) { /* Erase. */
415: teeol();
416: while (cp1 != cp3)
417: *cp2++ = *cp1++;
418: }
419: #endif
420: }
421:
422: /*
423: * Redisplay the mode line for the window pointed to by the "wp".
424: * This is the only routine that has any idea of how the modeline is formatted.
425: * You can change the modeline format by hacking at this routine.
426: * Called by "update" any time there is a dirty window.
427: */
428: modeline(wp)
429: register WINDOW *wp;
430: {
431: register uchar *cp;
432: register int c;
433: register int n;
434: register BUFFER *bp;
435:
436: n = wp->w_toprow+wp->w_ntrows; /* Location. */
437: vscreen[n]->v_flag |= VFCHG; /* Redraw next time. */
438: vtmove(n, 0); /* Seek to right line. */
439: vtputc('-');
440: bp = wp->w_bufp;
441: if ((bp->b_flag&BFCHG) != 0) /* "*" if changed. */
442: vtputc('*');
443: else
444: vtputc('-');
445: n = 2;
446: cp = PROMPT; /* Buffer name. */
447: while ((c = *cp++) != 0) {
448: vtputc(c);
449: ++n;
450: }
451: cp = &bp->b_bname[0];
452: while ((c = *cp++) != 0) {
453: vtputc(c);
454: ++n;
455: }
456: vtputc(' ');
457: ++n;
458: if (bp->b_fname[0] != 0) { /* File name. */
459: #if LIBHELP
460: if (bp->b_flag & BFHELP)
461: cp = "- Subject: ";
462: else
463: #endif
464: cp = "-- File: ";
465: while ((c = *cp++) != 0) {
466: vtputc(c);
467: ++n;
468: }
469: cp = &bp->b_fname[0];
470: while ((c = *cp++) != 0) {
471: vtputc(c);
472: ++n;
473: }
474: vtputc(' ');
475: ++n;
476: }
477: #if WFDEBUG
478: vtputc('-');
479: vtputc((wp->w_flag&WFMODE)!=0 ? 'M' : '-');
480: vtputc((wp->w_flag&WFHARD)!=0 ? 'H' : '-');
481: vtputc((wp->w_flag&WFEDIT)!=0 ? 'E' : '-');
482: vtputc((wp->w_flag&WFMOVE)!=0 ? 'V' : '-');
483: vtputc((wp->w_flag&WFFORCE)!=0 ? 'F' : '-');
484: n += 6;
485: #endif
486: while (n < term.t_ncol) { /* Pad to full width. */
487: vtputc('-');
488: ++n;
489: }
490: }
491:
492: /*
493: * Send a command to the terminal to move the hardware cursor to row "row"
494: * and column "col". The row and column arguments are origin 0.
495: * Optimize out random calls. Update "ttrow" and "ttcol".
496: */
497: movecursor(row, col)
498: {
499: if (row!=ttrow || col!=ttcol) {
500: ttrow = row;
501: ttcol = col;
502: tmove(row, col);
503: }
504: }
505:
506: /*
507: * Erase the message line.
508: * This is a special routine because the message line is not considered to be
509: * part of the virtual screen. It always works immediately; the terminal
510: * buffer is flushed via a call to the flusher.
511: */
512: mlerase()
513: {
514: movecursor(term.t_nrow, 0);
515: teeol();
516: tflush();
517: mpresf = FALSE;
518: }
519:
520: /*
521: * Ask a yes or no question in the message line.
522: * Return either TRUE, FALSE, or ABORT. The ABORT status is returned
523: * if the user bumps out of the question with a ^G.
524: * Used any time a confirmation is required.
525: */
526: mlyesno(prompt)
527: uchar *prompt;
528: {
529: register int s;
530: uchar buf[64];
531:
532: for (;;) {
533: strcpy(buf, prompt);
534: strcat(buf, " [y/n]? ");
535: s = mlreply(buf, buf, sizeof(buf));
536: if (s == ABORT)
537: return (ABORT);
538: if (s != FALSE) {
539: if (buf[0]=='y' || buf[0]=='Y')
540: return (TRUE);
541: if (buf[0]=='n' || buf[0]=='N')
542: return (FALSE);
543: }
544: }
545: }
546:
547: /*
548: * Write a prompt into the message line, then read back a response.
549: * Keep track of the physical position of the cursor.
550: * If we are in a keyboard macro throw the prompt away, and return
551: * the remembered response. This lets macros run at full speed.
552: * The reply is always terminated by a carriage return.
553: * Handle erase, kill, and abort keys.
554: */
555: mlreply(prompt, buf, nbuf)
556: uchar *prompt;
557: uchar *buf;
558: {
559: register int cpos;
560: register int i;
561: register int c;
562:
563: cpos = 0;
564: if (kbdmop != NULL) {
565: while ((c = *kbdmop++) != '\0')
566: buf[cpos++] = c;
567: buf[cpos] = 0;
568: if (buf[0] == 0)
569: return (FALSE);
570: return (TRUE);
571: }
572: mlwrite(prompt);
573: for (;;) {
574: c = tgetc();
575: switch (c) {
576: case 0x0D: /* Return, end of line */
577: buf[cpos++] = 0;
578: if (kbdmip != NULL) {
579: if ((kbdmip+cpos) > (kbdm + ((NKBDM - 3)/2))) {
580: ctrlg(FALSE, 0);
581: tflush();
582: return (ABORT);
583: }
584: for (i=0; i<cpos; ++i)
585: *kbdmip++ = buf[i];
586: }
587: tputc('\r');
588: ttcol = 0;
589: tflush();
590: if (buf[0] == 0)
591: return (FALSE);
592: return (TRUE);
593:
594: case 0x07: /* Bell, abort */
595: tputc('^');
596: tputc('G');
597: ttcol += 2;
598: ctrlg(FALSE, 0);
599: tflush();
600: return (ABORT);
601:
602: case 0x7f: /* Rubout, erase */
603: case 0x08: /* Backspace, erase */
604: if (cpos != 0) {
605: tputc('\b');
606: tputc(' ');
607: tputc('\b');
608: --ttcol;
609: if (buf[--cpos] < 0x20) {
610: tputc('\b');
611: tputc(' ');
612: tputc('\b');
613: --ttcol;
614: }
615: tflush();
616: }
617: break;
618:
619: case 0x15: /* C-U, kill */
620: while (cpos != 0) {
621: tputc('\b');
622: tputc(' ');
623: tputc('\b');
624: --ttcol;
625: if (buf[--cpos] < 0x20) {
626: tputc('\b');
627: tputc(' ');
628: tputc('\b');
629: --ttcol;
630: }
631: }
632: tflush();
633: break;
634:
635: default:
636: if (cpos < nbuf-1) {
637: buf[cpos++] = c;
638: if (c < ' ') {
639: tputc('^');
640: ++ttcol;
641: c ^= 0x40;
642: }
643: tputc(c);
644: ++ttcol;
645: tflush();
646: }
647: }
648: }
649: }
650:
651: /*
652: * Write a message into the message line.
653: * Keep track of the physical cursor position.
654: * A small class of printf like format items is handled.
655: * Assumes the stack grows down; this assumption is made by the "++"
656: * in the argument scan loop. Set the "message line" flag TRUE.
657: */
658: mlwrite(fmt)
659: uchar *fmt;
660: {
661: register int c;
662:
663: uchar buf[NPAT * 2];
664:
665: movecursor(term.t_nrow, 0);
666: sprintf(buf, "%r", &fmt);
667: if (strlen(buf) > (term.t_ncol - 1))
668: buf[term.t_ncol - 1] = 0;
669: for (fmt = buf; c = *fmt; fmt++) {
670: tputc(c);
671: ++ttcol;
672: }
673: teeol();
674: tflush();
675: mpresf = TRUE;
676: }
677:
678: /*
679: * Write out a string.
680: * Update the physical cursor position. This assumes that the characters in the
681: * string all have width "1"; if this is not the case things will get screwed up
682: * a little.
683: */
684: mlputs(s)
685: register uchar *s;
686: {
687: register int c;
688:
689: while ((c = *s++) != 0) {
690: tputc(c);
691: ++ttcol;
692: }
693: }
694:
695: /*
696: * find the logical cursor. Return line from top of
697: * window.
698: */
699: locatecursor(what)
700: LINE *what;
701: {
702: int row;
703: LINE *clp;
704:
705: row = 0;
706: for (clp = curwp->w_linep; clp != what; clp = lforw(clp)) {
707: if ((clp == curbp->b_linep) || (row > curwp->w_ntrows))
708: return (-1);
709: row++;
710: }
711: return (row + curwp->w_toprow);
712: }
713:
714: /*
715: * turn LINE and char pointer into display col.
716: */
717: truecol(clp, col)
718: LINE *clp;
719: {
720: register int i, tcol;
721: unsigned c;
722:
723: for (i = tcol = 0; i < col; i++) {
724: c = lgetc(clp, i);
725: switch (dblchr(c)) {
726: case 2:
727: taber(tcol);
728: break;
729: case 1:
730: tcol++;
731: }
732: tcol++;
733: }
734: if (tcol >= term.t_ncol) /* too far out */
735: tcol = term.t_ncol - 1;
736: return (tcol);
737: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.