/* tek4010emu.c - 19:21 GMT +10:00 Thu 29 July 1993 - modifier Geoffrey Tobin */

/* Not a complete description.
 * This file contains routines for use by TEK4010 emulating VDUs.
 * A real TEK4010 is not supported.
 */

/* From input file "../include/globals.p" */


#include "config.h"
#include "globals.h"

#include "screenio.h"
#include "vdu.h"

#include "tek4010emu.h"


/* Variables Exported, via "tek4010emu.h" */

int maxy, lineht, dragdown;
boolean havesentxy;


Static int oldhiy;   /* for remembering old address in SendXY */
Static int oldhix, oldloy, charwidth;
    /* set by TEK4010LoadFont and used in TEK4010ShowChar */

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 (VOID)
{
  WriteChar (GS);   /* switch to graphics mode */
}
/* Graphic */

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

Static Void Alpha (VOID)
{
  WriteChar (US);   /* switch to alphanumeric mode */
}
/* Alpha */

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

#ifdef __STDC__
Void SendXY (int x, int y)
#else
Void SendXY (x, y)
int x; int y;
#endif
{
  /* 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 TEK4010StartText (VOID)
{
  Alpha();
}
/* TEK4010StartText */

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

#ifdef __STDC__
Void TEK4010MoveToTextLine (int line)
#else
Void TEK4010MoveToTextLine (line)
  int line;
#endif
{
  /* 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 */
}
/* TEK4010MoveToTextLine */

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

Void TEK4010ClearScreen (VOID)
{
  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;
}
/* TEK4010ClearScreen */

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

Void TEK4010StartGraphics (VOID)
{
  if (charsize != loadedsize) 
  {  /* graphics mode was interrupted */
    charsize = loadedsize;
    dragdown = (charsize + 1) * 5;   /* used by VIS500/550 TEK4010ShowChar */

    Graphic();

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

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


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

#ifdef __STDC__
Void TEK4010LoadFont (Char *fontname, int fontsize, double mag,
                      double hscale, double vscale)
#else
Void TEK4010LoadFont (fontname, fontsize, mag, hscale, vscale)
Char *fontname; int fontsize; double mag; double hscale; double vscale;
#endif
{
  /* 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);
  /* Chooose 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 */
}
/* TEK4010LoadFont */

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

#ifdef __STDC__
Void TEK4010ShowChar (int screenh, int screenv, Char ch)
#else
Void TEK4010ShowChar (screenh, screenv, ch)
int screenh; int screenv; Char ch;
#endif
{
  /* 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 */
}
/* TEK4010ShowChar */

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

#ifdef __STDC__
Void TEK4010ShowRectangle (int screenh, int screenv, int width, int height,
			  Char ch)
#else
Void TEK4010ShowRectangle (screenh, screenv, width, height, ch)
  int screenh, screenv;  /* top left pixel */
  int width, height;     /* of rectangle */
  Char ch;               /* black pixel */
#endif
{
  /* 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 */
    }
  }
}
/* TEK4010ShowRectangle */

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

Void InitTEK4010emu (VOID)
{
  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 */
}
/* InitTEK4010emu */

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

/* end tek4010emu.c */
