/* Support for opening a tek4010 window from napsaterm on the Amiga.  This
   option is enabled if the symbol TEKTRONICS is defined at compile time. */

/* tek4010.c is by RKNOP, Robert Knop, rknop@mop.caltech.edu */

/* tek4010 version 1.4 beta */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "amiga.h"
#include "nifty.h"
#include "napsaprefs.h"
#include "wimp.h"
#include "nio.h"
#include "ascii_ctrl.h"
#include <graphics/displayinfo.h>
#include <graphics/gfxmacros.h>
#include <intuition/screens.h>
#include <libraries/asl.h>
#include <libraries/gadtools.h>
#include <proto/asl.h>
#include <proto/diskfont.h>
#include <proto/intuition.h>
#include <proto/gadtools.h>
#include <proto/graphics.h>
#include <proto/keymap.h>
#include <proto/console.h>
/*
#include <proto/exec.h>
*/

#ifdef TEKTRONICS
/*#echo "Compiling tek4010 unit"*/

/********** NOTE -- This is a hack I'm using to get bloody napsaterm to link 
            with the debugger! *************/

const UBYTE _DevName[]="Rob's Hack";

/**************** End of NOTE ***************************************************/

/* Local functions not needed by the outside */

int opentekwin(void);
void calctekwinsize(void);
void newtekfont(void);
int decode_integer(char);
void Safe_Move(struct RastPort *,WORD,WORD);
void Safe_Draw(struct RastPort *,WORD,WORD);
void Safe_Rect(struct RastPort *,WORD,WORD);
void scrtotek(void);
void graph_clear(void);
void setdrawmode(ULONG mode,WORD noerase);
void redrawtek(void);
void gin_mode_toggle(struct MenuItem *);
void ginmouse(struct IntuiMessage *msg);
void ginkeys(struct IntuiMessage *);
void sendreport(WORD,WORD,char);
void menu_close(void);
void tekmenu(struct IntuiMessage *);
void chk_active(struct MenuItem *);
void chk_front(struct MenuItem *);

#define FULLWIDTH  4096
#define FULLHEIGHT 3120
#define BUFFERSIZE 65536

int tekmode=0;          /*Global variable, 1 if tek window is current window*/
int bypassmode=0;
static int fullcursor=0;
static char eolstring[]="\015\0\0";    /*End of report string <CR>*/
static mode=TEK_ALPHA;
static int buffermode=0;
#define DEF_ACTIVE CHECKED
#define DEF_FRONT CHECKED
static int activateonswitch=DEF_ACTIVE;
static int forwardonswitch=DEF_FRONT;

extern struct KeyMap *default_map;     /* In keymap.c */
extern struct KeyMap *used_map;        /* In keymap.c */
extern struct IOStdReq consolereq;     /* In keymap.c */
#define ConsoleDevice (consolereq.io_Device)

extern struct Window *w;     /*vt100 window in wimp.c*/
struct Window *tekwindow=NULL;
static WORD maxx,maxy,minx,miny;
long tekwinmask=0;
static int winw,winh;
static int tekwidth=FULLWIDTH,tekheight=FULLHEIGHT,tekleft=0,tekbot=0;
static Point Aspect;
static char *tekbuffer=NULL;
static char *startbuffer,*endbuffer;
#define ENDCHECK  if (endbuffer<tekbuffer) endbuffer+=BUFFERSIZE;
static char tektitle[80];

/* Text/Font information */

static struct TextAttr deftekattr={"topaz.font",8,0x00,0x00};
static struct TextAttr tekattr;
static struct TextFont *tekfont;

/*Tektronics drawing variables*/

static int last_mode;
static int esc_mode=0;
static int first_vector=1,incr_pen_down=0;
static WORD increment=4;              /*Move in incr mode*/
static WORD lastx,lasty;              /*Previous position in screen coords*/
static WORD tekx,teky;                /*Prev. pos. in tek coords*/
static UWORD lyok;                    /* Flag, is last y out of bounds*/
static char HiY,Extra,LoY,HiX,LoX;    /*Tektronics position*/
static char LoYorExtra=0;           /*temp storage for ambiguity*/
static char LastTag;                  /*Tag bits of last vector byte*/

/*********************************************************************************/
/* Open the tek4010 window on the napsaterm public screen                        */
/*   By nature, it opens to the full size of the screen sans the title bar.      */
/*********************************************************************************/

static struct NewMenu tekmenus[]=
   {   {NM_TITLE, "TEK4010",         0, 0, 0, 0,},
       { NM_ITEM, "To VT102",        "T", 0, 0, tek_vt100,},
       { NM_ITEM, "To Tek",          "G", 0, 0, vt100_tek,},
       { NM_ITEM, "Graph Clear",     "C", 0, 0, graph_clear,},
       { NM_ITEM, "Redraw",          "R", 0, 0, redrawtek,},
       { NM_ITEM, "Set Font...",     "F", 0, 0, newtekfont,},
       { NM_ITEM, NM_BARLABEL,       0, 0, 0, 0,},
       { NM_ITEM, "Close",           "Q", 0, 0, menu_close,},
       {NM_TITLE, "Config",          0, 0, 0, 0,},
       { NM_ITEM, "Activate Windows",0,MENUTOGGLE|CHECKIT|DEF_ACTIVE,0,chk_active,},
       { NM_ITEM, "Front Windows",   0,MENUTOGGLE|CHECKIT|DEF_FRONT,0,chk_front,},
       {NM_TITLE, "GIN Cursor",      0, 0, 0, 0,},
       { NM_ITEM, "Cursor On",       0,MENUTOGGLE|CHECKIT,0,gin_mode_toggle,},
#define CURSOR_MENUNUM 2
       {  NM_END, NULL,              0, 0, 0, 0,},
   };
static APTR visinfo;
static struct Menu *menuStrip;

int opentekwin(void)
{  ULONG modeID;
   struct DisplayInfo di;
   struct Screen *s;
   char *pubscname;
   static int first=1;     /*Flag, first call to opentekwin*/

   if (!(tekbuffer=AllocMem(BUFFERSIZE,MEMF_PUBLIC))) return(0);
   startbuffer=endbuffer=tekbuffer;

   pubscname=np.pubscname;
   s=LockPubScreen(pubscname);   /*Try to lock the public screen*/
   if (!s && pubscname) s=LockPubScreen(pubscname=NULL);  /*Try def. pub scr*/
   if (!s)     /*Complete and utter failure*/
   {  FreeMem(tekbuffer,65536);
      return(0);
   }

   modeID=GetVPModeID(&s->ViewPort);
   if (!GetDisplayInfoData(NULL,(UBYTE *)&di,sizeof(di),DTAG_DISP,modeID))
   {  FreeMem(tekbuffer,65536);
      UnlockPubScreen(NULL,s);
      return(0);
   }
   Aspect.x=di.Resolution.x;  Aspect.y=di.Resolution.y;

   sprintf(tektitle,"%s (TEK-4010)",np.title ? np.title : ProgName);
   tekwindow=
      OpenWindowTags(NULL, WA_PubScreen,s , WA_Left,0 , WA_Top,s->BarHeight ,
                     WA_Width,s->Width , WA_Height,s->Height-s->BarHeight ,
                     WA_MinWidth,70 , WA_MinHeight, 30 ,
                     WA_MaxWidth,s->Width , WA_MaxHeight,s->Height ,
                     WA_ScreenTitle,ProgName , WA_Title,tektitle ,
                     WA_Flags,WFLG_SMART_REFRESH | WFLG_CLOSEGADGET |
                              WFLG_DEPTHGADGET | WFLG_DRAGBAR |
                              WFLG_NOCAREREFRESH | WFLG_ACTIVATE |
                              WFLG_SIZEGADGET ,
                     WA_IDCMP,IDCMP_CLOSEWINDOW | IDCMP_NEWSIZE | 
                              IDCMP_MENUPICK | IDCMP_RAWKEY ,
                     TAG_DONE);
   UnlockPubScreen(NULL,s);

   if (!tekwindow) return(0);
   visinfo=GetVisualInfo(s,TAG_END);      /*Check ret val?*/
   menuStrip=CreateMenus(tekmenus,TAG_END);      /*Check return?*/
   LayoutMenus(menuStrip,visinfo,TAG_END);       /*Check return?*/
   SetMenuStrip(tekwindow,menuStrip);            /*Check return?*/

   tekwinmask=1<<tekwindow->UserPort->mp_SigBit;
   calctekwinsize();
   setdrawmode(JAM1,1);
   if (first)
   {  tekattr=deftekattr;        /*Set to default the first time only*/
      first=0;
   }
   if (!(tekfont=OpenDiskFont(&tekattr)))
   {  tekattr=deftekattr;       /*Failed to open font, open topaz 8*/
      tekfont=OpenDiskFont(&tekattr);
   }
   SetFont(tekwindow->RPort,tekfont);      /*Set font for alpha mode*/
   graph_clear();     /*Always start with cleared graphics screen*/
   return(1);
}

/******************************************************************************/
/* closetekwin() -- close the tek window, quit tek terminal mode              */
/******************************************************************************/

void closetekwin(void)
{  struct Message *msg;

   if (!tekwindow) return;
   tekmode=0;
   tekwinmask=0;
   while (msg=GetMsg(tekwindow->UserPort))     /*Clean messages out of user port*/
      ReplyMsg(msg);
   ClearMenuStrip(tekwindow);                  /*Get rid of those GadTools menus*/
   FreeMenus(menuStrip);
   FreeVisualInfo(visinfo);
   CloseWindow(tekwindow);
   tekwindow=NULL;                     /*Make sure we know there is no tekwindow*/
   CloseFont(tekfont);                 /*Close the font, we no longer need it*/
   FreeMem(tekbuffer,65536);
}

/******************************************************************************/
/* calctekwinsize() -- load winw and winh according to proper Tek screen size */
/******************************************************************************/

void calctekwinsize(void)
{  int w,h;

   minx=tekwindow->BorderLeft;
   maxx=tekwindow->Width-tekwindow->BorderRight-1;
   w=maxx-minx;
   miny=tekwindow->BorderTop;
   maxy=tekwindow->Height-tekwindow->BorderBottom-1;
   h=maxy-miny;

   winh=h;
   winw=(tekwidth*winh*Aspect.y) / (tekheight*Aspect.x);
   if (winw>w)
   {  winw=w;
      winh=(tekheight*winw*Aspect.x) / (tekwidth*Aspect.y);
   }
}

/*****************************************************************************/
/* Put up the ASL font requester to get a new font                           */
/*****************************************************************************/

void newtekfont(void)
{  struct FontRequester *req;
   struct TextAttr oldattr;

   if (!tekwindow) return;
   if (!(req=(struct FontRequester *)AllocAslRequestTags(
              ASL_FontRequest,ASL_FontName,(ULONG)tekattr.ta_Name,
              ASL_FontHeight,(ULONG)tekattr.ta_YSize,
              ASL_FuncFlags,FONF_FIXEDWIDTH,ASL_Window,tekwindow,TAG_END)))
      return;
   if (AslRequest(req,NULL))
   {  oldattr=tekattr;
      tekattr=req->fo_Attr;
      CloseFont(tekfont);       /*Close the old font*/
      if (!(tekfont=OpenDiskFont(&tekattr)))       /*Try to open new font*/
      {  tekattr=oldattr;
         if (!(tekfont=OpenDiskFont(&tekattr)))      /*If fail, go back to old*/
         {  tekattr=deftekattr;
            tekfont=OpenDiskFont(&tekattr);  /*If _that_ fails, go to topaz/8*/
         }
      }
      SetFont(tekwindow->RPort,tekfont);
   }
   FreeAslRequest(req);
}
/*****************************************************************************/
/* Set tektronics mode                                                       */
/*****************************************************************************/

void set_tekmode(unsigned short newmode)
{  struct RastPort *rp;
   WORD x,y;

   rp=tekwindow->RPort;
   if ((mode=newmode)==TEK_ALPHA)     /*Do some bounds checking*/
   {  if (!(rp->cp_x > (x=tekwindow->Width - tekwindow->BorderRight - 
                          tekfont->tf_XSize)))
         x=rp->cp_x;
      if (!(rp->cp_y < (y=tekwindow->BorderTop + tekfont->tf_Baseline)))
         y=rp->cp_y;
      Move(rp,x,y);    /*Now we know it is safe to render a letter*/
   }
}

/****************************************************************************/
/*  Switch from vt100 mode to tek mode without clearing the tek screen      */
/****************************************************************************/

void vt100_tek(void)
{
   if (!tekwindow)
   {  if (!opentekwin()) return;
   }
   if (activateonswitch) ActivateWindow(tekwindow);
   if (forwardonswitch) WindowToFront(tekwindow);
   tekmode=1;
}

/*******************************************************************************/
/*  Switch from tek mode to vt100 mode                                         */
/*******************************************************************************/

void tek_vt100(void)
{
   if (activateonswitch) ActivateWindow(w);
   if (forwardonswitch) WindowToFront(w);
   tekmode=0;
}

/******************************************************************************/
/* graph_clear() -- clear the graphics screen and the tek buffer              */
/******************************************************************************/

void graph_clear(void)
{  WORD xscr,yscr;

   startbuffer=endbuffer=tekbuffer;
   SetAPen(tekwindow->RPort,0);
   RectFill(tekwindow->RPort,tekwindow->BorderLeft,tekwindow->BorderTop,
            tekwindow->Width-1-tekwindow->BorderRight,
            tekwindow->Height-1-tekwindow->BorderBottom);
   HiY=Extra=LoY=HiX=LoX=LoYorExtra=(char)0;   /*Initialize tek position variables*/
   first_vector=1;               /*Always start out with first vector*/
   esc_mode=0;                   /*Always clear escape mode*/
   SetDrPt(tekwindow->RPort,0xffff);       /*Start with solid draw pattern*/
   setdrawmode(JAM1,1);
   esc_mode=0;      /*Start with esc_mode cleared*/
   set_tekmode(TEK_ALPHA);          /*Start in alpha mode -oo-*/
   bypassmode=0;    /*Start with bypass mode off*/
   xscr=tekwindow->BorderLeft;
   yscr=tekwindow->BorderTop+tekfont->tf_Baseline+1;
   Safe_Move(tekwindow->RPort,xscr,yscr);                       /*Home*/
   scrtotek();
}

/*****************************************************************************/
/* setdrawmode() -- changes drawing mode for screen                          */
/*****************************************************************************/

void setdrawmode(ULONG mode,WORD noerase)
{  struct DrawInfo *di;

   if (di=GetScreenDrawInfo(tekwindow->WScreen))
   {  if (noerase) SetAPen(tekwindow->RPort,di->dri_Pens[TEXTPEN]);
      else SetAPen(tekwindow->RPort,di->dri_Pens[BACKGROUNDPEN]);
      FreeScreenDrawInfo(tekwindow->WScreen,di);
   }
   else
   {  if (noerase) SetAPen(tekwindow->RPort,1);
      else SetAPen(tekwindow->RPort,0);
   }
   SetDrMd(tekwindow->RPort,mode);
}

/*****************************************************************************/
/* gin_mode_toggle() -- turn on/off GIN cursor                               */
/*****************************************************************************/

    /**** NOTE : deal with setting the checkmark in the menu!!!! *****/
    /**** Also deal with setting the pointer to crosshairs ****/

UWORD __chip Crosshairs[] =     /*Done by hand*/
{  0x0000,0x0000,   /* Two system reserved words */

   0x0000,0x0000,
   0x0000,0x0100,
   0x7efc,0x0100,
   0x4004,0x0100,
   0x4004,0x0100,
   0x4004,0x0100,
   0x4004,0x0100,
   0x4004,0x0100,
   0x0100,0xfffe,
   0x4004,0x0100,
   0x4004,0x0100,
   0x4004,0x0100,
   0x4004,0x0100,
   0x4004,0x0100,
   0x7efc,0x0100,
   0x0000,0x0100,

   0x0000,0x0000           /* Two more system reserved words */
};

void gin_mode_toggle(struct MenuItem *menu)
{  static int lasttekmode=TEK_ALPHA,lastbuffermode;
   struct MenuItem *item;

   if (menu) item=menu;
   else item=ItemAddress(menuStrip,CURSOR_MENUNUM);
   if ( (menu && (item->Flags&CHECKED)) || ((!menu) && (mode!=TEK_GIN)) )
   {  lasttekmode=mode;
      lastbuffermode=buffermode;
      buffermode=0;
      set_tekmode(TEK_GIN);
      bypassmode=1;
      if (fullcursor)
         ModifyIDCMP(tekwindow,tekwindow->IDCMPFlags |
                               IDCMP_MOUSEMOVE|IDCMP_MOUSEBUTTONS);
      else ModifyIDCMP(tekwindow,tekwindow->IDCMPFlags|IDCMP_MOUSEBUTTONS);
      SetPointer(tekwindow,Crosshairs,16,16,-8,-8);
      item->Flags|=CHECKED;
      ResetMenuStrip(tekwindow,menuStrip);
   }
   else
   {  set_tekmode(lasttekmode);
      /*set_tekmode(TEK_ALPHA);*/
      lasttekmode=TEK_ALPHA;
      buffermode=lastbuffermode;
      bypassmode=0;
      ModifyIDCMP(tekwindow,tekwindow->IDCMPFlags & 
                            ~(IDCMP_MOUSEMOVE|IDCMP_MOUSEBUTTONS));
      ClearPointer(tekwindow);
      item->Flags &= ~CHECKED;
      ResetMenuStrip(tekwindow,menuStrip);
   }
}

/*****************************************************************************/
/* ginmouse() -- deal with GIN mouse events                                  */
/*****************************************************************************/

void ginmouse(struct IntuiMessage *msg)
{  WORD xscr,yscr,xgin,ygin;

   if (msg->Class==MOUSEMOVE)
   {  /* Deal with moving big crosshairs if necessary */
   }
   else if (msg->Class==MOUSEBUTTONS)
   {  if (msg->Code==SELECTDOWN)
      {  Forbid();
         xscr=tekwindow->MouseX;
         yscr=tekwindow->MouseY;
         Permit();
         xgin=((xscr-tekwindow->BorderLeft)*tekwidth)/winw;
         ygin=(-(yscr-tekwindow->BorderTop-winh)*tekheight)/winh;
         sendreport(xgin,ygin,' ');
         gin_mode_toggle(NULL);  /*quit GIN mode*/
      }
   }
   ReplyMsg((struct Message *)msg);
}

/*****************************************************************************/
/* ginkeys() -- deal with GIN RAWKEY events                                  */
/*****************************************************************************/

void ginkeys(struct IntuiMessage *msg)
{  struct InputEvent ie;
   char buf[4];
   WORD xscr,yscr,xgin,ygin;

   if (msg->Code & IECODE_UP_PREFIX) return;
   ie.ie_Code=msg->Code;
   ie.ie_NextEvent=NULL;
   ie.ie_SubClass=0;
   ie.ie_Class=IECLASS_RAWKEY;
   ie.ie_Qualifier=msg->Qualifier;
   ie.ie_position.ie_addr=*((APTR *)msg->IAddress);
   RawKeyConvert(&ie,buf,4,np.national ? used_map : default_map);

   /*** Send report with mouse x,y tek position */
   Forbid();
   xscr=tekwindow->MouseX;
   yscr=tekwindow->MouseY;
   Permit();
   xgin=((xscr-tekwindow->BorderLeft)*tekwidth)/winw;
   ygin=(-(yscr-tekwindow->BorderTop-winh)*tekheight)/winh;
   sendreport(xgin,ygin,buf[0]);
   ReplyMsg((struct Message *)msg);
   gin_mode_toggle(NULL);
}

/*****************************************************************************/
/* sendreport() -- Send 4010 style report to host                            */
/*   Pass x,y in 4014 4096-pixel coordinate system.                          */
/*   This routine converts it to 1024 before sending the report              */
/*****************************************************************************/

void sendreport(WORD x,WORD y,char c)
{  char report[10];
   int i=-1,j;

   if (c) report[++i]=c;
   switch (mode)                  /* Set bits 2-3 of status based on mode*/
   {  case TEK_POINT  : report[++i]=0x01; break;   /* Bit 0 always set */
      case TEK_ALPHA  : report[++i]=0x05; break;
      case TEK_VECTOR : report[++i]=0x09; break;
      case TEK_GIN    : break;
      default         : report[++i]=0x0d; break;
   }
   report[++i]=0x20 | ( (x & 0x0f80)>>7 );      /*HiX*/
   report[++i]=0x20 | ( (x & 0x007c)>>2 );      /*LoX*/
   report[++i]=0x20 | ( (y & 0x0f80)>>7 );      /*HiY*/
   report[++i]=0x20 | ( (y & 0x007c)>>2 );      /*LoY*/
   for (j=0;j<strlen(eolstring);j++)
      report[++i]=eolstring[j];
   report[++i]='\0';
   nwrite(report,i);
}

/*****************************************************************************/
/* redrawtek() -- redraw tek window from buffer                              */
/*****************************************************************************/

void redrawtek(void)
{  char *c;

   /* Start with a simulation of graph_clear, all but the buffer clearing*/
   SetAPen(tekwindow->RPort,0);
   RectFill(tekwindow->RPort,tekwindow->BorderLeft,tekwindow->BorderTop,
            tekwindow->Width-1-tekwindow->BorderRight,
            tekwindow->Height-1-tekwindow->BorderBottom);
   HiY=Extra=LoY=HiX=LoX=LoYorExtra=(char)0;    /*Initialize tek position variables*/
   first_vector=1;               /*Always start out with first vector*/
   esc_mode=0;                   /*Always clear escape mode*/
   SetDrPt(tekwindow->RPort,0xffff);       /*Start with solid draw pattern*/
   setdrawmode(JAM1,1);

   buffermode=1;
   if (startbuffer>endbuffer)
   {  c=tekout(startbuffer,tekbuffer+BUFFERSIZE);
      c=tekout(tekbuffer,endbuffer);
   }
   else c=tekout(startbuffer,endbuffer);
   buffermode=0;
   /* Deal with c not being equal to text+count*/
}

/********************************************************************************/
/* tekout() -- handle characters incoming into the Tek screen                   */
/*   Generally, this will be called from termout (see emulate.c).  It returns   */
/*    a pointer to the first charcter after a return to VT100 mode, or to       */
/*    endtext.  This way, termout can call tekout and use it's return value for */
/*    updating its pointer into the string of characters which have been handled*/
/********************************************************************************/

/***** Meanings of esc modes:
   0      no escape
   1      ESC
   2      ESC [ or CSI
   3      ESC "
   4      ESC [ ?
   6      ESC %
   7      ESC % !
****/

char *tekout(char *text,char *endtext)
{  char *c;
   WORD xscr,yscr,xgin,ygin;
   static int esc_number=0,seq_length=0;

   for (c=text;c<endtext;c++)
   {  if (!buffermode)
      {  *endbuffer=*c;
         if (endbuffer-tekbuffer==BUFFERSIZE-1) endbuffer=tekbuffer;
         else endbuffer++;
         if (startbuffer>=endbuffer)
         {  startbuffer++;
            if  (startbuffer-tekbuffer==BUFFERSIZE-1) startbuffer=tekbuffer;
         }
      }

      if (mode==TEK_GIN)                   /* GIN (bypass) mode sequences */
      {  switch(*c)
         {  case ESC : if (esc_mode) {}
                       else
                       {  esc_mode=1;
                          esc_number=0;
                       }
                       break;

            case ENQ : if (esc_mode==1)
                       {  Forbid();
                          xscr=tekwindow->MouseX;
                          yscr=tekwindow->MouseY;
                          Permit();
                          xgin=((xscr-tekwindow->BorderLeft)*tekwidth)/winw;
                          ygin=(-(yscr-tekwindow->BorderTop-winh)*tekheight)/winh;
                          sendreport(xgin,ygin,'\0');
                          gin_mode_toggle(NULL);  /*quit GIN mode*/
                       }
                       break;

            case LF  :
            case CR  : gin_mode_toggle(NULL);     /*quit GIN mode, don't*/
                       break;                     /* change esc_mode */

            case BS :
            case VT :
            case HT : if (esc_mode==0) gin_mode_toggle(NULL);
                      else esc_mode=0;
                      break;

            case BEL : DisplayBeep(tekwindow->WScreen);
                       esc_mode=0;
                       gin_mode_toggle(NULL);
                       break;

            case CAN :
            case SUB : esc_mode=0;   /* -oo- These just set bypass or GIN */
                       break;

            case US : esc_mode=0;
                      gin_mode_toggle(NULL);
                      set_tekmode(TEK_ALPHA);
                      break;

            case FF : if (esc_mode==1)
                      {  gin_mode_toggle(NULL);
                         graph_clear();
                      }
                      break;
         }
      }    /*End of GIN mode handling */

      else if (!esc_mode && (*c>=0x20))   /*If not a CTRL char or ESC sequence...*/
      {  if ((mode==TEK_VECTOR)||(mode==TEK_POINT)||(mode==TEK_BLOCK))
         {  if (decode_integer(*c))       /*Non-zero result indicates LoX rec.*/
            {  tekx=((short)(HiX & 0x1f)<<7) |    /*Build new X tek position*/
                    ((short)(LoX & 0x1f)<<2) |
                    ((short)(Extra & 0x03));
               teky=((short)(HiY & 0x1f)<<7) |    /*Build new Y tek position*/
                    ((short)(LoY & 0x1f)<<2) |
                    ((short)(Extra & 0x0c)>>2);
               xscr=tekwindow->BorderLeft+(tekx*winw)/tekwidth;  /*Screen position*/
               yscr=tekwindow->BorderTop+winh-(teky*winh)/tekheight;
               if (mode==TEK_POINT)
               {  WritePixel(tekwindow->RPort,xscr,yscr);  /*Does check bounds*/
                  Safe_Move(tekwindow->RPort,xscr,yscr);
               }
               else
               {  if (first_vector)
                  {  Safe_Move(tekwindow->RPort,xscr,yscr);
                     first_vector=0;
                  }
                  else
                  {  if (mode==TEK_VECTOR)
                        Safe_Draw(tekwindow->RPort,xscr,yscr);
                     else
                        Safe_Rect(tekwindow->RPort,xscr,yscr);
                  }
               }
            }
         }                 /*End of TEK_VECTOR and TEK_POINT handling*/
         else if (mode==TEK_ALPHA)
         {  if (tekwindow->RPort->cp_x >
                   tekwindow->Width-tekwindow->BorderRight-tekfont->tf_XSize)
            {  xscr=tekwindow->BorderLeft;
               yscr=tekwindow->RPort->cp_y+tekfont->tf_YSize+1;
               if ( (yscr+tekfont->tf_YSize-tekfont->tf_Baseline) > 
                                          tekwindow->Height-tekwindow->BorderBottom )
                  yscr=tekwindow->BorderTop+tekfont->tf_Baseline+1;
               Safe_Move(tekwindow->RPort,xscr,yscr);
            }
            Text(tekwindow->RPort,c,1);  /*Write the character*/            
         }                 /*End of TEK_ALPHA handling*/
         else if (mode==TEK_INCR)
         {  if (*c==' ') incr_pen_down=0;
            else if (*c=='P') incr_pen_down=1;
            else
            {  switch(*c)
               {  case 'D' : teky+=1; break;
                  case 'E' : teky+=1; tekx+=1; break;
                  case 'A' : tekx+=1; break;
                  case 'I' : teky-=1; tekx+=1; break;
                  case 'H' : teky-=1;
                  case 'J' : teky-=1; tekx-=1; break;
                  case 'B' : tekx-=1;
                  case 'F' : tekx-=1; teky+=1; break;
               }
               xscr=tekwindow->BorderLeft+(tekx*winw)/tekwidth;  /*Screen position*/
               yscr=tekwindow->BorderTop+winh-(teky*winh)/tekheight;
               Safe_Draw(tekwindow->RPort,xscr,yscr);
            }
         }
      }


      /* NOTE -- ESC sequences will be interpreted in all modes, even if they
         aren't supposed to be.   Fix this later?*/
      /* GIN mode is a special case, above */

      else                        /*Handling of ESC sequences and CTRL codes*/
      {  ++seq_length;
         if ((esc_mode==3 || esc_mode==4) && *c>='0' && *c<='9')
            esc_number=10*esc_number+ *c-'0';
         else switch(*c)             /* *c is a control character of some sort*/
         {  /* Vector & Point mode sequences */
            case ESC : if (esc_mode) {}      /* ESC ESC == ESC */
                       else
                       {  esc_mode=seq_length=1;
                          esc_number=0;
                       }
                       break;

            case STX : if (esc_mode==1 && mode==TEK_VECTOR)
                       {  set_tekmode(TEK_BLOCK);
                          first_vector=1;
                       }
                       esc_mode=0;
                       break;     

            case ETX : if (esc_mode==1 && mode==TEK_BLOCK)
                       {  set_tekmode(TEK_VECTOR);
                          first_vector=1;
                       }
                       esc_mode=0;
                       break;

            case SOH : if (esc_mode==1) setdrawmode(JAM1,1);
                       esc_mode=0;              /*Writing mode = draw*/
                       break;

            case DLE : if (esc_mode==1) setdrawmode(JAM1,0);
                       esc_mode=0;              /*Writing mode = erase*/
                       break;

            case NAK : if (esc_mode==1) setdrawmode(INVERSVID,1);
                       esc_mode=0;              /*Writing mode=invert*/
                       break;

            case EM :  if (esc_mode==1) setdrawmode(JAM2,1);
                       esc_mode=0;              /*Writing mode=replace*/
                       break;

            case BEL : DisplayBeep(tekwindow->WScreen);
                       esc_mode=0;
                       break;
      
            case CR  : if (mode!=TEK_ALPHA) mode=TEK_ALPHA;
                       else
                       {  Safe_Move(tekwindow->RPort,tekwindow->BorderLeft,
                                    tekwindow->RPort->cp_y);
                          scrtotek();
                       }
                       break;
      
            case FF : if (esc_mode==1)
                      {  graph_clear();   /*More??*/
                         if (mode==TEK_INCR) set_tekmode(TEK_ALPHA);
                      }
                      esc_mode=0;
                      break;
      
            case CAN : if (esc_mode==0)
                       {  tek_vt100();          /*Switch back to vt100 mode*/
                          if (!buffermode)
                          {   endbuffer--;  /*Kill vt100 esc seq from buffer*/
                              ENDCHECK;
                          }
                          return(++c);          /*Return position to keep going*/
                       }
                       else if (esc_mode==1)
                       {  /*set bypass condition -oo- !!!!!!!!!*/
                          esc_mode=0;
                          if (mode==TEK_ALPHA) graph_clear();
                       }
                       else esc_mode=0;
                       break;
      
      
            case SUB : if (esc_mode==1)
                       {  gin_mode_toggle(NULL);
                          if (!buffermode)
                          {  endbuffer-=seq_length;  /*No buffering GIN mode*/
                             ENDCHECK;
                          }
                          esc_mode=esc_number=0;
                       }
                       else esc_mode=0;
                       break;
      
            case FS  : set_tekmode(TEK_POINT);
                       esc_mode=0;
                       break;
      
            case GS  : set_tekmode(TEK_VECTOR);
                       esc_mode=0;
                       first_vector=1;
                       break;
      
            case RS  : set_tekmode(TEK_INCR);
                       esc_mode=0;
                       break;
      
            case US  : set_tekmode(TEK_ALPHA);
                       esc_mode=0;
                       break;
      
            case '`' : if (esc_mode==1)
                       {  SetDrPt(tekwindow->RPort,0xffff);
                          SetAPen(tekwindow->RPort,1);
                          esc_mode=0;
                       }
                       break;
      
            case 'a' : if (esc_mode==1)
                       {  SetDrPt(tekwindow->RPort,0xaaaa);
                          SetAPen(tekwindow->RPort,1);
                       }
                       esc_mode=0;
                       break;
      
            case 'b' : if (esc_mode==1)
                       {  SetDrPt(tekwindow->RPort,0xfafa);
                          SetAPen(tekwindow->RPort,1);
                       }
                       esc_mode=0;
                       break;
      
            case 'c' : if (esc_mode==1)
                       {  SetDrPt(tekwindow->RPort,0xeeee);
                          SetAPen(tekwindow->RPort,1);
                       }
                       esc_mode=0;
                       break;
      
            case 'd' : if (esc_mode==1)
                       {  SetDrPt(tekwindow->RPort,0xfcfc);
                          SetAPen(tekwindow->RPort,1);
                       }
                       esc_mode=0;
                       break;
      
            case 'e' : if (esc_mode==1)
                       {  SetDrPt(tekwindow->RPort,0xff55);
                          SetAPen(tekwindow->RPort,1);
                       }
                       esc_mode=0;
                       break;
      
            case 'f' : if (esc_mode==1)
                       {  SetDrPt(tekwindow->RPort,0xff6c);
                          SetAPen(tekwindow->RPort,1);
                       }
                       esc_mode=0;
                       break;
      
            case 'g' : if (esc_mode==1)
                       {  SetDrPt(tekwindow->RPort,0xf0f0);
                          SetAPen(tekwindow->RPort,1);
                       }
                       else if (esc_mode==3)   /*From SuperMode*/
                       {  tek_vt100();
                          if (!buffermode)
                          {  endbuffer-=seq_length; /*Remove to vt100 seq from buf*/
                             ENDCHECK;
                          }
                          esc_mode=0;
                          return(++c);
                       }
                       /* Note -- I ought to check to make sure there was a 0*/
                       esc_mode=0;
                       break;
      
            case 'h' : if (esc_mode==1)
                       {  SetDrPt(tekwindow->RPort,0xffff);
                          SetAPen(tekwindow->RPort,2);
                       }
                       else if (esc_mode==4)     /*ESC [ ? <number> h*/
                       {  if (esc_number==38) {}  /*Switch to tek-- do nil*/
                       }
                       esc_mode=0;
                       break;
      
            case 'i' : if (esc_mode==1)
                       {  SetDrPt(tekwindow->RPort,0xaaaa);
                          SetAPen(tekwindow->RPort,2);
                       }
                       esc_mode=0;
                       break;
      
            case 'j' : if (esc_mode==1)
                       {  SetDrPt(tekwindow->RPort,0xfafa);
                          SetAPen(tekwindow->RPort,2);
                       }
                       esc_mode=0;
                       break;
      
            case 'k' : if (esc_mode==1)
                       {  SetDrPt(tekwindow->RPort,0xeeee);
                          SetAPen(tekwindow->RPort,2);
                       }
                       esc_mode=0;
                       break;
      
            case 'l' : if (esc_mode==1)
                       {  SetDrPt(tekwindow->RPort,0xfcfc);
                          SetAPen(tekwindow->RPort,2);
                       }
                       else if (esc_mode==4)     /*ESC [ ? <number> l*/
                       {  if (esc_number==38)   /*Switch to vt100*/
                          {  esc_mode=0;
                             tek_vt100();
                             if (!buffermode)
                             {  endbuffer-=seq_length; /*Remove esc seq from buffer*/
                                ENDCHECK;
                             }
                             return(++c);
                          }
                       }
                       esc_mode=0;
                       break;
      
            case 'm' : if (esc_mode==1)
                       {  SetDrPt(tekwindow->RPort,0xff55);
                          SetAPen(tekwindow->RPort,2);
                       }
                       esc_mode=0;
                       break;
      
            case 'n' : if (esc_mode==1)
                       {  SetDrPt(tekwindow->RPort,0xff6c);
                          SetAPen(tekwindow->RPort,2);
                       }
                       esc_mode=0;
                       break;
      
            case 'o' : if (esc_mode==1)
                       {  SetDrPt(tekwindow->RPort,0xf0f0);
                          SetAPen(tekwindow->RPort,2);
                       }
                       esc_mode=0;
                       break;
      
            case '?' : if (esc_mode==1)
                       {  LoY=0x7f;  /*DEL character replacement*/
                          esc_mode=0;
                       }
                       else if (esc_mode==2)     /*<CSI>*/
                          esc_mode=4;
                       else esc_mode=0;
                       break;
      
            /* End of Vector Mode sequences*/
            /* Alpha mode sequences */
            /* See Vector mode for ESC,BEL,ESC/FF,ESC/SUB,FS,GS,RS,CR*/
      
            case ENQ : /* -oo- report terminal status */
                       if (esc_mode) esc_mode=0;
                       break;
      
            case DEL:  if (esc_mode) break;
                       /* Otherwise same as BS */
            case BS  : /* Move one space left */
                       if (esc_mode) esc_mode=0;
                       if (mode==TEK_ALPHA)
                       {  if (tekwindow->RPort->cp_x < tekfont->tf_XSize)
                             xscr=tekwindow->BorderLeft;
                          else xscr=tekwindow->RPort->cp_x-tekfont->tf_XSize;
                          Safe_Move(tekwindow->RPort,xscr,tekwindow->RPort->cp_y);
                          scrtotek();
                       }
                       break;
      
            case HT  : /*Horiz tab*/
                       if (esc_mode) esc_mode=0;
                       break;
      
            case LF  : /*CRLF*/
                       if ( esc_mode==0 && mode==TEK_ALPHA )
                       {  xscr=tekwindow->BorderLeft;
                          yscr=tekwindow->RPort->cp_y+tekfont->tf_YSize+1;
                          if ( (yscr+tekfont->tf_YSize-tekfont->tf_Baseline) > 
                                     tekwindow->Height-tekwindow->BorderBottom )
                             yscr=tekwindow->BorderTop+tekfont->tf_Baseline+1;
                          Safe_Move(tekwindow->RPort,xscr,yscr);
                          scrtotek();
                       }
                       /* esc_mode=0; */ /* ESC LF == ESC */
                       break;
      
            case VT  : /*Move one line up*/
                       if (esc_mode) esc_mode=0;
                       break;
      
            /* End of Alpha mode sequences*/
            /* Incremental Plot Mode sequences*/
            /* End of Incremental Plot Mode sequences*/
            /* SuperMode sequences -- this needs some work!*/
      
            case '"' : if (esc_mode==1) esc_mode=3;
                       else esc_mode=0;
                       break;
      
            case '[' : if (esc_mode==1) esc_mode=2;  /*<CSI>*/
                       else esc_mode=0;
                       break;

            case '%' : if (esc_mode==1) esc_mode=6;
                       else esc_mode=0;
                       break;

            case '!' : if (esc_mode==6) esc_mode=7;
                       else esc_mode=0;
                       break;

            case '0' : if (esc_mode==7) vt100_tek();
                       esc_mode=0;
                       break;

            case '1' : if (esc_mode==7)
                       {  tek_vt100();
                          if (!buffermode)
                          {  endbuffer-=seq_length;  /*Remove esc seq from buffer*/
                             ENDCHECK;
                          }
                       }
                       esc_mode=0;
                       break;

            case '2' : if (esc_mode==1)
                       {  tek_vt100();
                          if (!buffermode)
                          {  endbuffer-=seq_length;
                             ENDCHECK;
                          }
                       }
                       esc_mode=0;
                       break;

            /* case g h l ?  handled above */
      
            /* End of SuperMode sequences */
      
            default : if (esc_mode) esc_mode=0;
                      break;
         }        /*End of switch over escaped characters*/
      }
   }            /*End of the massive for loop over input characters*/
   return(c);        /*Should be text+count*/
}

/**********************************************************************/
/* decode_integer()          Works on decoding a Tek integer          */
/*    Returns 1 if LoX was received, and terminal should act on the   */
/*      integer that has just been built.                             */
/**********************************************************************/

int decode_integer(char c)
{  char tagbits;
   int rv;

   rv=0;
   tagbits=c & 0x60;
   if (tagbits==0x20)        /*HiY or HiX*/
   {  if (LastTag==0x60)       /*Last was LoY, this is HiX*/
      {  HiX=c;
         if (LoYorExtra)
         {  LoY=LoYorExtra;
            LoYorExtra=0;
         }
      }
      else HiY=c;
   }
   else if (tagbits==0x60)     /*LoY or Extra*/
   {  if (LastTag==0x60)         /*Last was Extra, this is LoY*/

      {  Extra=LoYorExtra;
         LoYorExtra=0;
         LoY=c;
      }
      else                       /*Unknown if LoY or Extra*/
         LoYorExtra=c;
   }
   else if (tagbits==0x40)      /*LoX, draw the vector whatever*/
   {  if (LastTag==0x60 && LoYorExtra)  /* last was LoY */
        LoY=LoYorExtra;
      LoX=c;
      rv=1;
   }
   LastTag=tagbits;
   return(rv);
}

/********************************************************************************/
/* Draw to tekwindow, first checking to make sure we don't go outside boudries  */
/*  Note: because coordinates are represented with 12-bit unsigned integers,    */
/*    it is impossible for x to be out of bounds, and y can only be too large   */
/*    (assuming a y resolution of 3025 rather than 4096)                        */
/********************************************************************************/

void Safe_Move(struct RastPort *rp,WORD x,WORD y)
{
   lastx=x; lasty=y;
   if (!(lyok=(y<=maxy))) y=maxy;
   Move(rp,x,y);
}
   
void Safe_Draw(struct RastPort *rp,WORD x,WORD y)
{  WORD x0;

   if (y<=maxy)
   {  if (lyok) Draw(rp,x,y);     /*All clear*/
      else
      {  x0=x + (maxy-y) * (lastx-x) / (lasty-y);
         Move(rp,x0,maxy);
         Draw(rp,x,y);
      }
   }
   else
   {  if (!lyok) Safe_Move(rp,x,y);   /*Won't draw anyway*/
      else
      {  x0=lastx + (maxy-lasty) * (x-lastx) / (y-lasty);
         Draw(rp,x0,maxy);
      }
   }
   lastx=x; lyok=((lasty=y)<=maxy);
}

void Safe_Rect(struct RastPort *rp,WORD x,WORD y)
{  WORD xmin,xmax,ymin,ymax;

   if (y<=maxy) ymax=y; else ymax=maxy;
   if (lyok) ymin=lasty; else ymin=maxy;
   if (ymin>ymax) { xmin=ymin; ymin=ymax; ymax=xmin; }
   if (lastx>x) { xmax=lastx; xmin=x; }
   else { xmin=lastx; xmax=x; }
   RectFill(rp,xmin,ymin,xmax,ymax);
   lastx=x; lyok=((lasty=y)<=maxy);
}

/*****************************************************************************/
/* Convert screen coords in lastx,lasty  to tek coords in tekx,teky          */
/*****************************************************************************/

void scrtotek()
{
   tekx=((lastx-tekwindow->BorderLeft)*tekwidth) / winw;
   teky=((tekwindow->BorderTop+winh-lasty)*tekheight) / winh;
}


/********************************************************************************/
/* Accept IDCMP events from tek window                                          */
/********************************************************************************/

void tekpoll(void)
{
   struct IntuiMessage *msg;

   while (tekwindow && (msg=(struct IntuiMessage *)GetMsg(tekwindow->UserPort)))
   {  switch(msg->Class)
      {  case RAWKEY : if (bypassmode) ginkeys(msg);
                       else dokeys(msg);   /* Send to vt100 key handler */
                       break;
         case CLOSEWINDOW : ReplyMsg((struct Message *)msg);
                            closetekwin();
                            tekmode=0;
                            break;
         case NEWSIZE : calctekwinsize();
                        ReplyMsg((struct Message *)msg);
/*                        graph_clear();*/
                        redrawtek();
                        break;
         case MENUPICK : tekmenu(msg);
                         break;

         case MOUSEMOVE:
         case MOUSEBUTTONS: if (mode==TEK_GIN) ginmouse(msg);
                            else ReplyMsg((struct Message *)msg);
                            break;
         default: ReplyMsg((struct Message *)msg);
      }
   }
}

/*****************************************************************************/
/* Deal with menu events                                                     */
/*****************************************************************************/

static int close_it=0;

void menu_close(void)
{  close_it=1;
}

void tekmenu(struct IntuiMessage *msg)
{  UWORD Code; /*,menuNum,itemNum,subNum;*/
   struct MenuItem *item;
   void (*func)(struct MenuItem *);

   close_it=0;
   Code=msg->Code;
   while ((Code!=MENUNULL) && (!close_it))
   {   item=ItemAddress(menuStrip,Code);
/***
       menuNum=MENUNUM(Code);
       itemNum=ITEMNUM(Code);
       subNum=SUBNUM(Code);
***/
       func=(void(*)())GTMENUITEM_USERDATA(item);
       (*func)(item);
       Code=item->NextSelect;
   }
   ReplyMsg((struct Message *)msg);
   if (close_it)
   {  closetekwin();
      tekmode=0;
   }
}

/*****************************************************************************/
/* Check state of activatewindow and frontwindow menuitems                   */
/*****************************************************************************/

void chk_active(struct MenuItem *item)
{
   activateonswitch = item->Flags & CHECKED;
}

void chk_front(struct MenuItem *item)
{
   forwardonswitch = item->Flags & CHECKED;
}



#endif             /*end of ifdef TEKTRONICS*/
