From: marks@aivax.rl.af.mil (David Marks)
Newsgroups: comp.sys.sgi
Subject: tablet driver
Date: 29 Jul 92 11:37:02 GMT

I modified the old example graphics tablet driver which used to be included
in 4Dgifts to work with a SummaGraphics tablet.  Towards the end of the program
the code initializes a serial port, then sends commands to the serial port to
configure the tablet.  This was done while 3.2 (or 3.3.2) was runnign, back
when the WAS a tabletd running.  Now its done as part of the X-Server.
All I did was change the Hitatchi codes to the Summagraphics codes, which I
got from the Summagraphics manual, and add some more flexible serial I/O
code, and allowed one to change the MODE that the tablet uses (send while
in proximity, send on contact, send on button press, etc...)  And, try
as I could, I just couldn't squeeze the state of 4 buttons into 3 bits...



Dave Marks
Rome Laboratory
marks@ra.rl.af.mil

-------------------------
tabletd.c
-------------------------
/*
 *	I modified this to work with a Summagraphics Summasketch 
 *	tablet.  Tally-Ho! 
 *				David L. Marks - 1991
 */

/*
 *      tablet -
 *              This is the tablet reader for the Hitachi tablet.  The 
 *      Hitachi tablet is 11" x 11" and has 200 points per inch.
 *
 *      Define RAWCOORDS to get raw tablet coordinates.  In this case,
 *      tablet values will be between 0 and TABLETXMAX in X and between
 *      0 and TABLETYMAX in Y.  If the cursor is attached to the tablet,
 *      the lower left hand corner of the tablet maps into the screen
 *      area, since tablet values exceed XMAXSCREEN and YMAXSCREEN.
 *
 *      Define MAPTOSCREEN to get tablet coords that map the lower part
 *      of the tablet to screen coordinaes.  In this case tablet values 
 *      will be between 0 and XMAXSCREEN in X and between 0 and XMAXSCREEN 
 *      in Y.  If the cursor is attached to the tablet, the whole bottom
 *      area of the tablet maps into the screen area.
 *
 *      The (default) tablet daemon that is installed in /etc/gl/tabletd 
 *      is equivalent to this program with RAWCOORDS defined.  If you wish 
 *      to map the entire width of the tablet onto the screen, then 
 *      recompile this with MAPTOSCREEN defined.  Then, become superuser 
 *      and move /etc/gl/tabletd to /etc/gl/tabletd.default and move the
 *      new, recomplied version of this program into /etc/gl/tabletd.
 *      You can either reboot to run the new version, or as superuser,
 *      execute "tabletd /dev/tablet &" and the mapping will now reflect
 *      the fulll width of the Hitachi tablet.
 *
 *                                Paul Haeberli - 1986
 */


#include "gl.h" 
#include "device.h"
#include <stdio.h>
#include <fcntl.h>
#include <termio.h>
#include <signal.h>

#define MAPTOSCREEN


#define TABLETXMAX        2900
#define TABLETYMAX        2900

#ifdef RAWCOORDS
#define COORDMAP(x)        (x)
#endif

#ifdef MAPTOSCREEN
#define COORDMAP(x)        (((x)*XMAXSCREEN)/TABLETXMAX)
#endif

int l, m, r, f;
int x, y;
FILE *inf;
struct termio term, sterm;

main(argc,argv)
int argc;
char **argv;
{
    register unsigned char c;

    if(argc<2) {
        fprintf(stderr,"usage: tabletd </dev/tty?>\n");
        exit(1);
    }
    inf = fopen(argv[1],"r+");
    if(!inf) {
        fprintf(stderr,"tabletd: can't open %s\n",argv[1]);
        exit(1);
    }
    f = fileno(inf);
    x = 0;
    y = 0;
    init();
    while(1) {
        c = fgetc(inf);
        bitpad_softchar(c);
    }
}

int synced, state;
int x, y, ox, oy, obut;
int b0, b1, b2, b3;
int	 but;
/* The old driver interfaced to gl using a 4-bit code for the button status */
/* The summasketch uses a 3-bit code (information loss??) for its four */
/*   buttons, so, lets map the 3-bit code to the four bit code */
/* The only multi-button combinations I support are 1&4, 2&4, 1&2&4 */
/* Note: it is tough to get a multi-button hit reliably. Try 2&4 and you */
/*   get 2 then 4 or vice versa.  */
int	new_but[8]=  {0x00, 0x01, 0x02, 0x04,
		      0x08, 0x09, 0x0A, 0x0B };

#define SYNCHBIT        0x80
#define LOW_3_BITS	0x07

/*
 *        bitpad_softchar -
 *                This inteprets characters in bitpad I compatable format.
 */
 
bitpad_softchar( onechar )
register unsigned int onechar;
{
    int		xsign, ysign;
    int		tbut;

    onechar &= 0xff;
    if(synced) {
        switch(state) {
            case 0: x = onechar & 0x3f;
                    state++;
                    break;
            case 1: x |= (onechar & 0x3f)<<6;
                    state++;
                    break;
             case 2: y = onechar & 0x3f;
                    state++;
                    break;
            case 3: y |= (onechar & 0x3f)<<6;
                    state++;

#define X_SIGN_SHIFT 4
#define Y_SIGN_SHIFT 3
                    /* but has the sign information for x and y */
                    xsign = (but >>X_SIGN_SHIFT) & 0x01;
                    /* one (1) is positive, zero (0) negative */
                    if (!xsign)
                        x *= -1;
                    ysign = (but >>Y_SIGN_SHIFT) & 0x01;
                    if (!ysign)
                        y *= -1;
                    if(x != ox) {  /* low x */
                        ox = x;
                        gl_changedevice(BPADX,COORDMAP(x));        
                    }
                    if(y != oy) {
                        oy = y;
                        gl_changedevice(BPADY,COORDMAP(y));        
                    }
#ifdef DEBUG
       		    printf(" x = %d, y = %d, button = %x (hex)\n", x, y, but);
#endif
                    tbut = but;
                    tbut &= LOW_3_BITS;  /* take the low three bits */
                    /* now translate the bits to what the SGI driver wants */
		    but = new_but[tbut];
                    if(but != obut) {
                        obut = but;
                        if((but&1) != b0) {
                            b0 = but&1 ;
                            gl_changedevice(BPAD0,b0);
                        }
                         but >>= 1;
                        if((but&1) != b1) {
                            b1 = but&1 ;
                            gl_changedevice(BPAD1,b1);
                        }
                         but >>= 1;
                        if((but&1) != b2) {
                            b2 = but&1;
                            gl_changedevice(BPAD2,b2);
                        }
                         but >>= 1;
                        if((but&1) != b3) {
                            b3 = but&1;
                            gl_changedevice(BPAD3,b3);
                        }
                    }
                    break;
            case (4):
                    if ( (onechar & SYNCHBIT) == 0 ) {  
                    	/* if the synch bit IS NOT on this char */
		            synced = 0; /* then we ARE NOT synched */
		            state = 0;
		            break;
		    }
		    /* otherwise, we are in synch */
        	    but = (onechar & 0xff); /* save the whole status, not just buttons*/

	    	    state = 0;
                    break;
        } /* end switch */
    } else {
        if(onechar & SYNCHBIT) {  /* if the synch bit is on this char */
            synced = 1; /* then we are synched */
            /* It is from this character that we get the button info */
            /* and things that the old driver didn't care about (apparently) */
            /* like proximity, tablet identifier, coordinate sign. */
            but = (onechar & 0xff); /* save the whole status, not just buttons*/
        }
        state = 0;
    }
} /* end bitpad_softchar */

init()
{

/* Primary Modes */

/* Send reports continuously (and quickly !) whether or not button is pressed */
#define STREAM_MODE		0x40

/* Send reports continuously only when button is pressed (pencil mode?) */
#define SWITCH_STREAM_MODE	0x41

/* Send one (1) report only when a button is pressed */
#define POINT_MODE		0x42

/* Send one (1) report only when the host asks for one via the request report*/
/*   command. */
#define REMOTE_REQUEST_MODE	0x44


/* Modifiers (Secondary Modes) */

/* The report contains only button, delta-x, delta-y info */
/* THIS DRIVER DOES NOT SUPPORT THIS MODE RIGHT NOW!!! DON'T EVEN THINK */
/*   ABOUT "TRYING IT OUT", SINCE THIS MODE YIELDS 3-BYTE REPORTS, WHILE */
/*   NON-DELTA MODE YIELDS 5-BYTE REPORTS.   Thank You... */
#define DELTA_MODE		0x45

/* This mode sets a threshold level that must be met before a report is */
/*   sent.  This only makes sense when the primary mode is either continuous */
/*   or switch continuous. The tablet generates a report only after the stylus */
/*   has been moved the specified number of units.  Setting the increment to */
/*   one has the nice property of generating reports only when the stylus is */
/*   moved. */
#define INCREMENT_MODE		0x49
	
/* After setting the secondary mode to INCREMENT_MODE, you must say what the */
/*   threshold level (increment) is.  You can choose 0-32 (decimal, 0-20 hex)*/
/*   Add your chosen threshold level to the INCREMENT_BASE and send that */
/*   to the tablet. */
#define INCREMENT_BASE	0x20

/* used to get the tablet report when in REMOTE_REQUEST primary mode */
#define REMOTE_TRIGGER_CMD	0x50

/* Factor to reduce the rate of report transmission.  At 9600 BPS, the tablet */
/*   generates about 120 reports/second, which may be too much.  Send the */
/*   tablet one of these below to reduce the rate by a factor of 0, 2, 8 or 32 */
#define MAX_RATE	0x51
#define MAX_RATE_DIV_2	0x52   
#define MAX_RATE_DIV_8	0x53   
#define MAX_RATE_DIV_32	0x54   


/* user defineable parameters */

/*  Only matters if you use INCREMENT_MODE.  Change this at will, but make sure */
/*   the value is  0 < x < 33 (decimal). */
#define DESIRED_INCREMENT	1

	char	tab_msg;

    if (ioctl(f, TCGETA, &term) == -1) {
        fprintf(stderr,"tabletd: ioctl(TCGETA) failed\n");
        perror("init");
        return(0);
    }
    sterm = term;

    /* change the modes */
    term.c_iflag = IGNBRK | IGNPAR;
    term.c_oflag = 0;
    term.c_lflag = 0;
    
    /* If its not obvious, this is where you would change the baud rate */
    /*   if you want something other than 9600 */
    term.c_cflag = B9600 | CS8 | CREAD ;
    term.c_cc[VMIN] = 1;
    term.c_cc[VTIME] = 0;

    if (ioctl(f, TCSETA, &term) == -1) {
        fprintf(stderr,"tabletd: ioctl(TCSETA) failed\n");
        perror("init");
        return(0);
    }
    
   /* put the tablet into the right modes */
   
   /* set the appropriate primary mode */
   /* I chose POINT_MODE because it sends a report only when a button is */
   /*   pressed.  */
   tab_msg = POINT_MODE;
   write(f, &tab_msg, 1);
   
   /* set the secondary mode, if desired. if not, comment out */
/*   tab_msg = INCREMENT_MODE; */
/*   write(f, &tab_msg, 1); */

   
   /* sets the increment value , only if the secondary mode is INCREMENT*/
/*   tab_msg = INCREMENT_BASE + DESIRED_INCREMENT; */
/*   write(f, &tab_msg, 1); */

   
   /* set the report rate */
   tab_msg = MAX_RATE_DIV_8;
   write(f, &tab_msg, 1);

}

----------------------------
Makefile
----------------------------
SHELL = /bin/sh
# the iristools/include is for rasterfile.h for iisc
# The include directory under 
CFLAGS = -g -I/usr/include/gl  -DDEBUG
LDFLAGS = -lgl_s -lm
ALL = tabletd 

all: $(ALL)

clean:
	rm -f *.o  $(ALL)

tabletd : $$@.c
	$(CC) $(CFLAGS) -o $@ $@.c $(LDFLAGS)


---------------------------