/* vdu.c - VDU driver interface 
   $Id: vdu.c,v 0.2 1997/03/28 03:17:44 tjchol01 Exp $
   Authors: Andrew Trevorrow, Ian Dall, Geoffrey Tobin, Tomasz J. Cholewo
 */

#include "dvgt.h"
#include "vdu.h"
#include "options.h"
#include "screenio.h"

/* Exported via "vdu.h" */

boolean vdu_clears_lines;	/* Can current VDU clear a single line? */

int maxy, lineht, dragdown;
boolean havesentxy;

static int oldhiy;		/* for remembering old address in SendXY */
static int oldhix, oldloy, charwidth;
    /* set by LoadFont and used in ShowChar */

static int loadedsize;
/* remember alpha size set by last TEK4010LoadFont;
   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. */

static int charsize;		/* used to select alpha character size */


/*--------------------------------------------------------------------*/

static void 
Graphic ()
{
  WriteChar (GS);		/* switch to graphics mode */
}

/* Graphic */

/*--------------------------------------------------------------------*/

static void 
Alpha ()
{
  WriteChar (US);		/* switch to alphanumeric mode */
}

/* Alpha */

/*--------------------------------------------------------------------*/

void 
SendXY (int x, int 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.
   */

  int hiy, loy, hix, lox;
  boolean sendhix;

  /* we assume y is in [0..maxy] and x is in [0..1023] */
  hiy = y / 32 + ' ';
  hix = x / 32 + ' ';
  loy = (y & 31) + '`';
  lox = (x & 31) + '@';
  if (havesentxy)
    {
      if (hiy != oldhiy)
	{
	  WriteChar (hiy);
	  oldhiy = hiy;
	}
      sendhix = (hix != oldhix);
      if (loy != oldloy || sendhix)
	{
	  WriteChar (loy);
	  oldloy = loy;
	}
      if (sendhix)
	{
	  WriteChar (hix);
	  oldhix = hix;
	}
      WriteChar (lox);
    }
  else
    {
      /*
         SYSDEP:  We _assume_ XON/XOFF flow control is _enabled_,
         to avoid data loss.
       */
      WriteChar (hiy);
      oldhiy = hiy;
      WriteChar (loy);
      oldloy = loy;
      WriteChar (hix);
      oldhix = hix;
      WriteChar (lox);
      havesentxy = true;
      /* send first 4 bytes */
    }
}
/* SendXY */

/*--------------------------------------------------------------------*/
void 
ClearTextLine (int line)
{
  /* Do nothing as NCSA Telnet (version 2.3.0.3) can only clear
     the entire screen in tek4010 and NOT just a line.
     Live with the overwriting.  In any case,
     the next DisplayPage will clear and rewrite the status lines.
   */
}
/* ClearTextLine */

/*--------------------------------------------------------------------*/

void 
ResetVDU ()
{
  WriteChar (CAN);
  WriteChar (CAN);
}
/* ResetVDU */
/*--------------------------------------------------------------------*/

void 
StartText ()
{
  Alpha ();
}
/* StartText */

/*--------------------------------------------------------------------*/

void 
MoveToTextLine (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.
   */

  Graphic ();

  SendXY (0, maxy - line * lineht + 1);
  textcolumn = 0;

  WriteChar (ESC);		/* reset alpha character size */
  WriteChar ('0');
  charsize = 0;
  charwidth = 13;

  Alpha ();			/* back to alpha mode */
}

/* MoveToTextLine */

/*--------------------------------------------------------------------*/

void 
ClearScreen ()
{
#ifdef NCSA_TELNET
  WriteChar (ESC);
  WriteChar (FF);		/* erase graphics and put in alpha mode */
#endif

  Graphic ();

  WriteChar (ESC);		/* erase graphics and put in alpha mode */
  WriteChar (FF);

  havesentxy = false;		/* ESC FF will home cursor */
  charsize = 0;			/* ESC FF resets character size */
  charwidth = 13;
}

/* ClearScreen */

/*--------------------------------------------------------------------*/

void 
StartGraphics ()
{
  if (charsize != loadedsize)
    {				/* graphics mode was interrupted */
      charsize = loadedsize;
      dragdown = (charsize + 1) * 5;	/* used by VIS500/550 ShowChar */

      Graphic ();

      WriteChar (ESC);
      WriteChar (charsize + '0');	/* recall last LoadFont character size */
    }
  Graphic ();

  havesentxy = false;		/* safer to send all bytes anew */
}
/* StartGraphics */


/*--------------------------------------------------------------------*/

void 
LoadFont (char *fontname, int fontsize, double mag,
	  double hscale, double vscale)
{
  /* Use the given fontsize to select an appropriate character size
     (based on horizontal scaling only!) for future TEK4010ShowChar calls.
   */

  int newsize;

  /* convert fontsize into scaled screen pixels using mag and hscale */
  fontsize = (int) (fontsize * mag * hscale + 0.5);
  /* Choose one of the 4 alpha mode character sizes based on fontsize:
     charsize    max chars/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;
      WriteChar (ESC);
      WriteChar (charsize + '0');
    }
  /* 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 TEK4010ShowChar.
   */
  dragdown = (charsize + 1) * 5;	/* used by VIS500/550 TEK4010ShowChar */

  Graphic ();			/* must return from LoadFont in graphics mode */
}
/* LoadFont */

/*--------------------------------------------------------------------*/

void 
ShowChar (int screenh, int screenv, char ch)
{
  /* Show the given Terse character (mapped to ASCII) at the given ref pt.
     We use the charwidth set by last TEK4010LoadFont call.
   */

  char newch;			/* = TeXtoASCII[ch] */

  /* shift character left if it will overlap right edge of screen */

  if (screenh + charwidth > 1023)
    screenh = 1023 - charwidth;

  /* We no longer _assume_ TEK4010StartGraphics, TEK4010LoadFont,
     or last TEK4010ShowChar has just sent GS */

  Graphic ();
  SendXY (screenh, maxy - screenv);	/* move cursor to ref pt */
  Alpha ();			/* enter alpha mode */

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

  newch = TeXtoASCII[ch - NUL];
  if (newch != '?')
    {
      /* newch is similar to TeX ch */
      WriteChar (newch);
    }
  else
    {
      /* attempt to display something other than ? */
      switch (ch)
	{
	case 0xb:		/* ff, fi, fl, ffi, ffl */
	case 0xc:
	case 0xd:
	case 0xe:
	case 0xf:
	  WriteChar ('f');
	  /* only simulate rest of ligature if room at right edge */
	  if (screenh + charwidth * 2 <= 1023)
	    {
	      Graphic ();
	      SendXY (screenh + charwidth, maxy - screenv);
	      Alpha ();

	      switch (ch)
		{
		case 0xb:	/* ff */
		  WriteChar ('f');
		  break;

		case 0xc:	/* fi */
		  WriteChar ('i');
		  break;

		case 0xd:	/* fl */
		  WriteChar ('l');
		  break;

		case 0xe:
		case 0xf:
		  WriteChar ('f');
		  /* only simulate rest of ligature if room at right edge */
		  if (screenh + charwidth * 3 <= 1023)
		    {
		      Graphic ();
		      SendXY (screenh + charwidth * 2, maxy - screenv);
		      Alpha ();

		      if (ch == 0xe)	/* ffi */
			WriteChar ('i');
		      else	/* if (ch == 0xf) */
			/* ffl */
			WriteChar ('l');
		    }		/* fi */
		  break;
		}		/* switch */
	    }			/* fi */
	  break;
	case 0x19:		/* German sharp S */
	  WriteChar ('B');
	  break;
	case 0x1a:		/* diphthongs: ae, oe, AE, OE */
	case 0x1b:
	case 0x1d:
	case 0x1e:
	  switch (ch)
	    {
	    case 0x1a:		/* ae */
	      WriteChar ('a');
	      break;
	    case 0x1b:		/* oe */
	      WriteChar ('o');
	      break;
	    case 0x1d:		/* AE */
	      WriteChar ('A');
	      break;
	    case 0x1e:		/* OE */
	      WriteChar ('O');
	      break;
	    }
	  if (screenh + charwidth * 2 <= 1023)
	    {
	      Graphic ();
	      SendXY (screenh + charwidth, maxy - screenv);
	      Alpha ();

	      switch (ch)
		{
		case 0x1a:	/* ae */
		case 0x1b:	/* oe */
		  WriteChar ('e');
		  break;

		case 0x1d:	/* AE */
		case 0x1e:	/* OE */
		  WriteChar ('E');
		  break;
		}
	    }
	  break;
	case 0x1c:		/* Scandinavian slashed o and O */
	case 0x1f:
	  switch (ch)
	    {
	    case 0x1c:		/* o/ */
	      WriteChar ('o');
	      break;

	    case 0x1f:		/* O/ */
	      WriteChar ('O');
	      break;
	    }

	  Graphic ();
	  SendXY (screenh, maxy - screenv);	/* overwrite */
	  Alpha ();

	  WriteChar ('/');
	  break;
	case 0x20:		/* Polish suppressed l and L */
	  WriteChar ('\'');
	  break;
	case '?':		/* question mark */
	  WriteChar ('?');

	default:
	  /*  WriteChar ('?');  */
	  /*  show hexadecimal character code, instead of question mark:  */
	  WriteChar ('\\');
	  /* only simulate rest of ligature if room at right edge */
	  if (screenh + charwidth * 3 <= 1023)
	    {
	      String outstr;	/* ample size */

	      Graphic ();
	      SendXY (screenh + charwidth * 2, maxy - screenv);
	      Alpha ();

	      sprintf (outstr, "%.2x", ch);
	      WriteChar (outstr[0]);
	      WriteChar (outstr[1]);
	    }
	  break;
	}			/* switch */
    }				/* fi */

  Graphic ();			/* must return from ShowChar in graphics mode */
}
/* ShowChar */

/*--------------------------------------------------------------------*/

void 
ShowRectangle (int screenh, int screenv, int width, int 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.
   */

  int i, endpt;

  /* DVItoVDU ensures width and height > 0 */
  if (height < width)
    {
      /* show row vectors */
      endpt = screenh + width - 1;
      for (i = 0; i < height; i++)
	{
	  Graphic ();

	  SendXY (screenh, maxy - screenv - i);		/* move cursor to start of row */
	  SendXY (endpt, maxy - screenv - i);	/* draw vector to end of row */
	}
    }
  else
    {
      /* show column vectors */
      endpt = maxy - screenv - height + 1;
      for (i = 0; i < width; i++)
	{
	  Graphic ();

	  SendXY (screenh + i, maxy - screenv);
	  /* move cursor to start of column */
	  SendXY (screenh + i, endpt);	/* draw vector to end of column */
	}
    }
}
/* ShowRectangle */

/*--------------------------------------------------------------------*/

void 
InitVDU ()
{
  vdu_clears_lines = false;
  havesentxy = false;		/* for first SendXY call */
  charsize = 0;			/* the default character size */
  loadedsize = charsize;	/* for first TEK4010StartGraphics call */
  charwidth = 13;		/* 1024 / 80 = 12.8 */
  maxy = 779;			/* some VDUs may want to change this */
  lineht = 26;			/* 30 text lines; 26 * 30 = 780 */
  textlinewidth = 80;		/* text characters per line */
  /* The dialog region will be the top 4 text lines in Tek mode:
     Line 1 = DVI status line,
     Line 2 = window status line,
     Line 3 = message line,
     Line 4 = command line.
     The window region will be text lines 5 to 33 in TEK mode.
     gt - in NCSA Telnet in TEK mode on my monitor (Viewsonic 5E VGA),
     only 30 lines fit.  If we use line 33, it wraps around!
   */

  /* The following line numbers assume NCSA Telnet is in VT100 mode. */
  DVIstatusl = 1;		/* DVItoVDU assumes top text line = 1 */
  windowstatusl = 2;
  messagel = 3;
  commandl = 4;
#ifdef VIJAY_BOTTOML
  bottoml = 33;			/* also number of text lines on screen */
#else /* according to gt's experience */
  bottoml = 30;			/* also number of text lines on screen */
#endif

  /* The following values assume NCSA Telnet 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).
   */

  /* 
     Approx. height, in TEK4010 pixels, of 4 text lines, viz. 92
     (i.e. ~ 4 * 780/34), does not work in the case of NCSA Telnet's
     Tek mode.  Value 115 arrived at by pure trial and error.
   */
  windowv = 115;
  windowh = 0;
  windowht = 780 - windowv;
  windowwd = 1024;

  textlinewidth = 72;		/* text characters per line - a guess */

  WriteChar (GS);
}
/* InitTEK4010emu */

/*--------------------------------------------------------------------*/

/* end vdu.c */