/* $XConsortium: xf86Jstk.c /main/14 1996/10/25 14:11:36 kaleb $ */
/*
 * Copyright 1995-1997 by Frederic Lepied, France. <Frederic.Lepied@sugix.frmug.org>       
 *                                                                            
 * 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.
 *
 */

/* $XFree86: xc/programs/Xserver/hw/xfree86/common/xf86Jstk.c,v 3.18.2.2 1998/11/12 11:32:04 dawes Exp $ */

#define NEED_EVENTS
#include "X.h"
#include "Xproto.h"
#include "misc.h"
#include "inputstr.h"
#include "scrnintstr.h"
#include "XI.h"
#include "XIproto.h"

#include "compiler.h"

#include "xf86.h"
#include "xf86_Config.h"
#include "xf86Procs.h"
#include "xf86Xinput.h"
#include "xf86_OSlib.h"
#include "atKeynames.h"
#include "xf86Version.h"

#include "osdep.h"
#ifdef XFree86LOADER
#define strdup(a) xf86strdup(a)
#endif

/******************************************************************************
 * debugging macro
 *****************************************************************************/
#ifdef DBG
#undef DBG
#endif
#ifdef DEBUG
#undef DEBUG
#endif

static int      debug_level = 0;
#define DEBUG 1
#if DEBUG
#define DBG(lvl, f) {if ((lvl) <= debug_level) f;}
#else
#define DBG(lvl, f)
#endif

/******************************************************************************
 * device records
 *****************************************************************************/

typedef struct 
{
  int           jstkFd;         /* Joystick File Descriptor */
  OsTimerPtr    jstkTimer;      /* timer object */
  int           jstkTimeout;    /* timeout to poll device */
  char          *jstkDevice;    /* device file name */
  int           jstkOldX;       /* previous X position */
  int           jstkOldY;       /* previous Y position */
  int           jstkOldButtons; /* previous buttons state */
  int           jstkMaxX;       /* max X value */
  int           jstkMaxY;       /* max Y value */
  int           jstkMinX;       /* min X value */
  int           jstkMinY;       /* min Y value */
  int           jstkCenterX;    /* X center value */
  int           jstkCenterY;    /* Y center value */
  int           jstkDelta;      /* delta cursor */
} JoystickDevRec, *JoystickDevPtr;

/******************************************************************************
 * configuration stuff
 *****************************************************************************/
#define DEVICENAME 1
#define TIMEOUT 2
#define MAXX 3
#define MAXY 4
#define MINX 5
#define MINY 6
#define CENTERX 7
#define CENTERY 8
#define DELTA 9
#define PORT 10
#define DEBUG_LEVEL 11
#define HISTORY_SIZE 12
#define ALWAYS_CORE 13

static SymTabRec JstkTab[] = {
  { ENDSUBSECTION,	"endsubsection" },
  { DEVICENAME,		"devicename" },
  { TIMEOUT,		"timeout" },
  { MAXX,		"maximumxposition" },
  { MAXY,		"maximumyposition" },
  { MINX,		"minimumxposition" },
  { MINY,		"minimumyposition" },
  { CENTERX,		"centerx" },
  { CENTERY,		"centery" },
  { DELTA,		"delta" },
  { PORT,		"port" },
  { DEBUG_LEVEL,	"debuglevel" },
  { HISTORY_SIZE,	"historysize" },
  { ALWAYS_CORE,        "alwayscore" },
  { -1,			"" },
};

/******************************************************************************
 * external declarations
 *****************************************************************************/

extern void xf86eqEnqueue(
#if NeedFunctionPrototypes
    xEventPtr /*e*/
#endif
);

extern void miPointerDeltaCursor(
#if NeedFunctionPrototypes
    int /*dx*/,
    int /*dy*/,
    unsigned long /*time*/
#endif
);

extern int xf86JoystickGetState(
#ifdef NeedFunctionPrototypes
    int   /*fd*/,
    int * /*x*/,
    int * /*y*/,
    int * /*buttons*/
#endif
    );

extern void xf86JoystickInit(
#ifdef NeedFunctionPrototypes
void
#endif
);

extern int xf86JoystickOff(
#ifdef NeedFunctionPrototypes
int * /*fd*/,
int /*doclose*/
#endif
);

extern int xf86JoystickOn(
#ifdef NeedFunctionPrototypes
char * /*name*/,
int * /*timeout*/,
int * /*centerX*/,
int * /*centerY*/
#endif
);

/*
 * xf86JstkConfig --
 *      Configure the device.
 */
static Bool
xf86JstkConfig(LocalDevicePtr    *array,
               int               index,
               int               max,
               LexPtr            val)
{
  LocalDevicePtr        dev = array[index];
  JoystickDevPtr        priv = (JoystickDevPtr)(dev->private);
  int token;
  
  DBG(1, ErrorF("xf86JstkConfig\n"));
      
  /* Set defaults */
  priv->jstkOldX = -1;
  priv->jstkOldY = -1;
  priv->jstkOldButtons = -1;
  priv->jstkFd = -1;
  priv->jstkTimeout = 0;

  while ((token = xf86GetToken(JstkTab)) != ENDSUBSECTION) {
    switch(token) {
    case DEVICENAME:
      if (xf86GetToken(NULL) != STRING) xf86ConfigError("Option string expected");
      dev->name = strdup(val->str);
      break;
      
    case PORT:
      if (xf86GetToken(NULL) != STRING) xf86ConfigError("Option string expected");
      priv->jstkDevice = strdup(val->str);
      break;

    case TIMEOUT:
      if (xf86GetToken(NULL) != NUMBER) xf86ConfigError("Joystick Timeout expected");
      priv->jstkTimeout = val->num;
     break;

    case MAXX:
      if (xf86GetToken(NULL) != NUMBER)
        xf86ConfigError("Joystick MaximumXPosition expected");
      priv->jstkMaxX = val->num;
     break;
      
    case MAXY:
      if (xf86GetToken(NULL) != NUMBER)
        xf86ConfigError("Joystick MaximumYPosition expected");
      priv->jstkMaxY = val->num;
     break;
      
    case MINX:
      if (xf86GetToken(NULL) != NUMBER)
        xf86ConfigError("Joystick MinimumXPosition expected");
      priv->jstkMinX = val->num;
     break;
      
    case MINY:
      if (xf86GetToken(NULL) != NUMBER)
        xf86ConfigError("Joystick MinimumYPosition expected");
      priv->jstkMinY = val->num;
     break;
      
    case CENTERX:
      if (xf86GetToken(NULL) != NUMBER)
        xf86ConfigError("Joystick CenterX expected");
      priv->jstkCenterX = val->num;
     break;
      
    case CENTERY:
      if (xf86GetToken(NULL) != NUMBER)
        xf86ConfigError("Joystick CenterY expected");
      priv->jstkCenterY = val->num;
     break;
      
    case DELTA:
      if (xf86GetToken(NULL) != NUMBER)
        xf86ConfigError("Joystick Delta expected");
      priv->jstkDelta = val->num;
     break;
      
    case DEBUG_LEVEL:
	if (xf86GetToken(NULL) != NUMBER)
	    xf86ConfigError("Option number expected");
	debug_level = val->num;
	if (xf86Verbose) {
#if DEBUG
	    ErrorF("%s Joystick debug level sets to %d\n", XCONFIG_GIVEN,
		   debug_level);      
#else
	    ErrorF("%s Joystick debug level not sets to %d because debugging is not compiled\n",
		   XCONFIG_GIVEN, debug_level);
#endif
	}
        break;

    case HISTORY_SIZE:
      if (xf86GetToken(NULL) != NUMBER)
	xf86ConfigError("Option number expected");
      dev->history_size = val->num;
      if (xf86Verbose)
	ErrorF("%s Joystick Motion history size is %d\n", XCONFIG_GIVEN,
	       dev->history_size);      
      break;
	    
    case ALWAYS_CORE:
	xf86AlwaysCore(dev, TRUE);
	if (xf86Verbose)
	    ErrorF("%s Joystick device always stays core pointer\n",
		   XCONFIG_GIVEN);
	break;

    case EOF:
      FatalError("Unexpected EOF (missing EndSubSection)");
      break; /* :-) */

    default:
      xf86ConfigError("Joystick subsection keyword expected");
      break;
    }
  }
  
  DBG(1, ErrorF("xf86JstkConfig timeout=%d name=%s maxx=%d maxy=%d minx=%d miny=%d "
		"centerx=%d centery=%d delta=%d\n",
		priv->jstkTimeout, priv->jstkDevice, priv->jstkMaxX, priv->jstkMaxY,
		priv->jstkMinX, priv->jstkMinY, priv->jstkCenterX, priv->jstkCenterY,
		priv->jstkDelta));

  if (xf86Verbose) {
    ErrorF("%s %s: timeout=%d port=%s maxx=%d maxy=%d minx=%d miny=%d\n"
	   "\tcenterx=%d centery=%d delta=%d\n", XCONFIG_GIVEN, dev->name,
	   priv->jstkTimeout, priv->jstkDevice, priv->jstkMaxX, priv->jstkMaxY,
	   priv->jstkMinX, priv->jstkMinY, priv->jstkCenterX, priv->jstkCenterY,
	   priv->jstkDelta);
  }
  return Success;
}

/*
 ***************************************************************************
 *
 * xf86JstkConvert --
 *	Convert valuators to X and Y.
 *
 ***************************************************************************
 */
static Bool
xf86JstkConvert(LocalDevicePtr	local,
		int		first,
		int		num,
		int		v0,
		int		v1,
		int		v2,
		int		v3,
		int		v4,
		int		v5,
		int*		x,
		int*		y)
{
    if (first != 0 || num != 2)
      return FALSE;

    *x = v0;
    *y = v1;

    return TRUE;
}

/*
 * xf86JstkEvents --
 *      Read the new events from the device, and enqueue them.
 */
static CARD32
xf86JstkEvents(OsTimerPtr        timer,
               CARD32            atime,
               pointer           arg)
{
  DeviceIntPtr          device = (DeviceIntPtr)arg;
  JoystickDevPtr        priv = (JoystickDevPtr) PRIVATE(device);
  int                   timeout = priv->jstkTimeout;
  int                   x, y, buttons;

  DBG(5, ErrorF("xf86JstkEvents BEGIN device=0x%x priv=0x%x"
                " timeout=%d timer=0x%x\n",
                device, priv, timeout, priv->jstkTimer));
  
  if (xf86JoystickGetState(priv->jstkFd, &x, &y, &buttons)) {
    int loop;
    int length = priv->jstkMaxX - priv->jstkMinX;
    int height = priv->jstkMaxY - priv->jstkMinY;
    int v0 = ((x - priv->jstkMinX) * priv->jstkDelta) / length -
      (priv->jstkDelta / 2);
    int v1 = ((y - priv->jstkMinY) * priv->jstkDelta) / height - 
      (priv->jstkDelta / 2);

    DBG(4, ErrorF("xf86JoystickGetState x=%d y=%d centerX=%d centerY=%d v0=%d "
                  "v1=%d buttons=%d\n",
                  x, y, priv->jstkCenterX, priv->jstkCenterY,
                  v0, v1, buttons));
    
    if ((abs(v0) > (priv->jstkDelta / 20)) ||
        (abs(v1) > (priv->jstkDelta / 20)))
      {
	xf86PostMotionEvent(device, 0, 0, 2, v0, v1);
	
        priv->jstkOldX = x;
        priv->jstkOldY = y;          
      }
    for(loop=1; loop<3; loop++)
      {
        if ((priv->jstkOldButtons & loop) != (buttons & loop))
          {
	    xf86PostButtonEvent(device, 0, loop, ((buttons & loop) == loop),
				0, 2, v0, v1);
          }
      }
    priv->jstkOldButtons = buttons;
  }

  DBG(3, ErrorF("xf86JstkEvents END   device=0x%x priv=0x%x"
                " timeout=%d timer=0x%x\n",
                device, priv, timeout, priv->jstkTimer));

  return timeout;
}

static void
xf86JstkControlProc(DeviceIntPtr	device,
                    PtrCtrl		*ctrl)
{
  DBG(2, ErrorF("xf86JstkControlProc\n"));
}

/*
 * xf86JstkProc --
 *      Handle the initialization, etc. of a joystick
 */
static int
xf86JstkProc(pJstk, what)
     DeviceIntPtr       pJstk;
     int                what;
{
  CARD8                 map[5];
  int                   nbaxes;
  int                   nbbuttons;
  int                   jstkfd;
  int                   loop;
  LocalDevicePtr        local = (LocalDevicePtr)pJstk->public.devicePrivate;
  JoystickDevPtr        priv = (JoystickDevPtr)PRIVATE(pJstk);

  DBG(2, ErrorF("BEGIN xf86JstkProc dev=0x%x priv=0x%x xf86JstkEvents=0x%x\n",
                pJstk, priv, xf86JstkEvents));
  
  switch (what)
    {
    case DEVICE_INIT: 
      DBG(1, ErrorF("xf86JstkProc pJstk=0x%x what=INIT\n", pJstk));
  
      map[1] = 1;
      map[2] = 2;

      nbaxes = 2;
      nbbuttons = 2;

      if (InitButtonClassDeviceStruct(pJstk,
                                      nbbuttons,
                                      map) == FALSE) 
        {
          ErrorF("unable to allocate Button class device\n");
          return !Success;
        }
      
      if (InitFocusClassDeviceStruct(pJstk) == FALSE)
        {
          ErrorF("unable to init Focus class device\n");
          return !Success;
        }
          
      if (InitPtrFeedbackClassDeviceStruct(pJstk,
                                           xf86JstkControlProc) == FALSE)
        {
          ErrorF("unable to init ptr feedback\n");
          return !Success;
        }
          
      if (InitValuatorClassDeviceStruct(pJstk, 
                                    nbaxes,
                                    xf86GetMotionEvents, 
                                    local->history_size,
                                    Relative) /* relatif ou absolute */
          == FALSE) 
        {
          ErrorF("unable to allocate Valuator class device\n"); 
          return !Success;
        }
      else 
        {
	    InitValuatorAxisStruct(pJstk,
				   0,
				   0, /* min val */
				   screenInfo.screens[0]->width, /* max val */
				   1, /* resolution */
				   0, /* min_res */
				   1); /* max_res */
	    InitValuatorAxisStruct(pJstk,
				   1,
				   0, /* min val */
				   screenInfo.screens[0]->height, /* max val */
				   1, /* resolution */
				   0, /* min_res */
				   1); /* max_res */
	    
	  /* allocate the motion history buffer if needed */
	  xf86MotionHistoryAllocate(local);

          xf86JoystickInit();
          AssignTypeAndName(pJstk, local->atom, local->name);
        }

      break; 
      
    case DEVICE_ON:
      priv->jstkFd = jstkfd = xf86JoystickOn(priv->jstkDevice,
                                             &(priv->jstkTimeout),
					     &(priv->jstkCenterX),
					     &(priv->jstkCenterY));

      DBG(1, ErrorF("xf86JstkProc  pJstk=0x%x what=ON name=%s\n", pJstk,
                    priv->jstkDevice));

      if (jstkfd != -1)
      {
        priv->jstkTimer = TimerSet(NULL, 0, /*TimerAbsolute,*/
                                   priv->jstkTimeout,
                                   xf86JstkEvents,
                                   (pointer)pJstk);
        pJstk->public.on = TRUE;
        DBG(2, ErrorF("priv->jstkTimer=0x%x\n", priv->jstkTimer));
      }
      else
        return !Success;
    break;
      
    case DEVICE_OFF:
    case DEVICE_CLOSE:
      DBG(1, ErrorF("xf86JstkProc  pJstk=0x%x what=%s\n", pJstk,
                    (what == DEVICE_CLOSE) ? "CLOSE" : "OFF"));

      jstkfd = xf86JoystickOff(&(priv->jstkFd), (what == DEVICE_CLOSE));
      pJstk->public.on = FALSE;
    break;

    default:
      ErrorF("unsupported mode=%d\n", what);
      return !Success;
      break;
    }
  DBG(2, ErrorF("END   xf86JstkProc dev=0x%x priv=0x%x xf86JstkEvents=0x%x\n",
                pJstk, priv, xf86JstkEvents));
  return Success;
}

/*
 * xf86JstkAllocate --
 *      Allocate Joystick device structures.
 */
static LocalDevicePtr
xf86JstkAllocate()
{
  LocalDevicePtr        local = (LocalDevicePtr) xalloc(sizeof(LocalDeviceRec));
  JoystickDevPtr        priv = (JoystickDevPtr) xalloc(sizeof(JoystickDevRec));
  
  local->name = "JOYSTICK";
  local->flags = XI86_NO_OPEN_ON_INIT;
  local->device_config = xf86JstkConfig;
  local->device_control = xf86JstkProc;
  local->read_input = NULL;
  local->close_proc = NULL;
  local->control_proc = NULL;
  local->switch_mode = NULL;
  local->conversion_proc = xf86JstkConvert;
  local->reverse_conversion_proc = NULL;
  local->fd = -1;
  local->atom = 0;
  local->dev = NULL;
  local->private = priv;
  local->type_name = "Joystick";
  local->history_size  = 0;
  
  priv->jstkFd = -1;
  priv->jstkTimer = NULL;
  priv->jstkTimeout = 0;
  priv->jstkDevice = NULL;
  priv->jstkOldX = -1;
  priv->jstkOldY = -1;
  priv->jstkOldButtons = -1;
  priv->jstkMaxX = 1000;
  priv->jstkMaxY = 1000;
  priv->jstkMinX = 0;
  priv->jstkMinY = 0;
  priv->jstkCenterX = -1;
  priv->jstkCenterY = -1;
  priv->jstkDelta = 100;
  
  return local;
}

/*
 * joystick association
 */
DeviceAssocRec joystick_assoc =
{
  "joystick",                   /* config_section_name */
  xf86JstkAllocate              /* device_allocate */
};

#ifdef DYNAMIC_MODULE
/*
 * entry point of dynamic loading
 */
int
#ifndef DLSYM_BUG
init_module(unsigned long	server_version)
#else
init_xf86Jstk(unsigned long     server_version)
#endif
{
    xf86AddDeviceAssoc(&joystick_assoc);

    if (server_version != XF86_VERSION_CURRENT) {
	ErrorF("Warning: Joystick module compiled for version%s\n", XF86_VERSION);
	return 0;
    } else {
	return 1;
    }
}
#endif

#ifdef XFree86LOADER
/*
 * Entry point for the loader code
 */
XF86ModuleVersionInfo xf86JstkVersion = {
    "xf86Jstk",
    MODULEVENDORSTRING,
    MODINFOSTRING1,
    MODINFOSTRING2,
    XF86_VERSION_CURRENT,
    0x00010000,
    {0,0,0,0}
};

void
xf86JstkModuleInit(data, magic)
    pointer *data;
    INT32 *magic;
{
    static int cnt = 0;

    switch (cnt) {
      case 0:
	*magic = MAGIC_VERSION;
	*data = &xf86JstkVersion;
	cnt++;
	break;
	
      case 1:
	*magic = MAGIC_LOAD;
#ifdef linux
	*data = "lnx_jstk.o";
#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined (__OpenBSD__)
	*data = "bsd_jstk.o";
#else
#error "Joystick XInput module not supported on this machine"
#endif
	cnt++;
	break;

      case 2:
	*magic = MAGIC_ADD_XINPUT_DEVICE;
	*data = &joystick_assoc;
	cnt++;
	break;

      default:
	*magic = MAGIC_DONE;
	*data = NULL;
	break;
    } 
}
#endif

	
/* end of xf86Jstk.c */

