/*
 *           D M - S A B . C
 *   
 *  MGED display manager for the Saber Workstation
 *  The workstation has no internal displaylist capability.
 *  This version only works with the SABER WORKSTATION GOPS
 *  and MTU graphics processor under the Berkley 4.2 unix operating system.
 *  
 *  Authors
 *      Paul R. Stay
 *  	Michael J. Muuss
 *      Charles Kennedy
 *  
 *  Source -
 *      SECAD/VLD Computing Consortium, Bldg 394
 *      The U.S. army Ballistic Research Laboratory
 *      Aberdeen Proving Ground, Maryland 21005
 *  
 *  Copyright Notice
 *      This software is copyright (c) 1985 by the United States Army.
 *      All rights reserved.
 */
#ifndef lint
static char RCSid[] = "@(#)$Header: dm-sab.c,v 1.1 86/03/14 20:44:03 mike Exp $ (BRL)";
#endif

#include <fcntl.h>
#include <stdio.h>
#include "machine.h"	/* special copy */
#include "../h/vmath.h"
#include "ged.h"
#include "dm.h"
#include "solid.h"
#include "dm-sab.h"
#include <sgtty.h>
#include <sys/ioctl.h>
#include <sys/ttydev.h>

extern void	perror();
extern unsigned	sleep();

typedef unsigned char u_char;

/* Display Manager package interface */

int	Sab_open();
void	Sab_close();
int	Sab_input();
void	Sab_prolog(), Sab_epilog();
void	Sab_normal(), Sab_newrot();
void	Sab_update();
void	Sab_puts(), Sab_2d_line(), Sab_light();
int	Sab_object();
unsigned Sab_cvtvecs(), Sab_load();
void	Sab_statechange(), Sab_viewchange(), Sab_colorchange();
void	Sab_window(), Sab_debug();

struct dm dm_Sab = {
	Sab_open, Sab_close,
	Sab_input,
	Sab_prolog, Sab_epilog,
	Sab_normal, Sab_newrot,
	Sab_update,
	Sab_puts, Sab_2d_line,
	Sab_light,
	Sab_object,
	Sab_cvtvecs, Sab_load,
	Sab_statechange,
	Sab_viewchange,
	Sab_colorchange,
	Sab_window, Sab_debug,
	0,				/* No displaylist */
	4095.9,			/* ??? */
	"sab", "Saber Workstation"
};

struct timeval	{			/* needed for select() */
	long	tv_sec;			/* seconds */
	long	tv_usec;		/* microseconds */
};

extern struct device_values dm_values;	/* values read from devices */

static int outfp;		/* GRP device to output on */
static int noutfp;		/* GRP device to output on */
static int second_fd;
static int tablet_fd;

int SAB_ROW, SAB_COL, SAB_CHARWIDTH;	/* used for text commands */
int shift;
int loop;

float MTU_WIN[16], MTU_PERS[16], MTU_ID[16];
/*
 * Display coordinate conversion:
 *  Saber is using 0..1648 in x and 0...1248 in y
 *  GED is using -2048..+2047
 */
static int frame = 0; /* for dual display */

#define	GEDX_TO_SAB(x)	((((x) + 2048) * 1640 / 4096) )
#define	GEDY_TO_SAB(x)	((((x) + 2048) * 1240 / 4096))

#define SABX_TO_GED(x)	(((x) * 4096 / 1648) - 2048)
#define SABY_TO_GED(x)	(((x + YSCREEN_OFFSET) * 512 / 507) * 8)
/*
 *  Pseudo-Color Table - maps 0-15 into RaT colors.
 */
int	sab_colors[16][3] = {
	{ 0, 0, 0 },		/* Black */
	{ 255, 0, 0 },		/* Red */
	{ 0, 255, 0 },		/* Green */
	{ 0, 0, 255 },		/* Blue */
	{ 255, 255, 0 },	/* Yellow */
	{ 255, 0, 255 },	/* Magenta */
	{ 0, 255, 255 },	/* Cyan */
	{ 192, 160, 64 },	/* Orange */
	{ 64, 192, 160 },	/* ? */
	{ 160, 64, 192 },	/* Purple */
	{ 255, 128, 0 },	/* Doug's Orange */
	{ 128, 255, 0 },	/* Doug's ? */
	{ 128, 0, 255 },	/* Doug's Purple */
	{ 255, 128, 128 },	/* Pink */
	{ 100, 100, 100 },	/* Gray */
	{ 255, 255, 255 }	/* White */
};

/*
 *  Saber-specific macro definitions
 */
#define PACK16(ms,ls)	(((ms)<<16)|((ls)&0xFFFF))


/*
 *			S A B _ O P E N
 *
 * Fire up the display manager, and the display processor.
 *   Opens the Raster Tech, enters graphics mode,
 *     turn on 24 bit color, sets interlace on.
 *
 */
Sab_open()
{
	struct sgttyb *argp;
	struct sgttyb tablet;

	if( (outfp = open("/dev/rgf", 2)) < 0 )
	{
		fprintf(stderr, "error in opening graphics device\n");
		return( 1 );
	}

	loop = 0;	/* for double buffering */

 	_ldfont();  

	_ldcolor();	/* Load Color Lookup table */
	_ldreg();
	_sab_viewport(0.0, 1663.0, 0.0, 1247.0, -10000.0, 10000.0 ); /* viewport */
	_sab_window( -1.0, 1.0, -1.0, 1.0, -1.0, 1.0); /* window */

	return(0);		/* OK */
}

/*
 *  			S A B _ C L O S E
 *  
 *  Gracefully release the display.
 *    Issue quit command, and close connection.
 */
void
Sab_close()
{
	close( outfp );
	close( tablet_fd );
}

/*
 *			S A B _ P R O L O G
 *
 * There are global variables which are parameters to this routine.
 */
void
Sab_prolog()
{

	if ( !dmaflag )
		return;

	shift = loop++ & 1 ? 0 : 4;

	wmask( 15 << shift);	/* Select buffer */
	clear( 0, 1663, 0, 1247 );

	if( state == ST_VIEW )
		return;
}

/*
 *			S A B _ E P I L O G
 */
void
Sab_epilog()
{
	Sab_2d_line( -1, -1, 1, 1 ); /* Point in the center of the Screen */
	Sab_2d_line( 1, -1, -1, 1 ); /* Point in the center of the Screen */
}

/*
 *  			S A B _ N E W R O T
 *  Stub.
 */
/* ARGSUSED */
void
Sab_newrot(mat)
mat_t mat;
{
	return;
}

/*
 *  			S A B _ O B J E C T
 *  
 *  Set up for an object, transformed as indicated, and with an
 *  object center as specified.  The ratio of object to screen size
 *  is passed in as a convienience.
 *
 *  Returns 0 if object could be drawn, !0 if object was omitted.
 */
/* ARGSUSED */
int
Sab_object( sp, mat, ratio, white )
register struct solid *sp;
mat_t mat;
double ratio;
{
	union grops {
		int i;
		float f;
	} ops[2000];
	register union grops *op;
	register struct veclist *vp;
	register int nvec;
	register int ncount = 0;

	if (sp->s_soldash) sab_linemode( 1 ); /* Solid or dashed */
	Sab_colorchange( white ? DM_WHITE : ((ndrawn>>2)%13)+2 );

	nvec = sp->s_vlen;	/* number of vectors */

	mat_print( "object", mat);
	write_mtu_mat(mat);

	for( vp = sp->s_vlist; nvec-- > 0; vp++ ) {
		if ( vp->vl_pen == PEN_UP ) {
			/* start a new polyline */
			if( ncount > 0 )  {
				op++->i = PACK16(MTEOP, 0);
			        ops[0].i = PACK16(MTU_E3, op-ops-2);
				ops[1].i = PACK16(MTPLIN3, ncount);
				write( outfp, (char *)&(ops[0].i),
					(op-ops) * sizeof(union grops) );
				ncount = 0;
				op = &(ops[2].i);
			}
		}
		op++->f = vp->vl_pnt[0];
		op++->f = vp->vl_pnt[1];
		op++->f = vp->vl_pnt[2];
		ncount++;
	}
	op++->i = PACK16(MTEOP, 0);
	ops[0].i = PACK16(MTU_E3, op-ops-2);
	ops[1].i = PACK16(MTPLIN3, ncount);
	write( outfp, (char *)&(ops[0].i), (op-ops) * sizeof(union grops) );

	return( 1 );
}

/*
 *			S A B _ N O R M A L
 *
 * Restore the display processor to a normal mode of operation
 * (ie, not scaled, rotated, displaced, etc).
 * Turns off windowing.
 */

void
Sab_normal()
{
	Sab_2d_line( -2,- 2, 2, 2);
	Sab_2d_line(-2, 2, 2, -2);
}

/*
 *			S A B _ U P D A T E
 *
 * Switch to just-drawn portion of display.
 */
void
Sab_update()
{
	if( !dmaflag )
		return;
	Sab_colorchange( DM_WHITE );
	dmask( 15 << shift);

}

/*
 *			S A B _ P U T S
 *
 * Output a string into the displaylist.
 * The starting position of the beam is as specified.
 */
void
Sab_puts( str, x, y, size, color )
register u_char *str;
{
	int lx, ly, length;
	long grfbuf[9];

	lx = GEDX_TO_SAB(x);
	ly = GEDY_TO_SAB(y);

	grfbuf[0] = SETYW + ly;
	grfbuf[1] = SETXW + lx;
	grfbuf[2] = SETNL + SAB_ROW - 1;
	grfbuf[3] = SETNP + SAB_COL -1;
	grfbuf[4] = SET_FG_COLOR | (color << shift);
	grfbuf[5] = SET_BG_COLOR + 0;
	grfbuf[6] = LD_ICR_REG + 6;
	grfbuf[7] = LD_A_MOD_REG;
	grfbuf[8] = 0xffffffff;
	write( outfp, &grfbuf[0], 36);

	length = strlen( str );

	if ((((int) lx) <= 1663) && (lx + (length <<3) -1) <= 1663 ) {
		int cc;

		ly -= 8;
		grfbuf[0] = SETYW + ly;
		write( outfp, &grfbuf[0], 4 );

		for( cc = 0; cc < length; cc++) {
			/* loop over each char in the string */
			char ch;
			int row;

			ch = str[cc];

			grfbuf[0] = SETXW + lx;
			grfbuf[1] = PRINT_A_CHAR +
				( ch * SAB_ROW * SAB_CHARWIDTH);
			write( outfp, &grfbuf[0], 8 );

			lx += SAB_COL;
		}
	}
}


/*
 *			S A B _ 2 D _ G O T O
 *
 * Do it in yellow.
 */
void
Sab_2d_line( x1, y1, x2, y2, dashed )
int x1, y1;
int x2, y2;
int dashed;
{
	int aray[7];

	aray[0] = LGU_REG | (4 << shift);
	aray[1] = LGU_XREG | GEDX_TO_SAB(x1);
	aray[2] = LGU_YREG | GEDY_TO_SAB(y1);
	aray[3] = LGU_XEND | GEDX_TO_SAB(x2);
	aray[4] = LGU_YEND | GEDY_TO_SAB(y2);
	if (dashed)
		aray[5] = LGU_LINE | ( 0x00ffffff );
	else
		aray[5] = LGU_LINE | ( 0x00ffffff );
	aray[6] = LGU_EXCT;

	write( outfp, &aray[0], 28 );
	
	aray[0] = LGU_LINE | (0x00ffffff);

	write( outfp, &aray[0], 4 );
}

/*
 *			S A B _ I N P U T
 *
 * Execution must suspend in this routine until a significant event
 * has occured on either the command stream, or a device event has
 * occured, unless "noblock" is set.
 *
 * The GED "generic input" structure is filled in.
 *
 * Returns:
 *	0 if no command waiting to be read,
 *	1 if command is waiting to be read.
 */
Sab_input( cmd_fd, noblock )
{
	static long readfds;
	static struct timeval timeout;

	/* check for input on the keyboard or on the polled register.
	 */
	if (noblock)
		timeout.tv_sec = 0;
	else
		timeout.tv_sec = 30 * 60;

	timeout.tv_usec = 0;

	readfds = (1<<cmd_fd);

	select( 32, &readfds, 0L, 0L, &timeout);

	if ( readfds & (1<<cmd_fd))
		return (1);
	else
		return (0);
}

/* 
 *			S A B _ L I G H T
 */
/* ARGSUSED */
void
Sab_light( cmd, func )
int cmd;
int func;			/* BE_ or BV_ function */
{
	return;
}

/* ARGSUSED */
unsigned
Sab_cvtvecs( sp )
struct solid *sp;
{
	return( 0 );
}

/*
 * Loads displaylist
 */
/* ARGSUSED */
unsigned
Sab_load( addr, count )
unsigned addr, count;
{
	return( 0 );
}

void
Sab_statechange()
{
}

void
Sab_viewchange()
{
}

void
Sab_colorchange(col)
int col;
{
	int aray[2];

	aray[0] = 0xc0000000 | (col << shift);	/* LOAD FG REG -- PLF */
	aray[1] = 0x1a000000 | (col << shift);	/* LOAD FG COLOR REG */
	write (outfp, &aray[0], 8);

}

void
Sab_window(w)
register int w[];
{
	/* Compute the clipping bounds */
	return;
}

void
Sab_debug()
{
	return;
}

/*
 *   S A B E R   W O R K S T A T I O N  R O U T I N E S
 *  
 * 
 */

_ldfont()
{
	int 	fid,
		x,
		y,
		z,
		a,
		b,
		c,
		d,
		e;

	static char fontname[] = "/usr/font/A.1.1";
	char *name = fontname;

	short buff0[8];
	int buff[256];

	fid = open( name, 0 );	/* open font file */

	if (fid){
		x = read (fid, &buff0[0], 8);	/* Get header information */
		b = buff0[0]; 	/* Height */
		c = buff0[1];	/* Width */
		d = buff0[3];	/* number of characters */
		e = buff0[2];	/* first char */

		SAB_ROW = b;	/* global row definition */
		SAB_COL = c;	/* global column definition */
		SAB_CHARWIDTH = (((c - 1) >> 5) + 1);	/* character width */

		a = b *e * SAB_CHARWIDTH;	/* file size */

		buff[0] = SETNL + ( b * d * SAB_CHARWIDTH ) - 1;
		buff[1] = LD_SYMBOL_TBL + a;
		write ( outfp, &buff[0], 8 );	/* start symbol tbl loading*/

		a = b * d * SAB_CHARWIDTH * 4;

		while ( a )
		{
			if ( a < 1024 )
			{
				b = a;
			}
			else
			{
				b = 1024;
			}
			x = read( fid, &buff[0], b );
			a = a - x;
			write( outfp, &buff[0], x );
		}
		close (fid);
	}
}

_ldcolor () {
    int     color_aray[3];
    int     i;

    for( i = 0; i < 16; i++)
    {
	color_aray[0] = SETNL;
	color_aray[1] = LD_COLOR_TBL | i;
	color_aray[2] =  ((sab_colors[i][2] & 0xff) << 16) 
		| ((sab_colors[i][1] & 0xff) << 8) | 
		( sab_colors[i][0] & 0xff);	
        write (outfp, &color_aray[0], 12);
	color_aray[0] = SETNL;
	color_aray[1] = LD_COLOR_TBL | (i << 4);
	color_aray[2] =  ((sab_colors[i][2] & 0xff) << 16) 
		| ((sab_colors[i][1] & 0xff) << 8) | 
		( sab_colors[i][0] & 0xff);	
        write (outfp, &color_aray[0], 12);
    }
}

_sab_viewport( left, right, bottom, top, near, far )
float left, right, bottom, top, near, far;
{
	union {
		int i;
		float f;
	} view_mtu[10];

	view_mtu[0].i = PACK16(MTU_E3, 7);
	view_mtu[1].i = PACK16(MTSETVP, 0);
	view_mtu[2].f = left;
	view_mtu[3].f = bottom;
	view_mtu[4].f = near;
	view_mtu[5].f = right;
	view_mtu[6].f = top;
	view_mtu[7].f = far;
	view_mtu[8].i = PACK16( MTEOP, 0 );

	write( outfp, &view_mtu[0], 36 );
}

/* The old subroutine version of the macro */
pack16( ms, ls )
short ms, ls;
{
	union {
		int i;
		short i16[2];
	} equiv;

	equiv.i16[0] = ls;
	equiv.i16[1] = ms;

	return (equiv.i);
}
	
_sab_window(  left, right, bottom, top, near, far )
float left, right, bottom, top, near, far;
{
	
	bcopy( MTU_WIN, 0.0, sizeof (float) * 16);
	bcopy( MTU_PERS, 0.0, sizeof (float) * 16);
	bcopy( MTU_ID, 0.0, sizeof (float) * 16);

	MTU_WIN[0] = MTU_WIN[5] = MTU_WIN[10] = MTU_WIN[15] = 1.0;
	MTU_PERS[0] = MTU_PERS[5] = MTU_PERS[10] = MTU_PERS[15] = 1.0;
	MTU_ID[0] = MTU_ID[5] = MTU_ID[10] = MTU_ID[15] = 1.0;


	/* This matrix although not documented in the gops manual is
	 *  necessary for the mtu to work out right
	 */
	MTU_PERS[0] = 2.0 / (right - left);
	MTU_PERS[5] = 2.0 / (top - bottom);
	MTU_PERS[10] = -(far + near)/ (far - near);
	MTU_PERS[11] = - 0.01;
	MTU_PERS[15] = 1.5;

	/* Window matrix to be down loaded to the mtu with the perspective
	 *  matrix.
	 */
}

write_mtu_mat(mat2view)
mat_t mat2view;
{
	int k, j;

	union {
		int i;
		float f;
	} mtu_aray[36];


	mtu_aray[0].i = PACK16(MTU_E3, 33);
	mtu_aray[1].i = PACK16(MTSETMT3, 0);

	j = 2;

	for (k = 0; k < 4; k++)
	{
#ifdef PaulStay
		mtu_aray[j++].f = mat2view[k];
		mtu_aray[j++].f = mat2view[k + 4];
		mtu_aray[j++].f = mat2view[k + 8];
		mtu_aray[j++].f = mat2view[k + 12];
#else
		mtu_aray[j++].f = mat2view[j-2];
		mtu_aray[j++].f = mat2view[j-2];
		mtu_aray[j++].f = mat2view[j-2];
		mtu_aray[j++].f = mat2view[j-2];
#endif
	}
	for (k = 0; k < 4; k++)
	{
		mtu_aray[j++].f = MTU_PERS[k];
		mtu_aray[j++].f = MTU_PERS[k + 4];
		mtu_aray[j++].f = MTU_PERS[k + 8];
		mtu_aray[j++].f = MTU_PERS[k + 12];
	}

	mtu_aray[j].i = PACK16(MTEOP, 0);

	write(outfp, &mtu_aray[0], (j + 1) * 4);
}

wmask( mask )
int mask;
{
	int buff[2];

	buff[0] = SETNL;
	buff[1] = SET_WRT_MASK | ( mask & 0x00ffffff);
	write( outfp, &buff[0], 8);
}

dmask( mask )
int mask;
{
	int buff[2];

	buff[0] = SET_DISP_MASK | ( mask & 0x00ffffff);
	buff[1] = 0xfc000000;
	write( outfp, &buff[0], 8);
}


clear( left, right, bottom, top)
int left, right, bottom, top;
{
	int aray[8];

	aray[0] = LD_ICR_REG;
	aray[1] = 0x1000000;
	aray[2] = SETNP | ( right - left ); 	/* Length = 1663 */
	aray[3] = SETNL | ( top - bottom );	/* Height = 1247 */
	aray[4] = SETXW | left;			/* Load x-start register */
	aray[5] = SETYW | bottom;		/* Load y-start register */
	aray[6] = DRW_SOLID_BLK;		/* Draw solid block */
	aray[7] = MTEOP;			/* no op code */
	write ( outfp, aray, 32 );

}

sab_linemode( dashed )
int dashed;
{
	int aray[2];
	if (dashed)
		aray[1] = 0xc500ffff;
	else
		aray[1] = 0xc500ffff;

	write( outfp, &aray[0], 4);
}

tablet()
{
	struct sgttyb *argp;
	struct sgttyb tablet;

	if ((tablet_fd = open( "/dev/tty05", 2 )) < 0 )
	{
		perror(stderr,"Error in opening tablet ....");
		return (1);
	}

	/* Set up tablet at the right speed etc. */
	argp = &(tablet);
	ioctl( tablet_fd, TIOCGETP, argp );
	argp->sg_ispeed = B2400;
	argp->sg_ospeed = B2400;
	argp->sg_flags = CRMOD;
	ioctl( tablet_fd, TIOCSETP, argp );
}

_ldreg()
{
	int aray[4];

	aray[0] = 0xc5ffffff;	/* Load Line Style Reg */
	aray[1] = 0x10000000;   /* Load color */
	aray[2] = 0xc0ffffff;   /* Load FG reg */
	aray[3] = 0x1affffff;   /* Load FG color register */

	write( outfp, &aray[0], 16);
}
