/************************************************************************ * This program is Copyright (C) 1986 by Jonathan Payne. JOVE is * * provided to you without charge, and with no warranty. You may give * * away copies of JOVE, including sources, provided that this notice is * * included in all the files. * ************************************************************************/ /* * Modified: Sep-Oct 1988, by Tom Hageman [TRH]: * * extensive fine-tuning; * * fixed cursor boundary bugs: invariants now: * - cursend points one beyond last valid position in current line, * - screenline.s_length points one beyond last modified position in line * * Ins/Del line: in-place swap of screen lines allows us to get rid * of Savelines array. * * explicit handling of standout mode lines (see also disp.c), instead * of ORing 8th bit if standout. This both facititates transition to * 8 bit characters (some day...), AND alleviates the XS glitch since * standout lines are explicitly erased. And since mode lines are the * only place where standout is used, no real harm is done. This also * allows us to collect the largely duplicate code of swrite() and * BufSwrite() in a single routine. * * Dec 1988 [TRH]: * revised cursor addressing (tabs in particular) * Feb 1990 [TRH]: * add color */ #define NO_PROCDECL /* kludge for teensy pdp11 compiler */ #include "jove.h" RCS("$Id: screen.c,v 14.32.0.9 1994/05/22 16:21:16 tom Exp tom $") #include "io.h" #include "ctype.h" #define Extern public /* to define the variables in "screen.h" */ #include "screen.h" #include "termcap.h" void no_op() { /* dummy routine */ } /* These point to functions that make the terminal do the actual line insertion/deletion, or to `no_op' if I/D line is not supported. */ void (*TTins_line)__(( int _(top), int _(bottom), int _(num) )) = no_op, (*TTdel_line)__(( int _(top), int _(bottom), int _(num) )) = no_op; /* Output a character to the terminal at the current (virtual) cursor pos. Called whenever a difference is detected between the desired and current screen. This is called through the pointer `TTputc', so that we can setup various "delayed-output" actions that are to be executed only if a screen update is actually needed, by setting `TTputc' to another function. This all grew rather convoluted over time, so here is an overview of functions that can hook into `TTputc': dosputc - Set physical cursor to i_pos, output character. COFFputc - Count characters output since the last call to `scursoff'. When a certain (small) limit is reached, turn cursor off. Set up by `scursoff'. #ifdef COLOR COLORputc - Set to requested color if different from `CurrColor'. Set up by `req_color'. #endif SOputc - Start highlighting (originally: standout only.) Set up by `standout'. Each function in this list calls the one listed before it, so `dosputc' is ultimately invoked to do the "real" work. When such a hooked-in function has done its dastardly deeds, it unhooks itself from `TTputc'. {NOTE: the order of this list depends on the order in which `scursoff', `req_color' and `standout' can be invoked in the redisplay process.} */ private void dosputc __(( _PI_(char) _(c) )), COFFputc __(( _PI_(char) _(c) )), #ifdef COLOR COLORputc __(( _PI_(char) _(c) )), #endif SOputc __(( _PI_(char) _(c) )); private void (*TTputc)__(( _PI_(char) _(c) )) = dosputc; DEF_INT( "internal-tabstop", tabstop, V_BASE10|V_CLRSCREEN ) = 8; #ifdef COLOR # ifdef TERMCAP /* Set this if terminal erases a line with a fixed background color, as opposed to using the current background color. Ideally this should be determined from the termcap entry... */ DEF_INT( "color-erase-glitch", ColorEraseGlitch, V_BOOL|V_CLRSCREEN ) _IF(def TERMCAP)_IF(def COLOR)_IF(def PRIVATE) ZERO; /* Set this if terminal attributes affect the current color in an unpredictable way. Ideally this should be determined from the termcap entry... */ DEF_INT( "color-standout-glitch", ColorStandoutGlitch, V_BOOL|V_CLRSCREEN ) _IF(def TERMCAP)_IF(def COLOR)_IF(def PRIVATE) ZERO; # else # ifndef ColorEraseGlitch # define ColorEraseGlitch 0 # endif # ifndef ColorStandoutGlitch # define ColorStandoutGlitch 0 # endif # endif /* Stuff for delayed color setting. */ private int ReqColor; private void set_req_color __((void)); #endif /* COLOR */ #ifdef ID_CHAR private int IN_INSmode ZERO, DClen ZERO, MDClen = 9999, IClen ZERO, MIClen = 9999, IMlen ZERO, EIlen ZERO, CElen ZERO; #endif void make_scr() { register size_t i; register struct screenline *ns; register char *nsp; /* * [TRH] This has been changed. We now allocate one big chunk * for all the data structures and divide it ourselves. * This simplifies our administration somewhat. (and saves code...) */ #ifdef RESHAPING if (DesiredScreen) free((void_*) DesiredScreen); #endif i = LI * (CO + sizeof(struct scrimage) * 2 + sizeof(struct screenline)); if ((nsp = malloc(i)) == NULL) { message("Cannot malloc screen!"); finish(1); } bzero(nsp, i); i = LI * sizeof(struct scrimage); DesiredScreen = (struct scrimage *) nsp; nsp += i; PhysScreen = (struct scrimage *) nsp; nsp += i; Screen = ns = (struct screenline *) nsp; nsp += LI * sizeof(struct screenline); i = LI; do { ns->s_line = nsp; nsp += CO; ns->s_length = nsp; /* End of Line */ ns++; } while (--i); cl_scr(0); } /* * Invalidate current line. */ void v_inval() { register char *cp = cursor; while (cp < cursend) *cp++ = '\0'; Curline->s_length = cp; /* i.e. cursend */ Curimage->s_id = -1; } private void clrline __(( char *_(cp1), char *_(cp2) )); private void clrline(cp1, cp2) register char *cp1, *cp2; { while (cp1 < cp2) *cp1++ = ' '; } #define sputc(c) { if (*cursor != (char)(c)) (*TTputc)(c); cursor++, i_col++; } #define soutputc(c) if (--n <= 0) break; else sputc(c) void cl_eol() { register char *end = Curline->s_length; if (cursor >= end || cursor >= cursend) return; #ifdef COLOR # if 0 /* This optimization can cause the cursor's color to be different from the line's foreground color, which is distracting. */ if (CurrColor <= 0 || BG_COLOR(ReqColor) != BG_COLOR(CurrColor)) { set_color(ReqColor); } # else set_color(ReqColor); # endif #endif #ifdef DUMBTERMS if (!CE # ifdef COLOR || (ColorEraseGlitch && BG_COLOR(CurrColor) != BG_COLOR(DefColor)) # endif ) { /* Ugh. The slow way for dumb terminals. */ register char *savecp = cursor; while (cursor < end) sputc(' '); cursor = savecp; } else /* the next compound statement */ #endif Placur(i_line, i_col), putp(CE), clrline(cursor, end); Curline->s_length = cursor; } void cl_scr(doit) { register struct screenline *sp = Screen; register struct scrimage *phys_p = PhysScreen; register int i = LI; do { clrline(sp->s_line, sp->s_length); sp->s_length = sp->s_line; phys_p->s_id = 0; phys_p->s_flags = 0; #ifdef COLOR phys_p->s_color = 0; #endif sp++; phys_p++; } while (--i); if (doit) { CapCol = CapLine = 0; set_color(0); putpad(CL, LI); updmesg(); } } /* * Clear the line (to eol) if it stands out. * For XS braindamaged terminals erasing is the only * way to get rid of a standout field. Also it makes the * physical screen consistent with our idea of it. * (i.e. a normal blank line). */ void cl_standout() { register struct scrimage *phys_p = Curimage; if (!(phys_p->s_flags & VIS_ATTRIB)) return; /* * Current line stands out. Wipe it out. For "dumb" terminals that * cannot clear to EOL, zero the line image to force output of all * blanks. (though it is improbable that such a terminal has standout:-) */ phys_p->s_flags &= ~VIS_ATTRIB; #ifdef DUMBTERMS if (!CE) v_inval(); #endif #ifdef COLOR if (XS) ReqColor = CurrColor = 0; #endif cl_eol(); } /* * Switch standout/highlight mode on or off. * limitations: only one standout/highlight region per screen line is * supported (sufficient for standout mode lines) * This is all rather hairy due to the (heroic) attempt * to handle XS and SG braindamaged terminals correctly. */ #ifdef SG /* saves some code... */ # define sg SG #endif #if (HIGHLIGHT) private const char *so; #else # define so SO #endif private const char *se; private int so_start; /* turn on SO mode before putting a char to the screen (setup by standout) */ private void SOputc(c) char c; { #ifndef sg register int sg = SG; #endif #ifdef COLOR /* Set color here, i.e., _before_ the character-attribute is set, to accommodate terminals that implement attributes as color-manipulations. (e.g., MS-DOS ANSI.SYS) */ if (ReqColor != CurrColor) { set_req_color(); } #endif if (sg || XS) { Placur(i_line, sg ? so_start : i_col); CapCol += sg; } putp(so); TTputc = COFFputc; /* to turn cursor off after next char. */ COFFputc(c); } void standout(which) int which; { register int len; #ifndef sg register int sg = SG; #endif if (which &= VIS_ATTRIB) { #if (HIGHLIGHT) if (which == HIGHLIGHT) so = BO, se = ME; else /* assume (which == STANDOUT) */ so = SO, #endif se = SE; if (!se) { return; } if (sg) { so_start = i_col; cursend -= (sg + sg); } if ((len = (cursend - cursor)) > 0) { register struct scrimage *phys_p = Curimage; /* * If the line does not stand out, zero the current * line in the screen image to force output of all * standout characters. Also set standout flag in * physical screen line. */ if (!(phys_p->s_flags & which)) { phys_p->s_flags |= which; bzero(cursor, len); } /* * Replace character output routine by our own that * first puts terminal in SO mode. */ TTputc = SOputc; } } else if (!se) { /* nothing to do */ return; } /* {dumb cast for dumb compilers} */ else if ((void_*) TTputc == (void_*) SOputc) { /* we didn't do anything. just restore original routine */ TTputc = dosputc; } else { if (sg || XS) { /* position cursor to the end of the standout field */ Placur(i_line, i_col); CapCol += sg; } putp(se); se = NULL; #ifdef COLOR if (ColorStandoutGlitch) CurrColor = -1; #endif } if (sg) { /* adjust variables for magic cookie */ i_col += sg; cursor += sg; cursend += sg; } } /* * ring a bell N times. Output a visual bell only when enabled, * and this is a "normal" bell (N equals 1). */ void dobell(n) register int n; { register const char *bell = BL; if ((--n <= 0) && True(VisBell) && (bell = VB) == NULL) { #ifndef TINY /* no visual bell cap. so use the emulation */ # ifdef FAST_FLASH flash(); # else /* kludge up a visible bell if the terminal does not have the capability */ static const char Beep[] = "[beep]"; register int at = CapCol; if ((n = (CO - sizeof Beep)) < at) at = n; i_set(CapLine, at); set_color(MK_COLOR(WHITE, RED)); Overtype(Beep); flusho(); DoSit(PDelay); /* NOT SitFor since that redisplays! */ /* This kludge is to ensure restore of SO modeline (and color) */ bzero(cursor - (sizeof Beep - 1), (sizeof Beep - 1)); /* leave further screen restore to redisplay */ # endif /* FAST_FLASH */ return; #else /* TINY */ bell = BL; #endif } do { putp(bell); flusho(); } while (--n >= 0); } /* Cursor visibility */ private int curs_off, curs_count; #define CUROFF_DELAY 1 private void COFFputc(c) char c; { if (--curs_count == 0) { if (!curs_off) curs_off++, putp(VI); TTputc = dosputc; } dosputc(c); } /* * setup to turn off cursor after CUROFF_DELAY characters are written * by swrite. */ void scursoff() { if (VI) { curs_count = CUROFF_DELAY + 1; TTputc = COFFputc; } } /* turn cursor off immediately (flush always). */ void cursoff() { if (!curs_off) curs_off++, putp(VI); flusho(); } /* turn cursor on immediately, always flush (Also cancels delayed cursoff). */ void curson() { TTputc = dosputc; if (curs_off) curs_off = NO, putp(VS); flusho(); } /* move cursor to lower-left corner */ void curstoLL() { Placur(ILI, 0); } /* Output one character at the current position */ private void dosputc(c) register char c; { register File *screen = stdout; #ifdef ID_CHAR if (IN_INSmode) INSmode(0); #endif if (i_line != CapLine || i_col != CapCol) Placur(i_line, i_col); #ifdef DUMBTERMS if (UL && (c == '_' && *cursor != ' ')) putstr(" \b"); /* Erase so '_' looks right. */ #endif putc(c, screen); *cursor = c; CapCol++; } /* Write `line' at the current position of `cursor'. Stop when we reach the end of the screen. Aborts if there is a character waiting. */ private int do_swrite __(( const char *_(line), int _(col), int _(visspace), int _(abortable) )); private int do_swrite(line, col, visspace, abortable) const char *line; register int col; int visspace, abortable; { register int c, n, completed = YES; register const char *lp = line; if ((n = cursend - cursor) <= 0) return completed; OkayAbort = NO; /* set by _flush */ while (c = *lp++) { if (OkayAbort && abortable) { OkayAbort = NO; if (InputPending = charp()) { completed = NO; break; } } if (isctrl(c)) { if (c == '\t') { # define nchars c /* alias! */ nchars = TabIncr(col); col += nchars; if (visspace) { soutputc('>'); nchars--; } while (--nchars >= 0) soutputc(' '); if (n <= 0) break; # undef nchars } else { #ifdef NULLCHARS if (c == '\n') c = '\0'; #endif col += 2; soutputc('^'); soutputc(c ^ '@'); } } else { col++; if (c == ' ' && visspace) c = '_'; #ifdef DUMBTERMS else if (c == '~' && Hz) c = '`'; #endif soutputc(c); } } if (n <= 0) { if (*lp || isctrl(c)) c = '!'; sputc(c); } if (cursor > Curline->s_length) Curline->s_length = cursor; return completed; /* Didn't abort */ } int swrite(line, abortable) const char *line; { return do_swrite(line, i_col, NO, abortable); } /* This is for writing a buffer line to the screen. This is to minimize the amount of copying from one buffer to another buffer. This gets the info directly from the disk buffers. */ int BufSwrite(des_p) register struct scrimage *des_p; { register const char *lp = lcontents(des_p->s_lp); register int col = 0; if (*lp == '\0') return YES; if (des_p->s_offset) { register char c; #ifndef TINY swrite("!", NO); /* indicates that part of line is invisible. */ #endif do { if ((c = *lp++) == '\0') return YES; if (!isctrl(c)) ++col; else if (c == '\t') { if ((col += TabIncr(col)) > des_p->s_offset) { col = des_p->s_offset; --lp; break; } } else { col += 2; #ifdef TINY if (col > des_p->s_offset) { des_p->s_offset = col; break; } #endif } } while (col < des_p->s_offset); #ifndef TINY if (col == des_p->s_offset) { if ((c = *lp++) == '\0') return YES; ++col; if (isctrl(c)) { if (c != '\t') { # ifdef NULLCHARS if (c == '\n') c = '\0'; # endif /* the `^' is replaced by `!' */ c ^= '@'; sputc(c); ++col; } else if (col % tabstop != 0) --lp; } } #endif /* !TINY */ } return do_swrite(lp, col, des_p->s_window->w_flags & W_VISSPACE, YES); } /* * This is for Typeout and the like * it invalidates the current screen line, AND does the physical changes * it returns the current cursor position */ int Overtype(str) const char *str; { if (i_line == ILI) updmesg(); else Curimage->s_id = -1; cl_standout(); swrite(str, YES); #ifdef COLOR Curimage->s_color = CurrColor; #endif return i_col; } void i_set(nline, ncol) register int nline, ncol; { register char *lp; lp = (Curline = &Screen[nline])->s_line; Curimage = &PhysScreen[nline]; cursor = &lp[ncol]; cursend = &lp[CO]; /* * [TRH] some terminals scroll when something is written * in rightmost lower position, so this kludge avoids it. */ if (AM && nline == ILI) cursend--; i_line = nline; i_col = ncol; } /* Insert `num' lines at top, but leave all the lines BELOW `bottom' alone (at least they won't look any different when we are done). This changes the screen array AND does the physical changes. */ void v_ins_line(num, top, bottom) { struct screenline save; register struct screenline *dest = &Screen[bottom], *src = &dest[-num], *end = &Screen[top]; /* * Swap the lines in the range [top .. bottom - num] * with those in [top + num .. bottom] */ do { save = *dest; *dest = *src; *src = save; } while (--dest, --src >= end); /* * Now clear the lines in [top .. top + num> */ do { clrline(dest->s_line, dest->s_length); dest->s_length = dest->s_line; } while (--dest >= end); /* * Do the physical change */ (*TTins_line)(top, bottom, num); } /* Delete `num' lines starting at `top' leaving the lines below `bottom' alone. This updates the internal image as well as the physical image. */ void v_del_line(num, top, bottom) int num; { struct screenline save; register struct screenline *dest = &Screen[top], *src = &dest[num], *end = &Screen[bottom]; /* * Swap the lines in the range [top + num .. bottom] * with those in [top .. bottom - num] */ do { save = *dest; *dest = *src; *src = save; } while (++dest, ++src <= end); /* * Now clear the lines in s_line, dest->s_length); dest->s_length = dest->s_line; } while (++dest <= end); /* * Do the physical change */ (*TTdel_line)(top, bottom, num); } /* The cursor optimization happens here. You may decide that this is going too far with cursor optimization, or perhaps it should limit the amount of checking to when the output speed is slow. What ever turns you on ... [TRH June 90] the more arcane cursor optimizations are conditionally compiled on the presence of CURSOPT */ #define home() Placur(0, 0) #define LowLine() putp(LL), CapLine = ILI, CapCol = 0 #define PrintHo() putp(HO), CapLine = CapCol = 0 private void ForMotion __(( int _(destcol) )); private void ForMotion(destcol) register int destcol; { register int nchars = destcol - CapCol; register char *cp = &Screen[CapLine].s_line[CapCol]; /* PRE: destcol != CapCol */ do putch(*cp++); while (--nchars); CapCol = destcol; } private void BackMotion __(( int _(destcol) )); private void BackMotion(destcol) register int destcol; { register int nchars = CapCol - destcol; /* PRE: destcol != CapCol */ if (BC) do putp(BC); while (--nchars); else do putch('\b'); while (--nchars); CapCol = destcol; } #ifdef CURSOPT DEF_INT( "physical-tabstop", phystab, V_BASE10|V_CLRSCREEN ) = 8; _IF(def CURSOPT) /* Tries to move forward using tabs (if possible). It tabs to the closest tabstop which means it may go past 'destcol' and backspace to it. */ private int NumTab __(( int *_(from), int _(to) )); private int NumTab(from, to) /* calc. nr. of tabs to insert, and update start pos. */ register int *from; { register int tabgoal, tabstop, numchars; #if 0 /* [check this before calling NumTab] */ if (!TABS || *from >= to) return 0; #endif if ((tabstop = phystab) <= 0) /* sanity check... */ return 0; tabgoal = to + ((tabstop - 1) >> 1); tabgoal -= (tabgoal % tabstop); /* Don't tab to last place or else it is likely to screw up. */ if (tabgoal >= CO) tabgoal -= tabstop; if (numchars = (tabgoal - *from + tabstop - 1) / tabstop) *from = tabgoal; return numchars; } private int ForNum __(( int _(from), int _(to) )); private int ForNum(from, to) /* calc. cost of using ForTab() */ register int to; { register int ntab, nchr; #if 0 /* [check this before calling ForNum] */ if (!TABS || from >= to) ntab = 0; else #endif ntab = NumTab(&from, to); if ((nchr = to - from) < 0) nchr = -nchr; return ntab + nchr; } private void ForTab __(( int _(destcol) )); private void ForTab(destcol) register int destcol; { register int n; if (TABS && (n = NumTab(&CapCol, destcol))) do { putch('\t'); } while (--n); if ((n = destcol - CapCol) < 0) BackMotion(destcol); else if (n > 0) ForMotion(destcol); } #endif /* CURSOPT */ private void DownMotion __(( int _(destline) )); private void DownMotion(destline) register int destline; { register int nlines = destline - CapLine; while (--nlines >= 0) putch('\n'); CapLine = destline; } private void UpMotion __(( int _(destline) )); private void UpMotion(destline) register int destline; { register int nchars = CapLine - destline; while (--nchars >= 0) putp(UP); CapLine = destline; } private char *Cmstr; #define GoDirect(line, col) (putp(Cmstr), CapLine = (line), CapCol = (col)) #if 0 /* obsolete */ private void GoDirect __(( int _(line), int _(col) )); private void GoDirect(line, col) { putp(Cmstr); CapLine = line; CapCol = col; } #endif #ifdef CURSOPT private void RetTab __(( int _(col) )); private void RetTab(col) { putch('\r'); CapCol = 0; ForTab(col); } private void HomeGo __(( int _(line), int _(col) )); private void HomeGo(line, col) { PrintHo(); DownMotion(line); ForTab(col); } private void BottomUp __(( int _(line), int _(col) )); private void BottomUp(line, col) { LowLine(); UpMotion(line); ForTab(col); } #endif /* CURSOPT */ /* * [TRH] Dec-88, Optimize cursor optimization */ void Placur(line, col) { register int dline, /* Number of lines to move */ dcol, /* Number of columns to move */ Cost, /* cost of movement */ n; #ifdef CURSOPT int RetForward; /* Cost of tabbing from begin of line */ #endif void (*HorProc)__(( int _(col) )), (*VertProc)__(( int _(line) )); #ifdef ID_CHAR int XHorCost = 0, /* Misc addition to cost. */ XDirCost = 0; #else # define XHorCost 0 # define XDirCost 0 #endif /* [TRH] handle right margin */ if (CapCol == CO) if (AM) CapCol = 0, CapLine++; else if (!XN) CapCol--; /* [TRH] we cannot move beyond the end of the line */ if (col >= CO) if (line != ILI) col = 0, line++; else col = CO - 1; #ifdef ID_CHAR if (IN_INSmode) if (MI) XHorCost = (EIlen + IMlen); else XDirCost = (EIlen + IMlen); /* If we're already in insert mode, it is likely that we will want to be in insert mode again, after the insert. */ #endif #ifdef CURSOPT RetForward = -1; #endif /* * Handle horizontal motion. * Forward motion: * 1. Just move forward by typing the right character on the screen. * 2. Try tabbing to the correct place. * Backward motion: * 1. Move backward by printing backspaces. * 2. Try going to the beginning of the line, and then tab. * Whichever is the least costly. */ dline = line - CapLine; if ((dcol = col - CapCol) == 0) { if (dline == 0) /* we are already there */ return; Cost = 0; } else if (dcol > 0) { HorProc = ForMotion; Cost = dcol + XHorCost; #ifdef CURSOPT if (dcol > 1 && TABS) { if ((n = ForNum(CapCol, col) + XHorCost) < Cost) { HorProc = ForTab; Cost = n; } } #endif } else /* (dcol < 0) */ { HorProc = BackMotion; Cost = -dcol + XHorCost; #ifdef CURSOPT if (dcol < -1) { if ((RetForward = (TABS ? ForNum(0, col) : col) + XHorCost) + 1 < Cost) { HorProc = RetTab; Cost = RetForward + 1; } } #endif } /* * Moving vertically is more simple. */ if (dline) { if (dline > 0) { VertProc = DownMotion; Cost += dline; } else { VertProc = UpMotion; Cost -= dline * UPlen; } } if (Cost > 3) { #ifdef CURSOPT void (*DirectProc)__((int _(line), int _(col))) = NULL; /* * Homing first and lowering first are considered * direct motions. * Homing first's total is the sum of the cost of homing * and the sum of tabbing (if possible) to the right. */ if (HO || LL) { if (RetForward < 0) /* not yet calculated? */ RetForward = (TABS ? ForNum(0, col) : col) + XHorCost; if (HO && (n = HOlen + line + RetForward) < Cost) { DirectProc = HomeGo; Cost = n; } if (LL && (n = LLlen + (ILI - line) * UPlen + RetForward) < Cost) { DirectProc = BottomUp; Cost = n; } } #endif /* CURSOPT */ if (CM && (strlen(Cmstr = tgoto(CM, col, line)) + XDirCost) <= Cost) { /* prefer Direct if HV and Direct costs are equal */ #ifdef ID_CHAR if (IN_INSmode && !MI) INSmode(0); #endif GoDirect(line, col); return; } #ifdef CURSOPT if (DirectProc) { # ifdef ID_CHAR if (IN_INSmode) INSmode(0); # endif (*DirectProc)(line, col); return; } #endif /* CURSOPT */ } /* if we get here, we didn't go Direct */ if (dline) (*VertProc)(line); if (dcol) { #ifdef ID_CHAR if (IN_INSmode) /* We may use real characters ... */ INSmode(0); #endif (*HorProc)(col); } } #ifndef FAST_IDLINE #define scr_reg(top, bot) (putp(tgoto(CS, bot, top)), CapCol = CapLine = 1000) private void GENi_lines __(( int _(top), int _(bottom), int _(num) )); private void GENi_lines(top, bottom, num) register int top, num, bottom; { if (CS) { scr_reg(top, bottom); Placur(top, 0); Nputpad(SR, M_SR, num, bottom - top); scr_reg(0, ILI); } else { bottom += 1 - num; Placur(bottom, 0); Nputpad(DL, M_DL, num, ILI - bottom); Placur(top, 0); Nputpad(AL, M_AL, num, ILI - top); } } private void GENd_lines __(( int _(top), int _(bottom), int _(num) )); private void GENd_lines(top, bottom, num) register int top, num, bottom; { if (CS) { scr_reg(top, bottom); Placur(bottom, 0); Nputpad(SF, M_SF, num, bottom - top); scr_reg(0, ILI); } else { Placur(top, 0); Nputpad(DL, M_DL, num, ILI - top); bottom += 1 - num; Placur(bottom, 0); Nputpad(AL, M_AL, num, ILI - bottom); } } #ifdef WIRED_TERMS private void BGi_lines __(( int _(top), int _(bottom), int _(num) )); private void BGi_lines(top, bottom, num) { printf("\033[%d;%dr\033[%dL\033[r", top + 1, bottom + 1, num); CapCol = CapLine = 0; } private void BGd_lines __(( int _(top), int _(bottom), int _(num) )); private void BGd_lines(top, bottom, num) { printf("\033[%d;%dr\033[%dM\033[r", top + 1, bottom + 1, num); CapCol = CapLine = 0; } private void SUNi_lines __(( int _(top), int _(bottom), int _(num) )); private void SUNi_lines(top, bottom, num) { Placur(bottom - num + 1, 0); printf("\033[%dM", num); Placur(top, 0); printf("\033[%dL", num); } private void SUNd_lines __(( int _(top), int _(bottom), int _(num) )); private void SUNd_lines(top, bottom, num) { Placur(top, 0); printf("\033[%dM", num); Placur(bottom + 1 - num, 0); printf("\033[%dL", num); } private void C100i_lines __(( int _(top), int _(bottom), int _(num) )); private void C100i_lines(top, bottom, num) { if (num <= 1) { GENi_lines(top, bottom, num); return; } printf("\033v %c%c", ' ' + bottom + 1, ' ' + CO); CapLine = CapCol = 0; Placur(top, 0); do putpad(AL, ILI - top); while (--num); printf("\033v %c%c", ' ' + LI, ' ' + CO); CapLine = CapCol = 0; } private void C100d_lines __(( int _(top), int _(bottom), int _(num) )); private void C100d_lines(top, bottom, num) { if (num <= 1) { GENd_lines(top, bottom, num); return; } printf("\033v %c%c", ' ' + bottom + 1, ' ' + CO); CapLine = CapCol = 0; Placur(top, 0); do putpad(DL, ILI - top); while (--num); printf("\033v %c%c", ' ' + LI, ' ' + CO); CapLine = CapCol = 0; } #endif /* WIRED_TERMS */ private const struct ID_lookup { const char *ID_name; void (*I_proc)__(( int _(t), int _(b), int _(n) )), /* proc to insert lines */ (*D_proc)__(( int _(t), int _(b), int _(n) )); /* proc to delete lines */ int ID_scr_penalty; } ID_trms[] = { #ifdef WIRED_TERMS "sun", SUNi_lines, SUNd_lines, 15, "bg", BGi_lines, BGd_lines, 0, "c1", C100i_lines, C100d_lines, 10, #endif /* WIRED_TERMS */ NULL, GENi_lines, GENd_lines, 0 /* Default */ }; void IDline_setup(tname) const char *tname; { register const struct ID_lookup *idp = &ID_trms[0]; register int penalty = 0, cps; #ifdef WIRED_TERMS while (!LookingAt(idp->ID_name, tname, 0) && (++idp)->ID_name) ; penalty = idp->ID_scr_penalty; if (idp->ID_name == NULL) /* the next `if' */ #endif /* * apply a penalty to scroll limit for absence of capabilities * (I [TRH] admit the values are rather arbitrary) */ if (CS) { if (!M_SF) penalty = 10; else if (!M_SR) penalty = 5; } else { /* I don't like "dancing mode lines" that much... */ penalty = 15; if (!(M_AL && M_DL)) penalty += 35; } /* scale penalty down for slower terminals */ if ((cps = CharsPerSec) < 480) penalty = penalty * cps / 480; ScrollLimit = 100 - penalty; TTins_line = idp->I_proc; TTdel_line = idp->D_proc; CanScroll++; /* say we can do it */ } #endif /* FAST_IDLINE */ #ifdef ID_CHAR /* From here to the end of the file is code that tries to utilize the insert/delete character feature on some terminals. It is very confusing and not so well written code, AND there is a lot of it. You may want to use the space for something else. */ DEF_INT( "use-i/d-char", UseIC, V_BOOL ) _IF(def ID_CHAR) ZERO; int CanIC ZERO; void disp_opt_init() { # define INIT(len, cap) {if (cap) len = strlen(cap);} # define M_INIT(len, cap) {if (cap) len = strlen(tgoto(cap,0,10));} INIT(DClen, DC); M_INIT(MDClen, M_DC); INIT(IClen, IC); M_INIT(MIClen, M_IC); INIT(IMlen, IM); INIT(CElen, CE); INIT(EIlen, EI); # undef INIT # undef M_INIT if (IC || IM || M_IC) { CanIC = YES; if (CharsPerSec < 480) /* [TRH] slow terminals only */ UseIC = YES; } } void INSmode(on) { if (on) { if (!IN_INSmode) { putp(IM); IN_INSmode++; } } else { if (IN_INSmode) { putp(EI); IN_INSmode = 0; } } } /* This is largely a duplicate of BufSWrite/do_swrite */ void DeTab(s_offset, buf, outbuf, limit, visspace) register const char *buf; char *outbuf; register int limit; { register char *phys_p = outbuf, c; register int pos = 0; #define OkayOut(ch) { if (--limit >= 0) *phys_p++ = ch; } if (s_offset) { #ifndef TINY OkayOut('!'); /* indicates that part of line is invisible. */ #endif do { if ((c = *buf++) == '\0') { phys_p = '\0'; return; } if (!isctrl(c)) ++pos; else if (c == '\t') { if ((pos += TabIncr(pos)) > s_offset) { pos = s_offset; --buf; break; } } else { pos += 2; #ifdef TINY if (pos > s_offset) { s_offset = pos; break; } #endif } } while (pos < s_offset); #ifndef TINY if (pos == s_offset) { if ((c = *buf++) == '\0') { phys_p = '\0'; return; } ++pos; if (isctrl(c)) { if (c != '\t') { /* the `^' is replaced by `!' */ c ^= '@'; OkayOut(c); ++pos; } else if (pos % tabstop != 0) --buf; } } #endif /* !TINY */ } while (c = *buf++) { if (c == '\t') { int nchars = TabIncr(pos); pos += nchars; if (visspace) { OkayOut('>'); --nchars; } while (--nchars >= 0) OkayOut(' '); } else if (isctrl(c)) { OkayOut('^'); #ifdef NULLCHARS if (c == '\n') c = '\0'; #endif OkayOut(c ^ '@'); pos += 2; } else { if (c == ' ' && visspace) c = '_'; #ifdef DUMBTERMS else if (c == '~' && Hz) c = '`'; #endif OkayOut(c); pos++; } if (limit < 0) { phys_p[-1] = '!'; break; } } *phys_p = '\0'; } private NumSimilar __(( const char *_(s), const char *_(t), int _(n) )); private NumSimilar(s, t, num) register const char *s, *t; { register int n = num; while (--n >= 0 && *s++ == *t++) ; return num - n + 1; } private IDcomp __(( const char *_(s), const char *_(t), int _(len) )); private IDcomp(s, t, len) register const char *s, *t; { register int i, num = 0, nonspace = 0; register char c; for (i = 0; i < len; i++) { if ((c = *s++) != *t++) break; if (c != ' ') nonspace++; if (nonspace) num++; } return num; } #define OkayDelete(Saved, num, samelength) \ (((Saved) + (!(samelength) ? CElen : 0)) > min(MDClen, DClen * (num))) #ifndef OkayDelete private OkayDelete __(( int _(Saved), int _(num), int _(samelength) )); private OkayDelete(Saved, num, samelength) { /* If the old and the new are the same length, then we don't * have to clear to end of line. We take that into consideration. */ return ((Saved + (!samelength ? CElen : 0)) > min(MDClen, DClen * num)); } #endif #define OkayInsert(Saved, num) \ ((Saved) > (num) + min((num) * IClen, MIClen) + (IN_INSmode ? 0 : IMlen)) #ifndef OkayInsert private int OkayInsert __(( int _(Saved), int _(num) )); private int OkayInsert(Saved, num) { register int n = num; /* The characters themselves */ if (IC) /* Per character prefixes */ n = min(num * IClen, MIClen); if (IM && !IN_INSmode) { /* Good terminal. Fewer characters in this case */ n += IMlen; } return Saved > n; } #endif private void DelChar __(( int _(lineno), int _(col), int _(num) )); private void DelChar(lineno, col, num) { register char *from, *to; struct screenline *sp = (&Screen[lineno]); Placur(lineno, col); Nputpad(DC, M_DC, num, 1); to = sp->s_line + col; from = to + num; byte_copy(from, to, (int)(sp->s_length - from) + 1); clrline(sp->s_length - num, sp->s_length); sp->s_length -= num; } private void InsChar __(( int _(lineno), int _(col), int _(num), const char *_(new) )); private void InsChar(lineno, col, num, new) const char *new; { register char *sp1, *sp2, /* To push over the array. */ *sp3; /* Last character to push over. */ register int i; i_set(lineno, 0); sp2 = Curline->s_length + num; if (sp2 >= cursend) { i_set(lineno, CO - num - 1); cl_eol(); sp2 = cursend - 1; } Curline->s_length = sp2; sp1 = sp2 - num; sp3 = Curline->s_line + col; while (sp1 >= sp3) *sp2-- = *sp1--; new += col; byte_copy(new, sp3, num); /* The internal screen is correct, and now we have to do the physical stuff. */ Placur(lineno, col); if (IM) { if (!IN_INSmode) INSmode(1); } else Nputpad(IC, M_IC, num, 1); for (i = 0; i < num; i++) { putch(new[i]); if (IN_INSmode) putp(IP); } CapCol += num; } /* ID character routines full of special cases and other fun stuff like that. It actually works though ... Returns Non-Zero if you are finished (no differences left). */ IDchar(new, lineno, col) register const char *new; { register const char *old = Screen[lineno].s_line; register int i, j = col, oldlen = (Screen[lineno].s_length - old), newlen = strlen(new), NumSaved; if (!CanIC) /* [TRH] sanity check */ return (UseIC = NO); again: i = j - 1; /* [TRH] to remove tail recursion */ do { if (++i >= oldlen) return (new[i] == 0); if (new[i] == 0) return NO; } while (old[i] == new[i]); for (j = i + 1; j < oldlen && new[j]; j++) { if (new[j] == old[i]) { NumSaved = IDcomp(new + j, old + i, newlen) + NumSimilar(new + i, old + i, j - i); if (OkayInsert(NumSaved, j - i)) { InsChar(lineno, i, j - i, new); oldlen += j - i; goto again; } } } for (j = i + 1; j < oldlen && new[i]; j++) { if (new[i] == old[j]) { NumSaved = IDcomp(new + i, old + j, oldlen - j); if (OkayDelete(NumSaved, j - i, new[oldlen] == 0)) { DelChar(lineno, i, j - i); oldlen -= j - i; goto again; } } } return NO; } #endif /* ID_CHAR */ #ifdef COLOR /* * set_color(color) physically switches to the requested color. * Additionally, req_color(color) first sets up the line for a color change. */ DEF_INT( "use-color", UseColor, V_BOOL|V_CLRSCREEN ) _IF(def COLOR) ZERO; # ifdef TERMCAP char *Colors[2][NCOLORS]; /* for GENcolor. */ private int color_map[2][NCOLORS]; /* color remap for {PARM,FB}color */ private int prev_color[2] = { -1, -1 }; void (*TTcolor)__(( int _(fg_color), int _(bg_color) )) = no_op; void set_color(color) register int color; { register int fg_color, bg_color; if (color == 0) color = DefColor; if (CurrColor == 0) CurrColor = DefColor; if (color == CurrColor) /* fast reject */ return; if (False(UseColor)) return; if (XS && CurrColor >= 0) { CurrColor = color; return; } if (CurrColor < 0) /* fool optimization... */ prev_color[FG] = prev_color[BG] = -1; CurrColor = color; fg_color = FG_COLOR(color); bg_color = BG_COLOR(color); if (fg_color == bg_color) /* make sure they are different */ fg_color ^= WHITE; /* chooses its complementary color */ (*TTcolor)(fg_color, bg_color); } private void GENcolor __(( int _(fg_color), int _(bg_color) )); private void GENcolor(fg_color, bg_color) register int fg_color, bg_color; { if (prev_color[BG] != bg_color || XS) { putp(Colors[BG][bg_color]); prev_color[BG] = bg_color; } if (prev_color[FG] != fg_color || XS) { putp(Colors[FG][fg_color]); prev_color[FG] = fg_color; } } private void PARMcolor __(( int _(fg_color), int _(bg_color) )); private void PARMcolor(fg_color, bg_color) register int fg_color, bg_color; { bg_color = color_map[BG][bg_color]; fg_color = color_map[FG][fg_color]; if (prev_color[BG] != bg_color || prev_color[FG] != fg_color || XS) { putp(tgoto(COlor, bg_color, fg_color)); prev_color[BG] = bg_color; prev_color[FG] = fg_color; } } private void FBcolor __(( int _(fg_color), int _(bg_color) )); private void FBcolor(fg_color, bg_color) register int fg_color, bg_color; { bg_color = color_map[BG][bg_color]; fg_color = color_map[FG][fg_color]; if (prev_color[BG] != bg_color || XS) { putp(tgoto(CObg, 0, bg_color)); prev_color[BG] = bg_color; } if (prev_color[FG] != fg_color || XS) { putp(tgoto(COfg, 0, fg_color)); prev_color[FG] = fg_color; } } private void PAIRcolor __(( int _(fg_color), int _(bg_color) )); private void PAIRcolor(fg_color, bg_color) int fg_color, bg_color; { /* * assume default color settings i.e. black background except when * text color is black. */ if (fg_color != BLACK) CurrColor = MK_COLOR(fg_color, BLACK); /* * map ANSI to PAIR color: only black and white are interchanged. */ if (fg_color == WHITE || fg_color == BLACK) fg_color ^= WHITE; putp(tgoto(COpair, 0, fg_color)); } private void HPcolor __(( int _(fg_color), int _(bg_color) )); private void HPcolor(fg_color, bg_color) int fg_color, bg_color; { /* * HP terminals have 8 default "color pairs". * Although it is possible to change the colors assigned to * any color pair, this is rather involved (and we would have to * restore the default on exit) so we select a pair according to * foreground color only. */ /* * assume default color settings i.e. black background except when * text color is black. */ if (fg_color != BLACK) CurrColor = MK_COLOR(fg_color, BLACK); /* * map ANSI to HP color: only black and white are interchanged. */ if (fg_color == WHITE || fg_color == BLACK) fg_color ^= WHITE; printf("\33&v%dS", fg_color); } private void ANSIcolor __(( int _(fg_color), int _(bg_color) )); private void ANSIcolor(fg_color, bg_color) register int fg_color, bg_color; { static char Fmt0[] = "\33[%d", Fmt1[] = ";%d"; register char *fmt = Fmt0; if (prev_color[FG] != fg_color) { prev_color[FG] = fg_color; printf(fmt, 30 + fg_color); fmt = Fmt1; } if (prev_color[BG] != bg_color) { prev_color[BG] = bg_color; printf(fmt, 40 + bg_color); fmt = NULL; } if (fmt != Fmt0) /* i.e. ANY color sent */ putch('m'); } /* determine terminal type from CM capability */ private struct COLOR_lookup { char *C_CMid; void (*C_proc)__(( int _(fg_color), int _(bg_color) )); } COLOR_trms[] = { "[0-9.*]*\33&a", HPcolor, "[0-9.*]*\33\\[", ANSIcolor, }; #define NCOLOR_TRMS (sizeof COLOR_trms / sizeof COLOR_trms[0]) private void init_colormap __(( const char *_(desc), int *_(map) )); private void init_colormap(desc, map) const char *desc; register int *map; { register const char *s; register int n = 0; register int *end = &map[NCOLORS]; /* Interpret colormap description, if any. */ if (s = desc) do { if (isdigit(*s)) { n = 0; do { n = n * 10 + *s++ - '0'; } while (isdigit(*s)); } *map++ = n++; } while (*s++ == ',' && map < end); /* now fill in the remainder of the color map. */ while (map < end) *map++ = n++; } void COLOR_setup(tname) const char *tname; { if (Colors[FG][0]) TTcolor = GENcolor, ++UseColor; else if (COlor) TTcolor = PARMcolor, ++UseColor; else if (COfg && CObg) TTcolor = FBcolor, ++UseColor; else if (COpair) TTcolor = PAIRcolor, ++UseColor; else if (CM) { register struct COLOR_lookup *p = COLOR_trms; do { if (LookingAt(p->C_CMid, CM, 0)) { TTcolor = p->C_proc/*, ++UseColor*/; break; } } while (++p < &COLOR_trms[NCOLOR_TRMS]); } init_colormap(CObgmap, color_map[BG]); init_colormap(COfgmap, color_map[FG]); if (XS) ColorEraseGlitch = YES; } # endif /* TERMCAP */ private int color_col; void req_color(color) register int color; { register int col; if (False(UseColor)) return; col = -1; /* means don't set cursor. */ /* Is color of current line changed? */ if (Curimage->s_color != color) { v_inval(); if (XS) { CurrColor = -1; } col = i_col; } color_col = col; ReqColor = color; TTputc = COLORputc; } private void set_req_color() { register int col; if ((col = color_col) >= 0) Placur(i_line, col); set_color(ReqColor); } private void COLORputc(c) char c; { if (ReqColor != CurrColor) { set_req_color(); } TTputc = COFFputc; COFFputc(c); } #endif /* COLOR */ /*====================================================================== * $Log: screen.c,v $ * Revision 14.32.0.9 1994/05/22 16:21:16 tom * (cl_eol): disable color optimization that had undesirable side-effects on * the cursor's color; (SOputc): remove redundant COLORputc. * * Revision 14.32.0.8 1993/11/05 17:21:09 tom * (SOputc): add color hack; * (set_req_color): new function, factored out of COLORputc() for above hack. * * Revision 14.32.0.3 1993/07/28 15:59:39 tom * fix lazy COLOR mode *right* (finally); document what the hell is going on;-) * (use-color): add clear-screen flag; * (COLOR_setup): don't enable use-color for builtin maybe-color terminals. * * Revision 14.32.0.2 1993/07/15 00:24:17 tom * (set_color): `CurrColor' defaults to `DefColor' if 0; * (req_color): remember current column, in `color_col'; * (COLORputc): set cursor to `color_col' before changing color. * * Revision 14.32 1993/05/19 01:51:43 tom * (color-erase-glitch, color-standout-glitch): new terminal-descriptive vars; * optimize color setting. * * Revision 14.31 1993/02/11 01:04:10 tom * some cosmetic surgery. * * Revision 14.30 1993/02/05 00:55:22 tom * cleanup whitespace; some random optimizations; fix bug in [beep]. * * Revision 14.28 1992/10/24 01:24:23 tom * convert to "port{ansi,defs}.h" conventions; add default-color and * color-remap support. * * Revision 14.26 1992/08/26 23:56:58 tom * add RCS directives. * */