[Search for users] [Overall Top Noters] [List of all Conferences] [Download this site]

Conference hydra::axp-developer

Title:Alpha Developer Support
Notice:[email protected], 800-332-4786
Moderator:HYDRA::SYSTEM
Created:Mon Jun 06 1994
Last Modified:Fri Jun 06 1997
Last Successful Update:Fri Jun 06 1997
Number of topics:3722
Total number of notes:11359

3098.0. "Vision International" by HYDRA::LNARAYAN () Tue Jan 28 1997 15:44

    Company Name :  Vision International
    Contact Name :  Bill Thillrts
    Phone        :  207-945-6353
    Fax          :  207-942-9815
    Email        :  [email protected]
    Date/Time in :  28-JAN-1997 15:43:15
    Entered by   :  L. Narayan
    SPE center   :  MRO

    Category     :  UNIX
    OS Version   :  
    System H/W   :  Alpha


    Brief Description of Problem:
    -----------------------------

Wanted help ( example source ) relating to device driver development
Informed him about the writing device driver document.
Requested him to send the exact requirement by e-mail
T.RTitleUserPersonal
Name
DateLines
3098.1more infoHYDRA::LNARAYANWed Jan 29 1997 09:5961
From:	US3RMC::"[email protected]" "MAIL-11 Daemon" 28-JAN-1997 18:22:04.47
To:	asimov::lnarayan
CC:	
Subj:	Information on writing an XInput device driver 

As per our phone call, the following is a

Our System: AlphaStation 255/233 runing OS version 4.0a.
NOTE: Code will be used on different Alpha platforms of our customers.

We have a 3D input device (special mouse) that we use to collect data into our
system.  It feeds data in through a serial port.  Currently on the DEC Alpha,
we use code that reads the data into the program directly from the serial port.
 On the SGI however, we were able to write a streams module that took data from
the serial port and converted the data into the format expected by the XServer.
 We added the streams module as an XDevice to the Xserver.  Using the XInput
extensions, we were able to receive motion and input events from the device as
XEvents (XDeviceMotionEvent etc.).  More importantly, we are able to allow our
3D mouse device to replace the standard mouse as the XPointer device.  This
allows a user of our 3D mouse to change from a mode where the device events
from our 3D mouse are being received by our program only, to a mode where the
Xwindows pointer is being moved by our 3D mouse.  Our 3D mouse can then be used
to select menu items etc.  The user can then switch back so that the standard
mouse is the XPointer device and the events from our 3D mouse are only being
processed by our program.

We would like to be able to do the same thing for our Alpha users.  SGI had
some examples of XInput device drivers that were very handy in writing our
device driver and also had instructions on how to install the driver.  If it is
possible to do this on the Alpha, I would like to find out how.  I assume the
XChangePointerDevice (in X11/extensions/XInput.h) allows changing the pointer
device (SGI has a slightly different way) and I assume once the device driver
is written, it is added into the /var/X11/Xserver.conf file, but I don't know
what I need to do to create the driver.


-- 
Bill

------------------------------------
Bill Phillips
Vision International (Bangor Office)
(207) 945-6353 (voice)
(207) 942-9815 (fax)
[email protected]
------------------------------------

% ====== Internet headers and postmarks (see DECWRL::GATEWAY.DOC) ======
% Received: from mail13.digital.com by us3rmc.pa.dec.com (5.65/rmc-22feb94) id AA20194; Tue, 28 Jan 97 15:01:56 -0800
% Received: from purple.bangor.autometric.com by mail13.digital.com (8.7.5/UNX 1.5/1.0/WV) id RAA09493; Tue, 28 Jan 1997 17:44:40 -0500 (EST)
% Received: by purple.bangor.autometric.com (940816.SGI.8.6.9/940406.SGI) id RAA05607; Tue, 28 Jan 1997 17:41:15 -0500
% From: "Bill Phillips" <[email protected]>
% Message-Id: <[email protected]>
% Date: Tue, 28 Jan 1997 17:41:14 -0500
% Reply-To: [email protected]
% X-Mailer: Z-Mail (3.2.1 10oct95)
% To: asimov::lnarayan
% Subject: Information on writing an XInput device driver 
% Mime-Version: 1.0
% Content-Type: text/plain; charset=us-ascii
    
3098.2closedHYDRA::LNARAYANThu Jan 30 1997 12:481300
From:	HYDRA::LNARAYAN     "R Lakshminarayan, Software Partner Engg" 30-JAN-1997 12:47:18.04
To:	US3RMC::"[email protected]"
CC:	LNARAYAN
Subj:	RE: Information on writing an XInput device driver 

Bill Phillips

While Xinput is not documented outside of what's in /var/X11/Xserver.conf, 
there is some general X and graphics driver documentation.

Currently we do not support addition of kernel input device 
drivers, but we are planning to do so in the next major release.
It sounds like X input handles your situation.

The following is calcomp driver code and the associated text from the
engineer who gave this. You may use this as an example. Hope this helps.

Thank You
Nari

I'm forwarding you a copy of the CalComp driver code.  The tablet code didn't 
use streams because tty driver was not stream base and would required more 
effort and time than allowed. The tablet can become the system pointer device.
Due to the device driver,the system mouse will also be a system pointer device
at the same time.  This is only a problem when the puck is moved on the tablet
at the same time that the system mouse.  The PUT_EVENT_ON_QUEUE ioctl is used 
to put the formated input on the event queue.  This is handled as by the dix 
layer as another event that is recieved.


/*
 * @DEC_COPYRIGHT@
 */
#pragma ident "@(#)$RCSfile: db3.c,v $ $Revision: 1.1.2.5 $ (DEC) $Date: 1996/04/17 20:07:57 $"

/* 
   There are multiple hacks in here when the tablet is set to the
   corepointer.  Currently there is no method to separate the system mouse
   movement from the cursor movement because it's a hardware cursor
   controlled by the os kernel.  When there's an ioctl to disconnect the
   cursor glyph from the system mouse, then this needs to be looked at
   again.  At the same time the Xi hack needs to be removed too.
*/


#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/time.h>
#include <sys/tty.h>
#include <sys/devio.h>
#include <sys/workstation.h>
#include <sys/inputdevice.h>
#include <sys/wsdevice.h>

#include "misc.h"
#include "X.h"
#define NEED_EVENTS
#include "Xproto.h"
#include "scrnintstr.h"
#include "pixmap.h"
#include "input.h"
#include "inputstr.h"
#include "ws.h"
#include "dix.h"
#include "os.h"

#include <sys/termios.h>

#include "XI.h"
#include "XIproto.h"

/************************************************************************/
/*									*/
/* The PUT_EVENT_ON_QUEUE macro should be defined in 			*/
/* <sys/inputdevice.h> and is used in an ioctl call to add a ws_event 	*/
/* to the workstation event queue.					*/
/*									*/
/************************************************************************/
#ifndef PUT_EVENT_ON_QUEUE
#define PUT_EVENT_ON_QUEUE _IOW('w', 35, ws_event)
#endif

#define DBUTTON 0x7c /* These bits determine a button press */
#define CBUTTON 0x3c /* Button decode */


#define BUTTON(inbuf) (((inbuf[0]) & DBUTTON) >>2)

#define XAXIS(inbuf) ((((inbuf[0]) & 0x03) << 14) | (((inbuf[1]) & 0x7f) << 7) | ((inbuf[2]) & 0x7f) | (((inbuf[3]) & 0x18) << 13 ))

#define YAXIS(inbuf) (((inbuf[3]) & 0x07) << 14 | ((inbuf[4]) & 0x7f) << 7 \
	       | ((inbuf[5]) & 0x7f))

/* XTILT and YTILT sign extend bit 6, the range according to the manual is 
   -64 to 63 */

#define XTILT(inbuf, a) \
if((inbuf[6]) & 0x40) a = (signed char)(((inbuf[6]) & 0x7f) | 0x80);\
else a = (signed char)(((inbuf[6]) & 0x7f));

#define YTILT(inbuf, a) \
if((inbuf[7]) & 0x40) a = (signed char)(((inbuf[7]) & 0x7f) | 0x80);\
else a = (signed char)(((inbuf[7]) & 0x7f));

#define PRESS(inbuf) ((inbuf[8]) & 0x7f)

#define HEIGHT(inbuf) ((inbuf[9]) & 0x7f)

#define INPROX(inbuf) (!((inbuf[3]) & 0x20))

#define SETOUTOFPROX(inbuf) ((inbuf[3]) |= 0x20)


typedef struct _Db3DevStruct
{
    char *devName;	/* Name of the tty for the device */
    int	Num;		/* Which db3 this is (1,2,3...)	  */
    int fd;		/* File descriptor for the device */
    int id;		/* Xi ID of the tablet		  */
    Bool corePointer;	/* Use as the core pointer.	  */
    Bool absMode;	/* 1 = absolute 0 = relative mode */
    int x;		/* current x valuator		  */
    int y;		/* current y valuator		  */
    int pressure;
    int xPrev;
    int yPrev;
    int xAxis;		/* valuators to report as x and y axis */
    int yAxis;		/* valuators to report as x and y axis */
    int	mntrX, mntrY;	/* screen resolution of current screen */
    int	tabletX;	/* tablet dimensions using a scaling of 1000 lpi */
    int tabletY;	/* tablet dimensions using a scaling of 1000 lpi */
    int resolution;	/* lines per inch */
    int Xincrement;	/* incremental tablet value  1 */
    int Yincrement;	/* incremental tablet value  1 */
    int mouseScale;	/* mouse scale factor in relative mode */
    int numbtns;	/* number of buttons on the mouse */
    unsigned char coreBtn;  /* 0 = no "mouse" button down; otherwise 1 to 3 */
    unsigned char btn;	/* number of button down; 0 = no button down */
    Bool inProx;	/* TRUE when xducer is in proximity */
} db3PrivRec, *db3PrivPtr;

static DeviceIntPtr db3dev;
static int db3corepointer_id;
static BYTE btnCodes[16];   /* button codes for InitButtonClassDeviceStruct */
static char 	buff[256];
static ws_edge_connection wec[MAXSCREENS];

extern int BadMode;
extern InputInfo inputInfo;
extern ws_descriptor wsinfo;
extern int wsFd;
extern int rememberedScreen;
extern int wsGetMotionEvents();
extern void RegisterOtherDevice(DevicePtr);
extern ExtDevicePtr ExtDeviceArray[];
extern void (*(ExtProcessDevInput[]))();
extern void RegisterPointerDevice (DevicePtr);
extern Bool InitPointerDeviceStruct();


/* Forward declarations */
void XiDb3Init(int, ScreenPtr, int, char **, char *, pointer);
int Db3DevInitProc(DevicePtr, int);
int Db3ChangeDeviceControl(ClientPtr, DevicePtr, xDeviceCtl *control);
int Db3SetDeviceValuators(ClientPtr, DeviceIntPtr, int *, int, int);
int Db3SetDeviceMode(ClientPtr, DevicePtr, int);
void Db3RawInputProc(int);
Bool Db3ProcessInputEvents(xEvent *, ws_event *);

static void  CursorOffScreen(db3PrivPtr, unsigned char *, short *, short *);

extern int DeviceButtonPress, DeviceButtonRelease, DeviceButtonRelease,
	DeviceMotionNotify, ProximityIn, ProximityOut, DeviceValuator;


/*
 *
 * ParseTableArgs
 *      device:mode:tabletWidth:tabletHeight:corePointer:mouseScale:resolution:Xincrement:Yincrement
!


	parse tablet arguments and add appropriate init strings 


 *
 *      parse tablet arguments and add appropriate init strings 
 *
 *	Break the arguments into the following in the following
 *	order: port, mouse type, baud rate, emulate 3, chord middle,
 *	sample rate, clear dtr, clear rts, and whether the mouse should
 *	be the core pointer device or not.
 *
 */
static
int ParseTabletArgs(char *arguments, db3PrivPtr pPriv, char *init)
{
    char *myCopy;
    char *myToken;
    int x;
    int tabletWidth = 12;
    int tabletHeight = 12;

    /* Make a local copy of the arguments string. */
    myCopy = (char *)xalloc(strlen(arguments)+1);
    strcpy(myCopy, arguments);

    /* tablet dev */
    if (myToken = strtok(myCopy,":")){
	pPriv->devName = (char *)xalloc(strlen(myToken)+1);
	strcpy((char *)pPriv->devName, myToken);
	myToken = myToken + strlen(myToken) + 1;
    }
    else{
	pPriv->devName = (char *)xalloc(strlen("/dev/tty00")+1);
	strcpy((char *)pPriv->devName, "/dev/tty00");
	return;
    }
    
    /* mode */
    if( myToken && (*myToken != ':') && (myToken = strtok(NULL,":"))){
	x = atoi(myToken);
	pPriv->absMode = (x == 0) ? 0: 1;
	myToken = myToken + strlen(myToken) + 1;
    } 
    else
	if(myToken) myToken++;

    /* tabletWidth */
    if(myToken && (*myToken != ':') && (myToken = strtok(NULL,":"))){
	x = atoi(myToken);
	tabletWidth = x ? x : 12;
	myToken = myToken + strlen(myToken) + 1;
    }
    else
	if(myToken) myToken++;

    /* tabletHeight */
    if(myToken && (*myToken != ':') && (myToken = strtok(NULL,":"))){
	x = atoi(myToken);
	tabletHeight = x ? x : 12;
	myToken = myToken + strlen(myToken) + 1;
    }
    else
	if(myToken) myToken++;

    /* numbtns, default is 16, max is 16, min. is 3 */
    if(myToken && (*myToken != ':') && (myToken = strtok(NULL,":"))){
	x = atoi(myToken);
	pPriv->numbtns = ( x > 16 || x < 3) ? 16 : x;
	myToken = myToken + strlen(myToken) + 1;
    }
    else
	if(myToken) myToken++;

    /* corePointer */
    if(myToken && (*myToken != ':') && (myToken = strtok(NULL,":"))){
	x = atoi(myToken);
	pPriv->corePointer = (x == 0) ? 0 : 1;
	myToken = myToken + strlen(myToken) + 1;
    }
    else
	if(myToken) myToken++;

    /* mouseScale */
    if(myToken && (*myToken != ':') && (myToken = strtok(NULL,":"))){
	x = atoi(myToken);
	pPriv->mouseScale = (x) ? x : 8;
	myToken = myToken + strlen(myToken) + 1;
    }
    else
	if(myToken) myToken++;

    /* resolution */
    if(myToken && (*myToken != ':') && (myToken = strtok(NULL,":"))){
	x = atoi(myToken);
	pPriv->resolution = (x == 0) ? 1000 : x;
	if(pPriv->resolution != 1000){
	    x = strlen(init);
	    init = (char *)Xrealloc(init, x+1);
	    sprintf(init+x,"\033%%JR%04d,0\r", pPriv->resolution);
	}
	myToken = myToken + strlen(myToken) + 1;
    }
    else
	if(myToken) myToken++;

    pPriv->tabletX = pPriv->resolution * tabletWidth;
    pPriv->tabletY = pPriv->resolution * tabletHeight;

    if(myToken && (*myToken != ':') && (myToken = strtok(NULL,":"))){
	x = atoi(myToken);
	pPriv->Xincrement = ( x == 1 || x < 65536) ? x : 1;
	if(pPriv->Xincrement != 1){
	    x = strlen(init);
	    init = (char *)Xrealloc(init, x+1);
	    sprintf(init+x,"\033%%X%05d\r", pPriv->Xincrement);
	}
	myToken = myToken + strlen(myToken) + 1;
    }
    else
	if(myToken) myToken++;

    if(myToken && (*myToken != ':') && (myToken = strtok(NULL,":"))){
	x = atoi(myToken);
	pPriv->Yincrement = ( x == 1 || x < 65536) ? x : 1;
	if(pPriv->Yincrement != 1){
	    x = strlen(init);
	    init = (char *)Xrealloc(init, x+1);
	    sprintf(init+x,"\033%%Y%05d\r", pPriv->Yincrement);
	}
    }
    return;
}



/* I/O for drawingboard III */
int
Db3Output(db3PrivPtr pPriv, char *outbuff, int size)
{
    int numWritten;

    numWritten = write(pPriv->fd, outbuff, size);

    if(numWritten < size){
	sprintf(buff,"Db3Ouput: only wrote %d of %d chars", numWritten, size);
	ErrorF(buff);
    }
    return(numWritten);	
}

int
Db3Input(int fd, CARD8 *inbuff, int size)
{
    int x, bytesRead;
    int inx;

    bytesRead = 0;
    inx = size;

    while (!bytesRead){
	x = read(fd, &inbuff[bytesRead], 1);
	if( x == -1)
	    return(-1);
	/* make sure there is sync */
	if(!(inbuff[0] & 0x80))
	  continue;
	if(x > 0){
	    inx -= x;
	    bytesRead += x;
	}
    }
	
    /* first byte has been found, now read rest */
    while(inx){
	x = read(fd, &inbuff[bytesRead], inx);
	if(x > 0){
	    inx -= x;
	    bytesRead += x;
	}
    }

    if(inx != 0 || bytesRead != size){
	sprintf(buff,"Db3input: only read %d of %d chars\n", inx, size);
	ErrorF(buff);
    }
    return(bytesRead);	
}

/************************************************************************/
/*									*/
/* XiDb3Init - Routine responsible for opening the device, setting	*/
/*	       line attributes, initialization of the device, and 	*/
/*	       providing the server with proc routines that are 	*/
/*	       specific to this device.					*/   
/************************************************************************/

void 
XiDb3Init(int unused1, ScreenPtr unused2, int argc, char **argv, 
		  char *tabArgs, pointer unused3)
{
    ScreenPtr pScreen = wsScreens[0];
    DevicePtr pdev;
    db3PrivPtr pPriv;
    int	db3Fd;
    struct termios termios;
    char *init;
    int id;
    int i;
    
#ifdef DEBUG
    ErrorF("XiDb3Init.\n");
#endif

    init = (char *)Xalloc(30); /* length of current init */
    sprintf(init,"\033%%V9\r\033%%VR6\r\033%%W125\r\033%%VV7\r\033%%Z3\r");

    /* get screen edge connections */
    for(i = 0; i < MAXSCREENS; i++){
	wec[i].screen = i;
	ioctl(wsFd, GET_EDGE_CONNECTION, &wec[i]);
    }

    pPriv = (db3PrivPtr)xalloc(sizeof(db3PrivRec));

    pPriv->mntrX = pScreen->width;
    pPriv->mntrY = pScreen->height;

    pPriv->pressure = 0;
    pPriv->xAxis = 0;
    pPriv->yAxis = 1;
    pPriv->btn = 0;
    pPriv->coreBtn = 0;

    pPriv->numbtns = 16;	/* default */
    pPriv->absMode = 1;		/* default */
    pPriv->mouseScale = 8;	/* default */

    pPriv->corePointer = 1;	/* default */
    pPriv->Xincrement = 1;	/* default */
    pPriv->Yincrement = 1;	/* default */
    pPriv->resolution = 1000;	/* default */

    /* 
     * Use as the core pointer.	 
     * Tell the DB3 to initialize itself.
     */

    ParseTabletArgs(tabArgs, pPriv, (char *)init);

    pPriv->x = pPriv->xPrev = pPriv->tabletX / 2;
    pPriv->y = pPriv->yPrev = pPriv->tabletY / 2;

    /* Open the file descriptor for the DB3 device. */
    if ((db3Fd = open(pPriv->devName, O_RDWR, 0)) < 0)
    {
	sprintf(buff,"XiDb3Init:  couldn't open device %s",  pPriv->devName);
	ErrorF(buff);
	return;
    }

    if (fcntl (db3Fd, F_SETFL, (FNDELAY|FASYNC)) < 0) {
	ErrorF ("XiDb3Init - fcntl failed - fd = %d\n", db3Fd);
	return;
    }

    /* Set NDELAY mode - makes reads and writes be non-blocking.
     * This is important because we do not want to hang the server
     * with a blocked read or write.
     */

    /* Define the attributes of the port:
     *
     * iflag = oflag = Raw input. 
     * cflag = 8 bits, enabled input, close on hup, local mode.
     * lflag = No special processing.
     * VMIN  = 1 character makes a read work (default is 128).
     * ispeed = ospeed = 9600 baud.
     */

    tcgetattr(db3Fd, &termios);

    termios.c_iflag 	= 0;
    termios.c_oflag 	= OPOST | CR3;
    termios.c_cflag 	= CS8 | CREAD | HUPCL | CLOCAL;
    termios.c_lflag 	= 0;
    termios.c_cc[VMIN]  = 1;
    termios.c_cc[VTIME] = 0;
    
    termios.c_ispeed = termios.c_ospeed = B9600;
    
    if (tcsetattr( db3Fd, TCSANOW, &termios) < 0){
	close(db3Fd);
	sprintf(buff,"XiDb3Init: couldn't condition %s", pPriv->devName);
	ErrorF(buff);
	return;
    }

    tcflush(db3Fd, TCIOFLUSH);

    pPriv->fd = db3Fd;

    i = strlen(init);
    init = (char *)Xrealloc(init, (i + 12));
    sprintf(init+i,"\033%%IR\r\033%%Z0\r\n\0");

    Db3Output(pPriv, init, strlen(init)+1);
    Xfree(init);


    pdev = AddInputDevice((DeviceProc)Db3DevInitProc, TRUE);
    pdev->devicePrivate = (pointer)pPriv;
    RegisterOtherDevice(pdev);
    db3dev = (DeviceIntPtr)pdev;
    pPriv->id = id = ((DeviceIntPtr)(pdev))->id;

    ExtDeviceArray[id] = (ExtDevicePtr)xalloc(sizeof(ExtDeviceRec));

    ExtDeviceArray[id]->SetDeviceMode       = Db3SetDeviceMode;
    ExtDeviceArray[id]->SetDeviceValuators  = Db3SetDeviceValuators;
    ExtDeviceArray[id]->ChangeDeviceControl = Db3ChangeDeviceControl;
    ExtDeviceArray[id]->ProcessInputEvents  = Db3ProcessInputEvents;


    /* Let the server know it should look for input from the tablet and
     * what routine it should call to process the raw input.
     */
    ExtProcessDevInput[db3Fd] = Db3RawInputProc;
    AddEnabledDevice(db3Fd);
    AddEnabledExtDevice(db3Fd);

}

/************************************************************************/
/* Db3DevInitProv - handle DEVICE_INIT, DEVICE_ON, DEVICE_OFF, and	*/
/*			    DEVICE_CLOSE                                */
/************************************************************************/

int 
Db3DevInitProc(DevicePtr device, int what)
{
    db3PrivPtr pPriv;
    Atom typeatom;
    char name[30];
    int i;
    char nmstr[] = "\033%__p\r"; /* send back CalComp model number */
    char inbuf[15];

    pPriv = (db3PrivPtr)device->devicePrivate;
    
    switch (what) {
	case DEVICE_INIT:

	    /*
	     * Make an atom and let server know about it.
	     */
	    typeatom = MakeAtom (XI_TABLET, strlen(XI_TABLET), FALSE);
	    sprintf(name,"%s %d", XI_TABLET, pPriv->Num);
	    AssignTypeAndName ((DeviceIntPtr)device, typeatom, name);
	    db3corepointer_id = inputInfo.pointer->id;

	    /*
	     * Register procedures with server.
	     * The initial values here could be determined by a table,
	     * because this is specific for a CalComp tablet we will not
	     * take the time.  Just hardcode it.
	     */
	    InitValuatorClassDeviceStruct ((DeviceIntPtr)device, 3, 
		   wsGetMotionEvents, MOTION_BUFFER_SIZE, Absolute);

	    /* The last parameter is resolution expressed as
	       counts/meter.  1000 lines per inch = 393.70
	       lines per centimeter.  128 pressure counts. */

	    InitValuatorAxisStruct ((DeviceIntPtr)device, 0, 0, 
	       pPriv->tabletX, 1000, 1, 2540);
	    InitValuatorAxisStruct ((DeviceIntPtr)device, 1, 0, 
	       pPriv->tabletY, 1000, 1, 2540);
	    InitValuatorAxisStruct ((DeviceIntPtr)device, 2, 0, 
	       128, 1, 1, 1); /* pressure */

	    InitProximityClassDeviceStruct ((DeviceIntPtr)device);

	    /* Button map is indexed from 1. Refer to
	       XGetDeviceButtonMapping description - X Input
	       Extension Library Specification. */

	    /* nominal mapping; a button value of 0 isn't used */
	    for (i = 0; i < 17; i++)
		btnCodes[i] = i;

	    /* number of buttons are configurable; max 17 (3 - 16) */
	    InitButtonClassDeviceStruct ((DeviceIntPtr)device, 
		pPriv->numbtns, btnCodes);

	    InitFocusClassDeviceStruct ((DeviceIntPtr)device);

	    break;

        case DEVICE_ON: 
#ifdef DEBUG
	    ErrorF("ON\n");
#endif
	    /* forces some i/o incase the tablet is initially out 
	       of proximaty */
	    Db3Output((db3PrivPtr)device->devicePrivate, nmstr, strlen(nmstr));
	    read(pPriv->fd, inbuf, 12);

	    /* Tablet can become core pointer. */
	    if(pPriv->corePointer) 
		RegisterPointerDevice ((DevicePtr)device);
	    
	    device->on = TRUE;
	    break;

	    break; 

	case DEVICE_OFF: 
#ifdef DEBUG
	    ErrorF("OFF\n");
#endif
	    pPriv = (db3PrivPtr)device->devicePrivate;
	    RemoveEnabledDevice (pPriv->fd);
	    device->on = FALSE;
	    break;

	case DEVICE_CLOSE:
#ifdef DEBUG
	    ErrorF("CLOSE\n");
#endif
	    if(device->on){
	        RemoveEnabledDevice (pPriv->fd);
	        device->on = FALSE;
	    }
	    
	    xfree(ExtDeviceArray[pPriv->id]);
	    ExtDeviceArray[pPriv->id] = NULL;
	    
	    close(pPriv->fd);
	    xfree(pPriv->devName);
	    pPriv->id = 0;
	    xfree (device->devicePrivate);

	    break;
    }

    return Success;
}

/*
 * Db3SetDeviceMode -    Absolute or relative mode 
 */
int 
Db3SetDeviceMode(ClientPtr client, DevicePtr device, int mode)
{
    db3PrivPtr pPriv;;
    
    pPriv = (db3PrivPtr)device->devicePrivate;

    if(!pPriv)
	return BadMatch;

    switch(mode) {
        case Absolute:
	    pPriv->absMode = 1;
	    break;
	case Relative:
	    pPriv->absMode = 0;
	    break;
	default:
	    return(BadMode);
	}
    return Success;

} 

/************************************************************************/
/* Db3SetDeviceValuators - set the value of the valuators.		*/
/* Dummy routine place holder                                           */
/************************************************************************/
int 
Db3SetDeviceValuators(ClientPtr client, DeviceIntPtr device, int *valuators, 
		      int first_valuator, int num_valuators)
{
    DevicePtr pdev;
    db3PrivPtr pPriv;
    int val;
    int i;
    
    pPriv = (db3PrivPtr)((DevicePtr)device)->devicePrivate;

    if(!pPriv)
	return BadMatch;
    
    val = first_valuator;
    for (i = 0; i < num_valuators; i++, val++)
    {
	if ((valuators[i] > device->valuator->axes[val].max_value) ||
	    (valuators[i] < device->valuator->axes[val].min_value))
	    return BadValue;
	else{
	    device->valuator->axisVal[val] = valuators[i];
	    if(i == 0){
		pPriv->x = valuators[i];
		pPriv->xPrev  = valuators[i];
	    }
	    else if(i == 1){
		pPriv->y = valuators[i];
		pPriv->yPrev  = valuators[i];
	    }
	}

	val++;
    }
    return Success;
}


/************************************************************************/
/* PcmProcessInputEvents - get an event from the workstation event	*/
/*			   queue and turn it into an X Input Extension	*/
/*			   event.					*/
/************************************************************************/
Bool
Db3ProcessInputEvents(xEvent *x, ws_event *e)
{  
    DevicePtr pdev;
    db3PrivPtr pPriv;
    deviceKeyButtonPointer xev[4];
    deviceValuator *vev;
    int eventcount;
    int	xPos, yPos;


    if(((DeviceIntPtr)pdev)->id == db3dev->id){
	pdev = (DevicePtr)db3dev;
	pPriv = (db3PrivPtr)pdev->devicePrivate;
    }
    else {    
	pdev = (DevicePtr)inputInfo.devices;
	while(pdev){
	    if(e->device == ((DeviceIntPtr)pdev)->id){
		pPriv = (db3PrivPtr)pdev->devicePrivate;
		break;
	    }
	    pdev = (DevicePtr)((DeviceIntPtr)pdev)->next;
	}
    }

    if(!pPriv){
      ErrorF("Db3ProcessInputEvents: pPriv not found evnt id: %d devid: %d\n",
	     e->device, ((DeviceIntPtr)pdev)->id);
	return;
    }

    /* Just a sanity check */
    if(e->device_type != STABLET_DEVICE)
	return FALSE;

    GetSpritePosition(&xPos,&yPos);

    xev[0].root_x 	= xPos;
    xev[0].root_y 	= yPos;
    xev[0].detail 	= e->e.key.key;
    xev[0].time 	= e->time;
    xev[0].deviceid 	= e->device;

    /* 
     *  1st event is DeviceButtonPress, DeviceButtonRelease, 
     *  DeviceMotionNotify, ProximityIn, or ProximityOut.
     *  2nd event is DeviceValuator.
     */

    if(pPriv->corePointer){
	if(e->type == DeviceButtonPress){
	    xev[0].type = ButtonPress;
	    xev[0].deviceid |= MORE_EVENTS;
	}
	else if(e->type == DeviceButtonRelease){
	    xev[0].type = ButtonRelease;
	    xev[0].deviceid |= MORE_EVENTS;
	}
	else if(e->type == DeviceMotionNotify){ 
	    xev[0].type = MotionNotify;
	}
	else{
	    return(TRUE);
	}
    }
    else {
	if(e->type == DeviceButtonPress){
	    xev[0].type = DeviceButtonPress;
	    xev[0].deviceid |= MORE_EVENTS;
	}
	else if(e->type == DeviceButtonRelease){
	    xev[0].type = DeviceButtonRelease;
	    xev[0].deviceid |= MORE_EVENTS;
	}
	else if(e->type == DeviceMotionNotify){ 
	    xev[0].type = DeviceMotionNotify;
	}
	else if(e->type == ProximityIn){
	    xev[0].type = ProximityIn;
	    xev[0].detail = 0;
	}
	else if(e->type == ProximityOut){
	    xev[0].type = ProximityOut;
	    xev[0].detail = 0;
	}
	else {
	    return(TRUE);
	}
    }

    if(e->type == PROXIMITY_IN || e->type == PROXIMITY_OUT ||
       e->type == ProximityIn  || e->type == ProximityOut ){
	eventcount = 1;
    }
    else {
	vev = (deviceValuator *) &xev[1];
	vev->type = DeviceValuator;
	vev->deviceid = e->device;
	vev->device_state = 0;
	vev->num_valuators = 3;
	vev->first_valuator = 0;
	vev->valuator0 = e->e.key.x;
	vev->valuator1 = e->e.key.y;
	vev->valuator2 = e->e.key.pad;

	eventcount = 2;

    }

#ifdef DEBUG
	ErrorF ("Db3ProcessInputEvents: type0: 0x%x type1: 0x%x v0: %d v1: %d v2: %d\n",
		xev[0].type, vev->type, vev->valuator0, vev->valuator1, vev->valuator2);
#endif

    (*pdev->processInputProc) ((xEventPtr)xev, (DeviceIntPtr)pdev, eventcount);

    return TRUE;
}

/* 
   change resolution of the specified valuator, -1 == no change 
   only a lines per inch resolution is support
*/

int
Db3ChangeDeviceControl(ClientPtr client, DevicePtr device, 
		       xDeviceCtl *control)
{    
    xDeviceResolutionCtl *drsctl;
    CARD32 *resolution;
    char out[12];

    drsctl = (xDeviceResolutionCtl *)control;
    resolution = (CARD32 *) (drsctl + 1);

    if(drsctl->control != DEVICE_RESOLUTION)
	return (BadMatch);

    if(drsctl->first_valuator || drsctl->num_valuators != 1)
	return (BadValue);


    if(*(resolution) != -1){
	sprintf(out,"\033%%JR%d,0\r\0",*(resolution));
	Db3Output((db3PrivPtr)device->devicePrivate, out, strlen(out));
    }

    return (Success);
}


/* NOTE: ws_event is barely large enough to pass data the min. number
   of valuators for the tablet.  Ws_event needs to increase or a method
   to become a variable size.
   
   A tablet motion event abuses ws_event structure:
	key.key == pressure
	key.x   == absolute x
	key.y   == absolute y
*/

void
Db3RawInputProc(int db3Fd)
{
    ws_event e;
    DeviceIntPtr pdev;
    db3PrivPtr pPriv;
    ws_pointer_position pos;
    ScreenPtr pScreen;
    int	xPos, yPos;
    int size, in;
    int xaxis, yaxis, button, prox;
    int xtilt, ytilt, press, height;
    static int mylastscreen = 0;
    unsigned char inbuf[20];
    int x;

    pdev = inputInfo.devices;
    while(pdev){
	if(pdev->public.devicePrivate 
	   && ((db3PrivPtr)(pdev->public.devicePrivate))->fd == db3Fd){
	    pPriv = (db3PrivPtr)pdev->public.devicePrivate;
	    break;
	}
	pdev = pdev->next;
    }

    if(!pPriv){
	ErrorF("Db3RawInputProc: pPriv not found\n");
	return;
    }

    if(pPriv->fd != db3Fd){
	ErrorF("Db3RawInputProc: pPriv not found\n");
	return;
    }


    /* pointer hack, so system mouse and tablet can both be 
       pointer device, hack alart - xxx */
    if(pdev == inputInfo.pointer){
	inputInfo.pointer = inputInfo.devices;
	pPriv->corePointer = 1;
    }

    in = Db3Input(db3Fd, inbuf, 10);
    if(in != 10){
	ErrorF("Db3RawInputProc: Pkt Read failed\n");
	return;
    }

    if(inbuf[0] & 0x80){
	pPriv->x = XAXIS(inbuf);
	pPriv->y = YAXIS(inbuf);
	pPriv->pressure = PRESS(inbuf);
	prox = INPROX(inbuf);
	XTILT(inbuf, xtilt);
	YTILT(inbuf, ytilt);
	height = HEIGHT(inbuf);
	button = BUTTON(inbuf);

	if (button > 0 && button < 16) {
	    if (button == 1)
		button = 16;
	    else
		if (button == 2)
		    button = 17;
		else
		    if (button == 4)
			button = 18;
		    else
			if (button == 8)
			    button = 19;
			else
			    button = pPriv->btn + 15;  /* if chord, discard */
	}    
	if (button > 0)
	    button -= 15;

    }
    else
	ErrorF("Db3RawInputProc: out of sync");


    bzero(&e,sizeof(ws_event));
    e.device_type = STABLET_DEVICE;
    e.device = pdev->id;

    if(pPriv->corePointer){

	/* 
	 *  Because the device is switching the device id to the 
	 *  real system mouse, we do button translations here.
	 */

	e.type = MOTION_TYPE;
	e.device_type = MOUSE_DEVICE;
	e.device = db3corepointer_id;
	e.screen = rememberedScreen;

	if(button != pPriv->btn){

            if (button == 0) {
                if (pPriv->coreBtn) {
		    e.type = BUTTON_UP_TYPE;
		    e.e.key.key = pPriv->coreBtn;
                    pPriv->coreBtn = 0;
                }
            }
            else {
                /* Is a left, middle, or right button down? */
		  e.type = BUTTON_DOWN_TYPE;
		  e.e.key.key = pPriv->coreBtn = 
		      pdev->button->map[button & 0x0f];
            }
	    pPriv->btn =  pdev->button->map[button];
	}
	
	if(pPriv->absMode){


	    e.e.key.x = (pPriv->mntrX * pPriv->x)/pPriv->tabletX;
	    e.e.key.y =  pPriv->mntrY - ((pPriv->mntrY * pPriv->y)/pPriv->tabletY);

	    pPriv->inProx = prox;
          
	    CursorOffScreen(pPriv, &e.screen, &e.e.key.x, &e.e.key.y);

	    pScreen = wsScreens[e.screen];

	    pos.screen = WS_SCREEN(pScreen);
	    pos.device_number = wsinfo.console_pointer; 
	    pos.x = e.e.key.x;
	    pos.y = e.e.key.y;
	    if (pos.screen != rememberedScreen){
		wspCursorControl(rememberedScreen, CURSOR_OFF);
		wspCursorControl(pos.screen, CURSOR_ON);
		rememberedScreen = pos.screen;
	    }
	    if (ioctl (wsFd, SET_POINTER_POSITION, &pos) == -1) {
		ErrorF ("error warping cursor\n");
		return;
	    }

	    NewCurrentScreen(pScreen, pos.x, pos.y);

	    pPriv->xPrev = pPriv->x;
	    pPriv->yPrev = pPriv->y;

	}
	else {
	    if(!prox){
		pPriv->inProx = FALSE;
		return;
	    }

	    if(!pPriv->inProx){
		pPriv->xPrev = pPriv->x;
		pPriv->yPrev = pPriv->y;
		pPriv->inProx = TRUE;
	    }
	    
	    GetSpritePosition(&xPos,&yPos);
	    
	    e.e.key.x = (pPriv->x - pPriv->xPrev) / pPriv->mouseScale;
	    e.e.key.y = (pPriv->yPrev - pPriv->y) / pPriv->mouseScale;

	    e.e.key.x += xPos;
	    e.e.key.y += yPos;

	    CursorOffScreen(pPriv, &e.screen, &e.e.key.x, &e.e.key.y);

	    pScreen = wsScreens[e.screen];

	    pos.screen = WS_SCREEN(pScreen);
	    pos.device_number = wsinfo.console_pointer;
	    pos.x = e.e.key.x;
	    pos.y = e.e.key.y;

	    if (pos.screen != rememberedScreen){
		wspCursorControl(rememberedScreen, CURSOR_OFF);
		wspCursorControl(pos.screen, CURSOR_ON);
		rememberedScreen = pos.screen;
	    }
	    if (ioctl (wsFd, SET_POINTER_POSITION, &pos) == -1) {
		ErrorF ("error warping cursor\n");
		return;
	    }

	    NewCurrentScreen(pScreen, pos.x, pos.y);

	    pPriv->xPrev = pPriv->x;
	    pPriv->yPrev = pPriv->y;	    
	}

#ifdef DEBUG
  ErrorF ("Db3RawInputP: raw: 0x%x btn: 0x%x type: %d key: %d x: %d y: %d\n",
		inbuf[0], button, e.type, e.e.key.key, e.e.key.x, e.e.key.y);
#endif
	if (ioctl(wsFd, PUT_EVENT_ON_QUEUE, &e) < 0)
	    ErrorF("PUT_EVENT_ON_QUEUE: failed to put event on queue.\n");

	return;
    }

    /* we're not a core device */

    /* Precedence: Proximity, Button, Motion.  No chording
       allowed. E.g. If button 1 is down and user presses button 2,
       nothing is reported. A button release for 1 is sent when
       user releases button 1.  A button press for button 2 is not
       reported when button 1 is released.  Going out of prox -
               ButtonRelease (if needed), ProximityOut. (Discard 2nd
               out-of-prox packet from tablet.)  Coming into prox -
               ProximityIn. Don't use info from first in-prox packet. Wait
               for next packet.  Normal operation -
               ButtonPress/MotionNotify followed by DeviceValuator. */

    /* If button 1 is down and user presses button 2 no event is generated
       because there is no way to tell if it's button 3. Or if button 16 is
       pressed there is no indication if any other buttons are pressed. */

    e.screen = mylastscreen;

    /* xducer in proximity */
    if (prox != pPriv->inProx) {
        if (!pPriv->inProx) {
            pPriv->inProx = TRUE;
            e.type = ProximityIn;
	}
	else {
	    /* button down */
	    if (pPriv->btn) {
		e.type = DeviceButtonRelease;
		e.e.key.key = pPriv->btn;
		if (ioctl(wsFd, PUT_EVENT_ON_QUEUE, &e) < 0)
		    ErrorF("PUT_EVENT_ON_QUEUE: failed to put event on queue.\n");
		e.type = DeviceMotionNotify;
                e.e.key.x = pPriv->x;
                e.e.key.y = pPriv->y;
                e.e.key.pad = pPriv->pressure = 0; 
		if (ioctl(wsFd, PUT_EVENT_ON_QUEUE, &e) < 0)
		    ErrorF("PUT_EVENT_ON_QUEUE: failed to put event on queue.\n");
		pPriv->pressure = 0;
		pPriv->btn = 0;
	    }
            pPriv->inProx = FALSE;
            e.type = ProximityOut;
	}
	e.e.key.key = 0;
	if (ioctl(wsFd, PUT_EVENT_ON_QUEUE, &e) < 0)
	    ErrorF("PUT_EVENT_ON_QUEUE: failed to put event on queue.\n");

    }

    if(!pPriv->inProx)
	return;

    /* already in proximity */
    e.e.key.key = 0;        /* normal, not hint */
    if (button != pPriv->btn) {
	if (button == 0) {
	    e.type = DeviceButtonRelease;
	    e.e.key.key = pPriv->btn;
	    pPriv->btn = 0;
	}
	else {
	    if(!pPriv->btn){
		e.type = DeviceButtonPress;
		e.e.key.key = button;
	    }
	}

	pPriv->btn = button;    /* store new button number */
    }
    else {
	e.type = DeviceMotionNotify;
	e.e.key.key = 0;        /* normal, not hint */
    }

    e.e.key.x = pPriv->x;
    e.e.key.y = pPriv->y;
    e.e.key.pad = pPriv->pressure;


    if (ioctl(wsFd, PUT_EVENT_ON_QUEUE, &e) < 0)
	ErrorF("PUT_EVENT_ON_QUEUE: failed to put event on queue.\n");

}


/************************************************************************/
/*									*/
/* CursorOffScreen - Checks to see if the cursor went off to another	*/
/*		     screen.  This is wacky if the user goes		*/
/*		     off a corner in a diagonal direction.  I was too	*/
/*		     lazy to check for this particular set of cases.	*/
/*									*/
/************************************************************************/

void 
CursorOffScreen(db3PrivPtr pPriv, unsigned char *screen, short *x, short *y)
{
    int i;
    short next_screen = -1;
    short current_screen = *screen;
    ScreenPtr pScreen = wsScreens[*screen];
    static int myprevscrn = 0;
    static int xlastprox = 1; /* starts always in prox. */
    static int ylastprox = 1;

    /* absolute mode, not in prox., could be switching screen */
    /* process only one out of prox */
    if(pPriv->absMode && !pPriv->inProx){
	/* x off the left, high bits set 0x3xxxx */
	if(pPriv->x & 0x30000){
	    if(xlastprox){
		next_screen = wec[current_screen].adj_screens.left;
		if (next_screen != -1)
		    *x = wsScreens[next_screen]->width - 1;
		else 
		    *x = 0;
	    }
	    else {
		if(myprevscrn != current_screen)
		    *x = pScreen->width - 1;
		else
		    *x = 0;
	    }
	}
	/* x off the right and was in proximty the last time */
	else if(pPriv->x >= pPriv->tabletX){
	    if(xlastprox){
		next_screen = wec[current_screen].adj_screens.right;
		if (next_screen != -1)
		    *x = 0;
		else 
		    *x = pScreen->width - 1;
	    }
	    else {
		if(myprevscrn != current_screen)
		    *x = 0;
		else
		    *x = pScreen->width - 1;
	    }
	}

	/* y off the bottom, high bit is set 0x1xxxx */
	if (pPriv->y & 0x10000){
	    if(ylastprox){
		next_screen = wec[current_screen].adj_screens.bottom;
		if (next_screen != -1)
		    *y = 0;
		else 
		    *y = pScreen->height - 1;
	    }
	    else {
		if(myprevscrn != current_screen)
		    *y = 0;
		else 
		    *y = pScreen->height - 1;
	    }
	}
	/* y off the top */
	else if(pPriv->y >= pPriv->tabletY){
	    if(ylastprox){
		next_screen = wec[current_screen].adj_screens.top;
		if (next_screen != -1)
		    *y = *y + wsScreens[next_screen]->height;
		else 
		    *y = 0;
	    }
	    else{
		if(myprevscrn != current_screen)
		    *y = pScreen->height - 1;
		else
		    *y = 0;
	    }
	}
    }

    xlastprox = ylastprox = pPriv->inProx;

    if(!pPriv->absMode){
	/* Going off the top? */
	if (*y < 0) {
	    next_screen = wec[current_screen].adj_screens.top;
	    if (next_screen != -1)
		*y = *y + wsScreens[next_screen]->height;
	}
	/* Going off the bottom? */
	else if (*y > wsScreens[*screen]->height) {
	    next_screen = wec[current_screen].adj_screens.bottom;
	    if (next_screen != -1) 
		*y = *y - wsScreens[next_screen]->height;
	}
	
	/* Going off the left? */
	if (*x < 0) {
	    next_screen = wec[current_screen].adj_screens.left;
	    if (next_screen != -1) 
		*x = *x + wsScreens[next_screen]->width;
	}

	/* Going off the right?  */
	else if (*x > wsScreens[*screen]->width) {
	    next_screen = wec[current_screen].adj_screens.right;
	    if (next_screen != -1)
		*x = *x - wsScreens[next_screen]->width;
	}
    }

    if(next_screen != -1 && current_screen != next_screen){
	pPriv->mntrX = wsScreens[next_screen]->width;
	pPriv->mntrY = wsScreens[next_screen]->height;
    }

    myprevscrn = current_screen;

    if(next_screen != -1) pScreen = wsScreens[next_screen];
    
    /* Just for sanity */
    if(next_screen != -1) *screen = (unsigned char)next_screen;
    if (*x < 0) *x = 0;
    if (*x >= pScreen->width) *x = pScreen->width - 1;
    if (*y < 0) *y = 0;
    if (*y >= pScreen->height) *y = pScreen->height - 1;

} /* CursorOffScreen */