/*
 * Name:	MicroEMACS
 *		UNIX termcap/terminfo display driver
 * Version:	2
 * Last edit:	16-Apr-86
 * By:		paul@ohio-state
 *		cbosgd!osu-eddie!paul
 *
 * Termcap is a terminal information database and routines to describe 
 * terminals on a UNIX system.  This should be used ALLWAYS on a UNIX system
 * that has it.
 */
#include	"def.h"

#define	BEL	0x07			/* BEL character.		*/
#define	ESC	0x1B			/* ESC character.		*/
#define	LF	0x0A			/* Line feed.			*/

extern	int	ttrow;
extern	int	ttcol;
extern	int	tttop;
extern	int	ttbot;
extern	int	tthue;

int	tceeol;			/* Costs are set later */
int	tcinsl;
int	tcdell;

#define TCAPSLEN 315

char tcapbuf[TCAPSLEN];
char    PC,
        *CM,
        *CL,
        *CE,
        *UP,
	*IM,			/* insert mode */
	*IC,			/* insert a single space */
	*EI,			/* end insert mode */
	*DC,
	*AL,			/* add line */
	*DL,			/* del line */
	*TI,			/* term init -- start using cursor motion */
	*TE,			/* term end --- end using cursor motion */
	*SO,
	*SE,
        *CD;

/*
 * Initialize the terminal when the editor
 * gets started up. This is a no-op on the ANSI
 * display. On the SCALD display, it turns off the
 * half-screen scroll, because this appears to really
 * confuse the scrolling region firmware in the
 * display.
 */
static char tcbuf[1024];

ttinit()
{
        char *getenv();
        char *t, *p, *tgetstr();
        char *tv_stype;
        char err_str[72];

        if ((tv_stype = getenv("TERM")) == NULL)
        {
                puts("Environment variable TERM not defined!");
                exit(1);
        }

        if((tgetent(tcbuf, tv_stype)) != 1)
        {
                sprintf(err_str, "Unknown terminal type %s!", tv_stype);
                puts(err_str);
                exit(1);
        }

        p = tcapbuf;
        t = tgetstr("pc", &p);
        if(t)
                PC = *t;

        CD = tgetstr("cd", &p);
        CM = tgetstr("cm", &p);
        CE = tgetstr("ce", &p);
        UP = tgetstr("up", &p);
	IM = tgetstr("im", &p);
	IC = tgetstr("ic", &p);
	EI = tgetstr("ei", &p);
	DC = tgetstr("dc", &p);
	AL = tgetstr("al", &p);
	DL = tgetstr("dl", &p);
	TI = tgetstr("ti", &p);
	TE = tgetstr("te", &p);
	SO = tgetstr("so", &p);
	SE = tgetstr("se", &p);

        if(CD == NULL || CM == NULL || CE == NULL || UP == NULL)
        {
                puts("This terminal is not powerful enough to run Micro Emacs\n");
                exit(1);
        }
	if (!*CE) {
	    tceeol = ncol;
	} else {
	    tceeol = charcost(CE);
	}
	if (AL == NULL || !*AL) {
	    tcinsl = nrow * ncol; /* make this cost high enough that it */
				  /* won't ever happen */
	} else {
	    tcinsl = charcost(AL);
	}
	if (DL == NULL || !*DL) {
	    tcdell = nrow * ncol; /* make this cost high enough that it */
				  /* won't ever happen */
	} else {
	    tcdell = charcost(DL);
	}

        if (p >= &tcapbuf[TCAPSLEN])
        {
                puts("Terminal description too big!\n");
                exit(1);
        }
	if (TI != NULL && *TI) tputs (TI);	/* init the term */
}

/*
 * 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.
 */
tttidy()
{
	if (TE != NULL && *TE) tputs (TE);	/* set the term back to normal mode */
}

/*
 * 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!
 */
ttmove(row, col)
{
    if (ttrow!=row || ttcol!=col) {
	putpad(tgoto(CM, col, row));
	ttrow = row;
	ttcol = col;
    }
}

/*
 * Erase to end of line.
 */
tteeol()
{
        putpad(CE);
}

/*
 * Erase to end of page.
 */
tteeop()
{
        putpad(CD);
}

/*
 * Make a noise.
 */
ttbeep()
{
	ttputc(BEL);
	ttflush();
}

/*
 * Insert nchunk blank line(s) onto the
 * screen, scrolling the last line on the
 * screen off the bottom. This is done with
 * a cluster of clever insert and delete commands,
 * because there are no scroll regions.
 */
ttinsl(row, bot, nchunk)
{
    register int	i;
    
    if (row == bot) {		/* Case of one line insert is 	*/
	ttmove(row, 0);		/*	special			*/
	tteeol();
	return;
    }
    ttmove(1+bot-nchunk, 0);
    for (i=0; i<nchunk; i++) {	/* For all lines in the chunk	*/
	putpad(DL);
    }
    ttmove(row, 0);
    for (i=0; i<nchunk; i++) {	/* For all lines in the chunk	*/
	putpad(AL);
    }
    ttrow = row;			/* End up on current line	*/
    ttcol = 0;
}

/*
 * Delete nchunk line(s) "row", replacing the
 * bottom line on the screen with a blank
 * line. This is done with a crafty sequences
 * of insert and delete line; We assume that the terminal
 * is like the Heath (ie no scrolling region). The presence of the
 * echo area makes a boundry condition
 * go away.
 */
ttdell(row, bot, nchunk)
{
    register int	i;
    
    if (row == bot) {		/* One line special case	*/
	ttmove(row, 0);
	tteeol();
	return;
    }
    ttmove(row, 0);
    for (i=0; i<nchunk; i++) {	/* For all lines in chunk	*/
	putpad(DL);
    }
    ttmove(1+bot-nchunk,0);
    for (i=0; i<nchunk; i++) {	/* For all lines in chunk	*/
	putpad(AL);
    }
    ttrow = bot-nchunk;
    ttcol = 0;
}

/*
 * No-op.
 */
ttwindow(top, bot)
{
}

/*
 * No-op.
 */
ttnowindow()
{
}

/*
 * 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.
 */
ttcolor(color)
register int	color;
{
    if (color != tthue) {
	if (color == CTEXT) {		/* Normal video.	*/
	    putpad(SE);
	} else if (color == CMODE) {	/* Reverse video.	*/
	    putpad(SO);
	}
	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.
 */
ttresize()
{
	register int	c;
	register int	newnrow;
	register int	newncol;

	newnrow = tgetnum("li"); /* on a Sun, this returns the NEW size */
	newncol = tgetnum("co"); /* on anything else, returns same old size */

	if (newnrow < 1)			/* Check limits.	*/
		newnrow = 1;
	else if (newnrow > NROW)
		newnrow = NROW;
	if (newncol < 1)
		newncol = 1;
	else if (newncol > NCOL)
		newncol = NCOL;
	nrow = newnrow;
	ncol = newncol;
}

static int cci;

static
fakec(c)			/* fake char output for charcost() */
char c;
{
    cci++;
}

charcost (s)			/* calculate the cost of doing string s */
char *s;
{
    cci = 0;

    tputs(s, nrow, fakec);
    return cci;
}

putpad(str)
char    *str;
{
        tputs(str, 1, ttputc);
}


