/* * Scroll the screen up cnt lines physically. * If doclr is true, do a clear eol if the terminal * has standout (to prevent it from scrolling up) */ void vmoveitup(register int cnt, int doclr) { if (cnt == 0) return; #ifdef ADEBUG if (trace) tfixnl(), fprintf(trace, "vmoveitup(%d)\n", cnt); #endif if (doclr && (SO || SE)) vclrech(0); if (SF) { destline = WECHO; destcol = (NONL ? 0 : outcol % WCOLS); fgoto(); while (cnt > 0) vputp(SF, 0), cnt--; return; } destline = WECHO + cnt; destcol = (NONL ? 0 : outcol % WCOLS); fgoto(); if (state == ONEOPEN || state == HARDOPEN) { outline = destline = 0; vclrcell(vtube[0], WCOLS); } }
/* * Display a new line at physical line p, returning * the depth of the newly displayed line. We may decide * to expand the window on an intelligent terminal if it is * less than a full screen by deleting a line above the top of the * window before doing an insert line to keep all the good text * on the screen in which case the line may actually end up * somewhere other than line p. */ void vopen(line *tp, int p) { register int cnt; register struct vlinfo *vp, *vpc; #ifdef ADEBUG if (trace != NULL) tfixnl(), fprintf(trace, "vopen(%d, %d)\n", lineno(tp), p); #endif if (state != VISUAL) { if (vcnt) if (hold & HOLDROL) vup1(); else vclean(); /* * Forget all that we once knew. */ vcnt = vcline = 0; p = WBOT; LASTLINE = WBOT + 1; state = bastate; WTOP = basWTOP; WLINES = basWLINES; } vpc = &vlinfo[vcline]; for (vp = &vlinfo[vcnt]; vp >= vpc; vp--) vlcopy(vp[1], vp[0]); vcnt++; if (Pline == numbline) /* * Dirtying all the lines is rather inefficient * internally, but number mode is used rarely * and so its not worth optimizing. */ vdirty(vcline+1, WECHO); getline(*tp); /* * If we are opening at the top of the window, can try a window * expansion at the top. */ if (state == VISUAL && vcline == 0 && vcnt > 1 && p > ZERO) { cnt = p + vdepth() - LINE(1); if (cnt > 0) { p -= cnt; if (p < ZERO) p = ZERO; WTOP = p; WLINES = WBOT - WTOP + 1; } } vpc->vliny = p, vpc->vdepth = 0, vpc->vflags = 0; cnt = vreopen(p, lineno(tp), vcline); if (vcline + 1 == vcnt) LINE(vcnt) = LINE(vcline) + cnt; }
/* * Subtract (logically) cnt physical lines from the * displayed position of lines starting with line l. */ void vcloseup(int l, register int cnt) { register int i; #ifdef ADEBUG if (trace) tfixnl(), fprintf(trace, "vcloseup(%d, %d)\n", l, cnt); #endif for (i = l + 1; i <= vcnt; i++) LINE(i) -= cnt; }
/* * Deal with the screen, clearing, cursor positioning, putting characters * into the screen image, and deleting characters. * Really hard stuff here is utilizing insert character operations * on intelligent terminals which differs widely from terminal to terminal. */ void vclear(void) { #ifdef ADEBUG if (trace) tfixnl(), fprintf(trace, "------\nvclear\n"); #endif tputs(CL, TLINES, putch); destcol = 0; outcol = 0; destline = 0; outline = 0; if (inopen) vclrcell(vtube0, WCOLS * (WECHO - ZERO + 1)); }
/* * Deal with the screen, clearing, cursor positioning, putting characters * into the screen image, and deleting characters. * Really hard stuff here is utilizing insert character operations * on intelligent terminals which differs widely from terminal to terminal. */ void vclear(void) { #ifdef TRACE if (trace) tfixnl(), fprintf(trace, "------\nvclear, clear_screen '%s'\n", clear_screen); #endif tputs(clear_screen, lines, putch); destcol = 0; outcol = 0; destline = 0; outline = 0; if (inopen) vclrbyte(vtube0, WCOLS * (WECHO - ZERO + 1)); }
/* * Discard logical lines due to physical wandering off the screen. */ void vscrap(void) { register int i, j; #ifdef ADEBUG if (trace) tfixnl(), fprintf(trace, "vscrap\n"), tvliny(); #endif if (splitw) return; if (vcnt && WBOT != WECHO && LINE(0) < WTOP && LINE(0) >= ZERO) { WTOP = LINE(0); WLINES = WBOT - WTOP + 1; } for (j = 0; j < vcnt; j++) if (LINE(j) >= WTOP) { if (j == 0) break; /* * Discard the first j physical lines off the top. */ vcnt -= j, vcline -= j; for (i = 0; i <= vcnt; i++) vlcopy(vlinfo[i], vlinfo[i + j]); break; } /* * Discard lines off the bottom. */ if (vcnt) { for (j = 0; j <= vcnt; j++) if (LINE(j) > WBOT || LINE(j) + DEPTH(j) - 1 > WBOT) { vcnt = j; break; } LASTLINE = LINE(vcnt-1) + DEPTH(vcnt-1); } #ifdef ADEBUG if (trace) tvliny(); #endif /* * May have no lines! */ }
/* * Roll the screen up logically and physically * so that line dl is the bottom line on the screen. */ void vrollup(int dl) { register int cnt; register int dc = destcol; #ifdef ADEBUG if (trace) tfixnl(), fprintf(trace, "vrollup(%d)\n", dl); #endif cnt = dl - (splitw ? WECHO : WBOT); if (splitw && (state == VISUAL || state == CRTOPEN)) holdupd = 1; vmoveitup(cnt, 1); vscroll(cnt); destline = dl - cnt, destcol = dc; }
/* * Do the real work in deleting cnt lines starting at line p from * the display. First affected line is line l. */ void vdellin(int p, int cnt, int l) { register int i; if (cnt == 0) return; if (DL == NOSTR || cnt < 0) { /* * Can't do it; just remember that line l is munged. */ FLAGS(l) |= VDIRT; return; } #ifdef ADEBUG if (trace) tfixnl(), fprintf(trace, "vdellin(%d, %d, %d)\n", p, cnt, l); #endif /* * Send the deletes to the screen and then adjust logical * and physical internal data structures. */ vgoto(p, 0); if (DL_PARM && (cnt>1 || *DL==0)) { vputp(tgoto(DL_PARM, p, cnt), WECHO-p); } else if (xCS && *DL==0) { /* vt100: fake DL by changing scrolling region */ vputp(SC, 1); /* Save since xCS homes stupid cursor */ vputp(tgoto(xCS, TLINES-1, p), 1); vputp(tgoto(CM, 0, TLINES-1), 1);/* Go to lower left corner */ for (i=0; i<cnt; i++) /* .. and scroll cnt times */ putch('\n'); /* should check NL too */ vputp(tgoto(xCS, TLINES-1, 0), 1);/* restore scrolling region */ vputp(RC, 1); /* put cursor back */ } else { for (i = 0; i < cnt; i++) vputp(DL, WECHO - p); } vadjDL(p, cnt); vcloseup(l, cnt); }
void tvliny(void) { register int i; if (!trace) return; tfixnl(); fprintf(trace, "vcnt = %d, vcline = %d, vliny = ", vcnt, vcline); for (i = 0; i <= vcnt; i++) { fprintf(trace, "%d", LINE(i)); if (FLAGS(i) & VDIRT) fprintf(trace, "*"); if (DEPTH(i) != 1) fprintf(trace, "<%d>", DEPTH(i)); if (i < vcnt) fprintf(trace, " "); } fprintf(trace, "\n"); }
/* * Logically open up after line l, cnt of them. * We need to know if it was done ``physically'' since in this * case we accept what the hardware gives us. If we have to do * it ourselves (brute force) we will squish out @ lines in the process * if this will save us work. */ void vopenup(int cnt, int could, int l) { register struct vlinfo *vc = &vlinfo[l + 1]; register struct vlinfo *ve = &vlinfo[vcnt]; #ifdef ADEBUG if (trace) tfixnl(), fprintf(trace, "vopenup(%d, %d, %d)\n", cnt, could, l); #endif if (could) /* * This will push @ lines down the screen, * just as the hardware did. Since the default * for intelligent terminals is to never have @ * lines on the screen, this should never happen, * and the code makes no special effort to be nice in this * case, e.g. squishing out the @ lines by delete lines * before doing append lines. */ for (; vc <= ve; vc++) vc->vliny += cnt; else { /* * Will have to clean up brute force eventually, * so push the line data around as little as possible. */ vc->vliny += cnt, vc->vflags |= VDIRT; while (vc < ve) { register int i = vc->vliny + vc->vdepth; vc++; if (i <= vc->vliny) break; vc->vliny = i, vc->vflags |= VDIRT; } } vscrap(); }
/* * Adjust internal physical screen image to account for deleted lines. */ void vadjDL(int p, int cnt) { cell *tlines[TUBELINES]; register int from, to; #ifdef ADEBUG if (trace) tfixnl(), fprintf(trace, "vadjDL(%d, %d)\n", p, cnt); #endif /* * Would like to use structured assignment but early * v7 compiler (released with phototypesetter for v6) * can't hack it. */ copy(tlines, vtube, sizeof vtube); /*SASSIGN*/ for (from = p + cnt, to = p; from <= WECHO; from++, to++) vtube[to] = tlines[from]; for (from = p; to <= WECHO; from++, to++) { vtube[to] = tlines[from]; vclrcell(vtube[to], WCOLS); } }
/* * Adjust data structure internally to account for insertion of * blank lines on the screen. */ void vadjAL(int p, int cnt) { cell *tlines[TUBELINES]; register int from, to; #ifdef ADEBUG if (trace) tfixnl(), fprintf(trace, "vadjal(%d, %d)\n", p, cnt); #endif copy(tlines, vtube, sizeof vtube); /*SASSIGN*/ for (from = p, to = p + cnt; to <= WECHO; from++, to++) vtube[to] = tlines[from]; for (to = p; from <= WECHO; from++, to++) { vtube[to] = tlines[from]; vclrcell(vtube[to], WCOLS); } /* * Have to clear the echo area since its contents aren't * necessarily consistent with the rest of the display. */ vclrech(0); }
/* * Delete display positions stcol through endcol. * Amount of use of special terminal features here is limited. */ void physdc(int stcol, int endcol) { register cell *tp, *up; cell *tpe = NULL; register int i; register int nc = endcol - stcol; #ifdef IDEBUG if (trace) tfixnl(), fprintf(trace, "physdc(%d, %d)\n", stcol, endcol); #endif if (!DC || nc <= 0) return; if (IN) { /* * CONCEPT-100 like terminal. * If there are any ``spaces'' in the material to be * deleted, then this is too hard, just retype. */ vprepins(); up = vtube0 + stcol; i = nc; do { if ((*up++ & (QUOTE|TRIM)) == QUOTE) return; } while (--i); i = 2 * nc; do { if (*up == 0 || (*up++ & QUOTE) == QUOTE) return; } while (--i); vgotoCL(stcol); } else { /* * HP like delete mode. * Compute how much text we are moving over by deleting. * If it appears to be faster to just retype * the line, do nothing and that will be done later. * We are assuming 2 output characters per deleted * characters and that clear to end of line is available. */ i = stcol / WCOLS; if (i != endcol / WCOLS) return; i += LINE(vcline); stcol %= WCOLS; endcol %= WCOLS; up = vtube[i]; tp = up + endcol; tpe = up + WCOLS; while (tp < tpe && *tp) tp++; if (tp - (up + stcol) < 2 * nc) return; vgoto(i, stcol); } /* * Go into delete mode and do the actual delete. * Padding is on DC itself. */ godm(); for (i = nc; i > 0; i--) vputp(DC, DEPTH(vcline)); vputp(ED, 0); /* * Straighten up. * With CONCEPT like terminals, characters are pulled left * from first following null. HP like terminals shift rest of * this (single physical) line rigidly. */ if (IN) { up = vtube0 + stcol; tp = vtube0 + endcol; while (i = *tp++) { if ((i & (QUOTE|TRIM)) == QUOTE) break; *up++ = i; } do *up++ = i; while (--nc); } else { copy(up + stcol, up + endcol, (WCOLS - endcol) * sizeof *up); vclrcell(tpe - nc, nc); } }
/* * Get a keystroke, including a ^@. * If an key was returned with ungetkey, that * comes back first. Next comes unread input (e.g. * from repeating commands with .), and finally new * keystrokes. * * The hard work here is in mapping of \ escaped * characters on upper case only terminals. */ static int getbr(void) { char ch; register int c, d; register char *colp; #define BEEHIVE #ifdef BEEHIVE static char Peek2key; #endif extern short slevel, ttyindes; getATTN: if (Peekkey) { c = Peekkey; Peekkey = 0; return (c); } #ifdef BEEHIVE if (Peek2key) { c = Peek2key; Peek2key = 0; return (c); } #endif if (vglobp) { if (*vglobp) return (lastvgk = *vglobp++); lastvgk = 0; return (ESCAPE); } if (vmacp) { if (*vmacp) return(*vmacp++); /* End of a macro or set of nested macros */ vmacp = 0; if (inopen == -1) /* don't screw up undo for esc esc */ vundkind = VMANY; inopen = 1; /* restore old setting now that macro done */ vch_mac = VC_NOTINMAC; } flusho(); again: if (read(slevel == 0 ? 0 : ttyindes, &ch, 1) != 1) { if (errno == EINTR) goto getATTN; error("Input read error"); } c = ch & TRIM; #ifdef BEEHIVE if (XB && slevel==0 && c == ESCAPE) { if (read(0, &Peek2key, 1) != 1) goto getATTN; Peek2key &= TRIM; switch (Peek2key) { case 'C': /* SPOW mode sometimes sends \EC for space */ c = ' '; Peek2key = 0; break; case 'q': /* f2 -> ^C */ c = CTRL('c'); Peek2key = 0; break; case 'p': /* f1 -> esc */ Peek2key = 0; break; } } #endif #ifdef UCVISUAL /* * The algorithm here is that of the UNIX kernel. * See the description in the programmers manual. */ if (UPPERCASE) { if (isupper(c)) c = tolower(c); if (c == '\\') { if (precbksl < 2) precbksl++; if (precbksl == 1) goto again; } else if (precbksl) { d = 0; if (islower(c)) d = toupper(c); else { colp = "({)}!|^~'~"; while ((d = *colp++)) if (d == c) { d = *colp++; break; } else colp++; } if (precbksl == 2) { if (!d) { Peekkey = c; precbksl = 0; c = '\\'; } } else if (d) c = d; else { Peekkey = c; precbksl = 0; c = '\\'; } } if (c != '\\') precbksl = 0; } #endif #ifdef TRACE if (trace) { if (!techoin) { tfixnl(); techoin = 1; fprintf(trace, "*** Input: "); } tracec(c); } #endif lastvgk = 0; return (c); }
/* * Workhorse for rearranging line descriptors on changes. * The idea here is that, starting with line l, cnt lines * have been replaced with newcnt lines. All of these may * be ridiculous, i.e. l may be -1000, cnt 50 and newcnt 0, * since we may be called from an undo after the screen has * moved a lot. Thus we have to be careful. * * Many boundary conditions here. */ void vreplace(int l, int cnt, int newcnt) { register int from, to, i; bool savenote = 0; #ifdef ADEBUG if (trace) { tfixnl(), fprintf(trace, "vreplace(%d, %d, %d)\n", l, cnt, newcnt); tvliny(); } #endif if (l >= vcnt) return; if (l < 0) { if (l + cnt < 0) { /* * Nothing on the screen is relevant. * Settle for redrawing from scratch (later). */ vcnt = 0; return; } /* * Normalize l to top of screen; the add is * really a subtract from cnt since l is negative. */ cnt += l; l = 0; /* * Unseen lines were affect so notify (later). */ savenote++; } /* * These shouldn't happen * but would cause great havoc. */ if (cnt < 0) cnt = 0; if (newcnt < 0) newcnt = 0; /* * Surely worthy of note if more than report * lines were changed. */ if (cnt > value(REPORT) || newcnt > value(REPORT)) savenote++; /* * Same number of lines affeted as on screen, and we * can insert and delete lines. Thus we just type * over them, since otherwise we will push them * slowly off the screen, a clear lose. */ if (cnt == newcnt || vcnt - l == newcnt && AL && DL) { if (cnt > 1 && l + cnt > vcnt) savenote++; vdirty(l, newcnt); } else { /* * Lines are going away, squish them out. */ if (cnt > 0) { /* * If non-displayed lines went away, * always notify. */ if (cnt > 1 && l + cnt > vcnt) savenote++; if (l + cnt >= vcnt) cnt = vcnt - l; else for (from = l + cnt, to = l; from <= vcnt; to++, from++) vlcopy(vlinfo[to], vlinfo[from]); vcnt -= cnt; } /* * Open up space for new lines appearing. * All new lines are piled in the same place, * and will be unpiled by vredraw/vsync, which * inserts lines in front as it unpiles. */ if (newcnt > 0) { /* * Newlines are appearing which may not show, * so notify (this is only approximately correct * when long lines are present). */ if (newcnt > 1 && l + newcnt > vcnt + 1) savenote++; /* * If there will be more lines than fit, then * just throw way the rest of the stuff on the screen. */ if (l + newcnt > WBOT && AL && DL) { vcnt = l; goto skip; } from = vcnt, to = vcnt + newcnt; i = TUBELINES - to; if (i < 0) from += i, to += i; vcnt = to; for (; from >= l; from--, to--) vlcopy(vlinfo[to], vlinfo[from]); for (from = to + 1, to = l; to < l + newcnt && to <= WBOT + 1; to++) { LINE(to) = LINE(from); DEPTH(to) = 0; FLAGS(to) = VDIRT; } } } skip: if (Pline == numbline && cnt != newcnt) /* * When lines positions are shifted, the numbers * will be wrong. */ vdirty(l, WECHO); if (!savenote) notecnt = 0; #ifdef ADEBUG if (trace) tvliny(); #endif }
/* * Insert cnt blank lines before line p, * logically and (if supported) physically. */ void vinslin(register int p, register int cnt, int l) { register int i; bool could = 1; #ifdef ADEBUG if (trace) tfixnl(), fprintf(trace, "vinslin(%d, %d, %d)\n", p, cnt, l); #endif if (p + cnt > WBOT && CD) { /* * Really quick -- clear to end of screen. */ cnt = WECHO + 1 - p; vgoto(p, 0), vputp(CD, cnt); vclrech(1); vadjAL(p, cnt); } else if (SR && p == WTOP && costSR < costAL) { /* * Use reverse scroll mode of the terminal, at * the top of the window. Reverse linefeed works * too, since we only use it from line WTOP. */ for (i = cnt; i > 0; i--) { vgoto(p, 0), vputp(SR, 0); if (i > 1 && (hold & HOLDAT) == 0) putchar('@'); /* * If we are at the top of the screen, and the * terminal retains display above, then we * should try to clear to end of line. * Have to use CE since we don't remember what is * actually on the line. */ if (CE && (DA || p != 0)) vputp(CE, 1); } vadjAL(p, cnt); } else if (AL) { /* * Use insert line. */ vgoto(p, 0); if (AL_PARM && (cnt>1 || *AL==0)) { /* insert cnt lines. Should do @'s too. */ vputp(tgoto(AL_PARM, p, cnt), WECHO+1-p); } else if (xCS && *AL==0) { /* vt100 change scrolling region to fake AL */ vputp(SC, 1); vputp(tgoto(xCS, TLINES-1,p), 1); vputp(RC, 1); /* xCS homes stupid cursor */ for (i=cnt; i>0; i--) vputp(SR, 1); /* should do @'s */ vputp(tgoto(xCS, TLINES-1,0), 1); vputp(RC, 1); /* Once again put it back */ } else { vputp(AL, WECHO + 1 - p); for (i = cnt - 1; i > 0; i--) { vgoto(outline+1, 0); vputp(AL, WECHO + 1 - outline); if ((hold & HOLDAT) == 0) putchar('@'); } } vadjAL(p, cnt); } else could = 0; vopenup(cnt, could, l); }
/* * The guts of a sync. Similar to redraw but * just less ambitous. */ void vsync1(register int p) { register int l; char temp[LBSIZE]; register struct vlinfo *vp = &vlinfo[0]; short oldhold = hold; #ifdef ADEBUG if (trace) tfixnl(), fprintf(trace, "vsync1(%d)\n", p), tvliny(); #endif if (holdupd) { if (holdupd < 3) holdupd = 2; return; } if (state == HARDOPEN || splitw) return; vscrap(); CP(temp, linebuf); if (vcnt == 0) LINE(0) = WTOP; l = 0; while (l < vcnt && vp->vliny < p) l++, vp++; heldech = 0; hold |= HOLDECH; while (p <= WBOT && Peekkey != ATTN) { /* * Want to put a line here if not in visual and first line * or if there are lies left and this line starts before * the current line, or if this line is piled under the * next line (vreplace does this and we undo it). */ if (l == 0 && state != VISUAL || (l < vcnt && (vp->vliny <= p || vp[0].vliny == vp[1].vliny))) { if (l == 0 || vp->vliny < p || (vp->vflags & VDIRT)) { if (l == vcline) strcLIN(temp); else getline(dot[l - vcline]); /* * Be careful that a long line doesn't cause the * screen to shoot up. */ if (l != vcline && (vp->vflags & VDIRT)) { vp->vdepth = vdepth(); vp->vflags &= ~VDIRT; if (p + vp->vdepth - 1 > WBOT) break; } vreopen(p, lineDOT() + (l - vcline), l); } p = vp->vliny + vp->vdepth; vp++; l++; } else /* * A physical line between logical lines, * so we settle for an @ at the beginning. */ vclrlin(p, dot + (l - vcline)), p++; } strcLIN(temp); hold = oldhold; if (heldech) vclrech(0); }
/* * Fully cleanup the screen, leaving no @ lines except at end when * line after last won't completely fit. The routine vsync is * more conservative and much less work on dumb terminals. */ void vredraw(register int p) { register int l; register line *tp; char temp[LBSIZE]; bool anydl = 0; short oldhold = hold; #ifdef ADEBUG if (trace) tfixnl(), fprintf(trace, "vredraw(%d)\n", p), tvliny(); #endif if (holdupd) { holdupd = 3; return; } if (state == HARDOPEN || splitw) return; if (p < 0 /* || p > WECHO */) error(catgets(catd, 1, 221, "Internal error: vredraw")); /* * Trim the ragged edges (lines which are off the screen but * not yet logically discarded), save the current line, and * search for first logical line affected by the redraw. */ vscrap(); CP(temp, linebuf); l = 0; tp = dot - vcline; if (vcnt == 0) LINE(0) = WTOP; while (l < vcnt && LINE(l) < p) l++, tp++; /* * We hold off echo area clearing during the redraw in deference * to a final clear of the echo area at the end if appropriate. */ heldech = 0; hold |= HOLDECH; for (; l < vcnt && Peekkey != ATTN; l++) { if (l == vcline) strcLIN(temp); else getline(*tp); /* * Delete junk between displayed lines. */ if (LINE(l) != LINE(l + 1) && LINE(l) != p) { if (anydl == 0 && DB && CD) { hold = oldhold; vclrech(0); anydl = 1; hold |= HOLDECH; heldech = 0; } vdellin(p, LINE(l) - p, l); } /* * If line image is not know to be up to date, then * redisplay it; else just skip onward. */ LINE(l) = p; if (FLAGS(l) & VDIRT) { DEPTH(l) = vdepth(); if (l != vcline && p + DEPTH(l) - 1 > WBOT) { vscrap(); break; } FLAGS(l) &= ~VDIRT; vreopen(p, lineno(tp), l); p = LINE(l) + DEPTH(l); } else p += DEPTH(l); tp++; } /* * That takes care of lines which were already partially displayed. * Now try to fill the rest of the screen with text. */ if (state == VISUAL && p <= WBOT) { int ovcline = vcline; vcline = l; for (; tp <= dol && Peekkey != ATTN; tp++) { getline(*tp); if (p + vdepth() - 1 > WBOT) break; vopen(tp, p); p += DEPTH(vcline); vcline++; } vcline = ovcline; } /* * Thats all the text we can get on. * Now rest of lines (if any) get either a ~ if they * are past end of file, or an @ if the next line won't fit. */ for (; p <= WBOT && Peekkey != ATTN; p++) vclrlin(p, tp); strcLIN(temp); hold = oldhold; if (heldech) vclrech(0); #ifdef ADEBUG if (trace) tvliny(); #endif }