/* Original Author:         Andrew Trevorrow
   Implementation: Modula-2 under VAX/UNIX 4.2 BSD
   Date Started:   June, 1986

   Description:
   Implements the routines used in VDU-specific modules that emulate
   a Tektronix 4010 terminal (VIS500/550, VT640).
   The screen is assumed to be 780 pixels high by 1024 pixels wide.
   (The actual resolution of an emulating screen may be different, but
   such terminals provide automatic scaling.)
   The bottom left pixel is the point (x=0,y=0); x coordinates
   increase to the right and y coordinates increase up the screen.
   DVItoVDU uses a coordinate scheme in which horizontal (=h) coordinates
   also increase to the right but vertical (=v) coordinates increase DOWN the
   screen, i.e. the top left pixel on the screen is the point (h=0,v=0).
   This means that the Tektronix 4010 routines will have to do a
   simple translation of the vertical coordinates passed by DVItoVDU.

   This version converted to C and ported to BSD and System V UNIX by
   some chaps at Kernel Technology up to September 1989.  Contact
   mjh@uk.co.kernel (Mark J. Hewitt) with bug fixes etc.

   Involved were:	Mark J. Hewitt
   			Dave Dixon
			Marc Hadley
*/

#include "def.h"

static char *sccsid[] = "@(#)tek4010vdu.c	1.1";

unsigned int
   maxy = 779;              /* TEK4010 vertical coordinates are in 0..maxy;
                               some VDUs may change default value of 779     */
unsigned int
   lineht = 26;               /* height in TEK4010 pixels of a text line;
                               some VDUs may change default value of 26      */
unsigned int
   dragdown ;               /*550 VDUs can use this value to drag
                              Terse character baseline down to TeX ref pt   */
short
   havesentxy = 0x00;       /* don't need to send all 4 bytes of address?    */


unsigned int  oldhiy;		/* for remembering old address in SendXYtek() */
unsigned int  oldhix;
unsigned int  oldloy;

unsigned int  charwidth = 13;	/* set by LoadFont and used in ShowChar */
unsigned int  loadedsize = 0;	/* remember alpha size set by last LoadFont; VT640,
				   VIS500/550 VDUs don't actually need to worry
				   about this since they use non-TEK4010 fonts to
				   draw in dialogue region. VIS240 however uses
				   alpha mode font. */
unsigned int  charsize = 0;	/* used to select alpha character size */

/******************************************************************************/

void SendXYtek (x, y)
unsigned int  x, y;
{

/* Translates the given screen address into 4 bytes.
   havesentxy is used to minimize the number of bytes sent: after the first
   4 bytes have been sent, subsequent bytes that don't change need not be sent
   (except for the low x byte which is always sent).
   If the high x byte changes then the low y byte must also be sent.
*/

  unsigned int  hiy, loy, hix, lox;
  short   sendhix;

/* we assume y is in [0..maxy] and x is in [0..1023] */
  hiy = ((int) ' ') + (y / 32);
  hix = ((int) ' ') + (x / 32);
  loy = ((int) '`') + (y % 32);
  lox = ((int) '@') + (x % 32);
  if (havesentxy)
  {
    if (hiy != oldhiy)
    {
      Write ((char) hiy);
      oldhiy = hiy;
    }
    sendhix = (hix != oldhix);
    if ((loy != oldloy) || sendhix)
    {
      Write ((char) loy);
      oldloy = loy;
    }
    if (sendhix)
    {
      Write ((char) hix);
      oldhix = hix;
    }
    Write ((char) lox);
  }
  else				/* send first 4 bytes */
  {
    Write ((char) hiy);
    oldhiy = hiy;
    Write ((char) loy);
    oldloy = loy;
    Write ((char) hix);
    oldhix = hix;
    Write ((char) lox);
    havesentxy = TRUE;
  }
/* SYSDEP: We assume XON/XOFF flow control is enabled to avoid data loss. */
}

/******************************************************************************/

Void TEK4010StartText ()
{

/* DVItoVDU will only call MoveToTextLine, ClearTextLine, ClearScreen and
   ResetVDU while in text mode.
*/

  Write (US);
}

/******************************************************************************/

Void TEK4010MoveToTextLine (line)
unsigned int  line;
{

/* Move cursor to start of given line using lineht.
   At the end of this routine we must be in alpha mode and ready to display
   characters in the default charsize.
*/

  Write (GS);			/* switch to graphics mode */
  SendXYtek (0, maxy + 1 - (line * lineht));
  Write (ESC);			/* reset alpha character size */
  Write ('0');
  charsize = 0;
  charwidth = 13;
  Write (US);			/* back to alpha mode */
}

/******************************************************************************/

Void TEK4010ClearScreen ()
{
  Write (GS);			/* make sure we're in graphics mode */
  Write (ESC);
  Write (FF);			/* erase graphics and put in alpha mode */
  havesentxy = FALSE;		/* ESC FF will home cursor */
  charsize = 0;			/* ESC FF resets character size */
  charwidth = 13;
}

/******************************************************************************/

Void TEK4010StartGraphics ()
{

/* DVItoVDU will only call LoadFont, ShowChar and ShowRectangle while
   in graphics mode.
*/

  if (charsize != loadedsize)
  {				/* graphics mode was interrupted */
    charsize = loadedsize;
    dragdown = (charsize + 1) * 5;/* used by VIS500/550 ShowChar */
    Write (GS);
    Write (ESC);
    Write ((char) (((int) '0') + charsize));
				/* recall last LoadFont character size */
  }
  Write (GS);
  havesentxy = FALSE;		/* safer to send all location bytes anew */
}

/******************************************************************************/

/*ARGSUSED*/
Void TEK4010LoadFont (fontname, fontsize, localmag, hscale, vscale)

char  fontname[];
unsigned int  fontsize;
float   localmag, hscale, vscale;

/* Use the given fontsize to select an appropriate character size
   (based on horizontal scaling only!) for future ShowChar calls.
*/
{
  unsigned int  newsize;

/* convert fontsize into scaled screen pixels using localmag and hscale */
  fontsize = (int) (((float) fontsize) * localmag * hscale + 0.5);
/* Chooose one of the 4 alpha mode character sizes based on fontsize:
   charsize    maxchars/line    relative size     fontsize range
       0             80               x1               0..40
       1             40               x2              41..80
       2             26               x3              81..120
       3             20               x4             121...
   The fontsize ranges were chosen by trial and error.
*/
  if (fontsize < 41)
  {
    newsize = 0;
    charwidth = 13;		/* 1024/80 = 12.8 */
  }
  else
    if (fontsize < 81)
    {
      newsize = 1;
      charwidth = 26;		/* 1024/40 = 25.6 */
    }
    else
      if (fontsize < 121)
      {
	newsize = 2;
	charwidth = 40;		/* 1024/26 = 39.4 */
      }
      else
      {
	newsize = 3;
	charwidth = 52;		/* 1024/20 = 51.2 */
      }
  loadedsize = newsize;		/* remember in case graphics mode is interrupted */
  if (charsize != newsize)
  {				/* change character size */
    charsize = newsize;
    Write (ESC);
    Write ((char) (((int) '0') + charsize));
  }
/* Alpha character reference pts on some emulating VDUs (VIS500/550) are below
   baselines to allow for descenders.
   Such VDUs can use dragdown to drag baselines down to TeX reference pts
   when calling ShowChar.
*/
  dragdown = (charsize + 1) * 5;/* used by VIS500/550 ShowChar */
  Write (GS);			/* must exit in graphics mode */
}

/******************************************************************************/

Void TEK4010ShowChar (screenh, screenv, ch)
unsigned int  screenh, screenv;
char  ch;
/* Show the given Terse character (mapped to ASCII) at the given ref pt.
   We use the charwidth set by last LoadFont call.
*/
{
  char  newch;			/* = TeXtoASCII[ch] */

/* shift character left if it will overlap right edge of screen */
  if ((screenh + charwidth) > 1023)
  {
    screenh = 1023 - charwidth;
  }
/* we assume StartGraphics, LoadFont or last ShowChar has just sent GS */
  SendXYtek (screenh, maxy - screenv);/* move cursor to ref pt */

/* We use TeXtoASCII to map ch into a comparable ASCII character, apart
   from most of the ? characters which we attempt to simulate.
*/

  Write (US);			/* enter alpha mode */
  newch = TeXtoASCII[ch];
  if (newch != '?')
  {
  /* newch is similar to TeX ch */
    Write (newch);
  }
  else
  {				/* attempt to display something other than ? */
    switch (ch)
    {
      case 013: 
      case 014: 
      case 015: 
      case 016: 
      case 017:  /* ff, fi, fl, ffi, ffl */ ;
	{
	  Write ('f');
	/* only simulate rest of ligature if room at right edge */
	  if ((screenh + 2 * charwidth - (charwidth / 2)) <= 1023)
	  {
	    Write (GS);
	    SendXYtek (screenh + charwidth - (charwidth / 2), maxy - screenv);
	    Write (US);
	    switch (ch)
	    {
	      case 013: 
		{
		  Write ('f');
		  break;
		}
	      case 014: 
		{
		  Write ('i');
		  break;
		}
	      case 015: 
		{
		  Write ('l');
		  break;
		}
	      case 016: 
	      case 017: 
		{
		  Write ('f');
		  if (screenh + 3 * charwidth - 2 * (charwidth / 2) <= 1023)
		  {
		    Write (GS);
		    SendXYtek (screenh + 2 * charwidth - 2 * (charwidth / 2), maxy - screenv);
		    Write (US);
		    if (ch == 016)
		      Write ('i');
		    else
		      Write ('l');
		  }
		}
	    }
	  }
	  break;
	}
      case 031: 
	{
	  Write ('B');
	  break;
	}			/* German sharp S */
      case 032: 
      case 033: 
      case 035: 
      case 036: 		/* diphthongs: ae, oe, AE, OE */
	{
	  switch (ch)
	  {
	    case 032: 
	      {
		Write ('a');
		break;
	      }
	    case 033: 
	      {
		Write ('o');
		break;
	      }
	    case 035: 
	      {
		Write ('A');
		break;
	      }
	    case 036: 
	      {
		Write ('O');
		break;
	      }
	  }
	  if ((screenh + 2 * charwidth - (charwidth / 2)) <= 1023)
	  {
	    Write (GS);
	    SendXYtek (screenh + charwidth - (charwidth / 2), maxy - screenv);
	    Write (US);
	    switch (ch)
	    {
	      case 032: 
	      case 033: 
		{
		  Write ('e');
		  break;
		}
	      case 035: 
	      case 036: 
		{
		  Write ('E');
		  break;
		}
	    }
	  }
	  break;
	}
      case 034: 
      case 037: 		/* Scandinavian slashed o and O */
	{
	  switch (ch)
	  {
	    case 034: 
	      {
		Write ('o');
		break;
	      }
	    case 037: 
	      {
		Write ('O');
		break;
	      }
	  }
	  Write (GS);
	  SendXYtek (screenh, maxy - screenv);/* overwrite */
	  Write (US);
	  Write ('/');
	  break;
	}
      case 040: 
	{
	  Write ('\'');
	  break;
	}			/* Polish suppressed l and L */
      default: 
	Write ('?');
    }
  }
  Write (GS);			/* must exit in graphics mode */
}

/******************************************************************************/

/*ARGSUSED*/
Void TEK4010ShowRectangle (screenh, screenv,/* top left pixel */
  width, height,		/* of rectangle */
  ch)				/* black pixel */
unsigned int  screenh, screenv, width, height;
char  ch;
/* Display the given rectangle (without using the given black pixel character).
   DVItoVDU ensures that the top left position is visible and that the given
   dimensions do not go beyond the window edges.
*/
{

  unsigned int  i, endpt;

/* DVItoVDU ensures width and height > 0 */
  if (height < width)
  {
  /* show row vectors */
    endpt = screenh + width - 1;
    for (i = 0; i <= height - 1; i++)
    {
      Write (GS);
      SendXYtek (screenh, maxy - (screenv + i));/* move cursor to start of row */
      SendXYtek (endpt, maxy - (screenv + i));/* draw vector to end of row */
    }
  }
  else				/* show column vectors */
  {
    endpt = maxy - (screenv + height - 1);
    for (i = 0; i <= width - 1; i++)
    {
      Write (GS);
      SendXYtek (screenh + i, maxy - screenv);/* move cursor to start of column */
      SendXYtek (screenh + i, endpt);/* draw vector to end of column */
    }
  }
}

/******************************************************************************/

Void TEK4010ClearTextLine (line)

unsigned int  line;
{
  Write (CAN);			/* switch to Alphanumeric mode */
  TEK4010MoveToTextLine (line);	/* move to start of line */
  Write (ESC);
  Write ('K');			/* erase to end of line */
}

Void TEK4010ResetVDU ()
{
  Write (CAN);
}

void InitTEK4010 ()
{
  DVIstatusl = 1;		/* DVItoVDU assumes top text line = 1 */
  windowstatusl = 2;
  messagel = 3;
  commandl = 4;
  bottoml = 33;			/* also number of text lines on VIS500 screen */
/* The above values assume the VIS500 is in Alphanumeric mode;
   the following values assume it is emulating a Tektronix 4010.
   Note that windowv must be given a value using DVItoVDU's coordinate scheme
   where top left pixel is (0,0).
*/
  windowv = 92;			/* approx. height in TEK4010 pixels of 4 text lines;
				   i.e. 4 * 780/34 */
  windowh = 0;
  windowht = 780 - windowv;
  windowwd = 1024;

  MoveToTextLine = TEK4010MoveToTextLine;
  ClearTextLine = TEK4010ClearTextLine;
  ClearScreen = TEK4010ClearScreen;
  StartText = TEK4010StartText;
  StartGraphics = TEK4010StartGraphics;
  LoadFont = TEK4010LoadFont;
  ShowChar = TEK4010ShowChar;
  ShowRectangle = TEK4010ShowRectangle;
  ResetVDU = TEK4010ResetVDU;

  Write (GS);
  Write (ESC);
  Write ('@');			/* solid fill for rectangular draw and fill */
  Write (CAN);
}
