/*
 *
 * Copyright 2004 by Kenan Esau <kenan.esau@conan.de>, Baltmannsweiler, 
 * Germany.
 *
 * 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 names of copyright holders not be
 * used in advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.  The copyright holders
 * make no representations about the suitability of this
 * software for any purpose.  It is provided "as is" without express or
 * implied warranty.
 *
 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS 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.
 *
 */

/* This version has been changed by Henrik Ostergaard Madsen 2007 to
   support the VL-Systems (?) touch screen of the Zalman HD160XT 
   enclosure revision 2 and 3.
   This version wil NOT work properly with the eGalax touch screens!
*/


#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#define _evdev_touch_C_

#include <xf86Version.h>
#if XF86_VERSION_CURRENT >= XF86_VERSION_NUMERIC(3,9,0,0,0)
#define XFREE86_V4
#endif

/*****************************************************************************
 *        Standard Headers
 ****************************************************************************/
#ifdef XFREE86_V4
#include <unistd.h>
#else
#include <stdio.h>
#include <stdlib.h>
#endif
#include <string.h>
#include <errno.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define NEED_EVENTS
#include <X11/X.h>
#include <X11/Xproto.h>

#include "xf86.h"

#ifdef XINPUT
#include <X11/extensions/XI.h>
#include <X11/extensions/XIproto.h>
#include "extnsionst.h"
#include "extinit.h"
#else
#include "inputstr.h"
#endif

#include "xf86_OSproc.h"
#include "xf86Xinput.h"
#include "exevents.h"
#include "xf86OSmouse.h"
#include "randrstr.h"

#ifndef NEED_XF86_TYPES
#define NEED_XF86_TYPES	/* for xisb.h when !XFree86LOADER */
#endif

#include "compiler.h"

#include "xisb.h"

/*****************************************************************************
 *        Local Headers
 ****************************************************************************/
#include "libtouch.h"
#include "evtouch.h"

/*****************************************************************************
 *        Variables without includable headers
 ****************************************************************************/

/*****************************************************************************
 *        Local Variables
 ****************************************************************************/


static InputInfoPtr
EVTouchPreInit(InputDriverPtr drv, IDevPtr dev, int flags);
static void
EVTouchPtrCtrl(DeviceIntPtr device, PtrCtrl *ctrl);

static int debug_level = 0;

InputDriverRec EVTOUCH = {
        1,
        "evtouch",
        NULL,
        EVTouchPreInit,
        /*EVTouchUnInit*/ NULL,
        NULL,
        0
};

#ifdef XFree86LOADER
ModuleInfoRec EVTouchInfo = {
        1,
        "EVTOUCH",
        NULL,
        0,
        NULL /*EVTouchAvailableOptions*/,
};


static XF86ModuleVersionInfo VersionRec =
{
        "evtouch",
        "Kenan Esau",
        MODINFOSTRING1,
        MODINFOSTRING2,
        XF86_VERSION_CURRENT,
        0, 8, 6,
        ABI_CLASS_XINPUT,
        ABI_XINPUT_VERSION,
        MOD_CLASS_XINPUT,
        {0, 0, 0, 0}         /* signature, to be patched into the file by
                              * a tool */
};


static pointer
Plug( pointer module,
      pointer options,
      int *errmaj,
      int *errmin )
{
        xf86AddModuleInfo(&EVTouchInfo, module);
        xf86AddInputDriver(&EVTOUCH, module, 0);
        return module;
}


static void
Unplug(pointer p)
{
        DBGOUT(1, "EVTouch: Unplug\n");
}


XF86ModuleData evtouchModuleData = {&VersionRec, Plug, Unplug };

#endif /* XFree86LOADER */


static const char *default_options[] =
{
        "BaudRate", "9600",
        "StopBits", "1",
        "DataBits", "8",
        "Parity", "None",
        "Vmin", "5",
        "Vtime", "1",
        "FlowControl", "None"
};

static int
ControlProc(LocalDevicePtr device, xDeviceCtl *ctrl);

/*****************************************************************************
 *        Function Definitions
 ****************************************************************************/


static CARD32
emulate3Timer(OsTimerPtr timer, CARD32 now, pointer _local)
{
        int sigstate;

        DBGOUT(2, "EVTouch: %s\n", __FUNCTION__);

        LocalDevicePtr local = (LocalDevicePtr)_local;
        EVTouchPrivatePtr priv = (EVTouchPrivatePtr) local->private;

        sigstate = xf86BlockSIGIO();

        xf86PostMotionEvent(local->dev, TRUE, 0, 2, 
                            priv->cur_x, 
                            priv->cur_y);

        /* 
         * Emit a button press -- release is handled in EVTouchLBRBEvent
         */
        if ( ( priv->touch_flags & LB_STAT ) &&
             !( priv->touch_flags & RB_STAT ) ) {
                DBGOUT(2, "EVTouch: Left Press\n");
                xf86PostButtonEvent (local->dev, TRUE,
                                     1, 1, 0, 2, 
                                     priv->cur_x, 
                                     priv->cur_y);
        }

        if ( ( priv->touch_flags & RB_STAT ) &&
             !( priv->touch_flags & LB_STAT ) ) {
                DBGOUT(2, "EVTouch: Right Press\n");
                xf86PostButtonEvent (local->dev, TRUE,
                                     3, 1, 0, 2, 
                                     priv->cur_x, 
                                     priv->cur_y);
        }

        /*
          Handling "middle" button press
        */
        if ( ( priv->touch_flags & RB_STAT ) &&
             ( priv->touch_flags & LB_STAT ) ) {
                DBGOUT(2, "EVTouch: Middle Press\n");
                xf86PostButtonEvent (local->dev, TRUE,
                                     2, 1, 0, 2, 
                                     priv->cur_x, 
                                     priv->cur_y);
        }

        priv->emulate3_timer_expired = TRUE;
        xf86UnblockSIGIO(sigstate);
             
        return 0;
}

static void DoBtnAction(EVTouchPrivatePtr priv) 
{
        int btn = 0;
        LocalDevicePtr local = priv->local;
        DBGOUT(2, "EVTouch: %s btn_count=%d\n", __FUNCTION__, priv->btn_count);

        for (btn = 0; btn < priv->btn_count; btn++) {
                DBGOUT(9, "EVTouch: %s do_it = %d \n", 
                       __FUNCTION__, priv->btn_actions[btn].do_it);
                if (priv->btn_actions[btn].do_it != 0) {
                        if (priv->emulate3_timer != NULL)
                        {
                                TimerFree(priv->emulate3_timer);
                                priv->emulate3_timer=NULL;
                                priv->emulate3_timer_expired = FALSE;
                        }

                        DBGOUT(2, "EVTouch: %s btn = %d action = %d\n", 
                               __FUNCTION__, btn, 
                               priv->btn_actions[btn].action);

                        xf86PostButtonEvent (local->dev, TRUE, btn, 
                                             priv->btn_actions[btn].action, 
                                             0, 2,
                                             priv->cur_x,
                                             priv->cur_y);

                        priv->btn_actions[btn].do_it  = 0;
                        priv->btn_actions[btn].action = 0;
                }
        }
}

static void SetBtnAction(EVTouchPrivatePtr priv, int btn, int action)
{
        DBGOUT(2, "EVTouch: %s btn = %d action = %d\n", __FUNCTION__,
               btn, action);
        if (btn < priv->btn_count) {
                priv->btn_actions[btn].do_it  = 1;
                priv->btn_actions[btn].action = action;
        }
}

void EVTouchProcessAbs(EVTouchPrivatePtr priv)
{
        struct input_event *ev; /* packet being/just read */

        ev = &priv->ev;

        if (ev->code == ABS_X) {
                priv->cur_x = ev->value;
		libtouchSetXPos(priv->libtouch, priv->cur_x);
	}

        if (ev->code == ABS_Y) {
                priv->cur_y = ev->value;
		libtouchSetYPos(priv->libtouch, priv->cur_y);
	}

	if (ev->code == ABS_WHEEL) {
		LocalDevicePtr local = priv->local;

		if (ev->value > 0) {
			for (; ev->value > 0; ev->value--) {
				xf86PostButtonEvent (local->dev, TRUE,
						     4, 1, 0, 2, 
						     priv->cur_x, 
						     priv->cur_y);
				xf86PostButtonEvent (local->dev, TRUE,
						     4, 0, 0, 2, 
						     priv->cur_x, 
						     priv->cur_y);
			}
		} else if (ev->value < 0) {
			for (ev->value = -ev->value; ev->value > 0; ev->value--) {
				xf86PostButtonEvent (local->dev, TRUE,
						     5, 1, 0, 2, 
						     priv->cur_x, 
						     priv->cur_y);
				xf86PostButtonEvent (local->dev, TRUE,
						     5, 0, 0, 2, 
						     priv->cur_x, 
						     priv->cur_y);
			}
		}			
	}
}




void EVTouchProcessRel(EVTouchPrivatePtr priv)
{
        struct input_event *ev; /* packet being/just read */

        ev = &priv->ev;
        if ( ev->code == REL_X ) {
                priv->cur_x += ev->value;
                if (priv->cur_x > priv->max_x)
                        priv->cur_x = priv->max_x;
                if (priv->cur_x < priv->min_x)
                        priv->cur_x = priv->min_x;
                return;
        } 
        if ( ev->code == REL_Y ) {
                priv->cur_y += ev->value;
                if (priv->cur_y > priv->max_y)
                        priv->cur_y = priv->max_y;
                if (priv->cur_y < priv->min_y)
                        priv->cur_y = priv->min_y;
                return;
        }
	
        libtouchSetPos(priv->libtouch, priv->cur_x, priv->cur_y);
}




void EVTouchLBRBEvent(EVTouchPrivatePtr priv)
{
        struct input_event *ev; /* packet being/just read */
        LocalDevicePtr local = priv->local;

        ev = &priv->ev;
        DBGOUT(2, "EVTouch: %s\n", __FUNCTION__);

        if (priv->emulate3) {
                if ( (ev->value==1) && (priv->emulate3_timer==NULL) )
                        priv->emulate3_timer = TimerSet(priv->emulate3_timer, 
                                                        0,
                                                        priv->emulate3_timeout,
                                                        emulate3Timer,
                                                        local);
                
                if ( (ev->value == 1) && (ev->code == BTN_LEFT) ) {
                        priv->touch_flags |= LB_STAT;
                }
                if ( (ev->value == 1) && (ev->code == BTN_RIGHT) ) {
                        priv->touch_flags |= RB_STAT;
                }

                if ( (ev->value == 0) && 
                     (priv->touch_flags & RB_STAT) && 
                     (priv->touch_flags & LB_STAT) ) {
                        DBGOUT(2, "EVTouch: Middle Release\n");
                        priv->touch_flags &= ~LB_STAT;
                        priv->touch_flags &= ~RB_STAT;
                        SetBtnAction(priv, 2, BTN_RELEASE);
                } else if ( (ev->value == 0) && (ev->code == BTN_LEFT) &&
                     (priv->touch_flags & LB_STAT) ) {
                        DBGOUT(2, "EVTouch: Left Release\n");
                        priv->touch_flags &= ~LB_STAT;
                        SetBtnAction(priv, 1, BTN_RELEASE);
                } else if ( (ev->value == 0) && (ev->code == BTN_RIGHT) &&
                     (priv->touch_flags & RB_STAT) ) {
                        DBGOUT(2, "EVTouch: Right Release\n");
                        priv->touch_flags &= ~RB_STAT;
                        SetBtnAction(priv, 3, BTN_RELEASE);
                }                                
        } else {
                if (ev->code == BTN_LEFT) {
                        SetBtnAction(priv, 1, ev->value);
                }

		if (ev->code == BTN_MIDDLE) {
                        SetBtnAction(priv, 2, ev->value);
		}

                if (ev->code == BTN_RIGHT) {
                        SetBtnAction(priv, 3, ev->value);
                }
        }
}




void EVTouchProcessKey(EVTouchPrivatePtr priv)
{
        struct input_event *ev; /* packet being/just read */

        DBGOUT(2, "EVTouch: %s\n", __FUNCTION__);
        ev = &priv->ev;
        if ( (ev->code == BTN_LEFT) || 
             (ev->code == BTN_RIGHT) ||
             (ev->code == BTN_MIDDLE) ) {

                /* give lb and rb-events some special treatment 
                   (emulate3 or not, ...) */
                EVTouchLBRBEvent(priv);
                return;
        }

#ifdef _0_
        switch (ev->code) {
        case BTN_SIDE:
                break;
        case BTN_EXTRA:
                break;
        case BTN_FORWARD:
                break;
        case BTN_BACK:
                break;
        case BTN_TASK:
                break;
        default:
                return;
        }
        xf86PostButtonEvent(local->dev, TRUE,
                            btn, ev->value, 0, 2, 
                            priv->cur_x, 
                            priv->cur_y);
#endif

        return;
}




static Bool
QueryHardware (LocalDevicePtr local)
{
        DBGOUT(2, "EVTouch: %s\n", __FUNCTION__);

        return Success;
}




static Bool
DeviceOn (DeviceIntPtr dev)
{
        LocalDevicePtr local = (LocalDevicePtr) dev->public.devicePrivate;
        EVTouchPrivatePtr priv = (EVTouchPrivatePtr) (local->private);
        
        local->fd = xf86OpenSerial(local->options);

        DBGOUT(2, "EVTouch: %s\n", __FUNCTION__ );

        if (local->fd == -1)
        {
                xf86Msg(X_WARNING, "%s: cannot open input device\n", local->name);
                return (!Success);
        }

        priv->buffer = XisbNew(local->fd, 64);

        DBG (9, XisbTrace (priv->buffer, 1));


        if (!priv->buffer) 
        {
                xf86CloseSerial(local->fd);
                local->fd = -1;
                return (!Success);
        }

        if (QueryHardware(local) != Success)
        {
                ErrorF ("Unable to query/initialize EVTouch hardware.\n");
                return (!Success);
        }

        xf86FlushInput(local->fd);

#ifndef XFREE86_V4
        xf86AddEnabledDevice(local);
#else
        AddEnabledDevice(local->fd);
#endif

        dev->public.on = TRUE;

        return (Success);
}




static Bool
DeviceOff (DeviceIntPtr dev)
{
        LocalDevicePtr local = (LocalDevicePtr) dev->public.devicePrivate;
        EVTouchPrivatePtr priv = (EVTouchPrivatePtr) (local->private);

        DBGOUT(2, "EVTouch: %s\n", __FUNCTION__ );

        if (local->fd != -1)
        { 
                xf86RemoveEnabledDevice (local);
                if (priv->buffer)
                {
                        XisbFree(priv->buffer);
                        priv->buffer = NULL;
                }
                xf86CloseSerial(local->fd);
                local->fd = -1;
        }

        dev->public.on = FALSE;

        if ( (priv->calibrate) && (priv->fifo>0) ){
                close (priv->fifo);
        }

        return (Success);
}




static Bool
DeviceInit (DeviceIntPtr dev)
{
        DBGOUT(2, "EVTouch: %s\n", __FUNCTION__);
        LocalDevicePtr local = (LocalDevicePtr) dev->public.devicePrivate;
        EVTouchPrivatePtr priv = (EVTouchPrivatePtr) (local->private);
        unsigned char map[EV_MAX_BUTTONS + 1];
        int i;

        for (i = 0; i <= EV_MAX_BUTTONS; i++)
                map[i] = i;

        priv->btn_count = EV_MAX_BUTTONS;

        /* 
         * these have to be here instead of in the SetupProc, because when the
         * SetupProc is run at server startup, screenInfo is not setup yet
         */

        ScrnInfoPtr   pScrn = xf86Screens[priv->screen_num];
        priv->screen_width  = pScrn->virtualX;     /* It's the virtual screen size ! */
        priv->screen_height = pScrn->virtualY;
        priv->pViewPort_X0  = &(pScrn->frameX0);   /* initialize the pointers to the viewport coords */
        if ( (priv->screen_width != priv->phys_width) ||
             (priv->screen_height != priv->phys_height) ) 
              priv->virtual = 1;
        else  
                priv->virtual = 0;

        priv->pViewPort_Y0  = &(pScrn->frameY0);
        priv->pViewPort_X1  = &(pScrn->frameX1);
        priv->pViewPort_Y1  = &(pScrn->frameY1);

        DBGOUT(2, "EVTouch: Display X,Y: %d %d\n", priv->phys_width, priv->phys_height);
        DBGOUT(2, "EVTouch: Virtual X,Y: %d %d\n", priv->screen_width, priv->screen_height);
        DBGOUT(2, "EVTouch: DriverName, Rev.: %s %d\n", pScrn->driverName, pScrn->driverVersion);
        DBGOUT(2, "EVTouch: Viewport X0,Y0: %d %d\n", *priv->pViewPort_X0, *priv->pViewPort_Y0);
        DBGOUT(2, "EVTouch: Viewport X1,Y1: %d %d\n", *priv->pViewPort_X1, *priv->pViewPort_Y1);
        DBGOUT(2, "EVTouch: MaxValue H,V: %d %d\n", pScrn->maxHValue, pScrn->maxVValue);

        priv->screen_width = screenInfo.screens[priv->screen_num]->width;
        priv->screen_height = screenInfo.screens[priv->screen_num]->height;        

        /* 
         * Device reports button press for 5 buttons.
         */
        if (InitButtonClassDeviceStruct (dev, EV_MAX_BUTTONS, map) == FALSE)
        {
                ErrorF("Unable to allocate EVTouch touchscreen ButtonClassDeviceStruct\n");
                return !Success;
        } 

        DBGOUT(2, "EVTouch: %s btn_count=%d\n", __FUNCTION__, 
               priv->btn_count);
        priv->btn_actions = xcalloc(priv->btn_count, sizeof(BtnAction));
        xf86memset(priv->btn_actions, 0, 
                   priv->btn_count * sizeof(BtnAction));
        
        DBGOUT(2, "EVTouch: %s btn_count=%d\n", __FUNCTION__, 
               priv->btn_count);

        if (InitFocusClassDeviceStruct(dev) == FALSE) {
                ErrorF("Unable to allocate EVTouch touchscreen FocusClassDeviceStruct\n");
                return !Success;
        }

        /* 
         * Device reports motions on 2 axes in absolute coordinates.
         * Axes min and max values are reported in raw coordinates.
         */
        if (InitValuatorClassDeviceStruct(dev, 2, xf86GetMotionEvents,
                                          local->history_size, Absolute) == FALSE)
        {
                ErrorF ("Unable to allocate EVTouch touchscreen ValuatorClassDeviceStruct\n");
                return !Success;
        }

        InitValuatorAxisStruct (dev, 0, priv->min_x, priv->max_x,
                                1024,
                                EV_AXIS_MIN_RES /* min_res */ ,
                                EV_AXIS_MAX_RES /* max_res */ );
        InitValuatorAxisStruct (dev, 1, priv->min_y, priv->max_y,
                                1024,
                                EV_AXIS_MIN_RES /* min_res */ ,
                                EV_AXIS_MAX_RES /* max_res */ );

        /* Initial position of pointer on screen: Centered */
        priv->cur_x=(priv->max_x - priv->min_x)/2;
        priv->cur_y=(priv->max_y - priv->min_y)/2;
        libtouchSetPos(priv->libtouch, priv->cur_x, priv->cur_y);

        
        if (InitProximityClassDeviceStruct (dev) == FALSE)
        {
                ErrorF ("Unable to allocate EVTouch touchscreen ProximityClassDeviceStruct\n");
                return !Success;
        }

        if (InitPtrFeedbackClassDeviceStruct(dev, EVTouchPtrCtrl) == FALSE)
        {
                ErrorF ("unable to allocate EVTouch touchscreen PtrFeedbackClassDeviceStruct\n");
                return !Success;
        }


        /* 
         * Allocate the motion events buffer.
         */
        xf86MotionHistoryAllocate (local);
        return (Success);
}




static Bool
DeviceControl (DeviceIntPtr dev,
               int mode)
{
        Bool        RetValue;

        switch (mode)
        {
        case DEVICE_INIT:
                DeviceInit(dev);
                RetValue = Success;
                break;
        case DEVICE_ON:
                RetValue = DeviceOn(dev);
                break;
        case DEVICE_OFF:
        case DEVICE_CLOSE:
                RetValue = DeviceOff(dev);
                break;
        default:
                RetValue = BadValue;
        }

        return( RetValue );
}




static void
EVTouchNewPacket (EVTouchPrivatePtr priv)
{
        xf86memset(&priv->ev, 0, sizeof(struct input_event));
        priv->packeti = 0;
        priv->binary_pkt = FALSE;
}




static unsigned char
EVTouchRead(EVTouchPrivatePtr priv)
{
        unsigned char c;
        XisbBlockDuration (priv->buffer, EV_TIMEOUT);
        c = XisbRead(priv->buffer);
        return (c);
}


#define ZALMAN_PKG_SIZE 16  /* Byte length of packet header (event type and event code) */
/* Zalman packet format:
    byte 0-3   : Timetag in seconds
    byte 4-7   : Fast timetag
    byte 8-9   : Event type (0=SYN, 1= Pen up/down, 3= Abs position)
    byte 10-11 : Event code (0=SYN, 2=Y pos, 3=X pos, 0x014a= Pen up/down)
    byte 12-13 : Value ( LSB first, 11 bits )
    byte 14-15 : Zero (?)
SYN Packets preceeded by 1-3 pos or pen packets.
*/

#define ZALMAN_TIME_KEY 0	/* Timestamp position in buffer */
#define ZALMAN_ID_KEY  10	/* Event ID position in buffer */
#define ZALMAN_POS_KEY 12	/* Value position in buffer */


#define ZALMAN_SYN_ID  0    	/* Event ID Synchronization ID */
#define ZALMAN_X_ID  3    	/* Event ID for X position */
#define ZALMAN_Y_ID  2    	/* Event ID for Y position */
#define ZALMAN_UP_ID 0x4a 	/* Event ID for Pen up/down */        



static Bool
EVTouchGetPacket (EVTouchPrivatePtr priv)
{
        static int count = 0;
	static char local_buffer[ZALMAN_PKG_SIZE];
        int c;
	CARD32 now;

        if (count==0) EVTouchNewPacket(priv);

        while ((c = XisbRead(priv->buffer)) >= 0) {
	        if (count == 0) {
		        now = GetTimeInMillis();
		        libtouchSetTime(priv->libtouch, now);
	        }

		local_buffer[count]=c;
                count ++;

                if (ZALMAN_PKG_SIZE == count) {
                        count = 0;
			priv->ev.time=  *((struct timeval*)&local_buffer[ZALMAN_TIME_KEY]);
			switch (local_buffer[ZALMAN_ID_KEY]) {
			case ZALMAN_UP_ID:  priv->ev.value=local_buffer[ZALMAN_POS_KEY];
					    priv->ev.code=BTN_TOUCH;
					    priv->ev.type=EV_KEY;
					    break;
			case ZALMAN_X_ID:   priv->ev.value=(local_buffer[ZALMAN_POS_KEY+1]<<8)  +
							   (local_buffer[ZALMAN_POS_KEY] & 0xff);
					    priv->ev.code=ABS_X;
					    priv->ev.type=EV_ABS;
					    break;
			case ZALMAN_Y_ID:   priv->ev.value= (local_buffer[ZALMAN_POS_KEY+1]<<8)  +
							    (local_buffer[ZALMAN_POS_KEY] & 0xff);
					    priv->ev.code=ABS_Y;
					    priv->ev.type=EV_ABS;
					    break;				    
			case ZALMAN_SYN_ID: priv->ev.type=EV_SYN;
                                            break;  ;
			}
                        EVTouchDumpPacketToLog(priv);
                        
                        return Success;
                }
/*              ((char *)&priv->ev)[count] = c;
                if (sizeof(priv->ev) == count) {
                        count = 0;
                        EVTouchDumpPacketToLog(priv);
                        
                        return Success;
                } */
        }
        return (!Success);
}




static void ReadInput (LocalDevicePtr local)
{
        struct input_event *ev; /* packet being/just read */

        EVTouchPrivatePtr priv = (EVTouchPrivatePtr) (local->private);

        /* 
         * set blocking to -1 on the first call because we know there is data to
         * read. Xisb automatically clears it after one successful read so that
         * succeeding reads are preceeded buy a select with a 0 timeout to prevent
         * read from blocking infinately.
         */
        XisbBlockDuration (priv->buffer, -1);
        while (EVTouchGetPacket (priv) == Success)
        {
                ev = &priv->ev;
		DBGOUT(2, "EVTouch: %s type:%0.2x code: 0x%0.4x value:%d\n",
			__FUNCTION__, ev->type, ev->code, ev->value);

                xf86XInputSetScreen(local, 
                                    priv->screen_num, 
                                    priv->cur_x, 
                                    priv->cur_y);
                        
                xf86PostProximityEvent(local->dev, 1, 0, 2,
                                       priv->cur_x,
                                       priv->cur_y);

                switch (ev->type) {
                case EV_ABS:
                        EVTouchProcessAbs(priv);
                        break;
                case EV_REL:
                        EVTouchProcessRel(priv);
                        break;
		case EV_KEY:
                        xf86PostMotionEvent (local->dev, TRUE, 0, 2, 
                                             priv->cur_x,
                                             priv->cur_y);

			if (priv->ev.code == BTN_TOUCH) {
				if (priv->ev.value == 1) {
					priv->touch_flags |= TOUCHED;
					DBGOUT(2, "EVTouch: TOUCHED\n");
				}
				else {
					priv->touch_flags &= ~TOUCHED;
					DBGOUT(2, "EVTouch: UNTOUCHED\n");
				}
			}
                        EVTouchProcessKey(priv);
			break;
		case EV_SYN:
                        xf86PostMotionEvent (local->dev, TRUE, 0, 2, 
                                             priv->cur_x,
                                             priv->cur_y);

			if ( priv->touch_flags & TOUCHED )
				libtouchTriggerSM(priv->libtouch, PEN_TOUCHED);
			else
				libtouchTriggerSM(priv->libtouch, PEN_UNTOUCHED);
                        DoBtnAction(priv);

                        break;
                }

                DBGOUT( 2, "EVTouch: setting (x/y)=(%d/%d)\n", 
                        priv->cur_x, priv->cur_y);

        }
}




static int
ControlProc(LocalDevicePtr device, xDeviceCtl *ctrl)
{
        DBGOUT(2, "EVTouch: %s\n", __FUNCTION__);

/* 	xDeviceTSCalibrationCtl *c = (xDeviceTSCalibrationCtl *) ctrl; */
/* 	EVTouchPrivatePtr priv = (EVTouchPrivatePtr) (local->private); */

/* 	priv->min_x = c->min_x; */
/* 	priv->max_x = c->max_x; */
/* 	priv->min_y = c->min_y; */
/* 	priv->max_y = c->max_y; */
	return (Success);
}




static void
CloseProc (LocalDevicePtr local)
{
        DBGOUT(2, "EVTouch: %s\n", __FUNCTION__);
}




static int
SwitchMode (ClientPtr client, DeviceIntPtr dev, int mode)
{
        DBGOUT(2, "EVTouch: %s\n", __FUNCTION__);
        
        return (!Success);
}




static Bool
ConvertProc ( LocalDevicePtr local,
              int first,
              int num,
              int v0,
              int v1,
              int v2,
              int v3,
              int v4,
              int v5,
              int *x,
              int *y )
{
        DBGOUT(2, "EVTouch: %s\n", __FUNCTION__);
        /*
          correction factors depending on current position of pointer
        */
        float cx[3];
        float cy[3];
        float dx = 0, dy = 0;

        int max_x, max_y;
        int xc, yc;
        int screen_width  = 0;
        int screen_height = 0;
#ifdef EVDBG
        int i = 0;
#endif

        EVTouchPrivatePtr priv = (EVTouchPrivatePtr) (local->private);  
	ScrnInfoPtr pScrn = xf86Screens[priv->screen_num];
	Rotation rotation = RRGetRotation(pScrn->pScreen);

        DBGOUT(2, "EVTouch: FIRST: v0=%d   v1=%d\n", v0, v1);

        /*correction of raw coordinates*/
        if ( (priv->fifo>0) && (priv->calibrate) ){
                DBGOUT(2, "EVTouch: writing to FIFO\n");
                write (priv->fifo, &v0, sizeof(v0));
                write (priv->fifo, &v1, sizeof(v1));
        }

        if (!priv->calibrate) {
                DBGOUT(2, "EVTouch: Scaling coordinates\n");
                xc = v0 - priv->min_x;
                yc = v1 - priv->min_y;
        
                max_x = priv->max_x - priv->min_x;
                max_y = priv->max_y - priv->min_y;

                if (priv->rotate == EV_ROTATE_NONE) {
                        screen_width  = priv->phys_width;
                        screen_height = priv->phys_height;
                } else {
                        screen_width  = priv->phys_height;
                        screen_height = priv->phys_width;
                }


                if (xc < (max_x / 2)) {
                        /*
                          left
                        */
                        if (yc>(max_y / 2)) {
                                /*
                                  upper
                                */
                                cx[1] = ((float) xc / (max_x / 2) );
                                cx[0] = (float) 1 - cx[1];
                                cy[0] = ((float) (yc-(max_y/2)) / (max_y/2) );
                                cy[1] = (float) 1 - cy[0];

                                dx = ((float) (cx[1] * cy[0] * priv->diff[1][0]) +
                                      (float)(cx[0] * cy[0] * priv->diff[0][0]) +
                                      (float)(cx[1] * cy[1] * priv->diff[4][0]) +
                                      (float)(cx[0] * cy[1] * priv->diff[3][0]));
                                
                                dy = ((float) (cx[1] * cy[0] * priv->diff[1][1]) +
                                      (float)(cx[0] * cy[0] * priv->diff[0][1]) +
                                      (float)(cx[1] * cy[1] * priv->diff[4][1]) +
                                      (float)(cx[0] * cy[1] * priv->diff[3][1]));
                        }
                        else {
                                /*
                                  lower
                                */
                                cx[1] = ((float) xc / (max_x/2) );
                                cx[0] = (float) 1 - cx[1];
                                cy[0] = ((float) yc / (max_y/2) );
                                cy[1] = (float) 1 - cy[0];

                                dx = ((float) (cx[1] * cy[0] * priv->diff[4][0]) +
                                      (float)(cx[0] * cy[0] * priv->diff[3][0]) +
                                      (float)(cx[1] * cy[1] * priv->diff[7][0]) +
                                      (float)(cx[0] * cy[1] * priv->diff[6][0]));
                                
                                dy = ((float) (cx[1] * cy[0] * priv->diff[4][1]) +
                                      (float)(cx[0] * cy[0] * priv->diff[3][1]) +
                                      (float)(cx[1] * cy[1] * priv->diff[7][1]) +
                                      (float)(cx[0] * cy[1] * priv->diff[6][1]));

                        }
                } else {
                        /*
                          right
                        */
                        if (yc>(max_y/2)) {
                                /*
                                  upper
                                */
                                cx[1] = ((float) (xc-(max_x/2)) / (max_x/2) );
                                cx[0] = (float)1 - cx[1];
                                cy[0] = ((float) (yc-(max_y/2)) / (max_y/2) );
                                cy[1] = (float)1 - cy[0];

                                dx = ((float) (cx[1] * cy[0] * priv->diff[2][0]) +
				      (float)(cx[0] * cy[0] * priv->diff[1][0]) +
				      (float)(cx[1] * cy[1] * priv->diff[5][0]) +
				      (float)(cx[0] * cy[1] * priv->diff[4][0]));
                        
                                dy = ((float) (cx[1] * cy[0] * priv->diff[2][1]) +
				      (float)(cx[0] * cy[0] * priv->diff[1][1]) +
                                      (float)(cx[1] * cy[1] * priv->diff[5][1]) +
                                      (float)(cx[0] * cy[1] * priv->diff[4][1]));

                        } else {
                                /*
                                  lower
                                */
                                cx[1] = ((float) (xc-(max_x/2)) / (max_x/2) );
                                cx[0] = (float) 1 - cx[1];
                                cy[0] = ((float) yc / (max_y/2) );
                                cy[1] = (float) 1 - cy[0];

                                dx = ((float) (cx[1] * cy[0] * priv->diff[5][0]) +
                                      (float)(cx[0] * cy[0] * priv->diff[4][0]) +
                                      (float)(cx[1] * cy[1] * priv->diff[8][0]) +
                                      (float)(cx[0] * cy[1] * priv->diff[7][0]));
                                
                                dy = ((float) (cx[1] * cy[0] * priv->diff[5][1]) +
                                      (float)(cx[0] * cy[0] * priv->diff[4][1]) +
                                      (float)(cx[1] * cy[1] * priv->diff[8][1]) +
                                      (float)(cx[0] * cy[1] * priv->diff[7][1]));
                        }
                }


#ifdef EVDBG
                for (i=0; i<3; i++) 
                        xf86ErrorFVerb(2, "cx[%d]=%f   cy[%d]=%f\n", i, cx[i]
                                       ,i, cy[i]);
                
                DBGOUT(2, "EVTouch: ViewPort_X0=%d   ViewPort_Y0=%d\n", 
                    *(priv->pViewPort_X0), 
                    *(priv->pViewPort_Y0));
                DBGOUT(2, "EVTouch: dx=%f   dy=%f\n", dx, dy);
#endif

                xc = ( ((float)xc / max_x) * screen_width ) + dx;
                yc = ( ((float)yc / max_y) * screen_height) + dy;

                if (priv->swap_y == TRUE)
                        yc = screen_height - yc;

                /* ususally we DON'T swap x -- but if swap_x is 1 
                   => go on and swap */
                if (priv->swap_x == TRUE)
                        xc = screen_width - xc;

                int tmp = 0;
                /* rotation mixes x and y up a bit */
                if (priv->rotate == EV_ROTATE_CW) {
                        tmp = xc;
                        xc = yc;
                        yc = screen_width - tmp;
                } else if (priv->rotate == EV_ROTATE_CCW) {
                        tmp = xc;
                        xc = screen_height - yc;
                        yc = tmp;
                }

		switch (rotation) {
			case RR_Rotate_0:
                                v0 = xc;
                                v1 = yc;
				break;
			case RR_Rotate_180:
				v0 = screen_width - xc;
				v1 = screen_height - yc;
				break;
			case RR_Rotate_90:
                                tmp = xc;
				v0  = screen_height - yc;
                                v1  = tmp;
				break;
			case RR_Rotate_270:
                                tmp = xc;
				v0 = yc;
				v1 = screen_width - tmp;
				break;
			default:
				break;
		}
        }

        DBGOUT(2, "EVTouch: FINAL: v0=%d   v1=%d\n", v0, v1);

        *x = v0;
        *y = v1;

        return (TRUE);
}




static InputInfoPtr
EVTouchPreInit(InputDriverPtr drv, IDevPtr dev, int flags)
{
        /* LocalDevicePtr local; */
        InputInfoPtr local;
        EVTouchPrivatePtr priv;
        ScrnInfoPtr   pScrn;

        int i = 0;
        char *s;
        char tmp_str[8];
        int timeo = 0;

        DBGOUT(2, "EVTouch: %s\n", __FUNCTION__);

        priv = xcalloc (1, sizeof (EVTouchPrivateRec));
        if (!priv)
                return NULL;

        local = xf86AllocateInput(drv, 0);
        if (!local) {
                xfree(priv);
                return NULL;
        }

        local->name = dev->identifier;
        local->type_name = XI_TOUCHSCREEN;
        local->device_control = DeviceControl;
        local->read_input = ReadInput;
        local->control_proc = ControlProc;
        local->close_proc = CloseProc;
        local->switch_mode = SwitchMode;
        local->conversion_proc = ConvertProc;
        local->reverse_conversion_proc = NULL;
        local->fd = -1;
        local->dev = NULL;
        local->private = priv;
        priv->local = local;
        local->private_flags = 0;
        local->flags = XI86_POINTER_CAPABLE | XI86_SEND_DRAG_EVENTS;
        local->conf_idev = dev;

        xf86CollectInputOptions(local, default_options, NULL);

        xf86OptionListReport(local->options);

	priv->libtouch = xcalloc(1, sizeof(LibTouchRec));
	libtouchInit(priv->libtouch, local);
        priv->screen_num    = xf86SetIntOption(local->options, "ScreenNumber", 0 );

        priv->calibrate = xf86SetIntOption(local->options, "Calibrate", 0);

        pScrn = xf86Screens[priv->screen_num];
        priv->phys_width = pScrn->currentMode->HDisplay;  /* physical screen resolution */
        priv->phys_height = pScrn->currentMode->VDisplay;

        priv->min_x = xf86SetIntOption(local->options, "MinX", 0 );
        priv->max_x = xf86SetIntOption(local->options, "MaxX", priv->phys_width );
        priv->min_y = xf86SetIntOption(local->options, "MinY", 0 );
        priv->max_y = xf86SetIntOption(local->options, "MaxY", priv->phys_height );

        priv->button_number = xf86SetIntOption(local->options, "ButtonNumber", 2 );

        priv->emulate3      = xf86SetBoolOption(local->options, "Emulate3Buttons", TRUE);
        priv->emulate3_timeout = xf86SetIntOption(local->options, 
                                                  "Emulate3Timeout", 50);

        debug_level = xf86SetIntOption(local->options, "DebugLevel", 0);
        libtouchSetDebugLevel(debug_level);

        timeo = xf86SetIntOption(local->options, "TapTimer", 90);
        libtouchSetTapTimeo(priv->libtouch, timeo);

        timeo = xf86SetIntOption(local->options, "LongtouchTimer", 160);
        libtouchSetLongtouchTimeo(priv->libtouch, timeo);

        libtouchSetMoveLimit(priv->libtouch, 
                             xf86SetIntOption( local->options, 
                                               "MoveLimit", 180 ));

        priv->rotate     = EV_ROTATE_NONE;
        s = xf86FindOptionValue(local->options, "Rotate");
        if (s) {
                if (xf86NameCmp(s, "CW") == 0) {
                        priv->rotate = EV_ROTATE_CW;                           
                } else if (xf86NameCmp(s, "CCW") == 0 ) {
                        priv->rotate = EV_ROTATE_CCW;
                } 
        }

        if (priv->rotate == EV_ROTATE_NONE) {
                priv->max_rel_x = priv->max_x;
                priv->max_rel_y = priv->max_y;
                priv->min_rel_x = priv->min_x;
                priv->min_rel_y = priv->min_y;             
        } else {
                priv->max_rel_x = priv->max_y;
                priv->max_rel_y = priv->max_x;
                priv->min_rel_x = priv->min_y;
                priv->min_rel_y = priv->min_x;             
        }

        priv->swap_y = xf86SetBoolOption(local->options, "SwapY", FALSE);
        priv->swap_x = xf86SetBoolOption(local->options, "SwapX", FALSE);

        /* 
           get calibration parameters from XF86Config 
        */
        for (i = 0; i < 9; i++){
                sprintf(tmp_str, "x%d", i);
                priv->diff[i][0] = xf86SetIntOption( local->options,
                                                     tmp_str, 0 );
                sprintf(tmp_str, "y%d", i);
                priv->diff[i][1] = xf86SetIntOption( local->options,
                                                     tmp_str, 0 );
                DBGOUT(2, "(diff[%d][0]/diff[%d][1])=(%d/%d)\n", i, i, 
                    priv->diff[i][0], priv->diff[i][1]);
        }
        
        xf86AlwaysCore(local, TRUE);
        priv->touch_flags = 0;

        local->history_size = xf86SetIntOption( local->options, "HistorySize", 0 );

        /* prepare to process touch packets */
        EVTouchNewPacket (priv);

        /* 
           if started in calibration-mode:
           - open named pipe 
        */
        if (priv->calibrate) {
                priv->fifo = open("/tmp/ev_calibrate", O_RDWR, 0);
                if (priv->fifo < 0)
                        xf86ErrorFVerb(2, "open FIFO FAILED\n");
        }

        /* this results in an xstrdup that must be freed later */
        local->name = xf86SetStrOption( local->options, "DeviceName", "EVTouch TouchScreen" );
        xf86ProcessCommonOptions(local, local->options);
        local->flags |= XI86_CONFIGURED;

        local->fd = -1;
        return (local);
}

static void
EVTouchPtrCtrl(DeviceIntPtr device, PtrCtrl *ctrl)
{
  /* I have no clue what this does, except that registering it stops the 
     X server segfaulting in ProcGetPointerMapping()
     Ho Hum.
  */
}
