RCS_ID_C "$Id: display.c,v 1.5 1993/07/12 22:51:47 ppessi Exp $";
/* 
 * display.c --- generic display routines for niftyterm
 *
 * Copyright 1989, Chris Newman
 * All Rights Reserved
 * Permission is granted ot copy, modify, and use this as long
 * as this notice remains intact.  This is a nifty program.
 *
 * DISCLAIMER: the author (and maintainer) of this program is not responsible
 * for any damage or other problems caused by it.
 *
 * $Author: ppessi $ $Revision: 1.5 $ $Date: 1993/07/12 22:51:47 $
 *
 */

#include <stdio.h>
#include <ctype.h>
#include <exec/memory.h>
#include <clib/exec_protos.h>
#include "nifty.h"
#include "display.h"
#include "dispmacros.h"

#ifdef __SASC
extern struct ExecBase *SysBase;
#include <pragmas/exec_sysbase_pragmas.h>
#endif
static char AllocMemError[] = PROGNAME ": PANIC! AllocMem error\n";

/* display specific routines called:
 * dsscroll(), dsinvert(), dsstyle(), dsputs()
 * idssetfont()
 */

/* global display structure */
struct nifty_display disp;

/* global settings in emulate.c needed for soft reset */
extern int vt100_yucky_wrap_mode;
extern int ansi_LNM;
extern int visual;

/* global from main needed to set ansi_LNM correctly */
extern int use_stdin;

/* allocate the display buffers
 */
void
allocate_display(width,height)
int width, height;
{
    disp.buf = AllocMem((sizeof(char) * (width+1) * height), MEMF_CLEAR);
    disp.sbuf = (short *)AllocMem((sizeof(short) * (width+1) * height), MEMF_CLEAR);
    disp.tabstops = AllocMem((sizeof (char) * width), MEMF_CLEAR);
    disp.maxwidth = width;
    disp.maxheight = height;
    disp.bufwidth = width+1;
    if (disp.buf == NULL || disp.sbuf == NULL || disp.tabstops == NULL) {
	fputs(AllocMemError, stdout);
	exit(-1);
    }
}

/* Free up memory allocated for display */
void
free_display()
{
    if(disp.buf) FreeMem(disp.buf, sizeof(char) * (disp.maxwidth + 1) * disp.maxheight);
    if(disp.sbuf) FreeMem(disp.sbuf, sizeof(short) * (disp.maxwidth + 1) * disp.maxheight);
    if(disp.tabstops) FreeMem(disp.tabstops, sizeof(char) * disp.maxwidth);
}

/* clear out the display
 */
void
clear_display()
{
    register int y;
     
    for (y = 0; y < disp.maxheight; y++) {
	disp.plines[y] = disp.buf+y*disp.bufwidth;
	disp.pstyles[y] = disp.sbuf+y*disp.bufwidth;
	STYLEFLAG(y) = 0;
	LINELENST(y) = 0;
    }
    disp.curx = disp.cury = 0;
}

/* clear out the tab stops
 */
void
clear_tabstops()
{
    register int i;

    for (i=0; i<disp.maxwidth; i++)
	disp.tabstops[i] = 0;
}

/* initialize tabstops to 8 characters
 */
void
init_tabstops()
{
    register int i;

    for (i=0; i<disp.maxwidth; i++)
	disp.tabstops[i] = (i % 8) == 0;
}

/* reset the display
 */
void
reset_display(soft)
int soft;
{
    if(!soft) {
        clear_display();
	disp.scrolldefault = 1;
    }
    (void) dsstyle(STYLESOFF);
    disp.visual = visual;
    disp.insert = disp.origin = disp.scrolltop = disp.savex = disp.savey = 0;
    disp.scrollbot = soft ? disp.winheight - 1 : disp.maxheight;
    disp.scrollskip = disp.scrolldefault;
    disp.wrap = 1;
    init_tabstops();
}

/* move cursor to x, y
 */
void
dscursorto(x, y)
int x, y;
{
    int	wflag;
    
    if (disp.origin) {
	y += disp.scrolltop;
	MAXBOUNDCK(y, disp.scrollbot);
    } else
	SLIMITCK(y, disp.winheight);
    if (disp.cury != y)
	idssetfont(disp.style, disp.cury = y);
    wflag = STYLEFLAG(disp.cury) & DOUBLE_MASK ? 2 : 1;
    disp.curx = SLIMIT(x, disp.winwidth/wflag);
}

/* return size of the current window
 */
void 
dsgetsize(x, y)
int *x, *y;
{
    *x = disp.winwidth;
    *y = disp.winheight;
}

/* get cursor position
 */
void
dsgetcursor(x, y)
int *x, *y;
{
    *x = disp.curx;
    *y = disp.cury;
}

/* display style changes
 */
int 
dsstyle(style)
int style;
{
    switch (style) {
	case BOLD1: /* bold on */
	    disp.style |= BOLD;
	    break;
	case BOLD0: /* bold off */
	    disp.style &= ~BOLD;
	    break;
	case ITALIC1: /* italic on */
	    disp.style |= ITALIC;
	    break;
	case ITALIC0: /* italic off */
	    disp.style &= ~ITALIC;
	    break;
	case UNDERLINE1: /* underline on */
	    disp.style |= UNDERLINE;
	    break;
	case UNDERLINE0: /* underline off */
	    disp.style &= ~UNDERLINE;
	    break;
	case INVERSE1: /* inverse on */
	    disp.style |= INVERSE;
	    break;
	case INVERSE0: /* inverse off */
	    disp.style &= ~INVERSE;
	    break;
	case BLINK1:
	    disp.style |= FLASH;
	    break;
	case BLINK0:
	    disp.style &= ~FLASH;
	    break;
	case ALTERNATE1:
	    disp.style |= ALTERNATE;
	    break;
	case ALTERNATE0:
	    disp.style &= ~ALTERNATE;
	    break;
	case STYLESOFF: /* turn off all */
	    disp.style = 0;
	    break;
	case SAVE_STYLE: /* save the style */
	    disp.savestyle = disp.style;
	    break;
	case RESTORE_STYLE: /* restore the saved style */
	    disp.style = disp.savestyle;
	    break;
	case DWIDTH1: /* double width */
	    if ((STYLEFLAG(disp.cury) & DOUBLE_MASK) == (DOUBLE1 | DOUBLE2))
		break;
	    STYLEFLAG(disp.cury) |= DOUBLE1 | DOUBLE2;
	    redraw_line(disp.cury);
	    break;
	case DWIDTHTOP:/* double width/height top */
	    if ((STYLEFLAG(disp.cury) & DOUBLE_MASK) == DOUBLE1)
		break;
	    STYLEFLAG(disp.cury) &= ~DOUBLE2;
	    STYLEFLAG(disp.cury) |= DOUBLE1;
	    redraw_line(disp.cury);
	    break;
	case DWIDTHBOT: /* double width/height bottom */
	    if ((STYLEFLAG(disp.cury) & DOUBLE_MASK) == DOUBLE2)
		break;
	    STYLEFLAG(disp.cury) &= ~DOUBLE1;
	    STYLEFLAG(disp.cury) |= DOUBLE2;
	    redraw_line(disp.cury);
	    break;
	case DWIDTH0: /* single width, single height */
	    if ((STYLEFLAG(disp.cury) & DOUBLE_MASK) == 0)
		break;
	    STYLEFLAG(disp.cury) &= ~DOUBLE_MASK;
	    redraw_line(disp.cury);
	    break;
	default: /* style unavailable */
	    return -1;
    }
    idssetfont(disp.style, disp.cury);

    return 0;
}

/* check if cursor can move down without scrolling, and move cursor if it can.
 */
int 
dscheckdown()
{
    return (disp.cury < disp.scrollbot ? ++disp.cury : 0);
}

/* move the cursor
 */
void
dscursormove(move, n)
int move, n;
{
    int wflag = STYLEFLAG(disp.cury) & DOUBLE_MASK ? 2 : 1;

    if (n<1)  n=1;
    switch (move) {
	case CURSOR_UP: case REVERSE_INDEX:
	    disp.cury -= n;
	    if (disp.cury < disp.scrolltop) {
		if (move == REVERSE_INDEX) {
		    dsscroll(disp.scrolltop,disp.scrollbot - disp.scrolltop + 1,disp.cury - disp.scrolltop);
		}
		disp.cury = disp.scrolltop;
	    }
	    break;
	    
	case CURSOR_RIGHT:
	    disp.curx += n;
	    SLIMITCK(disp.curx, disp.winwidth/wflag);
	    break;

	case CURSOR_LEFT:
	    disp.curx -= n;
	    MINBOUNDCK(disp.curx, 0);
	    break;

	case DOWN1_AND_SCROLL:
	    {
		int scrolltop = disp.scrolltop;
		int scrollheight = disp.scrollbot - scrolltop;
		int scrollsize;
		
		if (disp.scrollskip > n)
		    n = disp.scrollskip;
		if (disp.cury > disp.scrollbot) {
		    scrollheight = disp.winheight;
		    scrolltop = 0;
		}
		if (n > (scrollsize = (scrollheight >> 1)))
		    n = scrollsize ? scrollsize : 1;
		disp.cury -= n-1;
		dsscroll(scrolltop, scrollheight + 1, n);
	    }
	    break;

	case CURSOR_DOWN: case NEXT_LINE: case INDEX:
	    if (disp.cury <= disp.scrollbot && (disp.cury+=n) > disp.scrollbot) {
		if (move == CURSOR_DOWN) {
		    disp.cury = disp.scrollbot;
		} else {
		    dsscroll(disp.scrolltop,
			     disp.scrollbot - disp.scrolltop + 1,
			     disp.cury - disp.scrollbot + disp.scrollskip - 1);
		    disp.cury = disp.scrollbot - disp.scrollskip + 1;
		}
	    } else if (disp.cury > disp.scrollbot && (disp.cury+=n) > disp.winheight-1) {
		if (move == CURSOR_DOWN) {
		    disp.cury = disp.winheight-1;
		} else {
		    dsscroll(0, disp.winheight, disp.cury - disp.winheight + disp.scrollskip);
		    disp.cury = disp.winheight - disp.scrollskip;
		}
	    }
	    if (move != NEXT_LINE)
		break;
	    /* fall through */
	case BEGIN_LINE:
	    disp.curx = 0;
	    break;

	case END_LINE:
	    disp.curx = disp.winwidth/wflag-1;
	    break;

	case BOTTOM_HOME:
	    disp.curx = 0;
	    disp.cury = disp.winheight-1;
	    break;

	case SAVE_CURSOR:
	    disp.savex = disp.curx;
	    disp.savey = disp.cury;
	    (void) dsstyle(SAVE_STYLE);
	    break;

	case RESTORE_CURSOR:
	    disp.curx = disp.savex;
	    disp.cury = disp.savey;
	    (void) dsstyle(RESTORE_STYLE);
	    break;

	case INSERT_LINE:
	    dsscroll(disp.cury, disp.scrollbot-disp.cury+1, -n);
	    break;

	case DELETE_LINE:
	    dsscroll(disp.cury, disp.scrollbot-disp.cury+1, n);
	    break;

	case INSERT_CHAR: {
	    char insertme[MAXWIDTH];
	    int temp;
	    
	    disp.insert++;
	    insertme[SLIMIT(n,MAXWIDTH)] = '\0';
	    for (temp = n; --temp >= 0; insertme[temp] = ' ');
	    temp = disp.curx;
	    dsputs(insertme);
	    disp.curx = temp;
	    disp.insert--;
	    }
	    break;

	case TAB_STOP:
	    if (n > 1) {
		while (disp.curx < disp.winwidth/wflag-1 && ++disp.curx % n);
	    } else {
		while (disp.curx < disp.winwidth/wflag-1 &&
		       !disp.tabstops[++disp.curx]);
	    }
	    break;
    }
    idssetfont(disp.style, disp.cury);
}

/* miscellaneous terminal functions
 */
void
dsfunction(what)
int what;
{
    int wflag = STYLEFLAG(disp.cury) & DOUBLE_MASK ? 2 : 1;

    switch(what) {
	case ERASETO_EOS:
	    if (disp.cury < disp.winheight-1)
		dsclear(0, disp.cury+1, disp.winwidth/wflag-1, disp.winheight-1);
	    /* fall through */
	case ERASETO_EOL:
	    if (disp.curx < disp.winwidth/wflag-1)
		dsclear(disp.curx, disp.cury, disp.winwidth/wflag-1, disp.cury);
	    break;

	case ERASETO_SOS:
	    if (disp.cury)
		dsclear(0, 0, disp.winwidth/wflag-1, disp.cury-1);
	    /* fall through */
	case ERASETO_SOL:
	    if (disp.curx)
		dsclear(0, disp.cury, disp.curx, disp.cury);
	    break;

	case ERASE_LINE:
	    dsclear(0, disp.cury, disp.winwidth/wflag-1, disp.cury);
	    break;

	case RESET_DISPLAY:
	    reset_display(1);
	    break;

	case SCROLL_UP:
	    dsscroll(disp.scrolltop, disp.scrollbot - disp.scrolltop + 1, 1);
	    break;

	case SCROLL_DOWN:
	    dsscroll(disp.scrolltop, disp.scrollbot - disp.scrolltop + 1, -1);
	    break;

	case ORIGIN_ON:
	    disp.origin = 1;
	    break;

	case ORIGIN_OFF:
	    disp.origin = 0;
	    break;

	case BLOCK_CURSOR:
	    disp.visual |= CUR_BLOCK;
	    break;

	case UNDERLINE_CURSOR:
	    disp.visual &= ~CUR_BLOCK;
	    break;

	case INVISIBLE_CURSOR:
	    disp.visual |= CUR_INVISIBLE;
	    break;

	case VISIBLE_CURSOR:
	    disp.visual &= ~CUR_INVISIBLE;
	    break;

	case WRAP_ON:
	    disp.wrap = 1;
	    break;

	case WRAP_OFF:
	    disp.wrap = 0;
	    break;

	case INSERT_ON:
	    disp.insert = 1;
	    break;

	case INSERT_OFF:
	    disp.insert = 0;
	    break;

	case SET_TAB:
	    disp.tabstops[disp.curx] = 1;
	    break;

	case CLEAR_TAB:
	    disp.tabstops[disp.curx] = 0;
	    break;

	case CLEAR_ALL_TABS:
	    clear_tabstops();
	    break;
    }
    idssetfont(disp.style, disp.cury);
}

/* set the scrolling region
 */
void
dssetscroll(top,bot)
int top,bot;
{
    if (top)
	top--;
    if (bot)
	bot--;
    if (bot - top < 1)
	disp.scrolltop = 0, disp.scrollbot = disp.winheight-1;
    else {
	if (top >= disp.winheight)
	    top = 0;
	if (bot >= disp.winheight)
	    bot = disp.winheight-1;
	disp.scrolltop = top;
	disp.scrollbot = bot;
    }
    disp.curx = 0;
    disp.cury = disp.scrolltop;
    if (disp.scrolltop || disp.scrollbot < disp.winheight-1)
	disp.scrollskip = 1;
    else
	disp.scrollskip = disp.scrolldefault;
    idssetfont(disp.style, disp.cury);
}
