File: [local] / src / usr.bin / mg / tty.c (download)
Revision 1.5, Thu Apr 13 06:12:17 2000 UTC (24 years, 1 month ago) by millert
Branch: MAIN
CVS Tags: OPENBSD_2_7_BASE, OPENBSD_2_7 Changes since 1.4: +122 -101 lines
The start of KNF + -Wall. The code has been run through indent but
needs hand fixup. I stopped at keymap.c...
|
/*
* Terminfo display driver
*
* Terminfo is a terminal information database and routines to describe
* terminals on most modern UNIX systems. Many other systems have adopted
* this as a reasonable way to allow for widly varying and ever changing
* varieties of terminal types. This should be used where practical.
*/
/*
* Known problems: If you have a terminal with no clear to end of screen and
* memory of lines below the ones visible on the screen, display will be
* wrong in some cases. I doubt that any such terminal was ever made, but I
* thought everyone with delete line would have clear to end of screen too...
*
* Code for terminals without clear to end of screen and/or clear to end of line
* has not been extensivly tested.
*
* Cost calculations are very rough. Costs of insert/delete line may be far
* from the truth. This is accentuated by display.c not knowing about
* multi-line insert/delete.
*
* Using scrolling region vs insert/delete line should probably be based on cost
* rather than the assuption that scrolling region operations look better.
*/
#include "def.h"
#include <term.h>
extern int ttrow;
extern int ttcol;
extern int tttop;
extern int ttbot;
extern int tthue;
extern int ttputc();
int tceeol; /* Costs are set later */
int tcinsl;
int tcdell;
static int insdel; /* Do we have both insert & delete line? */
static char *scroll_fwd; /* How to scroll forward. */
#ifdef NO_RESIZE
static setttysize();
#endif
/*
* Initialize the terminal when the editor
* gets started up.
*/
VOID
ttinit()
{
char *tv_stype;
char *t, *p;
#ifndef gettermtype /* (avoid declaration if #define) */
char *gettermtype(); /* system dependent function to
* determin terminal type */
#endif
if ((tv_stype = gettermtype()) == NULL)
panic("Could not determine terminal type");
if (setupterm(tv_stype, 1, NULL)) {
(void) asprintf(&p, "Unknown terminal type: %s", tv_stype);
panic(p);
}
scroll_fwd = scroll_forward;
if (!scroll_fwd || !*scroll_fwd) {
/* this is what GNU Emacs does */
scroll_fwd = parm_down_cursor;
if (!scroll_fwd || !*scroll_fwd)
scroll_fwd = "\n";
}
if (!cursor_address || !cursor_up)
panic("This terminal is to stupid to run mg");
ttresize(); /* set nrow & ncol */
if (!clr_eol)
tceeol = ncol;
else
tceeol = charcost(clr_eol);
/* Estimate cost of inserting a line */
if (change_scroll_region && scroll_reverse)
tcinsl = charcost(change_scroll_region) * 2 +
charcost(scroll_reverse);
else if (parm_insert_line)
tcinsl = charcost(parm_insert_line);
else if (insert_line)
tcinsl = charcost(insert_line);
else
tcinsl = NROW * NCOL; /* make this cost high enough */
/* Estimate cost of deleting a line */
if (change_scroll_region)
tcdell = charcost(change_scroll_region) * 2 +
charcost(scroll_fwd);
else if (parm_delete_line)
tcdell = charcost(parm_delete_line);
else if (delete_line)
tcdell = charcost(delete_line);
else
tcdell = NROW * NCOL; /* make this cost high enough */
/* Flag to indicate that we can both insert and delete lines */
insdel = (insert_line || parm_insert_line) && (delete_line || parm_delete_line);
if (enter_ca_mode)
putpad(enter_ca_mode, 1); /* enter application mode */
setttysize();
}
/*
* Re-initialize the terminal when the editor is resumed.
* The keypad_xmit doesn't really belong here but...
*/
VOID
ttreinit()
{
if (enter_ca_mode)
putpad(enter_ca_mode, 1); /* enter application mode */
if (keypad_xmit)
putpad(keypad_xmit, 1); /* turn on keypad */
setttysize();
}
/*
* Clean up the terminal, in anticipation of
* a return to the command interpreter. This is a no-op
* on the ANSI display. On the SCALD display, it sets the
* window back to half screen scrolling. Perhaps it should
* query the display for the increment, and put it
* back to what it was.
*/
VOID
tttidy()
{
#ifdef XKEYS
ttykeymaptidy();
#endif
/* set the term back to normal mode */
if (exit_ca_mode)
putpad(exit_ca_mode, 1);
}
/*
* Move the cursor to the specified
* origin 0 row and column position. Try to
* optimize out extra moves; redisplay may
* have left the cursor in the right
* location last time!
*/
VOID
ttmove(row, col)
{
char *tgoto();
if (ttrow != row || ttcol != col) {
putpad(tgoto(cursor_address, col, row), 1);
ttrow = row;
ttcol = col;
}
}
/*
* Erase to end of line.
*/
VOID
tteeol()
{
if (clr_eol)
putpad(clr_eol, 1);
else {
int i = ncol - ttcol;
while (i--)
ttputc(' ');
ttrow = ttcol = HUGE;
}
}
/*
* Erase to end of page.
*/
VOID
tteeop()
{
int line;
if (clr_eos)
putpad(clr_eos, nrow - ttrow);
else {
putpad(clr_eol, 1);
if (insdel)
ttdell(ttrow + 1, lines, lines - ttrow - 1);
else { /* do it by hand */
for (line = ttrow + 1; line <= lines; ++line) {
ttmove(line, 0);
tteeol();
}
}
ttrow = ttcol = HUGE;
}
}
/*
* Make a noise.
*/
VOID
ttbeep()
{
putpad(bell, 1);
ttflush();
}
/*
* Insert nchunk blank line(s) onto the
* screen, scrolling the last line on the
* screen off the bottom. Use the scrolling
* region if possible for a smoother display.
* If no scrolling region, use a set
* of insert and delete line sequences
*/
VOID
ttinsl(row, bot, nchunk)
{
int i, nl;
if (row == bot) { /* Case of one line insert is */
ttmove(row, 0); /* special */
tteeol();
return;
}
if (change_scroll_region && scroll_reverse) {
/* Use scroll region and back index */
nl = bot - row;
ttwindow(row, bot);
ttmove(row, 0);
while (nchunk--)
putpad(scroll_reverse, nl);
ttnowindow();
return;
} else if (insdel) {
ttmove(1 + bot - nchunk, 0);
nl = nrow - ttrow;
if (parm_delete_line)
putpad(tgoto(parm_delete_line, 0, nchunk), nl);
else
/* For all lines in the chunk */
for (i = 0; i < nchunk; i++)
putpad(delete_line, nl);
ttmove(row, 0);
nl = nrow - ttrow; /* ttmove() changes ttrow */
if (parm_insert_line)
putpad(tgoto(parm_insert_line, 0, nchunk), nl);
else
/* For all lines in the chunk */
for (i = 0; i < nchunk; i++)
putpad(insert_line, nl);
ttrow = HUGE;
ttcol = HUGE;
} else
panic("ttinsl: Can't insert/delete line");
}
/*
* Delete nchunk line(s) from "row", replacing the
* bottom line on the screen with a blank line.
* Unless we're using the scrolling region, this is
* done with a crafty sequences of insert and delete
* lines. The presence of the echo area makes a
* boundry condition go away.
*/
VOID
ttdell(row, bot, nchunk)
{
int i, nl;
if (row == bot) { /* One line special case */
ttmove(row, 0);
tteeol();
return;
}
if (change_scroll_region) { /* scrolling region */
nl = bot - row;
ttwindow(row, bot);
ttmove(bot, 0);
while (nchunk--)
putpad(scroll_fwd, nl);
ttnowindow();
} else if (insdel) {
ttmove(row, 0); /* Else use insert/delete line */
nl = nrow - ttrow;
if (parm_delete_line)
putpad(tgoto(parm_delete_line, 0, nchunk), nl);
else
/* For all lines in the chunk */
for (i = 0; i < nchunk; i++)
putpad(delete_line, nl);
ttmove(1 + bot - nchunk, 0);
nl = nrow - ttrow; /* ttmove() changes ttrow */
if (parm_insert_line)
putpad(tgoto(parm_insert_line, 0, nchunk), nl);
else
/* For all lines in the chunk */
for (i = 0; i < nchunk; i++)
putpad(insert_line, nl);
ttrow = HUGE;
ttcol = HUGE;
} else
panic("ttdell: Can't insert/delete line");
}
/*
* This routine sets the scrolling window
* on the display to go from line "top" to line
* "bot" (origin 0, inclusive). The caller checks
* for the pathalogical 1 line scroll window that
* doesn't work right, and avoids it. The "ttrow"
* and "ttcol" variables are set to a crazy value
* to ensure that the next call to "ttmove" does
* not turn into a no-op (the window adjustment
* moves the cursor).
*
*/
VOID
ttwindow(top, bot)
{
if (change_scroll_region && (tttop != top || ttbot != bot)) {
putpad(tgoto(change_scroll_region, bot, top), nrow - ttrow);
ttrow = HUGE; /* Unknown. */
ttcol = HUGE;
tttop = top; /* Remember region. */
ttbot = bot;
}
}
/*
* Switch to full screen scroll. This is
* used by "spawn.c" just before is suspends the
* editor, and by "display.c" when it is getting ready
* to exit. This function gets to full screen scroll
* by telling the terminal to set a scrolling regin
* that is lines or nrow rows high, whichever is larger.
* This behavior seems to work right on systems
* where you can set your terminal size.
*/
VOID
ttnowindow()
{
if (change_scroll_region) {
putpad(tgoto(change_scroll_region,
(nrow > lines ? nrow : lines) - 1, 0), nrow - ttrow);
ttrow = HUGE; /* Unknown. */
ttcol = HUGE;
tttop = HUGE; /* No scroll region. */
ttbot = HUGE;
}
}
/*
* Set the current writing color to the
* specified color. Watch for color changes that are
* not going to do anything (the color is already right)
* and don't send anything to the display.
* The rainbow version does this in putline.s on a
* line by line basis, so don't bother sending
* out the color shift.
*/
VOID
ttcolor(color)
int color;
{
if (color != tthue) {
if (color == CTEXT) /* Normal video. */
putpad(exit_standout_mode, 1);
else if (color == CMODE) /* Reverse video. */
putpad(enter_standout_mode, 1);
tthue = color; /* Save the color. */
}
}
/*
* This routine is called by the
* "refresh the screen" command to try and resize
* the display. The new size, which must be deadstopped
* to not exceed the NROW and NCOL limits, it stored
* back into "nrow" and "ncol". Display can always deal
* with a screen NROW by NCOL. Look in "window.c" to
* see how the caller deals with a change.
*/
VOID
ttresize()
{
setttysize(); /* found in "ttyio.c", */
/* ask OS for tty size */
if (nrow < 1) /* Check limits. */
nrow = 1;
else if (nrow > NROW)
nrow = NROW;
if (ncol < 1)
ncol = 1;
else if (ncol > NCOL)
ncol = NCOL;
}
#ifdef NO_RESIZE
static
setttysize()
{
nrow = lines;
ncol = columns;
}
#endif
static int cci;
/* ARGSUSED */
static int /* fake char output for charcost() */
fakec(c)
char c;
{
cci++;
}
/* calculate the cost of doing string s */
charcost(s)
char *s;
{
cci = 0;
tputs(s, nrow, fakec);
return (cci);
}