/* $XFree86: xc/programs/xsetpointer/xsetpointer.c,v 3.4 1996/01/30 15:28:11 dawes Exp $ */

/*
 * Copyright 1995 by Frederic Lepied, France. <fred@sugix.frmug.fr.net>       
 *                                                                            
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is  hereby granted without fee, provided that
 * the  above copyright   notice appear  in   all  copies and  that both  that
 * copyright  notice   and   this  permission   notice  appear  in  supporting
 * documentation, and that   the  name of  Frederic   Lepied not  be  used  in
 * advertising or publicity pertaining to distribution of the software without
 * specific,  written      prior  permission.     Frederic  Lepied   makes  no
 * representations about the suitability of this software for any purpose.  It
 * is provided "as is" without express or implied warranty.                   
 *                                                                            
 * FREDERIC  LEPIED DISCLAIMS ALL   WARRANTIES WITH REGARD  TO  THIS SOFTWARE,
 * INCLUDING ALL IMPLIED   WARRANTIES OF MERCHANTABILITY  AND   FITNESS, IN NO
 * EVENT  SHALL FREDERIC  LEPIED BE   LIABLE   FOR ANY  SPECIAL, INDIRECT   OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA  OR PROFITS, WHETHER  IN  AN ACTION OF  CONTRACT,  NEGLIGENCE OR OTHER
 * TORTIOUS  ACTION, ARISING    OUT OF OR   IN  CONNECTION  WITH THE USE    OR
 * PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include <stdio.h>
#include <math.h>
#include <X11/Xproto.h>
#include <X11/extensions/XInput.h>

int           event_type;

int StrCaseCmp(s1, s2)
char *s1, *s2;
{
	char c1, c2;

	if (*s1 == 0)
		if (*s2 == 0)
			return(0);
		else
			return(1);

	c1 = (isupper(*s1) ? tolower(*s1) : *s1);
	c2 = (isupper(*s2) ? tolower(*s2) : *s2);
	while (c1 == c2)
	{
		if (c1 == '\0')
			return(0);
		s1++; s2++;
		c1 = (isupper(*s1) ? tolower(*s1) : *s1);
		c2 = (isupper(*s2) ? tolower(*s2) : *s2);
	}
	return(c1 - c2);
}

#define WS 4

void fun_mainloop (Display *dpy, XDevice *device) {
  Window win;
  XEvent event;
  XDeviceState *xds;
  int i, j, n;
  XInputClass *dataptr;
  XKeyState *xks;
  XButtonState *xbs;
  XValuatorState *xvs;
  XEventClass xevc[4];
  int buttonpress_type, motionnotify_type, proximityin_type, proximityout_type;
  int xs[WS], ys[WS];
  int xd2, yd2;
  double j2, jitter;

  j2 = 0;
  win = XCreateSimpleWindow (dpy,
			     RootWindow (dpy, DefaultScreen (dpy)),
			     16,
			     100, 100,
			     100, 100,
			     BlackPixel (dpy, DefaultScreen (dpy)),
 			     WhitePixel (dpy, DefaultScreen (dpy)));
  XMapWindow (dpy, win);
  XSelectInput (dpy, win, ButtonPressMask);
  /*  XSetDeviceFocus (dpy, device, FollowKeyboard, RevertToFollowKeyboard, CurrentTime); */
  DeviceButtonPress (device, buttonpress_type, xevc[0]);
  ProximityIn (device, proximityin_type, xevc[1]);
  ProximityOut (device, proximityout_type, xevc[2]);
  DeviceMotionNotify (device, motionnotify_type, xevc[3]);
  XSelectExtensionEvent (dpy, win, xevc, 4);
  while (1) {
    XNextEvent (dpy, &event);
    switch (event.type) {
    case ButtonPress:
      printf ("button pressed!\n");
      xds = XQueryDeviceState (dpy, device);
      n = xds->num_classes;
      printf ("num_classes = %d\n", n);
      dataptr = xds->data;
      for (i = 0; i < n; i++) {
	printf ("  length = %d, class = %d",
		dataptr->length,
		dataptr->class);
        switch (dataptr->class) {
	case KeyClass:
	  xks = (XKeyState *)dataptr;
	  printf (" KeyClass,");
	  for (j = 0; j * 8 < xks->num_keys; j++)
	    printf (" %d", xks->keys[j]);
	  printf ("\n");
	  break;
	case ButtonClass:
	  xbs = (XButtonState *)dataptr;
	  printf (" ButtonClass,");
	  for (j = 0; j * 8 < xbs->num_buttons; j++)
	    printf (" %d", xbs->buttons[j]);
	  printf ("\n");
	  break;
	case ValuatorClass:
	  xvs = (XValuatorState *)dataptr;
	  printf (" ValuatorClass, mode = %d", xvs->mode);
	  for (j = 0; j < xvs->num_valuators; j++)
	    printf (" %d", xvs->valuators[j]);
	  printf ("\n");
	  break;
	}
	dataptr = (XInputClass *)((char *)dataptr + dataptr->length);
      }
      break;
    default:
      if (event.type == buttonpress_type) {
	XDeviceButtonEvent *xdbe;
	xdbe = (XDeviceButtonEvent *)(&event);
	printf ("Button press state=%d button=%d", xdbe->state, xdbe->button);
	for (j = 0; j < xdbe->axes_count; j++)
	  printf (" %d", xdbe->axis_data[j]);
	printf ("\n");
      } else if (event.type == motionnotify_type) {
	XDeviceMotionEvent *xdme;
	xdme = (XDeviceMotionEvent *)(&event);
	printf ("Motion notify");
	for (j = 0; j < xdme->axes_count; j++)
	  printf (" %d", xdme->axis_data[j]);
	for (j = WS - 1; j > 0; j--)
	  {
	    xs[j] = xs[j - 1];
	    ys[j] = ys[j - 1];
	  }
	xs[0] = xdme->axis_data[0];
	ys[0] = xdme->axis_data[1];
	xd2 = 2 * xs[1] - (xs[0] + xs[2]);
	yd2 = 2 * ys[1] - (ys[0] + ys[2]);
	j2 += 0.1 * (xd2 * xd2 + yd2 * yd2 - j2);
	jitter = sqrt (j2);
	printf (" jitter = %f", jitter);
	printf ("\n");
      } else if (event.type == proximityin_type) {
	XProximityNotifyEvent *xpne;
	xpne = (XProximityNotifyEvent *)(&event);
	printf ("Proximity in");
	for (j = 0; j < xpne->axes_count; j++)
	  printf (" %d", xpne->axis_data[j]);
	printf ("\n");
      } else if (event.type == proximityout_type) {
	XProximityNotifyEvent *xpne;
	xpne = (XProximityNotifyEvent *)(&event);
	printf ("Proximity out");
	for (j = 0; j < xpne->axes_count; j++)
	  printf (" %d", xpne->axis_data[j]);
	printf ("\n");
      } else {
	printf ("Unknown event %d\n", event.type);
      }
      break;
    }
  }
}

int
main(int argc, char * argv[])
{
  int           loop, num_extensions, num_devices;
  char          **extensions;
  XDeviceInfo   *devices;
  Window        win;
  Display       *dpy;
  Window        root_win;
  unsigned long screen;
  XEvent        Event;
  int		list = 0;
  
  if (argc != 2) {
    fprintf(stderr, "usage : %s (-l | <device name>)\n", argv[0]);
    exit(1);
  }

  if (strcmp(argv[1], "-l") == 0) {
    list = 1;
  }
  
  dpy = XOpenDisplay(NULL);

  if (!dpy) {
    printf("unable to connect to X Server try to set the DISPLAY variable\n");
    exit(1);
  }

#ifdef DEBUG
  printf("connected to %s\n", XDisplayString(dpy));
#endif

  screen = DefaultScreen(dpy);
  root_win = RootWindow(dpy, screen);

  extensions = XListExtensions(dpy, &num_extensions);
  for (loop = 0; loop < num_extensions &&
         (strcmp(extensions[loop], "XInputExtension") != 0); loop++);
  XFreeExtensionList(extensions);
  if (loop != num_extensions)
    {
      devices = XListInputDevices(dpy, &num_devices);
      for(loop=0; loop<num_devices; loop++)
        {
	  if (list) {
	      printf("\"%s\"	[", devices[loop].name ? devices[loop].name : "<noname>");
	      switch(devices[loop].use) {
	      case IsXPointer:
		  printf("XPointer]\n");
		  break;
	      case IsXKeyboard:
		  printf("XKeyboard]\n");
		  break;
	      case IsXExtensionDevice:
		  printf("XExtensionDevice]\n");
		  break;
	      default:
		  printf("invalid value]\n");
		  break;
	      }
	  }
	  else {
          if ((argc == 2) && devices[loop].name &&
              (StrCaseCmp(devices[loop].name, argv[1]) == 0))
            if (devices[loop].use == IsXExtensionDevice)
              {
                XDevice *device;
              
#ifdef DEBUG
                fprintf(stderr, "opening device %s\n",
                        devices[loop].name ? devices[loop].name : "<noname>");
#endif
                device = XOpenDevice(dpy, devices[loop].id);
                if (device)
                  {
#if 0
                    XChangePointerDevice(dpy, device, 0, 1);
#else
		    fun_mainloop (dpy, device);
#endif
                    exit(0);
                  }
                else
                  {
                    fprintf(stderr, "error opening device\n");
                    exit(1);
                  }
              }
	  }
        }
      XFreeDeviceList(devices);
    }
  else
    {
      fprintf(stderr, "No XInput extension available\n");
      exit(1);
    }
  
  if (list) {
    exit(0);
  }
  else {
    fprintf(stderr, "Extended device %s not found\n", argv[1]);
    exit(1);
  }
}
