/*
	SCCS id:	@(#) fb.c	1.29
	Last edit: 	2/7/86 at 15:17:14	G S M
	Retrieved: 	6/12/86 at 10:51:43
	SCCS archive:	/vld/src/libfb/s.fb.c

	Authors:	Mike J. Muuss
			Douglas P. Kingston III
			Gary S. Moss
			U. S. Army Ballistic Research Laboratory
			Aberdeen Proving Ground
			Maryland 21005-5066
*/
#if ! defined( lint )
static
char	sccsTag[] = "@(#) fb.c	1.29	last edit 2/7/86 at 15:17:14";
#endif
/*	F B . C								*/
#include <stdio.h>
#include <fb.h>
#include "./fb_ik.h"
#define FILE_CMAP_ADDR	((long)_fbsize*_fbsize*sizeof(Pixel))
extern char	*getenv(), *malloc();

/* Globals	 							*/
Pixel	_fbbackground;
char	*_fbfile;
int	_fbsize = 512;		/* Default to low resolution.		*/
int	_fbfd = -1;		/* Frame buffer file descriptor.	*/
int	_fbtype;		/* Frame buffer device type.		*/

fbgettype()	{ return _fbtype; } /* Get file or device type.		*/
fbgetsize()	{ return _fbsize; } /* Get file or device size.		*/
void
fbsetsize(size) { _fbsize = size; } /* Set size (square image).		*/

/*	f b o p e n ( )
	Fbsetsize() must be called first if not using LORES (512x512) mode.
	The name of the file (i.e. whether using ik0'h' or 'l') does not
	determine whether high resolution is selected, rather _fbsize does.

	This function sets the globals _fbfd, _fbtype and _fbfile.
	Returns -1 if cannot open file, _fbfd otherwise; this is different
	than previous behavior, i.e., exit on failure.
	'Mode' is a bitmask flag, values are defined in fb.h.
 */
fbopen(file, mode)
char	*file;
int	mode;
	{
	char *evp;

	_fbfile = file;
	if( _fbfile == NULL )
		{ /* No file name provided, use FB_FILE if set.		*/
		if( (evp = getenv("FB_FILE")) != 0 )
			_fbfile = evp;
		else
			_fbfile = FB_DEFAULT; /* Or use default.	*/
		}
	if( strncmp( _fbfile, "/dev/ik", 7 ) == 0 )
		{
		/* We have an Ikonas (aka Adage RDS3000).		*/
		_fbtype = FB_IKONAS;
		_fbfile[8] = _fbsize > 512 ? 'h' : 'l';
		}
	else
	if( strncmp( _fbfile, "/dev/rat", 8 ) == 0 )
		/* We have a Raster Technologies.			*/
		_fbtype = FB_RASTER_TECH;
	else
	if( strncmp( _fbfile, "/dev/tty", 8 ) == 0 )
		{
		/* We have a UNIX pseudo-tty presumably (tty[pqr]*).	*/
		_fbtype = FB_PTTY;
		return	_ptty_open( _fbfile );
		}
	else
		/* We have a UNIX file.					*/
		_fbtype = FB_FILE;
	/* Except for FB_PTTY, there are only 2 legal sizes.		*/
	_fbsize = _fbsize > 512 ? 1024 : 512;

	if( (_fbfd = open( _fbfile, 2 )) < 0 )
		{
		if(   !	(mode & CREATE)
		    ||	_fbtype == FB_IKONAS
		    ||	_fbtype == FB_RASTER_TECH
		    ||	_fbtype == FB_PTTY
			)
			{
			perror( _fbfile );
			return	-1;
			}
		else
			{ /* Must be a UNIX file, try to create it.	*/
			if ((_fbfd = creat( _fbfile, 0666 )) < 0)
				{
				perror( _fbfile );
				return	-1;
				}
			else
				{
				/* File created, open for rd. & wr.	*/
				(void) close( _fbfd );
				if( (_fbfd = open( _fbfile, 2 )) < 0 )
					{
					perror( _fbfile );
					return	-1;
					}
				}
			}
		}
	switch( _fbtype )
		{
	case FB_IKONAS :
		if( _ik_open() == -1 || fbinit() == -1 )
			return	-1;
		break;
	case FB_RASTER_TECH :
		return	_rat_open() == -1 ? -1 : fbinit();
		}
	if( lseek( _fbfd, 0L, 0 ) == -1L )	/* back to beginning again */
		{
		perror( "fbopen lseek" );
		return	-1;
		}
	return	_fbfd;
	}

/*	f b c l o s e ( )						*/
fbclose( fd )
	{
	if( _fbtype == FB_RASTER_TECH )
		return	_rat_close();
	if( _fbtype == FB_PTTY )
		return	_ptty_close();
	else
		return	close( fd );
	}

/*	f b w r i t e ( )						*/
fbwrite (x, y, addr, count)
int	x, y;
Pixel	*addr;
long	count;
	{
	long bytes = count * (long) sizeof (Pixel);
	long	todo;

	if( y < 0 )
		{	/* GSM : this should be an error.		*/
		(void) fprintf( stderr,
				"fbwrite() : negative address! (y=%d)\n",
				y
				);
		return	-1;
		}
#if defined( vax )
	if( _fbtype == FB_IKONAS && count == 1 )
		return	_ikwpixel (x, y, addr);
#endif
	if( _fbtype == FB_RASTER_TECH )
		return	_rat_write( x, y, addr, count );
	if( _fbtype == FB_PTTY )
		return	_ptty_write( x, y, addr, count );
	if( lseek(	_fbfd,
			((long) y * (long) _fbsize + (long) x)
			* (long) sizeof(Pixel),
			0
			)
	    == -1L
		)
		{
		perror( "fbwrite() lseek" );
		return	-1;
		}
	while( bytes > 0 )
		{
		todo = (bytes > MAX_IK_DMA ? MAX_IK_DMA : bytes);
		if( write( _fbfd, (char *) addr, todo ) != todo )
			return	-1;
		bytes -= todo;
		addr += todo / (sizeof (Pixel));
		}
	return	0;
	}

/*	f b r e a d ( )							*/
fbread (x, y, addr, count)
int	x, y;
Pixel *addr;
long	count;
	{
	register long bytes = count * (long)sizeof (Pixel);
	register long todo;

	if( y < 0 )
		{	/* GSM : this should be an error.		*/
		(void) fprintf( stderr,
				"fbread() : negative address! (y=%d)\n",
				y
				);
		return	-1;
		}
#if defined( vax )
	if( _fbtype == FB_IKONAS && count == 1 )
		return	_ikrpixel (x, y, addr);
#endif
	if( _fbtype == FB_RASTER_TECH )
		{
		if( count == 1 )
			return	_rat_rpixel( x, y, addr );
		else
			return	_rat_read( x, y, addr, count );
		}
	if( _fbtype == FB_PTTY )
		return	_ptty_read( x, y, addr, count );
	if( lseek(	_fbfd,
			(((long)y * (long)_fbsize) + (long)x)
			* (long) sizeof(Pixel),
			0
			)
	    == -1L
		)
		{
		perror( "fbread() lseek" );
		return	-1;
		}
	while( bytes > 0 )
		{
		todo = (bytes > MAX_IK_DMA ? MAX_IK_DMA : bytes);
		if( read(_fbfd, (char *)addr, todo ) != todo )
			return	-1;
		bytes -= todo;
		addr += todo / (sizeof(Pixel));
		}
	return	0;
	}

/*	f b _ w m a p ( )						*/
fb_wmap(cp)
register ColorMap *cp;
	{
	switch( _fbtype )
		{
	case FB_FILE :
		if( cp == (ColorMap *) NULL )
			{ /* Do not write default map to file.		*/
			return	0;
			}
		if( lseek( _fbfd, FILE_CMAP_ADDR, 0 ) == -1 )
			{
			perror( "fb_wmap lseek" );
			return	-1;
			}
		if(	write( _fbfd, (char *) cp, sizeof(ColorMap) )
		    !=	sizeof(ColorMap)
			)
			{
		    	perror( "fb_wmap write" );
			return	-1;
			}
		return	0;
	case FB_IKONAS :
		if( _fbtype == FB_IKONAS )
			return	_ik_wmap( cp );
	case FB_RASTER_TECH :
		if( _fbtype == FB_RASTER_TECH )
			return	_rat_wmap( cp );
	case FB_PTTY :
		return	-1;
	default :
		(void) fprintf( stderr, "Unknown device type %d!\n", _fbtype );
		return	-1;
		}
	}

/*	f b _ r m a p ( )
	Returns -1 if it fails to read the map, in the case of a FB_FILE
	there may not be a map.
 */
fb_rmap(cp)
register ColorMap *cp;
	{
	switch( _fbtype )
		{
	case FB_FILE :
		if( lseek( _fbfd, FILE_CMAP_ADDR, 0 ) == -1 )
			{
			perror( "fb_rmap lseek" );
		   	return	-1;
			}
		if(	read( _fbfd, (char *) cp, sizeof(ColorMap) )
		    !=	sizeof(ColorMap)
			)
			{ /* Not necessarily an error.  It is not required
				that a color map be saved and the standard 
				map is not generally saved.
			   */
		   	return	-1;
			}
		return	0;
	case FB_IKONAS :
		return	_ik_rmap( cp );
	case FB_RASTER_TECH :
		return	_rat_rmap( cp );
	case FB_PTTY :
		return	-1;
	default :
		(void) fprintf( stderr, "Unknown device type %d!\n", _fbtype );
		return	-1;
		}
	}

/*	f b s e t b a c k g r o u n d ( )				*/
void
fbsetbackground(pixelp)
Pixel *pixelp;
	{
	_fbbackground = *pixelp;
	if( _fbtype == FB_IKONAS )
		_ik_setbackground(pixelp);
	return;
	}

/*	f b c l e a r ( )						*/
fbclear ()
	{
	switch( _fbtype )
		{
	case FB_FILE :
		return	fast_dma_bg();
	case FB_IKONAS :
		return	_ikclear();
	case FB_RASTER_TECH :
		return	_rat_clear();
	case FB_PTTY :
		return	-1;
	default :
		(void) fprintf( stderr,	"Unknown device type %d!\n", _fbtype );
		return	-1;
		}
	}

/*	f b i n i t ( )							*/
fbinit ()
	{
	switch( _fbtype )
		{
	case FB_FILE :
		return	0;
	case FB_IKONAS :
		return	_ik_init();
	case FB_RASTER_TECH :
		return	_rat_init();
	case FB_PTTY :
		return	-1;
	default :
		(void) fprintf( stderr,	"Unknown device type %d!\n", _fbtype );
		return	-1;
		}
	}

/*	f a s t _ d m a _ b g
	Clear the frame buffer to the background color.
 */
fast_dma_bg()
	{
	static Pixel	*pix_buf = NULL;
	register int	i, y, scans_per_dma;
	register Pixel	*pix_to, *pix_from = &_fbbackground;

	if( pix_buf == NULL )
		if( (pix_buf =
		    (Pixel *) malloc(MAX_IK_PIX_DMA*sizeof(Pixel)))
			== (Pixel *) NULL )
			{
			(void) fprintf( stderr,
			"Malloc failed, fatal error (libfb.a:fast_dma_bg)\n"
					);
			exit( 1 );
			}
	/* Fill buffer with background color.				*/
	for( i = 0, pix_to = pix_buf; i < MAX_IK_PIX_DMA; ++i )
		*pix_to++ = *pix_from;
	/* Send until frame buffer is full.				*/
	scans_per_dma = MAX_IK_PIX_DMA / _fbsize;
	for( y = 0; y < _fbsize; y += scans_per_dma )
		if( fbwrite( 0, y, pix_buf, MAX_IK_PIX_DMA ) == -1 )
			return	-1;
	return	0;
	}
