/*
touchcal - a tool to calibrate Touch Screens (namely ones with serial 
controller manufactured by EloGraphics and MicroTouch) for use with XFree86.
 
Copyright (C) 1999 Christoph Baumann <cgb@debian.org>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA


The author wishes to thank Peter Baumann <Peter.Baumann@dlr.de> for the
Linux Serial Programming HOWTO which was very helpfull during writing
this program.
*/

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <curses.h>

/*define port parameters*/
#define BAUDRATE B9600
#define _POSIX_SOURCE 1 /* POSIX compliant source */


/*routine to initialize a MicroTouch serial controller*/
void init_mu(char dev[])
     {
       int fd,res,len,i;
       struct termios newtio;
       char buf[255],dummy[255];
       char format_tablet[4],get_page[5],set_page[5],mode_stream[4];
       char reset_controler[3],identify[4];
       char data[16];
 
      /*Open serial device for reading and writing*/
      fd = open(dev, O_RDWR | O_NOCTTY );
      if (fd <0) {perror(dev); exit(-1); }

      /*define some command strings*/
      /*they are taken from the controller's manual*/
      format_tablet[0]=0x01;
      format_tablet[1]='F';
      format_tablet[2]='T';
      format_tablet[3]=0x0d;

      mode_stream[0]=0x01;
      mode_stream[1]='M';
      mode_stream[2]='S';
      mode_stream[3]=0x0d;

      reset_controler[0]=0x01;
      reset_controler[1]='R';
      reset_controler[2]=0x0d;

      identify[0]=0x01;
      identify[1]='O';
      identify[2]='I';
      identify[3]=0x0d;

      get_page[0]=0x01;
      get_page[1]='G';
      get_page[2]='P';
      get_page[3]='1';
      get_page[4]=0x0d;

      set_page[0]=0x01;
      set_page[1]='S';
      set_page[2]='P';
      set_page[3]='1';
      set_page[4]=0x0d;

      /*define data string*/
      strcpy(data,"00180769001D0793");

      bzero(&newtio, sizeof(newtio)); /* clear struct for new port settings */

     /*
       BAUDRATE: Set bps rate
       CRTSCTS : output hardware flow control
       CS8     : 8n1 (8bit,no parity,1 stopbit)
       CLOCAL  : local connection
       CREAD   : enable receiving characters
     */
      newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;

     /*
       IGNPAR  : ignore bytes with parity errors
       ICRNL   : map CR to NL
     */
      newtio.c_iflag = IGNPAR | ICRNL;

     /*
      Raw output.
     */
      newtio.c_oflag = 0;

     /*
     ICANON  : enable canonical input
     */
      newtio.c_lflag = ICANON;

     /*
       initialize all control characters
     */
      newtio.c_cc[VINTR]    = 0;     /* Ctrl-c */
      newtio.c_cc[VQUIT]    = 0;     /* Ctrl-\ */
      newtio.c_cc[VERASE]   = 0;     /* del */
      newtio.c_cc[VKILL]    = 0;     /* @ */
      newtio.c_cc[VEOF]     = 4;   /* Ctrl-d */
      newtio.c_cc[VTIME]    = 0;   /* inter-character timer unused */
      newtio.c_cc[VMIN]     = 1;  /* blocking read until 1 character arrives */
      newtio.c_cc[VSWTC]    = 0;     /* '\0' */
      newtio.c_cc[VSTART]   = 0;     /* Ctrl-q */
      newtio.c_cc[VSTOP]    = 0;     /* Ctrl-s */
      newtio.c_cc[VSUSP]    = 0;     /* Ctrl-z */
      newtio.c_cc[VEOL]     = 0;     /* '\0' */
      newtio.c_cc[VREPRINT] = 0;     /* Ctrl-r */
      newtio.c_cc[VDISCARD] = 0;     /* Ctrl-u */
      newtio.c_cc[VWERASE]  = 0;     /* Ctrl-w */
      newtio.c_cc[VLNEXT]   = 0;     /* Ctrl-v */
      newtio.c_cc[VEOL2]    = 0;     /* '\0' */

     /*
       now clean the serial line and activate the settings for the port
     */
      tcflush(fd, TCIFLUSH);
      tcsetattr(fd,TCSANOW,&newtio);

     /*reset controller*/
     write(fd,reset_controler,3);
     res=read(fd,dummy,255);
     dummy[res]=0;
     printf("reset controler: %s\n",dummy);
     
     /*set to format tablet*/
     write(fd,format_tablet,4);
     res=read(fd,dummy,255);
     dummy[res]=0;
     printf("set to format tablet: %s\n",dummy);

     /*set mode stream*/
     write(fd,mode_stream,4);
     res=read(fd,dummy,255);
     dummy[res]=0;
     printf("set mode stream: %s\n",dummy);

     /*get ROM page 1*/ 
     write(fd,get_page,5);
     /*get response*/
     res=read(fd,dummy,255);
     dummy[res]=0;
     printf("get page: %s\n",dummy);  
     /*read data*/
     len = read(fd,buf,255);
     buf[len]=0;
     printf("page: %s\n",buf);
     /*get ack*/
     res = read(fd,dummy,255);
     dummy[res]=0;
     printf("data received: %s\n",dummy);

     /*change this data*/
     for (i=0;i<16;i++) buf[i]=data[i];
     printf("going to write: %s\n",buf);
       
     /*write changed data*/
     write(fd,set_page,5);
     res=read(fd,dummy,255);
     dummy[res]=0;
     printf("set page: %s\n",dummy);     
     /*write data*/
     write(fd,buf,52);
     /*get ack*/
     res=read(fd,dummy,255);
     dummy[res]=0;
     printf("write data: %s\n",dummy);

      close(fd);
      return;
     }

/*produce something like a crosshair using curses to position it*/
void cross(int position)
{
 clear();

if (position==1)
  {
   setsyx(0,0);
   printw(" |\n");
   printw("-+-\n");
   printw(" |\n");
  }

if (position==2)
  {
   setsyx(22,0);
   printw(" |\n");
   printw("-+-\n");
   printw(" |\n");
  }

if (position==3)
  {
   setsyx(0,77);
   printw(" |");
   setsyx(1,77);
   printw("-+-");
   setsyx(2,77);
   printw(" |");
  }

setsyx(12,35);
printw("Please touch the marker.\n");
refresh();
}


/*calibration routine for MicroTouch devices*/
void mutouch(char dev[])
{
  char read, protocol[6]; /*to store the output of the controller*/  
  int i,j,x[4],y[4],xmin,xmax,ymin,ymax; /*counters and coordinates*/
  FILE *in, *out; /*some file pointer*/

/*init controler*/ 
init_mu(dev);

/*in case we couldn't open the device*/
in=fopen(dev,"rb");
if (in==NULL)
    {
      fprintf(stderr,"\nCould not open device %s\n",dev);
      return;
    }

 initscr();

 j=1;

 /*read three coordinates*/
while(j<4)
  {
    /*display crosshair*/
    cross(j);

/*read the word (5 bytes)*/
  i=1;
  while(i<6)
   {
     read=fgetc(in);
     protocol[i]=read;
     i++;
   }

 /*read X and Y coordinates */
  x[j] = protocol[3];
  x[j] = x[j] << 7;
  x[j] = x[j] | (protocol[2] & 0x00ff);

  y[j] = protocol[5];
  y[j] = y[j] << 7;
  y[j] = y[j] | (protocol[4] & 0x00ff);

fprintf(stderr,"value read (x: %d y: %d). Please press RETURN.\n",x[j],y[j]);
  getc(stdin);
  j++;

/*flush buffer*/
  fclose(in);
  in=fopen(dev,"rb");
  }

endwin();
fclose(in);

/*extrapolish values*/
  if(x[3] > x[1])
    {
     xmax = x[3]+((x[3]-x[1])/76)*1.5;
     xmin = x[1]-((x[3]-x[1])/76)*1.5;
    }
  if(x[1] > x[3])
    {
     xmax = x[1]+((x[1]-x[3])/76)*1.5;
     xmin = x[3]-((x[1]-x[3])/76)*1.5;
    }
  if(y[2] > y[1])
    {
     ymax = y[2]+((y[2]-y[1])/21)*1.5;
     ymin = y[1]-((y[2]-y[1])/21)*1.5;
    }
  if(y[1] > y[2])
    {
     ymax = y[1]+((y[1]-y[2])/21)*1.5;
     ymin = y[2]-((y[1]-y[2])/21)*1.5;
    }
  /*output in XF86Config format*/
 printf("    MinimumXPosition %d\n",xmin);
 printf("    MaximumXPosition %d\n",xmax);
 printf("    MinimumYPosition %d\n",ymin);
 printf("    MaximumYPosition %d\n",ymax);

 /*this output into a file*/
 out=fopen("/tmp/cal.tmp","w");
 fprintf(out,"   MinimumXPosition %d\n",xmin);
 fprintf(out,"   MaximumXPosition %d\n",xmax);
 fprintf(out,"   MinimumYPosition %d\n",ymin);
 fprintf(out,"   MaximumYPosition %d\n",ymax);
 fclose(out);

return;

}


/*calibration routine for an EloGraphics device*/
void elotouch(char dev[])
{  
  unsigned char read, protocol[11];
  int i,j,x[4],y[4],xmin,xmax,ymin,ymax;
  FILE *in, *out;

/*in case we couldn't open the device*/
in=fopen(dev,"rb");
if (in==NULL)
    {
      fprintf(stderr,"\nCould not open device %s\n",dev); 
      return;
    }

 initscr();

 j=1;

 /*read three coordinates*/
while(j<4)
  {
    /*display crosshair*/
    cross(j);

/*drop strings not starting with an U*/
   read='i';
   while(read!='U')
    {
      read=fgetc(in);
    }

/*read the word (10 bytes) - not counted, the next word starts with 'U'*/
  i=1;
  protocol[i]=read;
  read='i';
                  
  while(read!='U')
   {
     read=fgetc(in);
     i++;
     if (read!='U') protocol[i]=read;
   }

 /*read X and Y coordinates*/      
  x[j] = protocol[5];
  x[j] = x[j] << 8;
  x[j] = x[j] | (protocol[4] & 0x00ff);     

  y[j] = protocol[7];
  y[j] = y[j] << 8;
  y[j] = y[j] | (protocol[6] & 0x00ff);    

fprintf(stderr,"value read (x: %d y: %d). Please press RETURN.\n",x[j],y[j]);
  getc(stdin);
  j++;

/*flush buffer*/
  fclose(in);
  in=fopen(dev,"rb");   
  } 

endwin();
fclose(in);

/*extrapolish values*/
  if(x[3] > x[1]) 
    {
     xmax = x[3]+((x[3]-x[1])/76)*1.5;
     xmin = x[1]-((x[3]-x[1])/76)*1.5;
    }
  if(x[1] > x[3]) 
    {
     xmax = x[1]+((x[1]-x[3])/76)*1.5;
     xmin = x[3]-((x[1]-x[3])/76)*1.5;
    }
  if(y[2] > y[1]) 
    {
     ymax = y[2]+((y[2]-y[1])/21)*1.5;
     ymin = y[1]-((y[2]-y[1])/21)*1.5;
    }
  if(y[1] > y[2]) 
    {
     ymax = y[1]+((y[1]-y[2])/21)*1.5;
     ymin = y[2]-((y[1]-y[2])/21)*1.5;
    }

  /*output in XF86Config format*/
 printf("    MinimumXPosition %d\n",xmin);
 printf("    MaximumXPosition %d\n",xmax);
 printf("    MinimumYPosition %d\n",ymin);
 printf("    MaximumYPosition %d\n",ymax);

 /*output into file*/
 out=fopen("/tmp/cal.tmp","w");
 fprintf(out,"   MinimumXPosition %d\n",xmin);
 fprintf(out,"   MaximumXPosition %d\n",xmax);
 fprintf(out,"   MinimumYPosition %d\n",ymin);
 fprintf(out,"   MaximumYPosition %d\n",ymax);
 fclose(out);

 return; 
}



void main(int anz, char *arg[])
{
  /*print version*/
  fprintf(stderr,"\n touchcal version 0.1\n");

  /*if not enough parameters are given*/
if (anz<3) 
  {
    fprintf(stderr,"\nUsage: touchcal e|m /dev/ttySx [x=0..3]\n"); 
    return;
  }

/*if the first parameter is an e*/
if (arg[1][0] == 'e')
  {
    printf("Calibrating EloGraphics device\n");
    elotouch(arg[2]);
  }

/*if the first parameter is a m*/
if (arg[1][0] == 'm')
  {
    printf("Calibrating MicroTouch device\n");
    mutouch(arg[2]);
  }

return;
}

