/* Copyright (c) 1979 Regents of the University of California */
* 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.
tfixnl(), fprintf(trace
, "------\nvclear\n");
vclrbyte(vtube0
, WCOLS
* (WECHO
- ZERO
+ 1));
* Clear a physical display line, high level.
if ((hold
& HOLDAT
) == 0)
putchar(tp
> dol
? ((UPPERCASE
|| HZ
) ? '^' : '~') : '@');
* Clear to the end of the current physical line
destline
+= destcol
/ WCOLS
;
if (destline
< 0 || destline
> WECHO
)
error("Internal error: vclreol");
tp
= vtube
[destline
] + destcol
;
if (IN
&& *tp
|| !ateopr()) {
while (i
> 0 && (j
= *tp
& (QUOTE
|TRIM
))) {
if (j
!= ' ' && (j
& QUOTE
) == 0) {
* If didphys then its been cleared physically (as
* a side effect of a clear to end of display, e.g.)
* so just do it logically.
* If work here is being held off, just remember, in
* heldech, if work needs to be done, don't do anything.
if (!didphys
&& (CD
|| CE
)) {
* If display is retained below, then MUST use CD or CE
* since we don't really know whats out there.
* Vigoto might decide (incorrectly) to do nothing.
vgoto(WECHO
, 0), vputp(CD
? CD
: CE
, 1);
vigoto(WECHO
, 0), vclreol();
vclrbyte(vtube
[WECHO
], WCOLS
);
* Fix the echo area for use, setting
* the state variable splitw so we wont rollup
* when we move the cursor there.
if (state
!= VISUAL
&& state
!= CRTOPEN
) {
vgoto(WECHO
, 0); flusho();
* Put the cursor ``before'' cp.
vgotoCL(value(NUMBER
) << 3);
vgotoCL(column(cp
- 1) - 1);
* Put the cursor ``at'' cp.
if (cp
<= linebuf
&& linebuf
[0] == 0)
vgotoCL(value(NUMBER
) << 3);
* Put the cursor ``after'' cp.
* Fix the cursor to be positioned in the correct place
* Compute the column position implied by the cursor at ``nc'',
* and move the cursor there.
* Move the cursor invisibly, i.e. only remember to do it.
* Move the cursor to the position implied by any previous
* vigoto (or low level hacking with destcol/destline as in readecho).
vgoto(destline
, destcol
);
* Goto column x of the current line.
* Invisible goto column x of current line.
* Move cursor to line y, column x, handling wraparound and scrolling.
* Fold the possibly too large value of x.
error("Internal error: vgoto");
outline
+= outcol
/ WCOLS
;
* In a hardcopy or glass crt open, print the stuff
* implied by a motion, or backspace.
if (state
== HARDOPEN
|| state
== ONEOPEN
) {
error("Line too long for open");
if (x
+ 1 < outcol
- x
|| (outcol
> x
&& !BS
))
tp
= vtube
[WBOT
] + outcol
;
vputc(c
&& (!OS
|| EO
) ? c
: ' '), outcol
++;
* If the destination position implies a scroll, do it.
if (destline
> WBOT
&& (!splitw
|| destline
> WECHO
)) {
* If there really is a motion involved, do it.
* The check here is an optimization based on profiling.
if ((destline
- outline
) * WCOLS
!= destcol
- outcol
) {
* This is the hardest code in the editor, and deals with insert modes
* on different kinds of intelligent terminals. The complexity is due
* to the cross product of three factors:
* 1. Lines may display as more than one segment on the screen.
* 2. There are 2 kinds of intelligent terminal insert modes.
* 3. Tabs squash when you insert characters in front of them,
* in a way in which current intelligent terminals don't handle.
* The two kinds of terminals are typified by the DM2500 or HP2645 for
* one and the CONCEPT-100 or the FOX for the other.
* The first (HP2645) kind has an insert mode where the characters
* fall off the end of the line and the screen is shifted rigidly
* no matter how the display came about.
* The second (CONCEPT-100) kind comes from terminals which are designed
* for forms editing and which distinguish between blanks and ``spaces''
* on the screen, spaces being like blank, but never having had
* and data typed into that screen position (since, e.g. a clear operation
* like clear screen). On these terminals, when you insert a character,
* the characters from where you are to the end of the screen shift
* over till a ``space'' is found, and the null character there gets
* The code here considers the line as consisting of several parts
* the first part is the ``doomed'' part, i.e. a part of the line
* which is being typed over. Next comes some text up to the first
* following tab. The tab is the next segment of the line, and finally
* We have to consider each of these segments and the effect of the
* insertion of a character on them. On terminals like HP2645's we
* must simulate a multi-line insert mode using the primitive one
* line insert mode. If we are inserting in front of a tab, we have
* to either delete characters from the tab or insert white space
* (when the tab reaches a new spot where it gets larger) before we
* insert the new character.
* On a terminal like a CONCEPT our strategy is to make all
* blanks be displayed, while trying to keep the screen having ``spaces''
* for portions of tabs. In this way the terminal hardward does some
* of the hacking for compression of tabs, although this tends to
* disappear as you work on the line and spaces change into blanks.
* There are a number of boundary conditions (like typing just before
* the first following tab) where we can avoid a lot of work. Most
* of them have to be dealt with explicitly because performance is
* much, much worse if we don't.
* A final thing which is hacked here is two flavors of insert mode.
* Datamedia's do this by an insert mode which you enter and leave
* and by having normal motion character operate differently in this
* mode, notably by having a newline insert a line on the screen in
* this mode. This generally means it is unsafe to move around
* the screen ignoring the fact that we are in this mode.
* This is possible on some terminals, and wins big (e.g. HP), so
* we encode this as a ``can move in insert capability'' mi,
* and terminals which have it can do insert mode with much less
* work when tabs are present following the cursor on the current line.
* Routine to expand a tab, calling the normal Outchar routine
* to put out each implied character. Note that we call outchar
* with a QUOTE. We use QUOTE internally to represent a position
* which is part of the expansion of a tab.
register int i
= (LINE(vcline
) - destline
) * WCOLS
+ destcol
;
while (++i
% value(TABSTOP
));
* Variables for insert mode.
int linend
; /* The column position of end of line */
int tabstart
; /* Column of start of first following tab */
int tabend
; /* Column of end of following tabs */
int tabsize
; /* Size of the following tabs */
int tabslack
; /* Number of ``spaces'' in following tabs */
int inssiz
; /* Number of characters to be inserted */
int inscol
; /* Column where insertion is taking place */
int shft
; /* Amount tab expansion shifted rest of line */
int slakused
; /* This much of tabslack will be used up */
* This routine MUST be called before insert mode is run,
* and brings all segments of the current line to the top
* of the screen image buffer so it is easier for us to
register char *cp
= vtube0
;
for (i
= 0; i
< DEPTH(vcline
); i
++) {
vmaktop(LINE(vcline
) + i
, cp
);
for (i
= ZERO
; i
<= WECHO
; i
++)
copy(temp
, vtube
[i
], WCOLS
);
copy(vtube
[i
], vtube
[p
], WCOLS
);
copy(vtube
[p
], temp
, WCOLS
);
* Insert character c at current cursor position.
* Multi-character inserts occur only as a result
* of expansion of tabs (i.e. inssize == 1 except
* for tabs) and code assumes this in several place
if ((!IM
|| !EI
) && ((hold
& HOLDQIK
) || !value(REDRAW
) || value(SLOWOPEN
))) {
* Don't want to try to use terminal
* insert mode, or to try to fake it.
* Just put the character out; the screen
* will probably be wrong but we will fix it later.
if (DEPTH(vcline
) * WCOLS
+ !value(REDRAW
) > destcol
)
* The next line is about to be clobbered
* make space for another segment of this line
* (on an intelligent terminal) or just remember
* that next line was clobbered (on a dumb one
* if we don't care to redraw the tail.
c
= LINE(vcline
) + DEPTH(vcline
);
if (c
< LINE(vcline
+ 1) || c
> WBOT
)
* Compute the number of positions in the line image of the
* current line. This is done from the physical image
* since that is faster. Note that we have no memory
* from insertion to insertion so that routines which use
* us don't have to worry about moving the cursor around.
* Search backwards for a non-null character
* from the end of the displayed line.
i
= WCOLS
* DEPTH(vcline
);
* We insert at a position based on the physical location
inscol
= destcol
+ (destline
- LINE(vcline
)) * WCOLS
;
* Characters inserted from a tab must be
* remembered as being part of a tab, but we can't
* use QUOTE here since we really need to print blanks.
* QUOTE|' ' is the representation of this.
inssiz
= value(TABSTOP
) - inscol
% value(TABSTOP
);
* If the text to be inserted is less than the number
* of doomed positions, then we don't need insert mode,
* rather we can just typeover.
* Have to really do some insertion, thus
* stake out the bounds of the first following
* group of tabs, computing starting position,
* ending position, and the number of ``spaces'' therein
* so we can tell how much it will squish.
for (i
= inscol
; i
< linend
; i
++)
while (tabend
< linend
) {
tabsize
= tabend
- tabstart
;
* For HP's and DM's, e.g. tabslack has no meaning.
fprintf(trace
, "inscol %d, inssiz %d, tabstart %d,\
tabend %d, tabslack %d, linend %d\n", inscol
, inssiz
, tabstart
, tabend
, tabslack
, linend
);
* There are tabs on this line.
* If they need to expand, then the rest of the line
* will have to be shifted over. In this case,
* we will need to make sure there are no ``spaces''
* in the rest of the line (on e.g. CONCEPT-100)
* and then grab another segment on the screen if this
* line is now deeper. We then do the shift
* implied by the insertion.
if (inssiz
>= doomed
+ value(TABSTOP
) - tabstart
% value(TABSTOP
)) {
vneedpos(value(TABSTOP
));
} else if (inssiz
> doomed
)
* No tabs, but line may still get deeper.
vneedpos(inssiz
- doomed
);
* Now put in the inserted characters.
* Now put the cursor in its final resting place.
destcol
= inscol
+ inssiz
;
* Rigidify the rest of the line after the first
* group of following tabs, typing blanks over ``spaces''.
register char *tp
= vtube0
+ tabend
;
for (col
= tabend
; col
< linend
; col
++)
if ((*tp
++ & TRIM
) == 0) {
* We need cnt more positions on this line.
* Open up new space on the screen (this may in fact be a
* On a dumb terminal we may infact redisplay the rest of the
* screen here brute force to keep it pretty.
register int d
= DEPTH(vcline
);
register int rmdr
= d
* WCOLS
- linend
;
e
= LINE(vcline
) + DEPTH(vcline
);
if (e
< LINE(vcline
+ 1)) {
e
= vglitchup(vcline
, d
);
Outchar
= vputchar
; vsync(e
+ 1); Outchar
= vinschar
;
* Do the shift of the next tabstop implied by
* insertion so it expands.
register char *tp
= vtube0
;
* Dumb terminals are easy, we just have
for (i
= tabend
; i
< linend
; i
++)
* CONCEPT-like terminals do most of the work for us,
* we don't have to muck with simulation of multi-line
* insert mode. Some of the shifting may come for free
* also if the tabs don't have enough slack to take up
* all the inserted characters.
slakused
= inssiz
- doomed
;
if (slakused
> tabslack
&& inscol
+ doomed
!= tabstart
) {
i
-= slakused
- tabslack
;
if (i
> 0 && tabend
!= linend
) {
* HP and Datamedia type terminals have to have multi-line
* insert faked. Hack each segment after where we are
* (going backwards to where we are.) We then can
* hack the segment where the end of the first following
for (j
= DEPTH(vcline
) - 1; j
> (tabend
+ shft
) / WCOLS
; j
--) {
up
= tp
+ j
* WCOLS
- shft
;
i
= shft
- (inssiz
- doomed
);
if (i
> 0 && inscol
+ doomed
!= tabstart
) {
tabslack
= inssiz
- doomed
;
* Now do the data moving in the internal screen
* image which is common to all three cases.
* Now do the insert of the characters (finally).
if (tabsize
&& (IM
&& EI
) && inssiz
- doomed
> tabslack
)
* There is a tab out there which will be affected
* by the insertion since there aren't enough doomed
* characters to take up all the insertion and we do
* have insert mode capability.
if (inscol
+ doomed
== tabstart
)
* The end of the doomed characters sits right at the
* start of the tabs, then we don't need to use insert
* mode; the tab has already been expanded as
* appropriate and we can just overwrite it.
* The last really special case to handle is case
* where the tab is just sitting there and doesn't
* have enough slack to let the insertion take
* place without shifting the rest of the line
* over. In this case we have to go out and
* delete some characters of the tab before we start
* or the answer will be wrong, as the rest of the
* line will have been shifted. This code means
* that terminals with only insert chracter (no
* delete character) won't work correctly.
i
= inssiz
- doomed
- tabslack
- slakused
;
for (i
= inssiz
- doomed
- tabslack
; i
> 0; i
--)
vputp(DC
, DEPTH(vcline
));
* Now put out the characters of the actual insertion.
for (i
= inssiz
; i
> 0; i
--) {
* We are a dumb terminal; brute force update
* the rest of the line; this is very much an n^^2 process,
* and totally unreasonable at low speed.
* You asked for it, you get it.
tp
= vtube0
+ inscol
+ doomed
;
for (i
= inscol
+ doomed
; i
< tabstart
; i
++)
vigotoCL(tabstart
+ inssiz
- doomed
);
for (i
= tabsize
- (inssiz
- doomed
) + shft
; i
> 0; i
--)
* On terminals without multi-line
* insert in the hardware, we must go fix the segments
* between the inserted text and the following
* tabs, if they are on different lines.
for (j
= (inscol
+ inssiz
- 1) / WCOLS
+ 1; j
<= (tabstart
+ inssiz
- doomed
- 1) / WCOLS
; j
++) {
* On terminals with multi line inserts,
* life is simpler, just reflect eating of
for (i
= tabsize
- (inssiz
- doomed
); i
>= 0; i
--) {
if ((*--tp
& (QUOTE
|TRIM
)) == QUOTE
) {
if (tabslack
>= slakused
)
* Blank out the shifted positions to be tab positions.
tp
= vtube0
+ tabend
+ shft
;
for (i
= tabsize
- (inssiz
- doomed
) + shft
; i
> 0; i
--)
if ((*--tp
& QUOTE
) == 0)
* Finally, complete the screen image update
* to reflect the insertion.
tp
= vtube0
+ tabstart
; up
= tp
+ inssiz
- doomed
;
for (i
= tabstart
; i
> inscol
+ doomed
; i
--)
for (i
= inssiz
; i
> 0; i
--)
* Go into ``delete mode''. If the
* sequence which goes into delete mode
* is the same as that which goes into insert
* mode, then we are in delete mode already.
* If we are coming out of delete mode, but
* delete and insert mode end with the same sequence,
* it wins to pretend we are now in insert mode,
* since we will likely want to be there again soon
* if we just moved over to delete space from part of
if (strcmp(DM
, IM
) == 0) {
* In and out of insert mode.
* Note that the code here demands that there be
* a string for insert mode (the null string) even
* if the terminal does all insertions a single character
* at a time, since it branches based on whether IM is null.
* Put the character c on the screen at the current cursor position.
* This routine handles wraparound and scrolling and understands not
* to roll when splitw is set, i.e. we are working in the echo area.
* There is a bunch of hacking here dealing with the difference between
* QUOTE, QUOTE|' ', and ' ' for CONCEPT-100 like terminals, and also
* code to deal with terminals which overstrike, including CRT's where
* you can erase overstrikes with some work. CRT's which do underlining
* implicitly which has to be erased (like CONCEPTS) are also handled.
destline
+= destcol
/ WCOLS
;
if (destline
> WBOT
&& (!splitw
|| destline
> WECHO
))
tp
= vtube
[destline
] + destcol
;
* We can get away without printing a space in a number
* of cases, but not always. We get away with doing nothing
* if we are not in insert mode, and not on a CONCEPT-100
* like terminal, and either not in hardcopy open or in hardcopy
* open on a terminal with no overstriking, provided,
* in all cases, that nothing has ever been displayed
if (!insmode
&& !IN
&& (state
!= HARDOPEN
|| OS
) && (*tp
&TRIM
) == 0) {
* When in insert mode, tabs have to expand
* to real, printed blanks.
if ((hold
& HOLDPUPD
) == 0)
* A ``space'' ontop of a part of a tab.
* Now get away with doing nothing if the characters
* are the same, provided we are not in insert mode
* and if we are in hardopen, that the terminal has overstrike.
if (d
== (c
& TRIM
) && !insmode
&& (state
!= HARDOPEN
|| OS
)) {
if ((hold
& HOLDPUPD
) == 0)
* Backwards looking optimization.
* The low level cursor motion routines will use
* a cursor motion right sequence to step 1 character
* right. On, e.g., a DM3025A this is 2 characters
* and printing is noticeably slower at 300 baud.
* Since the low level routines are not allowed to use
* spaces for positioning, we discover the common
* case of a single space here and force a space
if (destcol
== outcol
+ 1 && tp
[-1] == ' ' && outline
== destline
) {
* This is an inline expansion a call to vcsync() dictated
* by high frequency in a profile.
if (outcol
!= destcol
|| outline
!= destline
)
vgoto(destline
, destcol
);
* Deal with terminals which have overstrike.
* We handle erasing general overstrikes, erasing
* underlines on terminals (such as CONCEPTS) which
* do underlining correctly automatically (e.g. on nroff
* output), and remembering, in hardcopy mode,
* that we have overstruct something.
if (!insmode
&& d
&& d
!= ' ' && d
!= (c
& TRIM
)) {
if (EO
&& (OS
|| UL
&& (c
== '_' || d
== '_'))) {
* Unless we are just bashing characters around for
* inner working of insert mode, update the display.
if ((hold
& HOLDPUPD
) == 0)
* In insert mode, put out the IC sequence, padded
* based on the depth of the current line.
* A terminal which had no real insert mode, rather
* opening a character position at a time could do this.
* Actually should use depth to end of current line
* but this rarely matters.
vputp(IC
, DEPTH(vcline
));
* In insert mode, IP is a post insert pad.
vputp(IP
, DEPTH(vcline
));
* CONCEPT braindamage in early models: after a wraparound
* the next newline is eaten. It's hungry so we just
* feed it now rather than worrying about it.
if (XN
&& outcol
% WCOLS
== 0)
* Delete display positions stcol through endcol.
* Amount of use of special terminal features here is limited.
register int nc
= endcol
- stcol
;
tfixnl(), fprintf(trace
, "physdc(%d, %d)\n", stcol
, endcol
);
* CONCEPT-100 like terminal.
* If there are any ``spaces'' in the material to be
* deleted, then this is too hard, just retype.
if ((*up
++ & (QUOTE
|TRIM
)) == QUOTE
)
if (*up
== 0 || (*up
++ & QUOTE
) == QUOTE
)
* 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.
up
= vtube
[i
]; tp
= up
+ endcol
; tpe
= up
+ WCOLS
;
if (tp
- (up
+ stcol
) < 2 * nc
)
* Go into delete mode and do the actual delete.
* Padding is on DC itself.
vputp(DC
, DEPTH(vcline
));
* With CONCEPT like terminals, characters are pulled left
* from first following null. HP like terminals shift rest of
* this (single physical) line rigidly.
if ((i
& (QUOTE
|TRIM
)) == QUOTE
)
copy(up
+ stcol
, up
+ endcol
, WCOLS
- endcol
);
trubble
= 0, techoin
= 0;
fprintf(trace
, "vcnt = %d, vcline = %d, vliny = ", vcnt
, vcline
);
for (i
= 0; i
<= vcnt
; i
++) {
fprintf(trace
, "%d", LINE(i
));
fprintf(trace
, "<%d>", DEPTH(i
));
else if (c
< ' ' || c
== DELETE
)
fprintf(trace
, "^%c", ctlof(c
));
* Put a character with possible tracing.