/*
	The MagTape Widget Methods.

	11-feb-2025	Created by mk@lemo.dk
*/

/*
#undef DEBUG
*/

static int UAKK1=8;
static double ADJLIMIT=0.2;
#define UAKK2 22

#include <GL/glx.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glext.h>

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <malloc.h>
#include <time.h>
#include <sys/fcntl.h>
#include <Xm/XmP.h>

#include "MagTape_linux.h"
#include "MagTapeP_linux.h"
#include "spole6.xpm"
#include "GIER.h"
#include "common.h"

extern unsigned long long clock_count;
extern int gier_clock;
extern int tape_wait;

#define dtor(x) ( (x)/180.0*M_PI )
#define rtod(x) ( (x)*180.0/M_PI )

extern MagTapeApplicationData magTape_applicationdata;

extern void bbox(double ,double *,double *,double *,double *,char *);
extern void stroke(double ,double ,double ,double ,char *);

/*
Forward declarations */
static void Resize();
static void XsmagTapeInsertValues();
static void XsmagTapeInsertDefaultValues();
static void updateit(XsMagTapeWidget, int);
GLvoid DrawGLScene(XsMagTapeWidget,int);
static void startTimer(XsMagTapeWidget);

static char *tape_color_types[]={"TAPE_COLOR_BLACK",
                                 "TAPE_COLOR_WHITE",
                                 "TAPE_COLOR_RED",
                                 "TAPE_COLOR_GREEN",
                                 "TAPE_COLOR_BLUE",
                                 "TAPE_COLOR_BLACK_T",
                                 "TAPE_COLOR_GRAY8_T",
                                 "TAPE_COLOR_TAPE"};


#define offset(field) XtOffset(XsMagTapeWidget, magTape.field)

XtResource magTape_resources[]=
{

  {XtNbackgroundColor, XtCColor, XtRInt, sizeof(int),
   offset(backgroundcolor), XtRString, "0"},

  {XtNtapeLabels, XtCTapeLabels, XtRPointer, sizeof(caddr_t),
   offset(ptapelabels), XtRPointer, NULL},

  {XtNnDrives, XtCNDrives, XtRInt, sizeof(int),
   offset(ndrives), XtRString, "1"},

  {XtNdone, XtCCallback, XtRCallback, sizeof(caddr_t),
   offset(done), XtRCallback, NULL},

  /* OpenGL stuff */

  {XtNvisualInfo, XtCVisualInfo, XtRVisualInfo, sizeof (XVisualInfo *),
   offset(visualInfo), XtRImmediate, (XtPointer) NULL}
};

#undef offset

static void done_magTape(XsMagTapeWidget,int);
static void InsertChar(XsMagTapeWidget,XEvent *);
	    

static XtActionsRec actionsList[]=
{
  {"done", (XtActionProc) done_magTape},
  {"insert-char", (XtActionProc) InsertChar}
};

static void Initialize(), Realize(), Destroy(), Resize(), Redisplay();
static Boolean SetValues();

static char defaultTranslations[] =
	"~c <Key>:	insert-char()";

XsMagTapeClassRec XsmagTapeClassRec =
{
  /*    Core class part */
  {
    (WidgetClass) &widgetClassRec,      /* superclass */
    "MagTape",                              /* class_name */
    sizeof(XsMagTapeRec),                   /* widget_size */
    NULL,                               /* class_initialze */
    NULL,                               /* class_part_initialize */
    FALSE,                              /* class_inited */
    Initialize,                         /* initialize */
    NULL,                               /* initialize_hook */
    Realize,                            /* realize */
    actionsList,                        /* actions */
    XtNumber(actionsList),              /* num_actions */
    magTape_resources,                      /* resources */
    XtNumber(magTape_resources),            /* num_resources */
    NULLQUARK,                          /* xrm_class */
    TRUE,                               /* compress_motion */
    XtExposeCompressMaximal,            /* compress_exposure */
    TRUE,                               /* compress_enterleave */
    TRUE,                               /* visibility_interest */
    Destroy,                            /* destroy */
    Resize,                             /* resize */
    Redisplay,                          /* expose */
    SetValues,                          /* set_values */
    NULL,                               /* set_values_hook */
    XtInheritSetValuesAlmost,           /* set_values_almost */
    NULL,                               /* get_values_hook */
    NULL,                               /* accept_focus */
    XtVersion,                          /* version */
    NULL,                               /* callback private */
    defaultTranslations,                /* tm_table */
    NULL,                               /* query_geometry */

    NULL,                               /* display_accelerator */
    NULL,                               /* extension */
  },
  {          /* XmPrimitive        */
     XmInheritBorderHighlight,       /* border_highlight      */
     XmInheritBorderUnhighlight,     /* border_unhighlight    */
     XmInheritTranslations,          /* translations          */
     NULL,                           /* arm_and_activate      */
     NULL,                           /* synresources          */
     0,                              /* num syn_resources     */
     NULL,                           /* extension             */
  },
  /*    MagTape class fields */

  {
    0,
  }
};

WidgetClass XsmagTapeWidgetClass = (WidgetClass) &XsmagTapeClassRec;

int ImageLoad(const char **xpm, Image *image) {
    unsigned long i,r,c,size;                    // standard counter.
    unsigned int ncolors,charsPerPixel,index,*p,*clookup;

    sscanf(xpm[0], "%u %u %u %u",&image->sizeX,&image->sizeY,&ncolors,&charsPerPixel);

    clookup=(unsigned int *) malloc(sizeof(*clookup)*256*256);

    for(i=0;i<65536;i++) clookup[i]=0;
    for(i=1;i<=ncolors;i++)
    {
      index=xpm[i][0]+xpm[i][1]*256;
      if(xpm[i][3]=='c')
      {
	if(xpm[i][5]=='#')
	{
	  sscanf(xpm[i]+6, "%X", &clookup[index]);
	  clookup[index] |= 0xFF000000;
	}
	else if(xpm[i][5]=='N')
	{
	  clookup[index]=0x00FFFFFF;
	}
	else if(xpm[i][5]=='b')
	{
	  clookup[index]=0xFF000000;
	}
      }
    }
    size = image->sizeX * image->sizeY * 4;
    image->data = (char *) malloc(size);
    p = (unsigned int *) (image->data);
    for(r=0;r<image->sizeY;r++)
    {
      for(c=0;c<image->sizeX;c++)
      {
	index=xpm[ncolors+1+r][c*2] + xpm[i+r][c*2+1]*256;
	p[r*image->sizeX+c]=clookup[index];
      }
    }
    free((char *) clookup);
    
    return 1;
}


static double seconds(XsMagTapeWidget w)
{
  double td;
  long tn;
  w->magTape.time1 = clock_count;
  td = ((double) (w->magTape.time1-w->magTape.time0))/((double)gier_clock);
  return(td);
}

static int QUAREQ2(double A, double B, double C, double *x1, double *x2)
{
  double D;
  int ret=0;
  if(A==0.0)
  {
    if(B==0.0)
      ret=1;
    else
    {
      *x1 = -C/A;
      *x2 = *x1;
    }
  }
  else
  {
    D=SQR(B)-4.0*A*C;
    if(D<0.0)
      ret=1;
    else
    {
      *x1 = (-B - ((B<0.0)?-1.0:1.0)*sqrt(D))/2.0/A;
      *x2 = C/A/(*x1);
    }
  }
  return(ret);
}

static double radius2Length(XsMagTapeWidget w,double r)
{
  return((M_PI/w->magTape.thickness)*SQR(r)+M_PI*r-SQR(w->magTape.radius0)*M_PI/w->magTape.thickness);
}

static double length2Thickness(XsMagTapeWidget w,double l, double r)
{
  return(M_PI*(SQR(r)-SQR(w->magTape.radius0))/(l-M_PI*r));
}

static double length2Radius(XsMagTapeWidget w,double l)
{
  double A,B,C,x1,x2;
  A=M_PI/w->magTape.thickness;
  B=M_PI;
  C=-(SQR(w->magTape.radius0)*M_PI/w->magTape.thickness)-l;
  QUAREQ2(A, B, C, &x1, &x2);
  return(x2);
}

static double rotation2Length(XsMagTapeWidget w,double r)
{
  return(M_PI*w->magTape.thickness*SQR(r)+(2.0*M_PI*w->magTape.radius0)*r);
}

static double length2Rotation(XsMagTapeWidget w,double l)
{
  double A,B,C,x1,x2;
  A=M_PI*w->magTape.thickness;
  B=2.0*M_PI*w->magTape.radius0;
  C=-l;
  QUAREQ2(A, B, C, &x1, &x2);
  return(x2);
}


static void disk(GLdouble x0, GLdouble y0, GLdouble z0,GLdouble r1, GLdouble r2)
{
  GLdouble x1,y1,x2,y2,v,lastx1,lasty1,lastx2,lasty2;

  lastx1=x0+r1;
  lasty1=y0;
  lastx2=x0+r2;
  lasty2=y0;
  for(v=5;v<=360;v+=5)
  {
    x1=x0+r1*cos(v/180.0*M_PI);
    y1=y0+r1*sin(v/180.0*M_PI);
    x2=x0+r2*cos(v/180.0*M_PI);
    y2=y0+r2*sin(v/180.0*M_PI);
    glVertex3d(lastx1,lasty1,z0);
    glVertex3d(lastx2,lasty2,z0);
    glVertex3d(x1,y1,z0);
    glVertex3d(lastx2,lasty2,z0);
    glVertex3d(x2,y2,z0);
    glVertex3d(x1,y1,z0);
    lastx1=x1;
    lasty1=y1;
    lastx2=x2;
    lasty2=y2;
  }
}

static void circle(GLdouble x0, GLdouble y0, GLdouble r, GLdouble v0, GLdouble v1)
{
  GLdouble lastx,lasty,x,y,v;
  int i,n;

  n=r*2;
  lastx=x0+r*cos(v0/180.0*M_PI);
  lasty=y0-r*sin(v0/180.0*M_PI);
  for(i=1;i<=n;i++)
  {
    v=v0+(v1-v0)*((double)i)/((double) n);
    x=x0+r*cos(v/180.0*M_PI);
    y=y0-r*sin(v/180.0*M_PI);
    glVertex3d(lastx,lasty,0.0);
    glVertex3d(x,y,0.0);
    lastx=x;
    lasty=y;
  }
}


    
// Load Bitmaps And Convert To Textures
void LoadGLTextures(XsMagTapeWidget w) {	
    // Load Texture

    // allocate space for texture
    w->magTape.image1 = (Image *) malloc(sizeof(Image));
    if (w->magTape.image1 == NULL) {
	printf("Error allocating space for image");
	exit(0);
    }

    if (!ImageLoad(spole6, w->magTape.image1)) {
	exit(1);
    }        

    // Create Texture	
    glGenTextures(1, &(w->magTape.texture[0]));
//  glBindTexture(GL_TEXTURE_2D, w->magTape.texture[0]);   // 2d texture (x and y size)

    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); // scale linearly when image bigger than texture
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); // scale linearly when image smalled than texture

    // 2d texture, level of detail 0 (normal), 3 components (red, green, blue), x size from image, y size from image, 
    // border 0 (normal), rgb color data, unsigned byte data, and finally the data itself.
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w->magTape.image1->sizeX, w->magTape.image1->sizeY, 0, GL_RGBA, GL_UNSIGNED_BYTE, w->magTape.image1->data);
};

static void intersectionLineCircle(double a1, double c1,
                                   double x1, double y1, double r1,
				   double *xi1, double *yi1,
				   double *xi2, double *yi2)
{
  double A,B,C,d,tmp;
  A=(1.0+SQR(a1));
  B=(-2.0*x1+2.0*a1*(y1+c1));
  C=SQR(x1)+SQR(y1+c1)-SQR(r1);
  d=SQR(B)-4.0*A*C;
  if(d<0)
  {
    fprintf(stderr, "no intersection\n");
    exit(-1);
  }
  *xi1 = (-B+sqrt(d))/2.0/A;
  *xi2 = (-B-sqrt(d))/2.0/A;
  if((*xi1) > (*xi2))
  {
    tmp=*xi1;
    *xi1=*xi2;
    *xi2=tmp;
  }
  *yi1 = -c1-a1*(*xi1);
  *yi2 = -c1-a1*(*xi2);
}

static void intersectionLines(double a1, double c1,
                              double a2, double c2,
			      double *x, double *y)
{
  *x = (c1-c2)/(a2-a1);
  *y = -c1-a1*(*x);
}

static void intersectionCircles(double x1, double y1, double r1,
                                double x2, double y2, double r2,
				double *xi1, double *yi1,
				double *xi2, double *yi2)
{
  double R,tmp;
  /* https://math.stackexchange.com/questions/256100/how-can-i-find-the-points-at-which-two-circles-intersect */
  R=sqrt(SQR(x1-x2)+SQR(y1-y2));
  *xi1 = (x1+x2)/2.0 + (SQR(r1)-SQR(r2))/2.0/SQR(R)*(x2-x1)
        +0.5*sqrt(2.0*(SQR(r1)+SQR(r2))/SQR(R)-SQR(SQR(r1)-SQR(r2))/SQR(SQR(R))-1.0)*(y2-y1);
  *xi2 = (x1+x2)/2.0 + (SQR(r1)-SQR(r2))/2.0/SQR(R)*(x2-x1)
        -0.5*sqrt(2.0*(SQR(r1)+SQR(r2))/SQR(R)-SQR(SQR(r1)-SQR(r2))/SQR(SQR(R))-1.0)*(y2-y1);
  *yi1 = (y1+y2)/2.0 + (SQR(r1)-SQR(r2))/2.0/SQR(R)*(y2-y1)
        +0.5*sqrt(2.0*(SQR(r1)+SQR(r2))/SQR(R)-SQR(SQR(r1)-SQR(r2))/SQR(SQR(R))-1.0)*(x1-x2);
  *yi2 = (y1+y2)/2.0 + (SQR(r1)-SQR(r2))/2.0/SQR(R)*(y2-y1)
        -0.5*sqrt(2.0*(SQR(r1)+SQR(r2))/SQR(R)-SQR(SQR(r1)-SQR(r2))/SQR(SQR(R))-1.0)*(x1-x2);
  if((*xi1)>(*xi2))
  {
    tmp=(*xi1); *xi1 = (*xi2); *xi2=tmp;
    tmp=(*yi1); *yi1 = (*yi2); *yi2=tmp;
  }
}

static void points2Line(double x1, double y1,
                        double x2, double y2,
			double *a, double *c)
{
  *a = (y1-y2)/(x2-x1);
  *c = -(*a)*x1-y1;
}

static void twoCirclesTangent(double x1, double y1, double r1,
                              double x2, double y2, double r2,
			      int transverse,
			      double *xi11, double *yi11,
			      double *xi12, double *yi12,
			      double *xi21, double *yi21,
			      double *xi22, double *yi22)
{
  /* https://math.stackexchange.com/questions/1662110/common-tangent-to-two-circles */
  double a,c,xr,yr,ca,cc,cx1,cy1,cx2,cy2,ca2,cc2,x3,y3,r3,angle;
  points2Line(x1,y1,x2,y2,&ca,&cc);
  angle=atan2(y2-y1,x2-x1)+M_PI_2;
  cx1=x1+r1*cos(angle);
  cy1=y1+r1*sin(angle);
  cx2=x2+r2*cos(angle+(transverse?M_PI:0));
  cy2=y2+r2*sin(angle+(transverse?M_PI:0));
  points2Line(cx1,cy1,cx2,cy2,&ca2,&cc2);
  intersectionLines(ca,cc,ca2,cc2,&xr,&yr);
  x3=(x2+xr)/2.0;
  y3=(y2+yr)/2.0;
  r3=sqrt(SQR(xr-x2)+SQR(yr-y2))/2.0;
  intersectionCircles(x2,y2,r2,x3,y3,r3,xi21,yi21,xi22,yi22);
  x3=(x1+xr)/2.0;
  y3=(y1+yr)/2.0;
  r3=sqrt(SQR(xr-x1)+SQR(yr-y1))/2.0;
  intersectionCircles(x1,y1,r1,x3,y3,r3,xi11,yi11,xi12,yi12);
}

GLfloat LightAmbient[]  = {0.5, 0.5, 0.5, 1.0}; 
GLfloat LightDiffuse[]  = {1.0, 1.0, 1.0, 1.0}; 
GLfloat LightPosition[] = {0.0, 0.0, 2.0, 1.0};

static void setTimerRunning(XsMagTapeWidget w)
{
  int unitNo;
  w->magTape.timerRunning=0;
  for(unitNo=0; unitNo<w->magTape.ndrives; unitNo++)
  {
    if(w->magTape.status[unitNo] != TAPE_IDLE) w->magTape.timerRunning=1;
    if(fabs(w->magTape.inSpeed[unitNo])>IDLE_SPEED) w->magTape.timerRunning=1;
    if(fabs(w->magTape.capstanSpeed[unitNo])>IDLE_SPEED) w->magTape.timerRunning=1;
    if(fabs(w->magTape.outSpeed[unitNo])>IDLE_SPEED) w->magTape.timerRunning=1;
  }
  if(!w->magTape.timerRunning)
  {
    for(unitNo=0; unitNo<w->magTape.ndrives; unitNo++)
    {
      w->magTape.inSpeed[unitNo]=0.0;
      w->magTape.capstanSpeed[unitNo]=0.0;
      w->magTape.lastCapstan[unitNo]=0.0;
      w->magTape.outSpeed[unitNo]=0.0;
    }
  }
  tape_wait=w->magTape.timerRunning;
  if(debug&DEBUGbuffer) fprintf(debug_fh, "setTimerRunning: %d\n",w->magTape.timerRunning);
}

/* The main drawing function. */

void timercb(XtPointer client_data, XtIntervalId *timer_id)
{
  double total,inSpeed,outSpeed;
  int i,n;
  int unitNo;
  XsMagTapeWidget w = (XsMagTapeWidget) client_data;
  w->magTape.secs=seconds(w);
  w->magTape.dsecs2=w->magTape.secs-w->magTape.lastsecs;
  if(w->magTape.dsecs2>0)
  {
  w->magTape.lastsecs=w->magTape.secs;
  w->magTape.dsecs=w->magTape.dsecs2;
  if(w->magTape.dsecs2>TIME_STEP)
  {
    n=w->magTape.dsecs2/TIME_STEP;
    w->magTape.dsecs=TIME_STEP;
  }
  else
  {
    n=1;
  }
  for(unitNo=0; unitNo<w->magTape.ndrives; unitNo++)
  {
    w->magTape.inLast[unitNo] = w->magTape.INlength[unitNo];
    w->magTape.outLast[unitNo] = w->magTape.OUTlength[unitNo];
  }
  for(i=0;i<n;i++)
  {
    if(i==(n-1))
      w->magTape.dsecs=w->magTape.dsecs2-(n-1)*TIME_STEP;
    if(debug&DEBUGbuffer) fprintf(debug_fh, "kilroy2 dsecs2: %g i: %d n: %d dsecs: %g\n", w->magTape.dsecs2,i,n,w->magTape.dsecs);
    for(unitNo=0; unitNo<w->magTape.ndrives; unitNo++)
    {
      updateit(w,unitNo);
    }
  }
  for(unitNo=0; unitNo<w->magTape.ndrives; unitNo++)
  {
    inSpeed = -(w->magTape.INlength[unitNo] - w->magTape.inLast[unitNo])/w->magTape.dsecs2;
    outSpeed = -(w->magTape.OUTlength[unitNo] - w->magTape.outLast[unitNo])/w->magTape.dsecs2;
    if(inSpeed*w->magTape.lastCapstan[unitNo]<(-IDLE_SPEED) ||
       outSpeed*w->magTape.lastCapstan[unitNo]>IDLE_SPEED)
    {
      if(debug&DEBUGbuffer) fprintf(debug_fh, "%d: %11.3f: Gulerod %10.3f %10.3f %10.3f\n",
	unitNo,
	w->magTape.secs,
	inSpeed,
	w->magTape.lastCapstan[unitNo],
	outSpeed);
    }
  }
  }
  Redisplay(w,NULL,NULL);
  for(unitNo=0; unitNo<w->magTape.ndrives; unitNo++)
  {
    if(fabs(w->magTape.capstanSpeed[unitNo])<0.01)
    {
      w->magTape.color_status[unitNo] = TAPE_IDLE_COLOR;
      if(debug&DEBUGbuffer) fprintf(debug_fh, "%d: color_status I: %s\n", unitNo, tape_color_types[w->magTape.color_status[unitNo]]);
    }
  }
  setTimerRunning(w);
  if(debug&DEBUGbuffer) fprintf(debug_fh, "start timer: %d\n",w->magTape.timerRunning);
  if(w->magTape.timerRunning)startTimer(w);
}

static void startTimer(XsMagTapeWidget w)
{
  if(debug&DEBUGbuffer) fprintf(debug_fh, "kilroy1:\n");
  XtAppAddTimeOut(XtWidgetToApplicationContext((Widget) w), 10, timercb, (XtPointer) w);
}

static void updateit(XsMagTapeWidget w, int unitNo)
{
  double total;
  switch(w->magTape.status[unitNo])
  {
    case TAPE_IDLE:
      break;

    case FORWARD_ACCELERATE:
      if(w->magTape.capstanSpeed[unitNo]>READSPEED)
      {
	w->magTape.capstanAcceleration[unitNo]=0.0;
	w->magTape.capstanSpeed[unitNo]=READSPEED;
	w->magTape.status[unitNo]=FORWARD_KEEP;
      }
      break;
    case FORWARD_KEEP:
      if((w->magTape.taperead[unitNo]-w->magTape.readstart[unitNo]+GAPLEN)>=w->magTape.tapeappetite[unitNo])
      {
	w->magTape.capstanAcceleration[unitNo]=-CAPSTANACCELERATION;
	w->magTape.status[unitNo]=FORWARD_DECELERATE;
      }
      break;
    case FORWARD_DECELERATE:
      if(w->magTape.capstanSpeed[unitNo]<0.01)
      {
	w->magTape.capstanSpeed[unitNo]=0;
	w->magTape.capstanAcceleration[unitNo]=0;
	w->magTape.tapeadjust[unitNo]=w->magTape.readstart[unitNo]+w->magTape.tapeappetite[unitNo]-w->magTape.taperead[unitNo];
//	  if(debug&DEBUGbuffer) fprintf(debug_fh, "%d: tapeadjust: %.8f\n",unitNo, w->magTape.tapeadjust[unitNo]);
	w->magTape.taperead[unitNo]+=w->magTape.tapeadjust[unitNo];
	w->magTape.inTape[unitNo]-=w->magTape.tapeadjust[unitNo];
	w->magTape.outTape[unitNo]+=w->magTape.tapeadjust[unitNo];
	w->magTape.status[unitNo]=TAPE_IDLE;
	done_magTape(w,unitNo);
//	  goto out;
      }
      break;

    case REVERSE_ACCELERATE:
      if(w->magTape.capstanSpeed[unitNo]<(-READSPEED))
      {
	w->magTape.capstanAcceleration[unitNo]=0.0;
	w->magTape.capstanSpeed[unitNo]=-READSPEED;
	w->magTape.status[unitNo]=REVERSE_KEEP;
      }
      break;
    case REVERSE_KEEP:
      if((w->magTape.readstart[unitNo]-w->magTape.taperead[unitNo]+GAPLEN)>=w->magTape.tapeappetite[unitNo])
      {
	w->magTape.capstanAcceleration[unitNo]=CAPSTANACCELERATION;
	w->magTape.status[unitNo]=REVERSE_DECELERATE;
      }
      break;
    case REVERSE_DECELERATE:
      if(w->magTape.capstanSpeed[unitNo]>-0.01)
      {
	w->magTape.capstanSpeed[unitNo]=0;
	w->magTape.capstanAcceleration[unitNo]=0;
	w->magTape.tapeadjust[unitNo]=w->magTape.taperead[unitNo]-w->magTape.readstart[unitNo]+w->magTape.tapeappetite[unitNo];
//	  if(debug&DEBUGbuffer) fprintf(debug_fh, "%d: tapeadjust: %.8f\n",unitNo, w->magTape.tapeadjust[unitNo]);
	w->magTape.taperead[unitNo]-=w->magTape.tapeadjust[unitNo];
	w->magTape.inTape[unitNo]+=w->magTape.tapeadjust[unitNo];
	w->magTape.outTape[unitNo]-=w->magTape.tapeadjust[unitNo];
	w->magTape.status[unitNo]=TAPE_IDLE;
	w->magTape.tapeadjust[unitNo]=w->magTape.taperead[unitNo]-w->magTape.readstart[unitNo]-w->magTape.tapeappetite[unitNo];
	done_magTape(w,unitNo);
//	  goto out;
      }
      break;

    case REWIND_ACCELERATE:
      if(w->magTape.capstanSpeed[unitNo]<(-REWINDSPEED))
      {
	w->magTape.capstanAcceleration[unitNo]=0.0;
	w->magTape.capstanSpeed[unitNo]=-REWINDSPEED;
	w->magTape.status[unitNo]=REWIND_KEEP;
      }
      break;
    case REWIND_KEEP:
      if(w->magTape.OUTlength[unitNo]<GAPLEN)
      {
	w->magTape.capstanAcceleration[unitNo]=CAPSTANACCELERATION;
	w->magTape.status[unitNo]=REWIND_DECELERATE;
      }
      break;
    case REWIND_DECELERATE:
      if(w->magTape.capstanSpeed[unitNo]>-0.01)
      {
	w->magTape.capstanSpeed[unitNo]=0;
	w->magTape.capstanAcceleration[unitNo]=0;
	w->magTape.OUTlength[unitNo]=0.0;
	w->magTape.INlength[unitNo]=TAPELENGTH;
	w->magTape.taperead[unitNo]=0;
	w->magTape.status[unitNo]=TAPE_IDLE;
//	  w->magTape.color_status[unitNo] = TAPE_IDLE_COLOR;
	done_magTape(w,unitNo);
//	  goto out;
      }
      break;
  }
  w->magTape.INlength[unitNo] -= w->magTape.inSpeed[unitNo]*w->magTape.dsecs;
  w->magTape.OUTlength[unitNo] -= w->magTape.outSpeed[unitNo]*w->magTape.dsecs;
  w->magTape.inTape[unitNo] += w->magTape.inSpeed[unitNo]*w->magTape.dsecs;
  w->magTape.outTape[unitNo] += w->magTape.outSpeed[unitNo]*w->magTape.dsecs;
  w->magTape.inTape[unitNo] -= w->magTape.capstanSpeed[unitNo]*w->magTape.dsecs;
  w->magTape.outTape[unitNo] += w->magTape.capstanSpeed[unitNo]*w->magTape.dsecs;
  w->magTape.taperead[unitNo] += w->magTape.capstanSpeed[unitNo]*w->magTape.dsecs;
  w->magTape.inSpeed[unitNo] += w->magTape.inAcceleration[unitNo]*w->magTape.dsecs;
  w->magTape.outSpeed[unitNo] += w->magTape.outAcceleration[unitNo]*w->magTape.dsecs;
  w->magTape.capstanSpeed[unitNo] += w->magTape.capstanAcceleration[unitNo]*w->magTape.dsecs;
  
  w->magTape.inRatio[unitNo] = w->magTape.inTape[unitNo]/MAXCHAMPERTAPELENGTH - 0.5;
  w->magTape.outRatio[unitNo] = w->magTape.outTape[unitNo]/MAXCHAMPERTAPELENGTH - 0.5;

  w->magTape.inError[unitNo]=w->magTape.inSpeed[unitNo]-w->magTape.capstanSpeed[unitNo];
  w->magTape.outError[unitNo]=w->magTape.outSpeed[unitNo]+w->magTape.capstanSpeed[unitNo];
  w->magTape.inAcceleration[unitNo]= -w->magTape.inError[unitNo]*w->magTape.UAKK[unitNo];
  if(fabs(w->magTape.inRatio[unitNo])>ADJLIMIT)
  {
//    double inadjust=ADJLIMIT*SIGN(w->magTape.inRatio[unitNo])*w->magTape.UAKK[unitNo];
    double inadjust=w->magTape.inRatio[unitNo]*w->magTape.UAKK[unitNo];
    if(debug&DEBUGbuffer) fprintf(debug_fh, "%d: inadjust: %.8f %g\n",unitNo, inadjust,w->magTape.UAKK[unitNo]);
    w->magTape.inAcceleration[unitNo]-=inadjust;
  }
  w->magTape.outAcceleration[unitNo]= -w->magTape.outError[unitNo]*w->magTape.UAKK[unitNo];
  if(fabs(w->magTape.outRatio[unitNo])>ADJLIMIT)
  {
//    double outadjust=ADJLIMIT*SIGN(w->magTape.outRatio[unitNo])*w->magTape.UAKK[unitNo];
    double outadjust=w->magTape.outRatio[unitNo]*w->magTape.UAKK[unitNo];
    if(debug&DEBUGbuffer) fprintf(debug_fh, "%d: outadjust: %.8f %g\n",unitNo, outadjust,w->magTape.UAKK[unitNo]);
    w->magTape.outAcceleration[unitNo]-=outadjust;
  }
  total=w->magTape.INlength[unitNo]+w->magTape.OUTlength[unitNo]+w->magTape.inTape[unitNo]+w->magTape.outTape[unitNo];

  if(debug&DEBUGbuffer) fprintf(debug_fh, "%d: inSpeed: %g capstanSpeed: %g outSpeed: %g\n",unitNo,w->magTape.inSpeed[unitNo],w->magTape.capstanSpeed[unitNo],w->magTape.outSpeed[unitNo]);
  if(w->magTape.inRatio[unitNo] < -0.5 || w->magTape.inRatio[unitNo] > 0.5 || w->magTape.outRatio[unitNo] < -0.5 || w->magTape.outRatio[unitNo] > 0.5)
  {
    if(debug&DEBUGbuffer) fprintf(debug_fh, "%d: inRatio: %g outRatio: %g\n",unitNo,w->magTape.inRatio[unitNo],w->magTape.outRatio[unitNo]);
  }
  if(debug&DEBUGbuffer) fprintf(debug_fh, "%d: timercb done\n", unitNo);
}

static void Layout(XsMagTapeWidget w)
{
//if(debug&DEBUGbuffer) fprintf(debug_fh, "Layout: ndrives: %d\n",w->magTape.ndrives);
  switch(w->magTape.ndrives)
  {
    case 1:
    case 2:
    case 3:
      w->magTape.nx=w->magTape.ndrives;
      w->magTape.ny=1;
      break;
    case 4:
      w->magTape.nx=2;
      w->magTape.ny=2;
      break;
    case 5:
    case 6:
      w->magTape.nx=3;
      w->magTape.ny=2;
      break;
  }
}

static void pushmatrix(int lno)
{
  int mm;
  return;
  if(debug&DEBUGbuffer)
  {
    glGetIntegerv(GL_MATRIX_MODE, &mm);
    if(mm==GL_MODELVIEW) fprintf(debug_fh, "pushmatrix %d GL_MODELVIEW\n",lno);
    else if(mm==GL_PROJECTION) fprintf(debug_fh, "pushmatrix %d GL_PROJECTION\n",lno);
    else if(mm==GL_TEXTURE) fprintf(debug_fh, "pushmatrix %d GL_TEXTURE\n",lno);
    else if(mm==GL_COLOR) fprintf(debug_fh, "pushmatrix %d GL_COLOR\n",lno);
    else fprintf(debug_fh, "pushmatrix %d UNKNOWN %d\n",lno,mm);
  }
}

static void popmatrix(int lno)
{
  int mm;
  return;
  if(debug&DEBUGbuffer)
  {
    glGetIntegerv(GL_MATRIX_MODE, &mm);
    if(mm==GL_MODELVIEW) fprintf(debug_fh, "popmatrix %d GL_MODELVIEW\n",lno);
    else if(mm==GL_PROJECTION) fprintf(debug_fh, "popmatrix %d GL_PROJECTION\n",lno);
    else if(mm==GL_TEXTURE) fprintf(debug_fh, "popmatrix %d GL_TEXTURE\n",lno);
    else if(mm==GL_COLOR) fprintf(debug_fh, "popmatrix %d GL_COLOR\n",lno);
    else fprintf(debug_fh, "popmatrix %d UNKNOWN %d\n",lno,mm);
  }
}

GLvoid DrawGLScene(XsMagTapeWidget w,int unitNo)
{
  char txt[100];
  double x,y;
  int width, height;
  int widthr, heightr;
  int ix, iy;
  GLdouble window_xmin, window_xmax, window_ymin, window_ymax;
  GLdouble xcenter, ycenter, aspect, xsize, ysize, fHeight, fWidth;
  double a1,c1,xi1,yi1,xi2,yi2,angle1,angle2;
  double xi11,yi11,xi12,yi12,xi21,yi21,xi22,yi22;
  double xcenter1,ycenter1,xcenter2,ycenter2,acenter,ccenter;
  double r1,angle3,x1,y1,x2,y2,x3,y3,s,sx,sy;
  double tapewidth=1.0,angle;
  double textXmin, textXmax, textYmin, textYmax;

//if(debug&DEBUGbuffer) fprintf(debug_fh, "%d: glXMakeCurrent 1\n", unitNo);
  Layout(w);
  glXMakeCurrent(XtDisplay(w), XtWindow(w), w->magTape.glx_context);
//glClearColor(1.0, 1.0, 1.0, 1.0);	// Clear The Background Color To White 
  glClearDepth(0.9);				// Enables Clearing Of The Depth Buffer
  glDepthFunc(GL_LESS);			// The Type Of Depth Test To Do
  glEnable(GL_DEPTH_TEST);			// Enables Depth Testing
  glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);

  glShadeModel(GL_SMOOTH);			// Enables Smooth Color Shading
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  glDisable(GL_BLEND);
  glEnable(GL_LINE_SMOOTH);
    
  glMatrixMode(GL_MODELVIEW);

  glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient);
  glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse);
  glLightfv(GL_LIGHT1, GL_POSITION, LightPosition);
  glEnable(GL_LIGHT1);
  glMatrixMode(GL_PROJECTION);
//pushmatrix(__LINE__);
//glPushMatrix();
  glLoadIdentity();
//if(debug&DEBUGbuffer) fprintf(debug_fh, "%d: width: %d height: %d nx: %d ny: %d\n", unitNo, w->core.width, w->core.height, w->magTape.nx, w->magTape.ny);

  ix=unitNo%w->magTape.nx;
  iy=unitNo/w->magTape.nx;
  iy=w->magTape.ny-1-iy;
  widthr = w->core.width/w->magTape.nx;
  heightr = w->core.height/w->magTape.ny;
 
  glViewport(ix*widthr, iy*heightr, widthr, heightr);
  w->magTape.window_aspect = ((GLdouble) widthr)/((GLdouble) heightr)*((GLdouble) YMAX)/((GLdouble) XMAX);
//if(debug&DEBUGbuffer) fprintf(debug_fh, "%d: glViewport(%d, %d, %d, %d)\n",unitNo,ix*widthr, iy*heightr, widthr, heightr);
//if(debug&DEBUGbuffer) fprintf(debug_fh, "%d: window_aspect 1: %g\n",unitNo, w->magTape.window_aspect);
//if(debug&DEBUGbuffer) fprintf(debug_fh, "gulerod 1\n");
  glOrtho(0.0,((double)XMAX)*w->magTape.window_aspect,((double)YMAX),0.0,-1.0,1.0);
//if(debug&DEBUGbuffer) fprintf(debug_fh, "gulerod 2\n");
  glMatrixMode(GL_MODELVIEW);
//if(debug&DEBUGbuffer) fprintf(debug_fh, "gulerod 3\n");
//pushmatrix(__LINE__);
//glPushMatrix();
//if(debug&DEBUGbuffer) fprintf(debug_fh, "gulerod 4\n");
  glLoadIdentity();
//if(debug&DEBUGbuffer) fprintf(debug_fh, "gulerod 5\n");

  glDisable(GL_BLEND);
//if(debug&DEBUGbuffer) fprintf(debug_fh, "gulerod 6\n");
  glDisable(GL_TEXTURE_2D);
//if(debug&DEBUGbuffer) fprintf(debug_fh, "gulerod 7\n");
  glLoadIdentity();				// Reset The View
//if(debug&DEBUGbuffer) fprintf(debug_fh, "gulerod 8\n");
  glBegin(GL_LINES);
//if(debug&DEBUGbuffer) fprintf(debug_fh, "gulerod 9\n");
  glColor4d(0.0,0.0,0.0,1.0);
//if(debug&DEBUGbuffer) fprintf(debug_fh, "gulerod 10\n");
//glLineWidth(1.0);
//if(debug&DEBUGbuffer) fprintf(debug_fh, "gulerod 11\n");
  /*
  glVertex3d(BottomReelX-BottomReelOuterR,BottomReelY-BottomReelOuterR,0.0);
  glVertex3d(BottomReelX-BottomReelOuterR,BottomReelY+BottomReelOuterR,0.0);

  glVertex3d(BottomReelX-BottomReelOuterR,BottomReelY+BottomReelOuterR,0.0);
  glVertex3d(BottomReelX+BottomReelOuterR,BottomReelY+BottomReelOuterR,0.0);

  glVertex3d(BottomReelX+BottomReelOuterR,BottomReelY+BottomReelOuterR,0.0);
  glVertex3d(BottomReelX+BottomReelOuterR,BottomReelY-BottomReelOuterR,0.0);

  glVertex3d(BottomReelX+BottomReelOuterR,BottomReelY-BottomReelOuterR,0.0);
  glVertex3d(BottomReelX-BottomReelOuterR,BottomReelY-BottomReelOuterR,0.0);
  */

  glVertex3d(1.0, 0.0, 0.0);
  glVertex3d(XMAX, 0.0, 0.0);

  glVertex3d(XMAX, 0.0, 0.0);
  glVertex3d(XMAX, YMAX, 0.0);

  glVertex3d(XMAX, YMAX, 0.0);
  glVertex3d(1.0, YMAX, 0.0);

  glVertex3d(1.0, YMAX, 0.0);
  glVertex3d(1.0, 0.0, 0.0);

//if(debug&DEBUGbuffer) fprintf(debug_fh, "gulerod 12\n");

  circle(TopReelX, TopReelY, TopReelOuterR, 0.0, 360.0);
  circle(TopReelX, TopReelY, TopReelInnerR, 0.0, 360.0);

//if(debug&DEBUGbuffer) fprintf(debug_fh, "gulerod 13\n");
#ifdef USECORNER
  circle(TopCornerX, TopCornerY, TopCornerOuterR, 0.0, 360.0);
  circle(TopCornerX, TopCornerY, TopCornerInnerR, 0.0, 360.0);
#endif

  circle(TopChamperInX, TopChamperInY, TopChamperInInnerR, 0.0, 360.0);

  points2Line(TopChamperOuter1X, TopChamperOuter1Y, TopChamperOuter2X, TopChamperOuter2Y,
              &a1, &c1);
  intersectionLineCircle(a1,c1,TopChamperInX,TopChamperInY,TopChamperInInnerR,
              &xi1,&yi1,&xi2,&yi2);

  glVertex3d(xi2,yi2,0.0);
  glVertex3d(TopChamperOuter2X,TopChamperOuter2Y,0.0);

  intersectionLineCircle(a1,c1,TopChamperInX,TopChamperInY,TopChamperInOuterR,
              &xi1,&yi1,&xi2,&yi2);

  angle1=-atan2(yi2-TopChamperInY,xi2-TopChamperInX)*180.0/M_PI;
  points2Line(TopChamperOuter4X, TopChamperOuter4Y, TopChamperOuter1X, TopChamperOuter1Y,
              &a1,&c1);
  intersectionLineCircle(a1,c1,TopChamperInX,TopChamperInY,TopChamperInOuterR,
              &xi1,&yi1,&xi2,&yi2);
  angle2=360.0-atan2(yi2-TopChamperInY,xi2-TopChamperInX)*180.0/M_PI;

//printf("Top: angle1: %lg angle2: %lg\n",angle1,angle2);

  circle(TopChamperInX, TopChamperInY, TopChamperInOuterR, angle1, angle2);

  glVertex3d(TopChamperOuter2X,TopChamperOuter2Y,0.0);
  glVertex3d(TopChamperOuter3X,TopChamperOuter3Y,0.0);

  glVertex3d(TopChamperOuter3X,TopChamperOuter3Y,0.0);
  glVertex3d(TopChamperOuter4X,TopChamperOuter4Y,0.0);

  glVertex3d(TopChamperOuter4X,TopChamperOuter4Y,0.0);
  glVertex3d(TopChamperOuter1X,TopChamperOuter1Y,0.0);

  glVertex3d(TopChamperOuter1X+9.0,TopChamperOuter1Y+7.0,0.0);
  glVertex3d(735.0,37.0,0.0);

  glVertex3d(TopChamperInner2X, TopChamperInner2Y, 0.0);
  glVertex3d(TopChamperInner3X, TopChamperInner3Y, 0.0);

  points2Line(799.0,88.0,TopChamperInner4X,TopChamperInner4Y,&a1,&c1);
  intersectionLineCircle(a1,c1,TopChamperOutX,TopChamperOutY,TopChamperOutR,
                         &xi1,&yi1,&xi2,&yi2);
  glVertex3d(799.0,88.0,0.0);
  glVertex3d(xi2,yi2,0.0);
  circle(TopChamperOutX,TopChamperOutY,TopChamperOutR,0.0,360.0);

  if(debug&DEBUGbuffer) fprintf(debug_fh, "%d: color_status draw: %s\n", unitNo, tape_color_types[w->magTape.color_status[unitNo]]);
  if(w->magTape.color_status[unitNo] != TAPE_IDLE_COLOR)
  {
    glEnd();
    glBegin(GL_TRIANGLES);
    if(w->magTape.color_status[unitNo] == TAPE_READ_COLOR)
    {
      glColor4d(0.0,1.0,0.0,1.0);
    }
    else if(w->magTape.color_status[unitNo] == TAPE_WRITE_COLOR)
    {
      glColor4d(1.0,0.0,0.0,1.0);
    }
    else if(w->magTape.color_status[unitNo] == TAPE_SEEK_COLOR)
    {
      glColor4d(0.0,0.0,1.0,1.0);
    }
    glVertex3d(Head1X,Head1Y,0.0);
    glVertex3d(Head2X,Head2Y,0.0);
    glVertex3d(Head3X,Head3Y,0.0);
    
    glVertex3d(Head6X,Head6Y,0.0);
    glVertex3d(Head1X,Head1Y,0.0);
    glVertex3d(Head3X,Head3Y,0.0);
    
    glVertex3d(Head3X,Head3Y,0.0);
    glVertex3d(Head5X,Head5Y,0.0);
    glVertex3d(Head6X,Head6Y,0.0);
    
    glVertex3d(Head3X,Head3Y,0.0);
    glVertex3d(Head4X,Head4Y,0.0);
    glVertex3d(Head5X,Head5Y,0.0);
    
    glEnd();
    glBegin(GL_LINES);

  }
  glVertex3d(Head1X,Head1Y,0.0);
  glVertex3d(Head2X,Head2Y,0.0);

  glVertex3d(Head2X,Head2Y,0.0);
  glVertex3d(Head3X,Head3Y,0.0);

  glVertex3d(Head3X,Head3Y,0.0);
  glVertex3d(Head4X,Head4Y,0.0);

  glVertex3d(Head4X,Head4Y,0.0);
  glVertex3d(Head5X,Head5Y,0.0);

  glVertex3d(Head5X,Head5Y,0.0);
  glVertex3d(Head6X,Head6Y,0.0);

  glVertex3d(Head6X,Head6Y,0.0);
  glVertex3d(Head1X,Head1Y,0.0);

  glColor4d(0.0,0.0,0.0,1.0);
  circle(CapstanX,CapstanY,CapstanR,0.0,360.0);

#ifdef CROSS
  angle = fmod(w->magTape.taperead[unitNo]/(2.0*M_PI*CapstanR/PIXELSPERM)*360.0,360.0);

  glVertex3d(CapstanX+CapstanR*cos((angle      )/180.0*M_PI),CapstanY+CapstanR*sin((angle      )/180.0*M_PI),0.0);
  glVertex3d(CapstanX+CapstanR*cos((angle+180.0)/180.0*M_PI),CapstanY+CapstanR*sin((angle+180.0)/180.0*M_PI),0.0);

  glVertex3d(CapstanX+CapstanR*cos((angle+90.0 )/180.0*M_PI),CapstanY+CapstanR*sin((angle+90.0 )/180.0*M_PI),0.0);
  glVertex3d(CapstanX+CapstanR*cos((angle+270.0)/180.0*M_PI),CapstanY+CapstanR*sin((angle+270.0)/180.0*M_PI),0.0);
#endif

#ifdef USECORNER
  circle(BottomCornerX, BottomCornerY, BottomCornerOuterR, 0.0, 360.0);
  circle(BottomCornerX, BottomCornerY, BottomCornerInnerR, 0.0, 360.0);
#endif

  circle(BottomChamperInX, BottomChamperInY, BottomChamperInInnerR, 0.0, 360.0);

  points2Line(BottomChamperOuter1X, BottomChamperOuter1Y, BottomChamperOuter2X, BottomChamperOuter2Y,
              &a1, &c1);
  intersectionLineCircle(a1,c1,BottomChamperInX,BottomChamperInY,BottomChamperInInnerR,
              &xi1,&yi1,&xi2,&yi2);

  glVertex3d(xi2,yi2,0.0);
  glVertex3d(BottomChamperOuter2X,BottomChamperOuter2Y,0.0);

  intersectionLineCircle(a1,c1,BottomChamperInX,BottomChamperInY,BottomChamperInOuterR,
              &xi1,&yi1,&xi2,&yi2);

  angle1=-atan2(yi2-BottomChamperInY,xi2-BottomChamperInX)*180.0/M_PI;
  points2Line(BottomChamperOuter4X, BottomChamperOuter4Y, BottomChamperOuter1X, BottomChamperOuter1Y,
              &a1,&c1);
  intersectionLineCircle(a1,c1,BottomChamperInX,BottomChamperInY,BottomChamperInOuterR,
              &xi1,&yi1,&xi2,&yi2);
  angle2=360+atan2(yi2-BottomChamperInY,xi2-BottomChamperInX)*180.0/M_PI;

//printf("Bottom: angle1: %lg angle2: %lg\n",angle1,angle2);
  circle(BottomChamperInX, BottomChamperInY, BottomChamperInOuterR, -angle2, angle1);

  glVertex3d(BottomChamperOuter2X,BottomChamperOuter2Y,0.0);
  glVertex3d(BottomChamperOuter3X,BottomChamperOuter3Y,0.0);

  glVertex3d(BottomChamperOuter3X,BottomChamperOuter3Y,0.0);
  glVertex3d(BottomChamperOuter4X,BottomChamperOuter4Y,0.0);

  glVertex3d(BottomChamperOuter4X,BottomChamperOuter4Y,0.0);
  glVertex3d(BottomChamperOuter1X,BottomChamperOuter1Y,0.0);

  glVertex3d(BottomChamperOuter1X+9.0,BottomChamperOuter1Y-7.0,0.0);
  glVertex3d(735.0,MirrorY-37.0,0.0);

  glVertex3d(BottomChamperInner2X, BottomChamperInner2Y, 0.0);
  glVertex3d(BottomChamperInner3X, BottomChamperInner3Y, 0.0);

  points2Line(799.0,MirrorY-88.0,BottomChamperInner4X,BottomChamperInner4Y,&a1,&c1);
  intersectionLineCircle(a1,c1,BottomChamperOutX,BottomChamperOutY,BottomChamperOutR,
                         &xi1,&yi1,&xi2,&yi2);
  glVertex3d(799.0,MirrorY-88.0,0.0);
  glVertex3d(xi2,yi2,0.0);
  circle(BottomChamperOutX,BottomChamperOutY,BottomChamperOutR,0.0,360.0);

  // Tape:

//if(debug&DEBUGbuffer) fprintf(debug_fh, "gulerod 14\n");
  glEnd();
//if(debug&DEBUGbuffer) fprintf(debug_fh, "gulerod 15\n");
  glColor4d(150.0/255.0,75.0/255.0,0.0,1.0);
  glBegin(GL_TRIANGLES);
  w->magTape.inRadius[unitNo]=length2Radius(w,w->magTape.INlength[unitNo])*PIXELSPERM;
  w->magTape.inRotation[unitNo]=-fmod(length2Rotation(w,w->magTape.INlength[unitNo])*360.0,360.0);
  w->magTape.outRadius[unitNo]=length2Radius(w,w->magTape.OUTlength[unitNo])*PIXELSPERM;
//printf("inRadius: %g outRadius: %g\n",inRadius,outRadius);
  w->magTape.outRotation[unitNo]=fmod(length2Rotation(w,w->magTape.OUTlength[unitNo])*360.0,360.0);
  disk(TopReelX,TopReelY,0.0,TopReelInnerR,w->magTape.inRadius[unitNo]);
  disk(BottomReelX,BottomReelY, 0.5,BottomReelInnerR,w->magTape.outRadius[unitNo]);
  glEnd();
  glBegin(GL_LINES);

#ifdef USECORNER
  twoCirclesTangent(TopReelX,TopReelY,w->magTape.inRadius[unitNo],
                    TopCornerX,TopCornerY,TopCornerInnerR,0,
		    &xi11,&yi11,&xi12,&yi12,&xi21,&yi21,&xi22,&yi22);
  glVertex3d(xi11,yi11,0.0);
  glVertex3d(xi21,yi21,0.0);

  twoCirclesTangent(TopCornerX,TopCornerY,TopCornerInnerR,
                    TopChamperInX,TopChamperInY,TopChamperInInnerR,1,
		    &xi11,&yi11,&xi12,&yi12,&xi21,&yi21,&xi22,&yi22);
  glVertex3d(xi12,yi12,0.0);
  glVertex3d(xi21,yi21,0.0);
#else
  twoCirclesTangent(TopReelX,TopReelY,w->magTape.inRadius[unitNo],
                    TopChamperInX,TopChamperInY,TopChamperInInnerR,1,
		    &xi11,&yi11,&xi12,&yi12,&xi21,&yi21,&xi22,&yi22);
  glVertex3d(xi12,yi12,0.0);
  glVertex3d(xi21,yi21,0.0);
#endif

  xcenter1=(TopChamperInner1X+TopChamperInner4X)/2.0;
  ycenter1=(TopChamperInner1Y+TopChamperInner4Y)/2.0;
  xcenter2=(TopChamperInner2X+TopChamperInner3X)/2.0;
  ycenter2=(TopChamperInner2Y+TopChamperInner3Y)/2.0;
  points2Line(xcenter1,ycenter1,xcenter2,ycenter2,&acenter,&ccenter);
  x=xcenter1+(xcenter2-xcenter1)*(w->magTape.inRatio[unitNo]+0.5);
  y=-ccenter-acenter*x;
  r1=fabs(acenter*TopChamperInner1X+TopChamperInner1Y+ccenter)/sqrt(SQR(acenter)+1.0)-tapewidth*0.5;
  angle3=atan(acenter)*180.0/M_PI;
//printf("angle3: %g\n",angle3);
//glVertex3d(xcenter1,-ccenter-acenter*xcenter1,0.0);
//glVertex3d(xcenter2,-ccenter-acenter*xcenter2,0.0);
  circle(x,y,r1,angle3-90.0,angle3+90.0);

  x2=r1*cos((angle3+90.0)/180.0*M_PI)+x;
  y2=r1*sin((angle3-90.0)/180.0*M_PI)+y;

  x3=xcenter1+5.0;
  y3=-ccenter-acenter*x3;
  x1=r1*cos((angle3+90.0)/180.0*M_PI)+x3;
  y1=r1*sin((angle3-90.0)/180.0*M_PI)+y3;
  glVertex3d(x1,y1,0.0);
  glVertex3d(x2,y2,0.0);

  x2=r1*cos((angle3-90.0)/180.0*M_PI)+x;
  y2=r1*sin((angle3-270.0)/180.0*M_PI)+y;
  
  x3=xcenter1+15.0;
  y3=-ccenter-acenter*x3;
  x1=r1*cos((angle3-90.0)/180.0*M_PI)+x3;
  y1=r1*sin((angle3-270.0)/180.0*M_PI)+y3;
  glVertex3d(x1,y1,0.0);
  glVertex3d(x2,y2,0.0);

  twoCirclesTangent(TopChamperOutX,TopChamperOutY,TopChamperOutR,
                    Head3X,Head3Y,tapewidth*0.5,1,
		    &xi11,&yi11,&xi12,&yi12,&xi21,&yi21,&xi22,&yi22);
  glVertex3d(xi12,yi12,0.0);
  glVertex3d(xi21,yi21,0.0);
  x=xi21+Head4X-Head3X;
  y=yi21;
  glVertex3d(xi21,yi21,0.0);
  glVertex3d(x,y,0.0);
  
  twoCirclesTangent(Head4X,Head4Y,tapewidth*0.5,
                    CapstanX,CapstanY,CapstanR,0,
		    &xi11,&yi11,&xi12,&yi12,&xi21,&yi21,&xi22,&yi22);
  glVertex3d(x,y,0.0);
  glVertex3d(xi22,yi22,0.0);

  twoCirclesTangent(CapstanX,CapstanY,CapstanR,
                    BottomChamperOutX,BottomChamperOutY,BottomChamperOutR,1,
		    &xi11,&yi11,&xi12,&yi12,&xi21,&yi21,&xi22,&yi22);
  glVertex3d(xi12,yi12,0.0);
  glVertex3d(xi21,yi21,0.0);

  xcenter1=(BottomChamperInner1X+BottomChamperInner4X)/2.0;
  ycenter1=(BottomChamperInner1Y+BottomChamperInner4Y)/2.0;
  xcenter2=(BottomChamperInner2X+BottomChamperInner3X)/2.0;
  ycenter2=(BottomChamperInner2Y+BottomChamperInner3Y)/2.0;
  points2Line(xcenter1,ycenter1,xcenter2,ycenter2,&acenter,&ccenter);
  x=xcenter1+(xcenter2-xcenter1)*(w->magTape.outRatio[unitNo]+0.5);
  y=-ccenter-acenter*x;
  r1=fabs(acenter*BottomChamperInner1X+BottomChamperInner1Y+ccenter)/
     sqrt(SQR(acenter)+1.0)-tapewidth*0.5;
  angle3=atan(acenter)*180.0/M_PI;
  circle(x,y,r1,angle3-90.0,angle3+90.0);

  x2=r1*cos((angle3+90.0)/180.0*M_PI)+x;
  y2=r1*sin((angle3-90.0)/180.0*M_PI)+y;
  x3=xcenter1+15;
  y3=-ccenter-acenter*x3;
  x1=r1*cos((angle3+90.0)/180.0*M_PI)+x3;
  y1=r1*sin((angle3-90.0)/180.0*M_PI)+y3;
  glVertex3d(x1,y1,0.0);
  glVertex3d(x2,y2,0.0);

  x2=r1*cos((angle3-90.0)/180.0*M_PI)+x;
  y2=r1*sin((angle3-270.0)/180.0*M_PI)+y;
  x3=xcenter1+5.0;
  y3=-ccenter-acenter*x3;
  x1=r1*cos((angle3-90.0)/180.0*M_PI)+x3;
  y1=r1*sin((angle3-270.0)/180.0*M_PI)+y3;
  glVertex3d(x1,y1,0.0);
  glVertex3d(x2,y2,0.0);

#ifdef USECORNER
  twoCirclesTangent(BottomReelX,BottomReelY,outRadius,
                    BottomCornerX,BottomCornerY,BottomCornerInnerR,0,
		    &xi11,&yi11,&xi12,&yi12,&xi21,&yi21,&xi22,&yi22);
  glVertex3d(xi11,yi11,0.0);
  glVertex3d(xi21,yi21,0.0);

  twoCirclesTangent(BottomCornerX,BottomCornerY,BottomCornerInnerR,
                    BottomChamperInX,BottomChamperInY,BottomChamperInInnerR,1,
		    &xi11,&yi11,&xi12,&yi12,&xi21,&yi21,&xi22,&yi22);
  glVertex3d(xi12,yi12,0.0);
  glVertex3d(xi21,yi21,0.0);
#else
  twoCirclesTangent(BottomReelX,BottomReelY,w->magTape.outRadius[unitNo],
                    BottomChamperInX,BottomChamperInY,BottomChamperInInnerR,1,
		    &xi11,&yi11,&xi12,&yi12,&xi21,&yi21,&xi22,&yi22);
  glVertex3d(xi12,yi12,0.0);
  glVertex3d(xi21,yi21,0.0);
#endif


  glEnd();

// Label on top reel:
//#define TopReelLabelWidth 100.0
//#define TopReelLabelHeight 50.0
//#define TopReelLabelDistance 100.0

// kilroy
  pushmatrix(__LINE__);
  glPushMatrix();
  glTranslated(TopReelX,TopReelY,0.0);
  glRotated(w->magTape.inRotation[unitNo],0.0,0.0,1.0);
  glPolygonMode(GL_FRONT, GL_FILL);
  glPolygonMode(GL_BACK, GL_FILL);
  glBegin(GL_QUADS);
    glColor4d(0.8,0.8,0.8,0.0);
    glVertex3d(-TopReelLabelWidth/2.0, TopReelLabelDistance+TopReelLabelHeight/2.0, 0.5);
    glVertex3d( TopReelLabelWidth/2.0, TopReelLabelDistance+TopReelLabelHeight/2.0, 0.5);
    glVertex3d( TopReelLabelWidth/2.0, TopReelLabelDistance-TopReelLabelHeight/2.0, 0.5);
    glVertex3d(-TopReelLabelWidth/2.0, TopReelLabelDistance-TopReelLabelHeight/2.0, 0.5);
  glEnd();
  glColor4d(0.0,0.0,0.0,0.0);
//glTranslated(-TopReelLabelWidth/2.0, TopReelLabelDistance+TopReelLabelHeight/2.0, 1.0);
  glTranslated(0.0, TopReelLabelDistance, 1.0);
  bbox(1.0, &textXmin, &textXmax, &textYmin, &textYmax, w->magTape.tapeLabels[unitNo]);
//if(debug&DEBUGbuffer) fprintf(debug_fh, "%d: textXmin: %g textXmax: %g textYmin: %g textYmax: %g\n", unitNo, textXmin, textXmax, textYmin, textYmax);
  sx=TopReelLabelWidth/(textXmax-textXmin);
  sy=TopReelLabelHeight/(textYmax-textYmin);
  s=sx;
  if(s>sy) s=sy;
//if(debug&DEBUGbuffer) fprintf(debug_fh, "%d: sx: %g sy: %g s: %g\n",unitNo, sx,sy,s);
  glScaled(s,-s,1.0);
  glTranslated(-(textXmin+textXmax)*0.5, -(textYmin+textYmax)*0.5, 0.0);
  glBegin(GL_LINES);
  stroke(1.0, 0.0, 0.0, 0.0, w->magTape.tapeLabels[unitNo]);
  glEnd();

  popmatrix(__LINE__);
  glPopMatrix();
  
  glEnable(GL_BLEND);
  pushmatrix(__LINE__);
  glPushMatrix();
  glTranslated(BottomReelX-BottomReelOuterR,BottomReelY-BottomReelOuterR,0.0);
  glScaled(BottomReelOuterR*2.0,BottomReelOuterR*2.0,1.0);
  glTranslated(0.5,0.5,0.0);
  glRotated(w->magTape.outRotation[unitNo],0.0,0.0,1.0);
  glTranslated(-0.5,-0.5,0.0);
  
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); // scale linearly when image bigger than texture
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); // scale linearly when image smalled than texture

    // 2d texture, level of detail 0 (normal), 3 components (red, green, blue), x size from image, y size from image, 
    // border 0 (normal), rgb color data, unsigned byte data, and finally the data itself.
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w->magTape.image1->sizeX, w->magTape.image1->sizeY, 0, GL_RGBA, GL_UNSIGNED_BYTE, w->magTape.image1->data);
  glEnable(GL_TEXTURE_2D);			// Enable Texture Mapping
  glBindTexture(GL_TEXTURE_2D, w->magTape.texture[0]);   // choose the texture to use.


  glBegin(GL_QUADS);
    glColor4d(1.0,1.0,1.0,1.0);
    glTexCoord2d(0.0, 0.0);
    glVertex3d(0.0, 0.0, 1.0);
    glTexCoord2d(0.0, 1.0);
    glVertex3d(0.0, 1.0, 1.0);
    glTexCoord2d(1.0, 1.0);
    glVertex3d(1.0, 1.0, 1.0);
    glTexCoord2d(1.0, 0.0);
    glVertex3d(1.0, 0.0, 1.0);

  glEnd();
  popmatrix(__LINE__);
  glPopMatrix();

}

void GLAPIENTRY
MessageCallback( GLenum source,
                 GLenum type,
                 GLuint id,
                 GLenum severity,
                 GLsizei length,
                 const GLchar* message,
                 const void* userParam )
{
  if(debug&DEBUGbuffer)
  fprintf( debug_fh, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
           ( type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "" ),
            type, severity, message );
}

static void FindFonts(XsMagTapeWidget w)
{
  XFontStruct *fontInfo;
  w->magTape.fontbase = glGenLists(96);
  fontInfo = XLoadQueryFont(XtDisplay(w), "-adobe-helvetica-medium-r-normal--18-*-*-*-p-*-iso8859-1");
  if(fontInfo==NULL)
  {
    fprintf(stderr, "No font\n");
    exit(-1);
  }
  glXUseXFont(fontInfo->fid, 32, 96, w->magTape.fontbase);
  XFreeFont(XtDisplay(w), fontInfo);
}

static void Realize(w, valueMask, attributes)
XsMagTapeWidget w;
Mask *valueMask;
XSetWindowAttributes *attributes;
{
  int i;

  if(debug&DEBUGbuffer) fprintf(debug_fh, "Realize called.\n");
   
  if(debug&DEBUGbuffer)
  {
    for(i=0;i<6;i++)
      fprintf(debug_fh, "Realize: unit: %d status: %d %s\n",i,w->magTape.status[i],(w->magTape.status[i] == TAPE_IDLE)?"IDLE":"busy");
  }
    XtCreateWindow ((Widget) w, (unsigned int)InputOutput,
		    CopyFromParent,
		    *valueMask, attributes);

  if(debug&DEBUGbuffer) fprintf(debug_fh, "Realize: glXCreateContext.\n");

  w->magTape.cmap = DefaultColormap(XtDisplay(w), DefaultScreen(XtDisplay(w)));

  w->magTape.glx_context = glXCreateContext(XtDisplay(w), w->magTape.visualInfo, 0, GL_TRUE);
  if(w->magTape.glx_context == NULL)
  {
    printf("glx context failed.\n");
    exit(0);
  }
//if(debug&DEBUGbuffer) fprintf(debug_fh, "glXMakeCurrent 2\n" );
  glXMakeCurrent(XtDisplay(w), XtWindow(w), w->magTape.glx_context);
  // During init, enable debug output
//glEnable              ( GL_DEBUG_OUTPUT );
//glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
//glDebugMessageCallback( MessageCallback, 0 );
  LoadGLTextures(w);

  FindFonts(w);
  if(debug&DEBUGbuffer) fprintf(debug_fh, "Realize done.\n");
  Redisplay(w, NULL, NULL);
}

static int magTape_attrib0[] ={GLX_RGBA, GLX_DOUBLEBUFFER, GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8,
                            GLX_ALPHA_SIZE, 8, GLX_DEPTH_SIZE, 16, None};

static void createVisualInfo(XsMagTapeWidget w)
{
    static XVisualInfo *visualInfo;

    w->magTape.visualInfo = glXChooseVisual(XtDisplay(w),
					XScreenNumberOfScreen(XtScreen(w)),
					magTape_attrib0);
    if (!w->magTape.visualInfo)
    {
      fprintf(stderr, "Error: Couldn't find any suitable visual for GL.\n");
      exit(-1);
    }
}

static void Initialize(request, new, args, num_args)
XsMagTapeWidget request, new;
ArgList args;
Cardinal *num_args;
{
  if(getenv("UAKK1")) UAKK1=atoi(getenv("UAKK1"));
  if(getenv("ADJLIMIT")) ADJLIMIT=strtod(getenv("ADJLIMIT"),NULL);
  if(debug&DEBUGbuffer) fprintf(debug_fh, "Initialize called. num_args = %d UAKK1 = %d ADJLIMIT = %g\n", *num_args, UAKK1, ADJLIMIT);
  if(request->core.width == 0) new->core.width = wXMAX*3;
  if(request->core.height == 0) new->core.height = wYMAX*2;

  /* determine the visual info if needed */
  if (new->magTape.visualInfo == NULL)
  {
      new->magTape.myVisual = TRUE;
      if(debug&DEBUGbuffer) fprintf(debug_fh, "Initialize: createVisualInfo\n");
      createVisualInfo(new);
  }
  else
  {
      if(debug&DEBUGbuffer) fprintf(debug_fh, "Initialize: myVisual = FALSE\n");
      new->magTape.myVisual = FALSE;
  }

  new->core.depth = new->magTape.visualInfo->depth;
  if(debug&DEBUGbuffer) fprintf(debug_fh, "Initialize: XtGetApplicationResources\n");

  XsmagTapeInsertDefaultValues(new);
  XsmagTapeInsertValues(new);
  if(debug&DEBUGbuffer) fprintf(debug_fh, "Initialize done\n");
}

static void Destroy(w)
XsMagTapeWidget w;
{
  /*
	This must release all GC's and
	remove all callbacks
  */
  if(debug&DEBUGbuffer) fprintf(debug_fh, "MagTape: Destroy called.\n");

  XtRemoveAllCallbacks((Widget) w, XtNdone);

  if (w->magTape.myVisual && w->magTape.visualInfo)
      XtFree ((XtPointer)w->magTape.visualInfo);

  glDeleteLists(w->magTape.fontbase,96);

  fclose(w->magTape.buffer_fh);
}

static void Resize(w)
XsMagTapeWidget w;
{
  long glxsize, glysize;
  /*
	Recalculates everything.
  */
  if(debug&DEBUGbuffer) fprintf(debug_fh, "MagTape: Resize called.\n");

  if(!XtIsRealized((Widget) w))
  {
    if(debug&DEBUGbuffer) fprintf(debug_fh, "MagTape: Isn't realized.\n");
    return;
  }

//if(debug&DEBUGbuffer) fprintf(debug_fh, "glXMakeCurrent 3\n");
  glXMakeCurrent(XtDisplay(w), XtWindow(w), w->magTape.glx_context);

}

GLvoid displayClockStroke(XsMagTapeWidget w)
{
  double textXmin, textXmax, textYmin, textYmax;
  double size=100.0;
  double t=seconds(w);
  char txt[100];
  sprintf(txt, "%10.3f", t);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glViewport(0, 0, w->core.width, w->core.height);
  glOrtho(0.0,(double)XMAX,0.0,((double)YMAX),-1.0,1.0);
  glMatrixMode(GL_MODELVIEW);
  glDisable(GL_TEXTURE_2D);
  glDisable(GL_BLEND);
  bbox(size, &textXmin, &textXmax, &textYmin, &textYmax, txt);
//printf("textXmin: %g textXmax: %g textYmin: %g textYmax: %g\n",textXmin,textXmax,textYmin,textYmax);
  glBegin(GL_LINES);
  glColor4d(1.0,0.0,0.0,1.0);
  stroke(size, -textXmin, YMAX-textYmax, 1.0, txt);
  glEnd();
}

GLvoid displayClock(XsMagTapeWidget w)
{
  double t=seconds(w);
  char txt[100];
  sprintf(txt, "%10.3f", t);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glViewport(0, 0, w->core.width, w->core.height);
  glOrtho(0.0,(double)XMAX,0.0,((double)YMAX),-1.0,1.0);
  glMatrixMode(GL_MODELVIEW);
  glDisable(GL_TEXTURE_2D);
  glDisable(GL_BLEND);
  glColor4d(1.0,0.0,0.0,1.0);
  glRasterPos2d(140.0, YMAX-30.0);
  glPushAttrib(GL_LIST_BIT);
  glListBase(w->magTape.fontbase - 32);
  glCallLists(strlen(txt), GL_UNSIGNED_BYTE, txt);
  glPopAttrib();
}

static void Redisplay(w, event, region)
XsMagTapeWidget w;
XEvent *event;
Region region;
{
  int unitNo,i;
  if(debug&DEBUGbuffer)
  {
    fprintf(debug_fh, "MagTape: Redisplay entry.\n");
    for(i=0;i<6;i++)
      fprintf(debug_fh, "Redisplay I: unit: %d status: %d %s\n",i,w->magTape.status[i],(w->magTape.status[i] == TAPE_IDLE)?"IDLE":"busy");
  }

//if(debug&DEBUGbuffer) fprintf(debug_fh, "glXMakeCurrent 4\n");
  glXMakeCurrent(XtDisplay(w), XtWindow(w), w->magTape.glx_context);

  glClearColor(1.0, 1.0, 1.0, 1.0);	// Clear The Background Color To White 
  glClear(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT);

  for(unitNo=0; unitNo<w->magTape.ndrives; unitNo++)
  {
    DrawGLScene(w,unitNo);
  }
  displayClock(w);


  // since this is double buffered, swap the buffers to display what just got drawn.
  if(debug&DEBUGbuffer) fprintf(debug_fh, "swap buffers\n");
  glXSwapBuffers(XtDisplay(w), XtWindow(w));
  if(debug&DEBUGbuffer)
  {
    for(i=0;i<6;i++)
    fprintf(debug_fh, "Redisplay II: unit: %d status: %d %s\n",i,w->magTape.status[i],(w->magTape.status[i] == TAPE_IDLE)?"IDLE":"busy");
  }
  if(debug&DEBUGbuffer) fprintf(debug_fh, "Redisplay exit.\n");
}

static Boolean SetValues(current, request, new)
XsMagTapeWidget current, request, new;
{
  if(debug&DEBUGbuffer) fprintf(debug_fh, "SetValues called\n");
  XsmagTapeInsertValues(new);

  if(debug&DEBUGbuffer) fprintf(debug_fh, "SetValues exit\n");
  return(TRUE); /* Always redraw */
}

static void enter_magTape(w, event)
XsMagTapeWidget w;
XEvent *event;
{
}

static void leave_magTape(w, event)
XsMagTapeWidget w;
XEvent *event;
{
}

static void set_update_mode(w)
XsMagTapeWidget w;
{
  int i,j;

}

static void XsmagTapeInsertValues(w)
XsMagTapeWidget w;
{
  int i;
  if(debug&DEBUGbuffer) fprintf(debug_fh, "XsmagTapeInsertValues entry.\n");

  if(w->magTape.ptapelabels != NULL)
  {
    for(i=0; i<NDRIVES; i++)
    {
      if(w->magTape.tapeLabels[i] != NULL)
      {
	free(w->magTape.tapeLabels[i]);
      }
      if(debug&DEBUGbuffer) fprintf(debug_fh, "ptapelabels[%d]: %s\n",i,w->magTape.ptapelabels[i]);
      w->magTape.tapeLabels[i] = malloc(strlen(w->magTape.ptapelabels[i])+1);
      strcpy(w->magTape.tapeLabels[i], w->magTape.ptapelabels[i]);
    }
  }
}


static void XsmagTapeInsertDefaultValues(w)
XsMagTapeWidget w;
{
  int i;

  if(debug&DEBUGbuffer) fprintf(debug_fh, "XsmagTapeInsertDefaultValues entry.\n");

  for(i=0;i<NDRIVES;i++)
  {
    w->magTape.inTape[i] = MAXCHAMPERTAPELENGTH*0.5;
    w->magTape.outTape[i] = MAXCHAMPERTAPELENGTH*0.5;
    w->magTape.inError[i] = 0.0;
    w->magTape.outError[i] = 0.0;
    w->magTape.OUTlength[i] = 0.0;
    w->magTape.INlength[i] = TAPELENGTH;
    w->magTape.tapeappetite[i] = 0.0;
    w->magTape.status[i] = TAPE_IDLE;
    if(debug&DEBUGbuffer) fprintf(debug_fh, "XsmagTapeInsertDefaultValues: unit: %d status: %d %s\n",i,w->magTape.status[i],(w->magTape.status[i] == TAPE_IDLE)?"IDLE":"busy");
    w->magTape.readstart[i] = 0;
    w->magTape.taperead[i] = 0;
    w->magTape.UAKK[i] = UAKK1;
    w->magTape.capstanAcceleration[i] = 0;
  }
  w->magTape.time0 = clock_count;
  w->magTape.thickness = THICKNESS;
  w->magTape.radius0 = 0.113/2.0;
  w->magTape.lastsecs=seconds(w);
  w->magTape.timerRunning=0;
  w->magTape.buffer_fh = fopen("buffer.log","w");
//fprintf(stderr, "buffer_fh I: %ld\n", (long)(w->magTape.buffer_fh));

  if(debug&DEBUGbuffer) fprintf(debug_fh, "XsmagTapeInsertDefaultValues exit.\n");
}

static XComposeStatus compose={NULL,0};
static int lastUnitNo=0;

static void InsertChar(XsMagTapeWidget w, XEvent *event)
{
  unsigned char strbuf[100];
  KeySym keycode;
  int ilen,unitNo;
  ilen=XLookupString((XKeyEvent *)event, strbuf, 100, &keycode, &compose);
  strbuf[ilen]='\0';
  printf("strbuf: %s %ld\n",strbuf,keycode);
  if(keycode==65360)
  {
    /* home */
    for(unitNo=0; unitNo<w->magTape.ndrives; unitNo++)
    {
      XsmagTapeCommand(w, unitNo, TAPE_DO_REWIND, 0.0, TAPE_SEEK_COLOR);
    }
  }
  else if(keycode>=48 && keycode<=53)
  {
    lastUnitNo = keycode-48;
  }
  else if(keycode == 'f')
  {
    XsmagTapeCommand(w, lastUnitNo, TAPE_FORWARD, BLOCKLEN(200,0,0), TAPE_READ_COLOR);
  }
}


static void done_magTape(XsMagTapeWidget w,int unitNo)
{
  static long lunitNo;

  lunitNo=unitNo;

  if(XtHasCallbacks((Widget) w, XtNdone) == XtCallbackHasSome)
  {
    if(debug&DEBUGbuffer) fprintf(debug_fh, "%d: done_magTape\n",unitNo);
    XtCallCallbacks((Widget) w, XtNdone, &lunitNo);
  }
}

/* Exported functions */

extern double tape_position(int);

void XsmagTapeLog(XsMagTapeWidget w, int unitNo, char *txt)
{
  double t=seconds(w);
  double position;
  int i;
  position=tape_position(unitNo+1);
//fprintf(stderr, "buffer_fh II: %ld\n", (long)(w->magTape.buffer_fh));
  fprintf(w->magTape.buffer_fh, "%d: %8.3f\t",unitNo,t);
//for(i=0;i<unitNo;i++) fprintf(w->magTape.buffer_fh, "\t");
  fprintf(w->magTape.buffer_fh, "%s %.2f%s\n", txt, position, (position>TAPELENGTH)?" EOT":"");
  fflush(w->magTape.buffer_fh);
  if(debug&DEBUGbuffer) fprintf(debug_fh, "XsmagTapeLog: %d %8.3f\t%s %.2f%s\n", unitNo,t,txt,position,(position>TAPELENGTH)?" EOT":"");
  
}

int XsmagTapeIdle(XsMagTapeWidget w,int unitNo)
{
//fprintf(stderr, "XsmagTapeIdle. buffer_fh: %ld\n", (long)(w->magTape.buffer_fh));
//if(debug&DEBUGbuffer) fprintf(debug_fh, "XsmagTapeIdle: unit: %d status: %d %s\n",unitNo,w->magTape.status[unitNo],(w->magTape.status[unitNo] == TAPE_IDLE)?"IDLE":"busy");
  return(w->magTape.status[unitNo] == TAPE_IDLE);
}

int XsmagTapeCommand(XsMagTapeWidget w, int unitNo, int cmd, double parameter, int type)
{
  int ret=1;
  char *cmds[]={"TAPE_IDLE","TAPE_FORWARD","TAPE_REVERSE","TAPE_DO_REWIND","TAPE_SETPOS"};
  if(debug&DEBUGbuffer)
  {
    fprintf(debug_fh, "unit: %d\n",unitNo);
    fprintf(debug_fh, "status: %d\n",w->magTape.status[unitNo]);
    fprintf(debug_fh, "taperead: %g\n",w->magTape.taperead[unitNo]);
    fprintf(debug_fh, "type: %s\n",tape_color_types[type]);
    fprintf(debug_fh, "cmd: %s\n",cmds[cmd]);
    fprintf(debug_fh, "%d: XsmagTapeCommand status: %d taperead: %g type: %s cmd: %s\n", 
                       unitNo, w->magTape.status[unitNo], w->magTape.taperead[unitNo], tape_color_types[type], cmds[cmd]);
  }

  w->magTape.color_status[unitNo] = type;
  if(w->magTape.status[unitNo]!=TAPE_IDLE)
  {
    if(debug&DEBUGbuffer) fprintf(debug_fh, "%d TapeCommand: Tape is already running: %d!\n", unitNo, w->magTape.status[unitNo]);
    return 1;
  }
  if(cmd==TAPE_SETPOS)
  {
    if(parameter==0.0)
    {
      cmd=TAPE_DO_REWIND;
    }
    else if(parameter>w->magTape.taperead[unitNo])
    {
      cmd=TAPE_FORWARD;
      parameter -= w->magTape.taperead[unitNo];
    }
    else if(parameter<w->magTape.taperead[unitNo])
    {
      cmd=TAPE_REVERSE;
      parameter = w->magTape.taperead[unitNo]-parameter;
    }
    else
    {
      /* already at position */
      return 0;
    }
  }
  switch(cmd)
  {
    case TAPE_DO_REWIND:
      if(w->magTape.taperead[unitNo]>0)
      {
	w->magTape.capstanAcceleration[unitNo]=-CAPSTANACCELERATION;
	w->magTape.lastCapstan[unitNo]=-1.0;
	w->magTape.UAKK[unitNo]=UAKK2;
	w->magTape.status[unitNo]=REWIND_ACCELERATE;
	if(debug&DEBUGbuffer) fprintf(debug_fh, "%d: start REWIND\n", unitNo);
	ret=0;
      }
      break;
    case TAPE_FORWARD:
      w->magTape.readstart[unitNo]=w->magTape.taperead[unitNo];
      w->magTape.tapeappetite[unitNo]=parameter;
      w->magTape.capstanAcceleration[unitNo]=CAPSTANACCELERATION;
      w->magTape.lastCapstan[unitNo]=1.0;
      w->magTape.UAKK[unitNo]=UAKK1;
      w->magTape.status[unitNo]=FORWARD_ACCELERATE;
      if(debug&DEBUGbuffer) fprintf(debug_fh, "%d: start FORWARD\n", unitNo);
      ret=0;
      break;
    case TAPE_REVERSE:
      if(w->magTape.taperead[unitNo]>0)
      {
	w->magTape.readstart[unitNo]=w->magTape.taperead[unitNo];
	w->magTape.tapeappetite[unitNo]=parameter;
	w->magTape.capstanAcceleration[unitNo]=-CAPSTANACCELERATION;
	w->magTape.lastCapstan[unitNo]=-1.0;
	w->magTape.UAKK[unitNo]=UAKK1;
	w->magTape.status[unitNo]=REVERSE_ACCELERATE;
	if(debug&DEBUGbuffer) fprintf(debug_fh, "%d: start REVERSE\n", unitNo);
	ret= 0;
      }
      break;
  }
  if(debug&DEBUGbuffer) fprintf(debug_fh, "%d: XsmagTapeCommand: timerRunning I: %d\n",unitNo,w->magTape.timerRunning);
  if(!w->magTape.timerRunning)
  {
    setTimerRunning(w);
    if(debug&DEBUGbuffer) fprintf(debug_fh, "%d: XsmagTapeCommand: timerRunning II: %d\n",unitNo,w->magTape.timerRunning);
    if(w->magTape.timerRunning)
    {
      w->magTape.lastsecs=seconds(w);
      startTimer(w);
    }
  }
  fflush(stdout);
  return(ret) ;
}

