/*

 * compile by: cc -o quantumtest quantumtest.c -lXext -lX11 -lXi

 */

#include <X11/Xlib.h>

#include <X11/Xutil.h>

#include <X11/extensions/XInput.h>

#include <X11/extensions/SGIMisc.h>

#include <stdlib.h>

#include <stdio.h>

#include <string.h>

#include <wacom.h>

#include <syslog.h>





#define MAX_WAC	5

int num_wac = 0;

/* XID wac_list[MAX_WAC]; */

XDeviceInfoPtr wac_list[MAX_WAC];



int	Dflag = 1;

int	details = 0;



XDeviceInfoPtr 	list_wacom_tablets ();

void watch_dev(Display *dsp, XID id, char *name);

Display *init_X(void);



char              *transType[4] =

{

   "puck   ",

   "stylus ",

   "eraser ",

   "unknown"

};





main(int argc, char **argv)

{

	Display		*display;

	int 		ndevices;

	int		selectionNumber;

	char		selection[80];

	int		index;



	if (argc > 2)

	{

		fprintf (stderr, "Syntax: %s [-v]\n", argv[0]);

		exit (1);

	}

	if (argc > 1)

	{

		if (!strcmp (argv[1], "-v"))

		{

			details = 1;

		}

		else

		{

		   fprintf (stderr, "Syntax: %s [-v]\n", argv[0]);

		   exit (1);

		}

	}

	if ( (display = init_X()) == NULL )

	{

		printf ("No connection to server - aborting.\n");

		exit(1);

	}



	(void) list_wacom_tablets (display, &ndevices);





	if ( num_wac )

	{

		if (num_wac > 1)

		{

			while (1)

			{

				printf ("\nFound multiple tablets.  Select a number . . .\n");

				printf ("\t0.  exit\n");

				for (index = 1; index <= num_wac; index++)

				{

					printf ("\t%d.  %s\n", index, wac_list[index - 1]->name);

				}

				printf ("\n\tPlease select a number ==> ");

				gets (selection);

				selectionNumber = atoi(selection);

				if (selectionNumber == 0)

				{

					exit (0);

				}

				if (selectionNumber <= num_wac)

				{

					selectionNumber--;

					break;

				}

				printf ("Invalid selection\n");

			}

		}

		else

		{

			selectionNumber = num_wac - 1;

		}

		watch_dev(display, wac_list[selectionNumber]->id,

			wac_list[selectionNumber]->name); 

	}

	/* never returns if successful */

	return 1;

}



/*

 * Trivial valuator watcher.  Polls the device, so it's expensive when the

 * device is doing nothing (which is most of the time).  Better to use

 * motion hints to reduce overhead, but the valuator examination is

 * identical in either case.

 */

void

watch_dev(Display* display, XID id, char *name)

{

	XDevice				*dev;

	int					i, j,  k;

	XDeviceState		*s;

	XInputClass			*class;

	XValuatorState		*vs;

	XButtonState		*bt;

	int					dataArray[WACOM_NUM_VALUATORS];

	int					oldDataArray[WACOM_NUM_VALUATORS];

	char 					oldButtons[32] = "                               ";

	int					notEqualFlag;

	int					index;

	int					maxValuators;

	int					maxButtons;

	XAxisInfoPtr		a;

	static int			firstPass = 1;



	printf("Opening %s\n", name);

	if ( (dev = XOpenDevice(display, id)) == NULL )

	{

		fprintf(stderr, "failed opening wacom device %d\n", id);

		return;

	}

	printf ("Use Delete to Exit\n");

	printf ("READY TO TEST\n");

	for (;;)

	{

		/* poll the device */

		if ( (s = XQueryDeviceState(display, dev)) != NULL )

		{

			class = s->data;

			a = (XAxisInfoPtr) ((char *) class + sizeof (XValuatorInfo));

			for (j = 0; j < s->num_classes; j++, a++)

			{

				switch (class->class)

				{

				  case ValuatorClass:

				  {

					 vs = (XValuatorState *)class;

					 maxValuators = (int) vs->num_valuators;

					 for (index = 0; index < maxValuators; index++)

					 {

						dataArray[index] = vs->valuators[index];

					 }

					 notEqualFlag = 0;

					 if( firstPass )

					 {

						 for (index = 0; index < maxValuators; index++)

						 {

							 oldDataArray[index] = dataArray[index];

					 	 }

						 firstPass = 0;

					 }

					 else

					 {

					 	 for (index = 0; index < maxValuators; index++)

					 	 {

							 if (dataArray[index] != oldDataArray[index]) 

							 {

								 notEqualFlag = 1;

								 index = maxValuators;

							 }

					 	 }

					 }

					 if (notEqualFlag)

					 {

						   printf ("\n");

							if (((dataArray[WAC_TRANSDUCER_I] ) !=

								oldDataArray[WAC_TRANSDUCER_I] ) &&

								(dataArray[WAC_TRANSDUCER_I] &

								WAC_TRANSDUCER_PROX_MSK))

							{ 

								printf ("Enter Prox Primary   %s\tID=%x\tSerial No.=%x\n",

								transType[dataArray[WAC_TRANSDUCER_I] &

								WAC_TRANSDUCER_MSK],

								WAC_TRANSDUCER_ID(dataArray[WAC_TRANSDUCER_I]),

								dataArray[WAC_SERIAL_NUM_I]);

							}

							else if (((dataArray[WAC_2TRANSDUCER_I] ) !=

								oldDataArray[WAC_2TRANSDUCER_I] ) &&

								(dataArray[WAC_2TRANSDUCER_I] &

								WAC_TRANSDUCER_PROX_MSK))

							{ 

								printf ("Enter Prox Secondary %s\tID=%x\tSerial No.=%x\n",

								transType[dataArray[WAC_2TRANSDUCER_I] &

								WAC_TRANSDUCER_MSK],

								WAC_TRANSDUCER_ID(dataArray[WAC_2TRANSDUCER_I]),

								dataArray[WAC_2SERIAL_NUM_I]);

							}

							printf("VAL  X  %05d  ", dataArray[WAC_XCOORD_I]);

							printf("Y  %05d  ", dataArray[WAC_YCOORD_I]);

							printf("P  %04d  ", (dataArray[WAC_PRESSURE_I]

								& WAC_FIRST_MSK));

							printf("B  %04x  ", dataArray[WAC_BUTTON_I]);

							printf("Tx  %4d  ", (short int)(dataArray[WAC_XTILT_I]

								& WAC_FIRST_MSK));

							printf("Ty  %4d  ", (short int)(dataArray[WAC_YTILT_I]

								& WAC_FIRST_MSK));

							printf("Z  %05d  ", (short int)(dataArray[WAC_ZCOORD_I]

								& WAC_FIRST_MSK));

							printf("Tan  %04d  ", (short int)(dataArray[WAC_TAN_PRESSURE_I]

								& WAC_FIRST_MSK));

							printf("R  %4d\n", (short int)(dataArray[WAC_ROTATION_I]

								& WAC_FIRST_MSK));



							printf("     X2 %05d  ", dataArray[WAC_2XCOORD_I]);

							printf("Y2 %05d  ", dataArray[WAC_2YCOORD_I]);

							printf("P2 %04d  ", (short int)((dataArray[WAC_PRESSURE_I]

								& WAC_SECOND_MSK) >> WAC_SECOND_OFFSET));

							printf("B2 %04x  ", dataArray[WAC_2BUTTON_I]);

							printf("Tx2 %4d  ", (short int)((dataArray[WAC_XTILT_I]

								& WAC_SECOND_MSK) >> WAC_SECOND_OFFSET));

							printf("Ty2 %4d  ", (short int)((dataArray[WAC_YTILT_I]

								& WAC_SECOND_MSK) >> WAC_SECOND_OFFSET));

							printf("Z2 %05d  ", (short int)((dataArray[WAC_ZCOORD_I]

								& WAC_SECOND_MSK) >> WAC_SECOND_OFFSET));

							printf("Tan2 %04d  ", (short int)((dataArray[WAC_TAN_PRESSURE_I]

								& WAC_SECOND_MSK) >> WAC_SECOND_OFFSET));

							printf("R2 %4d\n", (short int)((dataArray[WAC_ROTATION_I]

								& WAC_SECOND_MSK) >> WAC_SECOND_OFFSET));



							if ( dataArray[WAC_TRANSDUCER_I] !=

								   oldDataArray[WAC_TRANSDUCER_I] ) 

							{

								/*    Menu item info     */

								if (dataArray[WAC_TRANSDUCER_I] & WAC_MENU_ID)

								{

									printf ( "Menu pressed ");

								}

								/* Print exiting info   */

								else if ( !(dataArray[WAC_TRANSDUCER_I] &

												WAC_TRANSDUCER_PROX_MSK) )

								{ 

									printf ("Exit  Prox Primary   %s\tID=%x\tSerial No.=%x\n",

									transType[dataArray[WAC_TRANSDUCER_I] &

										WAC_TRANSDUCER_MSK],

										WAC_TRANSDUCER_ID(dataArray[WAC_TRANSDUCER_I]),

										dataArray[WAC_SERIAL_NUM_I]);

								}

							}

							else if ( ( dataArray[WAC_2TRANSDUCER_I] !=

								  		 oldDataArray[WAC_2TRANSDUCER_I] ) &&

										(!(dataArray[WAC_2TRANSDUCER_I] &

												WAC_TRANSDUCER_PROX_MSK)) )

							{ 

								printf ("Exit  Prox Secondary %s\tID=%x\tSerial No.=%x\n",

								transType[dataArray[WAC_2TRANSDUCER_I] &

										WAC_TRANSDUCER_MSK],

										WAC_TRANSDUCER_ID(dataArray[WAC_2TRANSDUCER_I]),

										dataArray[WAC_2SERIAL_NUM_I]);

							}



							/*    Menu item number     */

							if ( (dataArray[WAC_BUTTON_I] != oldDataArray[WAC_BUTTON_I]) &&

										(dataArray[WAC_BUTTON_I] & WAC_MENU_ITEM_MSK) )

							{ 

								printf ( "Item Number = %d\n", WAC_MENU_BUTTONS(dataArray[WAC_BUTTON_I]) );

							}

					 		for (index = 0; index < maxValuators; index++)

					 		{

						 		oldDataArray[index] = dataArray[index];

					 		}

					 }

				    break;

				  }

			   }

			   class = (XInputClass *)((char *)class + class->length);		

			}

		}

		else

		{

			printf("Query bad device %d\n", id);

		}

	}

}





/******************************************************************

 *

 * Find all the wacom tablets.  This builds a static list of wacom tablets.

 * This was quickly hacked from xlist, and that code remains as a useful

 * example.

 */



XDeviceInfoPtr

list_wacom_tablets (Display *display, int *ndevices)

{

    /* most of the variables are left over from xlist */

    int					i,j,k;

    XDeviceInfoPtr		list, slist;

    XAnyClassPtr		any;

    XKeyInfoPtr			K;

    XButtonInfoPtr		b;

    XValuatorInfoPtr	v;

    XAxisInfoPtr		a;

    char				rtrn[SGIDeviceRtrnLen];



    /* find all input devices */

    list = (XDeviceInfoPtr) XListInputDevices (display, ndevices);

    slist = list;

    /* Apply heuristic to select devices; frequently, X devices are 

     * selected by list->name.  Users/applications (e.g., Alias) can change

     * name, so that isn't the best stratgy.  Here we use a

     * query that most things won't respond to, and we hope that nobody

     * else identifies themselves as "Wacom ..."!  But this takes far too

     * long to complete (devices take a while to fail the query).  Probably

     * should check first list->name for either "wacom" or "tablet" (Alias),

     * and then "model" query to rule out other manufacturers.

     */



	printf ("Wait for READY TO TEST message.\nChecking for Tablets ...  \n");

	for (i = 0; i < *ndevices; i++, list++)

	{

		printf("\tchecking %d of %d (%s) . . . ", i+1, *ndevices, list->name);

		fflush(stdout);

		syslog (LOG_WARNING, "%s\n", "Querying device");

		if (!XSGIDeviceQuery(display, list->id, "model", rtrn))

		{

			printf("model infomation not available\n");

			continue;

		}

		else

		{

			if (!strncmp(rtrn, "Wacom", 5))

			{ /* found one */

				printf("model:  %s \n", rtrn);

				wac_list[num_wac++] = list;

			}

			else

			{

				printf("device model '%s'\n", rtrn);

				continue;

			}

		}



		if (details == 1)

		{

			/*

			 * This is the original code for xlist, demonstrating more of

			 * internal structure.

			 */

			if (Dflag)

			{

				printf ("\n\t\tDevice id is %d\n",list->id);

				printf ("\t\tDevice type is %d\n",list->type);

				printf ("\t\tDevice name is %s\n",list->name);

				XSGIDeviceQuery(display, list->id, "version", rtrn);

				printf ("\t\tDriver Version is %s\n",rtrn);

				XSGIDeviceQuery(display, list->id, "datarate", rtrn);

				printf ("\t\tDatarates %s\n",rtrn);

				XSGIDeviceQuery(display, list->id, "dimensions", rtrn);

				printf ("\t\tDimensions is %s\n",rtrn);

				XSGIDeviceQuery(display, list->id, "resolution", rtrn);

				printf ("\t\tResolution is %s\n",rtrn);

				XSGIDeviceQuery(display, list->id, "minx", rtrn);

				printf ("\t\tMinx is %s\n",rtrn);

				XSGIDeviceQuery(display, list->id, "miny", rtrn);

				printf ("\t\tMiny is %s\n",rtrn);

				printf ("\t\tNum_classes is %d\n",list->num_classes);

			}

			if (list->num_classes > 0)

			{

				any = (XAnyClassPtr) (list->inputclassinfo);

				for (j=0; j<list->num_classes; j++)

				{

					if (Dflag)

					{

						printf ("\t\t\tInput class %d\n", any->class);

						printf ("\t\t\t\tLength is %d\n", any->length);

					}

					switch (any->class)

					{

						case KeyClass:

							K = (XKeyInfoPtr) any;

							if (Dflag)

							{

								printf ("\t\t\t\tNum_keys is %d\n",K->num_keys);

								printf ("\t\t\t\tMin_keycode is %d\n",K->min_keycode);

								printf ("\t\t\t\tMax_keycode is %d\n",K->max_keycode);

							}

							break;

						case ButtonClass:

							b = (XButtonInfoPtr) any;

							if (Dflag)

							{

								printf ("\t\t\t\tNum_buttons is %d\n",b->num_buttons);

							}

							break;

						case ValuatorClass:

							v = (XValuatorInfoPtr) any;

							a = (XAxisInfoPtr) ((char *) v + 

								sizeof (XValuatorInfo));

							if (Dflag)

							{

								printf ("\t\t\t\tNum_axes is %d\n\n",v->num_axes);

							}

							for (k=0; k<v->num_axes; k++,a++)

							{

								if (Dflag)

								{

									syslog (LOG_WARNING, "%s\n", "Printing Valuatorvalue");

									printf ("\t\t\t\t\tMin_value is %d\n",a->min_value);

									printf ("\t\t\t\t\tMax_value is %d\n",a->max_value);

									printf ("\t\t\t\t\tResolution is %d\n\n",a->resolution);

								}

							}

							break;

						default:

							printf ("\t\t\t\t\tunknown class\n");

					}

					any = (XAnyClassPtr) ((char *) any + any->length);

				}

			}

		}

		else

			printf ("To view the valuators, please type quantumtest -v");

		sleep(1);

	}

	return (slist);

}



/*

 * SGI initialization; open display, check extensions, etc.

 */

Display *

init_X()

{

    Display			*dpy;

    int				tmp;

    int				ieMajor, ieFEV, ieFER;



	if ((dpy = XOpenDisplay(NULL)) == NULL)

	{

		fprintf(stderr, "Can't connect to server.\n");

		return NULL;

	}



	XSynchronize(dpy,1);

	if (!XQueryExtension(dpy, INAME, &ieMajor, &ieFEV, &ieFER)) {

		fprintf(stderr, "Server doesn't support the input extension.\n");

		XCloseDisplay(dpy);

		return NULL;

	}



	if (!XSGIMiscQueryExtension(dpy,&tmp,&tmp)) {

		fprintf(stderr, "Server doesn't support the SGI misc extension.\n");

		XCloseDisplay(dpy);

		return NULL;

	}

	return dpy;

}

