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

   This module was the idea of John Mann (Monash University).
   Chunky bitmap graphics are used in ShowRectangle.
   VT220 is switched to 132 column mode and is assumed to
   be in VT200 mode with 7 bit controls.

   Description:

   Implements the InitVT220() procedure that initializes the generic VDU
   routines and parameters used in DVItoVDU.  DVItoVDU assumes text
   lines start at 1 and increase down the screen.  When updating the
   window in graphics mode (using ShowChar and ShowRectangle), DVItoVDU
   assumes the top left screen pixel is (0,0); h coordinates increase to
   the right and v coordinates increase down.  The VT220 coordinate
   scheme for graphics is exactly the same.  The bottom 20 rows in
   Bitmap represent a 132 by 20 character window region.  Each character
   is made up of 5 vertical "pixels" and so 32 (=2^5) characters can be
   down-loaded and used to draw all possible 1 by 5 pixel rectangles.

   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[] = "@(#)vt220vdu.c	1.1";

extern Void ANSIClearTextLine(),
            ANSIMoveToTextLine(),
            ANSILoadFont();

Void ShowBitmap ();
Void VT220ClearScreen ();
Void VT220StartGraphics ();
Void VT220ShowChar ();
Void VT220ShowRectangle ();
Void VT220ResetVDU ();
Void LoadPixels ();
Void MoveQuick ();

unsigned int  cursrow, curscol;	/* VT220ShowChar()remembers cursor location */

#define HPIXELS 1		/* horizontal pixels per char position */
#define VPIXELS 5		/* vertical pixels per char position */
#define FLAG 7			/* SYSDEP: to overcome a compiler limitation */
#define HSCREENMAX 131		/* max horizontal char coordinate */
#define VSCREENMAX 23		/* max vertical char coordinate */

typedef short BitValue;

 /* SYSDEP: was 0..VPIXELS but compiler seemed to have trouble with an array element
    size of 6 bits!!! */
typedef short ByteSet;

int  rectcount;	/* keeps a count of ShowRectangle calls; reset in
				   ShowBitmap() */
short   Bitmap[HSCREENMAX + 1][VSCREENMAX + 1];

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

void InitVT220 ()
{

/* The dialogue region is the top 4 lines.
   The window region is the remaining area of the screen
   (the bottom 20 rows in Bitmap).
*/

  DVIstatusl = 1;
  windowstatusl = 2;
  messagel = 3;
  commandl = 4;
  bottoml = VSCREENMAX + 1;
/* DVItoVDU's coordinate scheme is the same as the VT220 scheme. */
  windowh = 0;
  windowv = 4 * VPIXELS;	/* = height of 4 dialogue lines */
  windowwd = (HSCREENMAX + 1) * HPIXELS;
  windowht = (VSCREENMAX + 1) * VPIXELS - windowv;

  StartText = ShowBitmap;	/* flush the graphics Bitmap */
  MoveToTextLine = ANSIMoveToTextLine;
  ClearTextLine = ANSIClearTextLine;
  ClearScreen = VT220ClearScreen;
  StartGraphics = VT220StartGraphics;
  LoadFont = ANSILoadFont;
  ShowChar = VT220ShowChar;
  ShowRectangle = VT220ShowRectangle;
  ResetVDU = VT220ResetVDU;

  Write (ESC);
  WriteString ("[?3h");		/* 132 column mode */
  LoadPixels ();
}

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

Void LoadPixels ()
{

/* Down-load the chunky graphics character set into the VT220. */

  Write (ESC);
  Write ('P');
  WriteString ("1;1;0;3{Z");	/* WriteString("1;1;2;3;2;1{&%C"); */
  WriteString ("BBBBBBBB/????????;");
  WriteString ("KKKKKKKK/????????;");
  WriteString ("NNNNNNNN/????????;");
  WriteString ("oooooooo/????????;");
  WriteString ("rrrrrrrr/????????;");
  WriteString ("{{{{{{{{/????????;");
  WriteString ("~~~~~~~~/????????;");
  WriteString ("????????/BBBBBBBB;");
  WriteString ("BBBBBBBB/BBBBBBBB;");
  WriteString ("KKKKKKKK/BBBBBBBB;");
  WriteString ("NNNNNNNN/BBBBBBBB;");
  WriteString ("oooooooo/BBBBBBBB;");
  WriteString ("rrrrrrrr/BBBBBBBB;");
  WriteString ("{{{{{{{{/BBBBBBBB;");
  WriteString ("~~~~~~~~/BBBBBBBB;");
  WriteString ("????????/KKKKKKKK;");
  WriteString ("BBBBBBBB/KKKKKKKK;");
  WriteString ("KKKKKKKK/KKKKKKKK;");
  WriteString ("NNNNNNNN/KKKKKKKK;");
  WriteString ("oooooooo/KKKKKKKK;");
  WriteString ("rrrrrrrr/KKKKKKKK;");
  WriteString ("{{{{{{{{/KKKKKKKK;");
  WriteString ("~~~~~~~~/KKKKKKKK;");
  WriteString ("????????/NNNNNNNN;");
  WriteString ("BBBBBBBB/NNNNNNNN;");
  WriteString ("KKKKKKKK/NNNNNNNN;");
  WriteString ("NNNNNNNN/NNNNNNNN;");
  WriteString ("oooooooo/NNNNNNNN;");
  WriteString ("rrrrrrrr/NNNNNNNN;");
  WriteString ("{{{{{{{{/NNNNNNNN;");
  WriteString ("~~~~~~~~/NNNNNNNN");
 /* SYSDEP: compiler did not like '\' !!! */
  Write (ESC);
  Write (0134);

  Write (ESC);
  WriteString ("*Z");		/* set as G1 character set */
}

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

void ClearBitmap ()
{

/* Clear the Bitmap. */

  unsigned int  h, v;

  for (v = 4; v <= VSCREENMAX; v++)/* ignore dialogue lines 0..3 */
  {
    for (h = 0; h <= HSCREENMAX; h++)
    {
      Bitmap[h][v] = 0;		/* ByteSet {} */
    }
  }
}

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

Void ShowBitmap ()
{

/* Display only the flagged characters in the Bitmap. */

  unsigned int  h, v;

/*Write(SO);    assume only working over 7 bit comm lines */

  Write (ESC);
  Write ('n');
  for (v = 4; v <= VSCREENMAX; v++)/* ignore dialogue lines */
  {
    for (h = 0; h <= HSCREENMAX; h++)
    {
      if ((1 << FLAG) & Bitmap[h][v])
      {
      /* send flagged character */
	Bitmap[h][v] = (Bitmap[h][v] & (~(1 << FLAG)));/* clear flag */
	MoveQuick (h, v);
	Write ((char) (((unsigned) Bitmap[h][v]) + 32));
      }
    }
  }
  Write (SI);			/* assume only working over 7 bit comm lines */
  WriteBuffer ();
  rectcount = 0;
}

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

Void VT220ClearScreen ()
{
  Write (ESC);
  WriteString ("[2J");		/* erase entire screen */
  ClearBitmap ();		/* reset Bitmap */
}

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

Void VT220StartGraphics ()
{

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

  rectcount = 0;
  cursrow = 0;			/* for MoveQuick() */
}

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

Void MoveQuick (screenh, screenv)

unsigned int  screenh, screenv;
{

/* Move cursor to given screen position.
   We remember the cursor position (cursrow,curscol) so we can reduce the
   output bytes needed to do the next MoveQuick()
   StartGraphics resets the position to an undefined state (cursrow = 0).
   We also reset when the cursor reaches the right edge (= windowwd) to
   avoid possibility of any auto wrap.
*/

  unsigned int  amount;

/* first translate DVItoVDU coordinates into actual screen location */
  screenh++;
  screenv++;
  if (cursrow == screenv)
  {
  /* The cursor is on the same line as in previous MoveQuick()call so we only need
     to move left or right, and probably just a small amount (if at all). */
    if (screenh == curscol)
    {				/* cursor in correct location */
      curscol++;		/* cursor will move right when ch written */
    }
    else
      if (screenh < curscol)
      {
      /* move cursor left */
	amount = curscol - screenh;
	Write (ESC);
	Write ('[');
	if (amount > 1)
	{			/* default is 1 col */
	  WriteCard (amount);
	  curscol -= amount - 1;/* no need to do this if amount==1 */
	}
	Write ('D');
      }
      else			/* move cursor right */
      {
	amount = screenh - curscol;
	Write (ESC);
	Write ('[');
	if (amount > 1)
	  WriteCard (amount);	/* default is 1 col */
	curscol += amount + 1;
	Write ('C');
      }
  }
  else				/* cursrow undefined or ch on a new line */
  {
    MoveAbs (screenv, screenh);
    cursrow = screenv;
    curscol = screenh + 1;	/* cursor will move right when ch written */
  }
  if (screenh == ((unsigned) windowwd))
  {
  /* ch will be written at right edge */
    cursrow = 0;		/* so avoid auto wrap next time around */
  }
}

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

Void VT220ShowChar (screenh, screenv, ch)

unsigned int  screenh, screenv;
char  ch;
{

/* Show the given Terse character (mapped to ASCII) at the given position. */

  if (rectcount > 0)
  {
  /* flush Bitmap if ShowRectangle/s are pending */
    ShowBitmap ();
  }
  MoveQuick (screenh, screenv / VPIXELS);
  Write (TeXtoASCII[ch]);
}

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

/*ARGSUSED*/
Void VT220ShowRectangle (screenh, screenv,/* top left pixel */
  width, height,		/* size of rectangle */
  ch)				/* black pixel */
unsigned int  screenh, screenv, width, height;
char  ch;
{
/* Set the given rectangular Bitmap region.
   DVItoVDU ensures the top left position is visible and the given
   dimensions do not go beyond the window edges.
*/

  unsigned int  h, v, vrow;

  for (v = screenv; v <= screenv + height - 1; v++)
  {
    for (h = screenh; h <= screenh + width - 1; h++)
    {
    /* set bit h,v in Bitmap */
      vrow = v / VPIXELS;
      Bitmap[h][vrow] = (Bitmap[h][vrow] | (1 << (v % VPIXELS)));
      Bitmap[h][vrow] = (Bitmap[h][vrow] | (1 << FLAG));
    /* flag so char will be sent */
    }
  }
  rectcount++;
  if (rectcount == 400)
  {
  /* avoid too much of a pause before flushing Bitmap */
    ShowBitmap ();
  }
}

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

Void VT220ResetVDU ()
{

/* We don't do a hardware reset, but leave VDU in 80 column mode. */
/* we should really save the state in InitVT220()and restore it here */

  Write (ESC);
  WriteString ("[?3l");		/* 80 column mode */
}

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









