/*
	The MagTape Widget Methods.

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

/*
#undef DEBUG
*/

#include <GL/glx.h>
#include <GL/gl.h>
#include <GL/glu.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.h"
#include "MagTapeP.h"
#include "spole5.xpm"


#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();
GLvoid DrawGLScene(XsMagTapeWidget);
static void startTimer(XsMagTapeWidget);

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

XtResource magTape_resources[]=
{

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

  {XtNtapeLabel, XtCTapeLabel, XtRString, sizeof(caddr_t),
   offset(tapeLabel), XtRString, "Tape name"},

  {XtNunitNo, XtCUnitNo, XtRInt, sizeof(int),
   offset(unitNo), XtRString, "0"},

  {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();
	    

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

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

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 */
    NULL,                /* 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;


static void error(w,string)
Widget w;
char *string;
{
    char buf[100];
    sprintf (buf, "XsmagTape: %s\n", string);
    XtAppError(XtWidgetToApplicationContext(w), buf);
}

static void warning(w,string)
Widget w;
char *string;
{
    char buf[100];
    sprintf (buf, "XsmagTape: %s\n", string);
    XtAppWarning(XtWidgetToApplicationContext(w), buf);
}

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];
      }
    }
#ifdef uakk
    p = (unsigned int *) image->data;
    for(r=0;r<image->sizeY;r++)
      for(c=0;c<image->sizeX;c++)
      {
	printf("%4ld %4ld %8.8X\n",r,c,(*p));
	p++;
      }
#endif
    free((char *) clookup);
    
    return 1;
}


static double seconds(XsMagTapeWidget w)
{
  double td;
  long tn;
  clock_gettime(CLOCK_MONOTONIC, &(w->magTape.time1));
  tn=w->magTape.time1.tv_nsec - w->magTape.time0.tv_nsec;
  td=w->magTape.time1.tv_sec - w->magTape.time0.tv_sec;
  if(tn<0)
  {
    tn += 1000000000L;
    td -= 1.0;
  }
  td += ((double)tn)*1e-9;
  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(spole5, 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};

GLvoid InitGL(XsMagTapeWidget w, GLsizei Width, GLsizei Height)
{
#ifdef DEBUG
  printf("%s: InitGL: %d %d\n", w->magTape.tapeLabel, Width, Height);
#endif

  w->magTape.window_width=Width;
  w->magTape.window_height=Height;
  w->magTape.window_aspect = ((GLdouble) Width)/((GLdouble) Height)*((GLdouble) YMAX)/((GLdouble) XMAX);
#ifdef DEBUG
  printf("%s: window_aspect 2: %g\n",w->magTape.tapeLabel, w->magTape.window_aspect);
#endif

  LoadGLTextures(w);				// Load The Texture(s) 
  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);
}

/* The main drawing function. */

//XtTimerCallbackProc timercb(XtPointer client_data, XtIntervalId *timer_id)
void timercb(XtPointer client_data, XtIntervalId *timer_id)
{
  double total;
  XsMagTapeWidget w = (XsMagTapeWidget) client_data;
  w->magTape.secs=seconds(w);
  w->magTape.dsecs2=w->magTape.secs-w->magTape.lastsecs;
#ifdef DEBUG
  printf("%s: kilroy2 dsecs2: %g\n", w->magTape.tapeLabel,w->magTape.dsecs2);
#endif
  fflush(stdout);
  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);
  w->magTape.lastsecs=w->magTape.secs;
  while(w->magTape.dsecs2 > 0.0)
  {
    if(w->magTape.dsecs2 >= 0.02)
    {
      w->magTape.dsecs = 0.02;
    }
    else
    {
      w->magTape.dsecs = w->magTape.dsecs2;
    }
    w->magTape.dsecs2 -= w->magTape.dsecs;
    switch(w->magTape.status)
    {
      case TAPE_IDLE:
	break;

      case FORWARD_ACCELERATE:
	if(w->magTape.capstanSpeed>READSPEED)
	{
	  w->magTape.capstanAcceleration=0.0;
	  w->magTape.capstanSpeed=READSPEED;
	  w->magTape.status=FORWARD_KEEP;
	}
	break;
      case FORWARD_KEEP:
	if((w->magTape.taperead-w->magTape.readstart+GAPLEN)>=w->magTape.tapeappetite)
	{
	  w->magTape.capstanAcceleration=-CAPSTANACCELERATION;
	  w->magTape.status=FORWARD_DECELERATE;
	}
	break;
      case FORWARD_DECELERATE:
	if(w->magTape.capstanSpeed<0.01)
	{
	  w->magTape.capstanSpeed=0;
	  w->magTape.capstanAcceleration=0;
	  w->magTape.tapeadjust=w->magTape.readstart+w->magTape.tapeappetite-w->magTape.taperead;
#ifdef DEBUG
	  printf("%s: tapeadjust: %.8f\n",w->magTape.tapeLabel, w->magTape.tapeadjust);
#endif
	  w->magTape.taperead+=w->magTape.tapeadjust;
	  w->magTape.inTape-=w->magTape.tapeadjust;
	  w->magTape.outTape+=w->magTape.tapeadjust;
	  w->magTape.status=TAPE_IDLE;
	  done_magTape(w);
//	  goto out;
	}
	break;

      case REVERSE_ACCELERATE:
	if(w->magTape.capstanSpeed<(-READSPEED))
	{
	  w->magTape.capstanAcceleration=0.0;
	  w->magTape.capstanSpeed=-READSPEED;
	  w->magTape.status=REVERSE_KEEP;
	}
	break;
      case REVERSE_KEEP:
	if((w->magTape.readstart-w->magTape.taperead+GAPLEN)>=w->magTape.tapeappetite)
	{
	  w->magTape.capstanAcceleration=CAPSTANACCELERATION;
	  w->magTape.status=REVERSE_DECELERATE;
	}
	break;
      case REVERSE_DECELERATE:
	if(w->magTape.capstanSpeed>-0.01)
	{
	  w->magTape.capstanSpeed=0;
	  w->magTape.capstanAcceleration=0;
	  w->magTape.tapeadjust=w->magTape.taperead-w->magTape.readstart+w->magTape.tapeappetite;
#ifdef DEBUG
	  printf("%s: tapeadjust: %.8f\n",w->magTape.tapeLabel, w->magTape.tapeadjust);
#endif
	  w->magTape.taperead-=w->magTape.tapeadjust;
	  w->magTape.inTape+=w->magTape.tapeadjust;
	  w->magTape.outTape-=w->magTape.tapeadjust;
	  w->magTape.status=TAPE_IDLE;
	  w->magTape.tapeadjust=w->magTape.taperead-w->magTape.readstart-w->magTape.tapeappetite;
	  done_magTape(w);
//	  goto out;
	}
	break;

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

    w->magTape.inError=w->magTape.inSpeed-w->magTape.capstanSpeed;
    w->magTape.outError=w->magTape.outSpeed+w->magTape.capstanSpeed;
    w->magTape.inAcceleration= -w->magTape.inError*w->magTape.UAKK;
    if(fabs(w->magTape.inRatio)>0.2)w->magTape.inAcceleration-=w->magTape.inRatio*w->magTape.UAKK;
    w->magTape.outAcceleration= -w->magTape.outError*w->magTape.UAKK;
    if(fabs(w->magTape.outRatio)>0.2)w->magTape.outAcceleration-=w->magTape.outRatio*w->magTape.UAKK;
    total=w->magTape.INlength+w->magTape.OUTlength+w->magTape.inTape+w->magTape.outTape;

#ifdef DEBUG
    printf("%s: secs: %10.4f: INlength: %.4f inTape: %.4f inSpeed: %.4f inAcceleration: %.4f\n",w->magTape.tapeLabel, w->magTape.secs,w->magTape.INlength,w->magTape.inTape,w->magTape.inSpeed,w->magTape.inAcceleration);
#endif
    if(w->magTape.inRatio < -0.5 || w->magTape.inRatio > 0.5 || w->magTape.outRatio < -0.5 || w->magTape.outRatio > 0.5)
    {
      printf("%s: inRatio: %g outRatio: %g\n",w->magTape.tapeLabel,w->magTape.inRatio,w->magTape.outRatio);
    }
  }
out:
  DrawGLScene(w);
  if((w->magTape.status != TAPE_IDLE) ||
     (fabs(w->magTape.inSpeed)>IDLE_SPEED) ||
     (fabs(w->magTape.outSpeed)>IDLE_SPEED) )
  {
#ifdef DEBUG
  printf("%s: restart timer: %d %.3f %.3f\n", w->magTape.tapeLabel, w->magTape.status, w->magTape.inSpeed, w->magTape.outSpeed);
#endif
    startTimer(w);
  }
  else
  {
#ifdef DEBUG
  printf("%s: do not restart timer: %d %.3f %.3f\n", w->magTape.tapeLabel, w->magTape.status, w->magTape.inSpeed, w->magTape.outSpeed);
#endif
  }
#ifdef DEBUG
  printf("%s: timercb done\n", w->magTape.tapeLabel);
#endif
}

static void startTimer(XsMagTapeWidget w)
{
#ifdef DEBUG
  printf("%s: kilroy1:\n", w->magTape.tapeLabel);
#endif
  XtAppAddTimeOut(XtWidgetToApplicationContext((Widget) w), 10, timercb, (XtPointer) w);
}

GLvoid DrawGLScene(XsMagTapeWidget w)
{
  char txt[100];
  double x,y;
  int width, height;
  int widthr, heightr;
  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;
  double textXmin, textXmax, textYmin, textYmax;

#ifdef DEBUG
  printf("%s: glXMakeCurrent 1\n", w->magTape.tapeLabel);
#endif
  glXMakeCurrent(XtDisplay(w), XtWindow(w), w->magTape.glx_context);
  glMatrixMode(GL_PROJECTION);
  glPushMatrix();
  glLoadIdentity();
//glViewport(0, 0, window_width, window_height);
#ifdef DEBUG
  printf("%s: window_aspect 1: %g\n",w->magTape.tapeLabel, w->magTape.window_aspect);
#endif
  glOrtho(0.0,((double)XMAX)*w->magTape.window_aspect,((double)YMAX),0.0,-1.0,1.0);
  glMatrixMode(GL_MODELVIEW);
  glPushMatrix();
  glLoadIdentity();

  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);		// Clear The Screen And The Depth Buffer
  glDisable(GL_BLEND);
  glDisable(GL_TEXTURE_2D);
  glLoadIdentity();				// Reset The View
  glBegin(GL_LINES);
  glColor4d(0.0,0.0,0.0,1.0);
  glLineWidth(1.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(BottomReelX-BottomReelOuterR,BottomReelY-BottomReelOuterR,0.0);
  */

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

#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);

  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);

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

#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:

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

#ifdef USECORNER
  twoCirclesTangent(TopReelX,TopReelY,w->magTape.inRadius,
                    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,
                    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+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+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,
                    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
  glPushMatrix();
  glTranslated(TopReelX,TopReelY,0.0);
  glRotated(w->magTape.inRotation,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.tapeLabel);
#ifdef DEBUG
  printf("%s: textXmin: %g textXmax: %g textYmin: %g textYmax: %g\n",
      w->magTape.tapeLabel, textXmin, textXmax, textYmin, textYmax);
#endif
  sx=TopReelLabelWidth/(textXmax-textXmin);
  sy=TopReelLabelHeight/(textYmax-textYmin);
  s=sx;
  if(s>sy) s=sy;
#ifdef DEBUG
  printf("%s: sx: %g sy: %g s: %g\n",w->magTape.tapeLabel, sx,sy,s);
#endif
  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.tapeLabel);
  glEnd();

  glPopMatrix();
  
  glEnable(GL_BLEND);
  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,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();
  glPopMatrix();

  // since this is double buffered, swap the buffers to display what just got drawn.
#ifdef DEBUG
  printf("%s: swap buffers\n",w->magTape.tapeLabel);
#endif
  glXSwapBuffers(XtDisplay(w), XtWindow(w));
}

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

#ifdef DEBUG
  printf("%s: Realize called.\n",w->magTape.tapeLabel );
#endif
   
  /*
	280A

	0010100000001010
          D B       3 1
	  CWColormap
	    CWEventMask
	            CWBorderPixel
		      CWBackPixel
		    w->magTape.visualInfo->visual,
  */
    XtCreateWindow ((Widget) w, (unsigned int)InputOutput,
		    CopyFromParent,
		    *valueMask, attributes);

#ifdef DEBUG
  printf("%s: Realize: glXCreateContext.\n",w->magTape.tapeLabel);
#endif

  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);
  }
#ifdef DEBUG
  printf("%s: glXMakeCurrent 2\n", w->magTape.tapeLabel);
#endif
  glXMakeCurrent(XtDisplay(w), XtWindow(w), w->magTape.glx_context);

#ifdef DEBUG
  printf("%s: Realize done.\n",w->magTape.tapeLabel);
#endif

}

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;
{
#ifdef DEBUG
  printf("%s: Initialize called. num_args = %d\n", new->magTape.tapeLabel, *num_args);
#endif
  if(request->core.width == 0) new->core.width = wXMAX;
  if(request->core.height == 0) new->core.height = wYMAX;

  /* determine the visual info if needed */
  if (new->magTape.visualInfo == NULL)
  {
      new->magTape.myVisual = TRUE;
#ifdef DEBUG
      printf("%s: Initialize: createVisualInfo\n", new->magTape.tapeLabel);
#endif
      createVisualInfo(new);
  }
  else
  {
#ifdef DEBUG
      printf("%s: Initialize: myVisual = FALSE\n",new->magTape.tapeLabel);
#endif
      new->magTape.myVisual = FALSE;
  }

  new->core.depth = new->magTape.visualInfo->depth;
#ifdef DEBUG
  printf("%s: Initialize: XtGetApplicationResources\n",new->magTape.tapeLabel);
#endif

  XsmagTapeInsertValues(new);
#ifdef DEBUG
  printf("%s: Initialize Resize\n",new->magTape.tapeLabel);
#endif
  InitGL(new, new->core.width, new->core.height);
  Resize(new);
#ifdef DEBUG
  printf("%s: Initialize done\n",new->magTape.tapeLabel);
#endif
}

static void Destroy(w)
XsMagTapeWidget w;
{
  /*
	This must release all GC's and
	remove all callbacks
  */
#ifdef DEBUG
  printf("%s: MagTape: Destroy called.\n",w->magTape.tapeLabel);
#endif

  XtRemoveAllCallbacks((Widget) w, XtNdone);

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

}

static void Resize(w)
XsMagTapeWidget w;
{
  long glxsize, glysize;
  /*
	Recalculates everything.
  */
#ifdef DEBUG
  printf("%s: MagTape: Resize called.\n",w->magTape.tapeLabel);
#endif

  if(!XtIsRealized((Widget) w))
  {
#ifdef DEBUG
    printf("%s: MagTape: Isn't realized.\n",w->magTape.tapeLabel);
#endif
//  return;
  }

#ifdef DEBUG
  printf("%s: glXMakeCurrent 3\n", w->magTape.tapeLabel);
#endif
  glXMakeCurrent(XtDisplay(w), XtWindow(w), w->magTape.glx_context);
#ifdef DEBUG
  printf("%s: Calling glViewport.\n",w->magTape.tapeLabel);
#endif
  glViewport(0, 0, w->core.width, w->core.height);

}

static void Redisplay(w, event, region)
XsMagTapeWidget w;
XEvent *event;
Region region;
{

#ifdef DEBUG
  printf("%s: MagTape: Redisplay entry.\n",w->magTape.tapeLabel);
#endif

#ifdef DEBUG
  printf("%s: glXMakeCurrent 4\n", w->magTape.tapeLabel);
#endif
  glXMakeCurrent(XtDisplay(w), XtWindow(w), w->magTape.glx_context);

  glClear(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT);

  glViewport(0, 0, w->core.width, w->core.height);

  Resize(w);
  DrawGLScene(w);

#ifdef DEBUG
  printf("%s: Redisplay exit.\n", w->magTape.tapeLabel);
#endif
}

static Boolean SetValues(current, request, new)
XsMagTapeWidget current, request, new;
{
#ifdef DEBUG
  printf("%s: SetValues called\n",new->magTape.tapeLabel);
#endif
  XsmagTapeInsertValues(new);

#ifdef DEBUG
  printf("%s: SetValues exit\n",new->magTape.tapeLabel);
#endif
  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,j;

#ifdef DEBUG
  printf("%s: XsmagTapeInsertValues entry.\n",w->magTape.tapeLabel);
#endif

  w->magTape.inTape = MAXCHAMPERTAPELENGTH*0.5;
  w->magTape.outTape = MAXCHAMPERTAPELENGTH*0.5;
  w->magTape.inError = 0.0;
  w->magTape.outError = 0.0;
  w->magTape.thickness = THICKNESS;
  w->magTape.OUTlength = 0.0;
  w->magTape.INlength = TAPELENGTH;
  w->magTape.radius0 = 0.113/2.0;
  clock_gettime(CLOCK_MONOTONIC, &(w->magTape.time0));
  w->magTape.tapeappetite = 0.0;
  w->magTape.status = TAPE_IDLE;
  w->magTape.readstart = 0;
  w->magTape.taperead = 0;
  w->magTape.UAKK = 4;
  w->magTape.capstanAcceleration = 0;

#ifdef DEBUG
  printf("%s: XsmagTapeInsertValues exit.\n",w->magTape.tapeLabel);
#endif
}


static void done_magTape(XsMagTapeWidget w)
{
  if(XtHasCallbacks((Widget) w, XtNdone) == XtCallbackHasSome)
  {
    XtCallCallbacks((Widget) w, XtNdone, NULL);
  }
}

/* Exported functions */

int XsmagTapeIdle(XsMagTapeWidget w)
{
  return(w->magTape.status == TAPE_IDLE);
}

int XsmagTapeCommand(XsMagTapeWidget w, int cmd, double parameter)
{
#ifdef DEBUG
  printf("%s: XsmagTapeCommand status: %d\n", w->magTape.tapeLabel, w->magTape.status);
#endif
  if(w->magTape.status!=TAPE_IDLE) return 1;
  switch(cmd)
  {
    case TAPE_REWIND:
      if(w->magTape.taperead>0)
      {
	w->magTape.lastsecs=seconds(w);
	w->magTape.capstanAcceleration=-CAPSTANACCELERATION;
	w->magTape.UAKK=22;
	w->magTape.status=REWIND_ACCELERATE;
#ifdef DEBUG
	printf("%s: start REWIND\n", w->magTape.tapeLabel);
#endif
	startTimer(w);
	return 0;
      }
      break;
    case TAPE_FORWARD:
      w->magTape.lastsecs=seconds(w);
      w->magTape.readstart=w->magTape.taperead;
      w->magTape.tapeappetite=parameter;
      w->magTape.capstanAcceleration=CAPSTANACCELERATION;
      w->magTape.UAKK=4;
      w->magTape.status=FORWARD_ACCELERATE;
#ifdef DEBUG
      printf("%s: start FORWARD\n", w->magTape.tapeLabel);
#endif
      startTimer(w);
      return 0;
      break;
    case TAPE_REVERSE:
      if(w->magTape.taperead>0)
      {
	w->magTape.lastsecs=seconds(w);
	w->magTape.readstart=w->magTape.taperead;
	w->magTape.tapeappetite=parameter;
	w->magTape.capstanAcceleration=-CAPSTANACCELERATION;
	w->magTape.UAKK=4;
	w->magTape.status=REVERSE_ACCELERATE;
#ifdef DEBUG
	printf("%s: start REVERSE\n", w->magTape.tapeLabel);
#endif
	startTimer(w);
	return 0;
      }
      break;
  }
  return 1;
}

