/*
 * Copyright 1995-1999 by Frederic Lepied, France. <Lepied@XFree86.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.
 *
 */
/*
 * Copyright (c) 2000-2002 by The XFree86 Project, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * Except as contained in this notice, the name of the copyright holder(s)
 * and author(s) shall not be used in advertising or otherwise to promote
 * the sale, use or other dealings in this Software without prior written
 * authorization from the copyright holder(s) and author(s).
 */

#ifdef HAVE_XORG_CONFIG_H
#include <xorg-config.h>
#endif

#include <X11/Xfuncproto.h>
#include <X11/Xmd.h>
#include <X11/extensions/XI.h>
#include <X11/extensions/XIproto.h>
#include "xf86.h"
#include "xf86Priv.h"
#include "xf86Xinput.h"
#include "XIstubs.h"
#include "xf86Optrec.h"
#include "mipointer.h"
#include "xf86InPriv.h"

#ifdef DPMSExtension
#define DPMS_SERVER
#include <X11/extensions/dpms.h>
#include "dpmsproc.h"
#endif

#include "exevents.h"	/* AddInputDevice */
#include "exglobals.h"

#define EXTENSION_PROC_ARGS void *
#include "extnsionst.h"

#include "windowstr.h"	/* screenIsSaved */

#include <stdarg.h>

#include <X11/Xpoll.h>

#include "mi.h"

#include <ptrveloc.h>          /* dix pointer acceleration */

#ifdef XFreeXDGA
#include "dgaproc.h"
#endif

EventListPtr xf86Events = NULL;

/**
 * Eval config and modify DeviceVelocityRec accordingly
 */
static void
ProcessVelocityConfiguration(char* devname, pointer list, DeviceVelocityPtr s){
    int tempi, i;
    float tempf, tempf2;

    if(!s)
        return;

    tempf = xf86SetRealOption(list, "FilterHalflife", -1);
    if(tempf > 0)
        tempf = 1.0 / tempf;   /* set reciprocal if possible */

    tempf2 = xf86SetRealOption(list, "FilterChainProgression", 2.0);
    xf86Msg(X_CONFIG, "%s: (accel) filter chain progression: %.2f\n",
            devname, tempf2);
    if(tempf2 < 1)
        tempf2 = 2;

    tempi = xf86SetIntOption(list, "FilterChainLength", 1);
    if(tempi < 1 || tempi > MAX_VELOCITY_FILTERS)
	tempi = 1;

    if(tempf > 0.0f && tempi >= 1 && tempf2 >= 1.0f)
	InitFilterChain(s, tempf, tempf2, tempi, 40);

    for(i = 0; i < tempi; i++)
	xf86Msg(X_CONFIG, "%s: (accel) filter stage %i: %.2f ms\n",
                devname, i, 1.0f / (s->filters[i].rdecay));

    tempf = xf86SetIntOption(list, "ConstantDeceleration", 1);
    if(tempf > 1.0){
        xf86Msg(X_CONFIG, "%s: (accel) constant deceleration by %.1f\n",
                devname, tempf);
        s->const_acceleration = 1.0 / tempf;   /* set reciprocal deceleration
                                                  alias acceleration */
    }

    tempf = xf86SetIntOption(list, "AdaptiveDeceleration", 1);
    if(tempf > 1.0){
        xf86Msg(X_CONFIG, "%s: (accel) adaptive deceleration by %.1f\n",
                devname, tempf);
        s->min_acceleration = 1.0 / tempf;   /* set minimum acceleration */
    }

    tempf = xf86SetRealOption(list, "VelocityCoupling", -1);
    if(tempf >= 0){
	xf86Msg(X_CONFIG, "%s: (accel) velocity coupling is %.1f%%\n", devname,
                tempf*100.0);
	s->coupling = tempf;
    }

    /*  Configure softening. If const deceleration is used, this is expected
     *  to provide better subpixel information so we enable
     *  softening by default only if ConstantDeceleration is not used
     */
    s->use_softening = xf86SetBoolOption(list, "Softening",
                                         s->const_acceleration == 1.0);

    s->average_accel = xf86SetBoolOption(list, "AccelerationProfileAveraging",
                                         s->average_accel);

    s->reset_time = xf86SetIntOption(list, "VelocityReset", s->reset_time);

    tempf = xf86SetRealOption(list, "ExpectedRate", 0);
    if(tempf > 0){
        s->corr_mul = 1000.0 / tempf;
    }else{
        s->corr_mul = xf86SetRealOption(list, "VelocityScale", s->corr_mul);
    }

    /* select profile by number */
    tempi= xf86SetIntOption(list, "AccelerationProfile",
                            s->statistics.profile_number);

    if(SetAccelerationProfile(s, tempi)){
        xf86Msg(X_CONFIG, "%s: (accel) set acceleration profile %i\n", devname, tempi);
    }else{
        xf86Msg(X_CONFIG, "%s: (accel) acceleration profile %i is unknown\n",
                devname, tempi);
    }
}

static void
ApplyAccelerationSettings(DeviceIntPtr dev){
    int scheme;
    DeviceVelocityPtr pVel;
    LocalDevicePtr local = (LocalDevicePtr)dev->public.devicePrivate;
    char* schemeStr;

    if(dev->valuator){
	schemeStr = xf86SetStrOption(local->options, "AccelerationScheme", "");

	scheme = dev->valuator->accelScheme.number;

	if(!xf86NameCmp(schemeStr, "predictable"))
	    scheme = PtrAccelPredictable;

	if(!xf86NameCmp(schemeStr, "lightweight"))
	    scheme = PtrAccelLightweight;

	if(!xf86NameCmp(schemeStr, "none"))
	    scheme = PtrAccelNoOp;

        /* reinit scheme if needed */
        if(dev->valuator->accelScheme.number != scheme){
            if(dev->valuator->accelScheme.AccelCleanupProc){
                dev->valuator->accelScheme.AccelCleanupProc(dev);
            }

            if(InitPointerAccelerationScheme(dev, scheme)){
		xf86Msg(X_CONFIG, "%s: (accel) selected scheme %s/%i\n",
		        local->name, schemeStr, scheme);
	    }else{
        	xf86Msg(X_CONFIG, "%s: (accel) could not init scheme %s\n",
        	        local->name, schemeStr);
        	scheme = dev->valuator->accelScheme.number;
            }
        }else{
            xf86Msg(X_CONFIG, "%s: (accel) keeping acceleration scheme %i\n",
                    local->name, scheme);
        }

        xfree(schemeStr);

        /* process special configuration */
        switch(scheme){
            case PtrAccelPredictable:
                pVel = GetDevicePredictableAccelData(dev);
                ProcessVelocityConfiguration (local->name, local->options,
                                              pVel);
                break;
        }
    }
}

static Bool
xf86SendDragEvents(DeviceIntPtr	device)
{
    LocalDevicePtr local = (LocalDevicePtr) device->public.devicePrivate;
    
    if (device->button && device->button->buttonsDown > 0)
        return (local->flags & XI86_SEND_DRAG_EVENTS);
    else
        return (TRUE);
}

/***********************************************************************
 *
 * xf86ProcessCommonOptions --
 * 
 *	Process global options.
 *
 ***********************************************************************
 */
_X_EXPORT void
xf86ProcessCommonOptions(LocalDevicePtr local,
                         pointer	list)
{
    if (!xf86SetBoolOption(list, "AlwaysCore", 1) ||
        !xf86SetBoolOption(list, "SendCoreEvents", 1) ||
        !xf86SetBoolOption(list, "CorePointer", 1) ||
        !xf86SetBoolOption(list, "CoreKeyboard", 1)) {
        xf86Msg(X_CONFIG, "%s: doesn't report core events\n", local->name);
    } else {
        local->flags |= XI86_ALWAYS_CORE;
        xf86Msg(X_CONFIG, "%s: always reports core events\n", local->name);
    }

    if (xf86SetBoolOption(list, "SendDragEvents", 1)) {
        local->flags |= XI86_SEND_DRAG_EVENTS;
    } else {
        xf86Msg(X_CONFIG, "%s: doesn't report drag events\n", local->name);
    }

    /* Backwards compatibility. */
    local->history_size = GetMotionHistorySize();
    /* Preallocate xEvent store */
    if (!xf86Events)
        GetEventList(&xf86Events);
    if (!xf86Events)
        FatalError("Couldn't allocate event store\n");
}

/***********************************************************************
 *
 * xf86ActivateDevice --
 * 
 *	Initialize an input device.
 *
 ***********************************************************************
 */
_X_EXPORT void
xf86ActivateDevice(LocalDevicePtr local)
{
    DeviceIntPtr	dev;

    if (local->flags & XI86_CONFIGURED) {
        dev = AddInputDevice(serverClient, local->device_control, TRUE);

        if (dev == NULL)
            FatalError("Too many input devices");
        
        local->atom = MakeAtom(local->type_name,
                               strlen(local->type_name),
                               TRUE);
        AssignTypeAndName(dev, local->atom, local->name);
        dev->public.devicePrivate = (pointer) local;
        local->dev = dev;      
        
        dev->coreEvents = local->flags & XI86_ALWAYS_CORE; 
        dev->isMaster = FALSE;
        dev->spriteInfo->spriteOwner = FALSE;

        if (DeviceIsPointerType(dev))
        {
            dev->deviceGrab.ActivateGrab = ActivatePointerGrab;
            dev->deviceGrab.DeactivateGrab = DeactivatePointerGrab;
        } else 
        {
            dev->deviceGrab.ActivateGrab = ActivateKeyboardGrab;
            dev->deviceGrab.DeactivateGrab = DeactivateKeyboardGrab;
        }

        RegisterOtherDevice(dev);
#ifdef XKB
        if (!noXkbExtension)
            XkbSetExtension(dev, ProcessKeyboardEvent);
#endif

        if (serverGeneration == 1) 
            xf86Msg(X_INFO, "XINPUT: Adding extended input device \"%s\" (type: %s)\n",
                    local->name, local->type_name);
    }
}


/***********************************************************************
 *
 * Caller:	ProcXOpenDevice
 *
 * This is the implementation-dependent routine to open an input device.
 * Some implementations open all input devices when the server is first
 * initialized, and never close them.  Other implementations open only
 * the X pointer and keyboard devices during server initialization,
 * and only open other input devices when some client makes an
 * XOpenDevice request.  This entry point is for the latter type of 
 * implementation.
 *
 * If the physical device is not already open, do it here.  In this case,
 * you need to keep track of the fact that one or more clients has the
 * device open, and physically close it when the last client that has
 * it open does an XCloseDevice.
 *
 * The default implementation is to do nothing (assume all input devices
 * are opened during X server initialization and kept open).
 *
 ***********************************************************************
 */

void
OpenInputDevice(DeviceIntPtr	dev,
                ClientPtr	client,
                int		*status)
{
    if (!dev->inited)
        ActivateDevice(dev);

    *status = Success;
}

void
CloseInputDevice(DeviceIntPtr dev,
                 ClientPtr client)
{
}

/****************************************************************************
 *
 * Caller:	ProcXSetDeviceMode
 *
 * Change the mode of an extension device.
 * This function is used to change the mode of a device from reporting
 * relative motion to reporting absolute positional information, and
 * vice versa.
 * The default implementation below is that no such devices are supported.
 *
 ***********************************************************************
 */

int
SetDeviceMode (ClientPtr client, DeviceIntPtr dev, int mode)
{
  LocalDevicePtr        local = (LocalDevicePtr)dev->public.devicePrivate;

  if (local->switch_mode) {
    return (*local->switch_mode)(client, dev, mode);
  }
  else
    return BadMatch;
}


/***********************************************************************
 *
 * Caller:	ProcXSetDeviceValuators
 *
 * Set the value of valuators on an extension input device.
 * This function is used to set the initial value of valuators on
 * those input devices that are capable of reporting either relative
 * motion or an absolute position, and allow an initial position to be set.
 * The default implementation below is that no such devices are supported.
 *
 ***********************************************************************
 */

int
SetDeviceValuators (ClientPtr client, DeviceIntPtr dev, int *valuators,
                    int first_valuator, int num_valuators)
{
    LocalDevicePtr local = (LocalDevicePtr) dev->public.devicePrivate;

    if (local->set_device_valuators)
	return (*local->set_device_valuators)(local, valuators, first_valuator,
					      num_valuators);

    return BadMatch;
}


/***********************************************************************
 *
 * Caller:	ProcXChangeDeviceControl
 *
 * Change the specified device controls on an extension input device.
 *
 ***********************************************************************
 */

int
ChangeDeviceControl (ClientPtr client, DeviceIntPtr dev, xDeviceCtl *control)
{
  LocalDevicePtr        local = (LocalDevicePtr)dev->public.devicePrivate;

  if (!local->control_proc) {
      switch (control->control) {
      case DEVICE_CORE:
          return BadMatch;
      case DEVICE_RESOLUTION:
      case DEVICE_ABS_CALIB:
      case DEVICE_ABS_AREA:
      case DEVICE_ENABLE:
        return Success;
      default:
        return BadMatch;
      }
  }
  else {
      return (*local->control_proc)(local, control);
  }
}

void
AddOtherInputDevices()
{
}

int
NewInputDeviceRequest (InputOption *options, DeviceIntPtr *pdev)
{
    IDevRec *idev = NULL;
    InputDriverPtr drv = NULL;
    InputInfoPtr pInfo = NULL;
    InputOption *option = NULL;
    DeviceIntPtr dev = NULL;
    int rval = Success;
    int is_auto = 0;

    idev = xcalloc(sizeof(*idev), 1);
    if (!idev)
        return BadAlloc;

    for (option = options; option; option = option->next) {
        if (strcasecmp(option->key, "driver") == 0) {
            if (idev->driver) {
                rval = BadRequest;
                goto unwind;
            }
            /* Memory leak for every attached device if we don't
             * test if the module is already loaded first */
            drv = xf86LookupInputDriver(option->value);
            if (!drv)
                if (xf86LoadOneModule(option->value, NULL))
                    drv = xf86LookupInputDriver(option->value);
            if (!drv) {
                xf86Msg(X_ERROR, "No input driver matching `%s'\n",
                        option->value);
                rval = BadName;
                goto unwind;
            }
            idev->driver = xstrdup(option->value);
            if (!idev->driver) {
                rval = BadAlloc;
                goto unwind;
            }
        }

        if (strcasecmp(option->key, "name") == 0 ||
            strcasecmp(option->key, "identifier") == 0) {
            if (idev->identifier) {
                rval = BadRequest;
                goto unwind;
            }
            idev->identifier = xstrdup(option->value);
            if (!idev->identifier) {
                rval = BadAlloc;
                goto unwind;
            }
        }

        /* Right now, the only automatic config we know of is HAL. */
        if (strcmp(option->key, "_source") == 0 &&
            strcmp(option->value, "server/hal") == 0) {
            if (!xf86Info.autoAddDevices) {
                rval = BadMatch;
                goto unwind;
            }

            is_auto = 1;
        }
    }
    if (!idev->driver || !idev->identifier) {
        xf86Msg(X_ERROR, "No input driver/identifier specified (ignoring)\n");
        rval = BadRequest;
        goto unwind;
    }

    if (!drv) {
        xf86Msg(X_ERROR, "No input driver specified (ignoring)\n");
        return BadMatch;
    }

    if (!idev->identifier) {
        xf86Msg(X_ERROR, "No device identifier specified (ignoring)\n");
        return BadMatch;
    }

    if (!drv->PreInit) {
        xf86Msg(X_ERROR,
                "Input driver `%s' has no PreInit function (ignoring)\n",
                drv->driverName);
        rval = BadImplementation;
        goto unwind;
    }

    for (option = options; option; option = option->next) {
        /* Steal option key/value strings from the provided list.
         * We need those strings, the InputOption list doesn't. */
        idev->commonOptions = xf86addNewOption(idev->commonOptions,
                                               option->key, option->value);
        option->key = NULL;
        option->value = NULL;
    }

    pInfo = drv->PreInit(drv, idev, 0);

    if (!pInfo) {
        xf86Msg(X_ERROR, "PreInit returned NULL for \"%s\"\n", idev->identifier);
        rval = BadMatch;
        goto unwind;
    }
    else if (!(pInfo->flags & XI86_CONFIGURED)) {
        xf86Msg(X_ERROR, "PreInit failed for input device \"%s\"\n",
                idev->identifier);
        rval = BadMatch;
        goto unwind;
    }

    xf86ActivateDevice(pInfo);

    dev = pInfo->dev;
    ActivateDevice(dev);
    /* Enable it if it's properly initialised, we're currently in the VT, and
     * either it's a manual request, or we're automatically enabling devices. */
    if (dev->inited && dev->startup && xf86Screens[0]->vtSema &&
        (!is_auto || xf86Info.autoEnableDevices))
        EnableDevice(dev);

    /* send enter/leave event, update sprite window */
    CheckMotion(NULL, dev);

    *pdev = dev;
    return Success;

unwind:
    if(pInfo) {
        if(drv->UnInit)
            drv->UnInit(drv, pInfo, 0);
        else
            xf86DeleteInput(pInfo, 0);
    }
    if(idev->driver)
        xfree(idev->driver);
    if(idev->identifier)
        xfree(idev->identifier);
    xf86optionListFree(idev->commonOptions);
    xfree(idev);
    return rval;
}

void
DeleteInputDeviceRequest(DeviceIntPtr pDev)
{
    LocalDevicePtr pInfo = (LocalDevicePtr) pDev->public.devicePrivate;
    InputDriverPtr drv;
    IDevRec *idev;
    IDevPtr *it;

    if (pInfo) /* need to get these before RemoveDevice */
    {
        drv = pInfo->drv;
        idev = pInfo->conf_idev;
    }

    OsBlockSignals();
    RemoveDevice(pDev);

    if (!pDev->isMaster)
    {
        if(drv->UnInit)
            drv->UnInit(drv, pInfo, 0);
        else
            xf86DeleteInput(pInfo, 0);

        /* devices added through HAL aren't in the config layout */
        it = xf86ConfigLayout.inputs;
        while(*it && *it != idev)
            it++;

        if (!(*it)) /* end of list, not in the layout */
        {
            xfree(idev->driver);
            xfree(idev->identifier);
            xf86optionListFree(idev->commonOptions);
            xfree(idev);
        }
    }
    OsReleaseSignals();
}

/* 
 * convenient functions to post events
 */

_X_EXPORT void
xf86PostMotionEvent(DeviceIntPtr	device,
                    int			is_absolute,
                    int			first_valuator,
                    int			num_valuators,
                    ...)
{
    va_list var;
    int i = 0;
    static int valuators[MAX_VALUATORS];

    if (num_valuators > MAX_VALUATORS) {
	xf86Msg(X_ERROR, "xf86PostMotionEvent: num_valuator %d"
	    " is greater than MAX_VALUATORS\n", num_valuators);
	return;
    }

    va_start(var, num_valuators);
    for (i = 0; i < num_valuators; i++)
        valuators[i] = va_arg(var, int);
    va_end(var);

    xf86PostMotionEventP(device, is_absolute, first_valuator, num_valuators, valuators);
}

_X_EXPORT void
xf86PostMotionEventP(DeviceIntPtr	device,
                    int			is_absolute,
                    int			first_valuator,
                    int			num_valuators,
                    int			*valuators)
{
    int i = 0, nevents = 0;
    int dx = 0, dy = 0;
    Bool drag = xf86SendDragEvents(device);
    xEvent *xE = NULL;
    int index;
    int flags = 0;

    if (num_valuators > MAX_VALUATORS) {
	xf86Msg(X_ERROR, "xf86PostMotionEvent: num_valuator %d"
	    " is greater than MAX_VALUATORS\n", num_valuators);
	return;
    }

    if (is_absolute)
        flags = POINTER_ABSOLUTE;
    else
        flags = POINTER_RELATIVE | POINTER_ACCELERATE;

#if XFreeXDGA
    /* The evdev driver may not always send all axes across. */
    if (num_valuators >= 1 && first_valuator <= 1) {
        if (miPointerGetScreen(device)) {
            index = miPointerGetScreen(device)->myNum;
            if (first_valuator == 0)
            {
                dx = valuators[0];
                if (is_absolute)
                    dx -= device->last.valuators[0];
            }

            if (first_valuator == 1 || num_valuators >= 2)
            {
                dy = valuators[1 - first_valuator];
                if (is_absolute)
                    dy -= device->last.valuators[1];
            }

            if (DGAStealMotionEvent(device, index, dx, dy))
                return;
        }
    }
#endif

    GetEventList(&xf86Events);
    nevents = GetPointerEvents(xf86Events, device, MotionNotify, 0,
                               flags, first_valuator, num_valuators,
                               valuators);

    for (i = 0; i < nevents; i++) {
        xE = (xf86Events + i)->event;
        /* Don't post core motion events for devices not registered to send
         * drag events. */
        if (xE->u.u.type != MotionNotify || drag) {
            mieqEnqueue(device, (xf86Events + i)->event);
        }
    }
}

_X_EXPORT void
xf86PostProximityEvent(DeviceIntPtr	device,
                       int		is_in,
                       int		first_valuator,
                       int		num_valuators,
                       ...)
{
    va_list var;
    int i, nevents;
    int valuators[MAX_VALUATORS];


    if (num_valuators > MAX_VALUATORS) {
	xf86Msg(X_ERROR, "xf86PostMotionEvent: num_valuator %d"
	    " is greater than MAX_VALUATORS\n", num_valuators);
	return;
    }

    va_start(var, num_valuators);
    for (i = 0; i < num_valuators; i++)
        valuators[i] = va_arg(var, int);
    va_end(var);

    GetEventList(&xf86Events);
    nevents = GetProximityEvents(xf86Events, device,
                                 is_in ? ProximityIn : ProximityOut, 
                                 first_valuator, num_valuators, valuators);
    for (i = 0; i < nevents; i++)
        mieqEnqueue(device, (xf86Events + i)->event);

}

_X_EXPORT void
xf86PostButtonEvent(DeviceIntPtr	device,
                    int			is_absolute,
                    int			button,
                    int			is_down,
                    int			first_valuator,
                    int			num_valuators,
                    ...)
{
    va_list var;
    int valuators[MAX_VALUATORS];
    int i = 0, nevents = 0;
    int index;

#if XFreeXDGA
    if (miPointerGetScreen(device)) {
        index = miPointerGetScreen(device)->myNum;
        if (DGAStealButtonEvent(device, index, button, is_down))
            return;
    }
#endif
    if (num_valuators > MAX_VALUATORS) {
	xf86Msg(X_ERROR, "xf86PostMotionEvent: num_valuator %d"
	    " is greater than MAX_VALUATORS\n", num_valuators);
	return;
    }

    va_start(var, num_valuators);
    for (i = 0; i < num_valuators; i++)
        valuators[i] = va_arg(var, int);
    va_end(var);

    GetEventList(&xf86Events);
    nevents = GetPointerEvents(xf86Events, device,
                               is_down ? ButtonPress : ButtonRelease, button,
                               (is_absolute) ? POINTER_ABSOLUTE : POINTER_RELATIVE,
                               first_valuator, num_valuators, valuators);

    for (i = 0; i < nevents; i++)
        mieqEnqueue(device, (xf86Events + i)->event);

}

_X_EXPORT void
xf86PostKeyEvent(DeviceIntPtr	device,
                 unsigned int	key_code,
                 int		is_down,
                 int		is_absolute,
                 int		first_valuator,
                 int		num_valuators,
                 ...)
{
    va_list var;
    int i = 0, nevents = 0;
    static int valuators[MAX_VALUATORS];

    /* instil confidence in the user */
    DebugF("this function has never been tested properly.  if things go quite "
           "badly south after this message, then xf86PostKeyEvent is "
           "broken.\n");

    if (num_valuators > MAX_VALUATORS) {
	xf86Msg(X_ERROR, "xf86PostMotionEvent: num_valuator %d"
	    " is greater than MAX_VALUATORS\n", num_valuators);
	return;
    }

    if (is_absolute) {
        va_start(var, num_valuators);
        for (i = 0; i < num_valuators; i++)
            valuators[i] = va_arg(var, int);
        va_end(var);

        GetEventList(&xf86Events);
        nevents = GetKeyboardValuatorEvents(xf86Events, device,
                                            is_down ? KeyPress : KeyRelease,
                                            key_code, first_valuator,
                                            num_valuators, valuators);
    }
    else {
        nevents = GetKeyboardEvents(xf86Events, device,
                                    is_down ? KeyPress : KeyRelease,
                                    key_code);
    }

    for (i = 0; i < nevents; i++)
        mieqEnqueue(device, (xf86Events + i)->event);
}

_X_EXPORT void
xf86PostKeyboardEvent(DeviceIntPtr      device,
                      unsigned int      key_code,
                      int               is_down)
{
    int nevents = 0, i = 0;
    int index;

#if XFreeXDGA
    DeviceIntPtr pointer;

    /* Some pointers send key events, paired device is wrong then. */
    pointer = IsPointerDevice(device) ? device : GetPairedDevice(device);

    if (miPointerGetScreen(pointer)) {
        index = miPointerGetScreen(pointer)->myNum;
        if (DGAStealKeyEvent(device, index, key_code, is_down))
            return;
    }
#endif

    GetEventList(&xf86Events);
    nevents = GetKeyboardEvents(xf86Events, device,
                                is_down ? KeyPress : KeyRelease, key_code);

    for (i = 0; i < nevents; i++)
        mieqEnqueue(device, (xf86Events + i)->event);
}

_X_EXPORT LocalDevicePtr
xf86FirstLocalDevice()
{
    return xf86InputDevs;
}

/* 
 * Cx     - raw data from touch screen
 * Sxhigh - scaled highest dimension
 *          (remember, this is of rows - 1 because of 0 origin)
 * Sxlow  - scaled lowest dimension
 * Rxhigh - highest raw value from touch screen calibration
 * Rxlow  - lowest raw value from touch screen calibration
 *
 * This function is the same for X or Y coordinates.
 * You may have to reverse the high and low values to compensate for
 * different orgins on the touch screen vs X.
 */

_X_EXPORT int
xf86ScaleAxis(int	Cx,
              int	Sxhigh,
              int	Sxlow,
              int	Rxhigh,
              int	Rxlow )
{
    int X;
    int dSx = Sxhigh - Sxlow;
    int dRx = Rxhigh - Rxlow;

    dSx = Sxhigh - Sxlow;
    if (dRx) {
	X = ((dSx * (Cx - Rxlow)) / dRx) + Sxlow;
    }
    else {
	X = 0;
	ErrorF ("Divide by Zero in xf86ScaleAxis");
    }
    
    if (X > Sxhigh)
	X = Sxlow;
    if (X < Sxlow)
	X = Sxhigh;
    
    return (X);
}

/*
 * This function checks the given screen against the current screen and
 * makes changes if appropriate. It should be called from an XInput driver's
 * ReadInput function before any events are posted, if the device is screen
 * specific like a touch screen.
 */
_X_EXPORT void
xf86XInputSetScreen(LocalDevicePtr	local,
		    int			screen_number,
		    int			x,
		    int			y)
{
    if (miPointerGetScreen(local->dev) !=
          screenInfo.screens[screen_number]) {
	miPointerSetScreen(local->dev, screen_number, x, y);
    }
}


_X_EXPORT void
xf86InitValuatorAxisStruct(DeviceIntPtr dev, int axnum, int minval, int maxval,
			   int resolution, int min_res, int max_res)
{
    if (!dev || !dev->valuator)
        return;

    InitValuatorAxisStruct(dev, axnum, minval, maxval, resolution, min_res,
			   max_res);
}

/*
 * Set the valuator values to be in synch with dix/event.c
 * DefineInitialRootWindow().
 */
_X_EXPORT void
xf86InitValuatorDefaults(DeviceIntPtr dev, int axnum)
{
    if (axnum == 0) {
	dev->valuator->axisVal[0] = screenInfo.screens[0]->width / 2;
        dev->last.valuators[0] = dev->valuator->axisVal[0];
    }
    else if (axnum == 1) {
	dev->valuator->axisVal[1] = screenInfo.screens[0]->height / 2;
        dev->last.valuators[1] = dev->valuator->axisVal[1];
    }

    if(axnum == 0)  /* to prevent double invocation */
	ApplyAccelerationSettings(dev);
}


/**
 * Deactivate a device. Call this function from the driver if you receive a
 * read error or something else that spoils your day.
 * Device will be moved to the off_devices list, but it will still be there
 * until you really clean up after it.
 * Notifies the client about an inactive device.
 * 
 * @param panic True if device is unrecoverable and needs to be removed.
 */
_X_EXPORT void
xf86DisableDevice(DeviceIntPtr dev, Bool panic)
{
    devicePresenceNotify ev;
    DeviceIntRec dummyDev;

    if(!panic)
    {
        DisableDevice(dev);
    } else
    {
        ev.type = DevicePresenceNotify;
        ev.time = currentTime.milliseconds;
        ev.devchange = DeviceUnrecoverable;
        ev.deviceid = dev->id;
        dummyDev.id = 0;
        SendEventToAllWindows(&dummyDev, DevicePresenceNotifyMask,
                (xEvent *) &ev, 1);

        DeleteInputDeviceRequest(dev);
    }
}

/**
 * Reactivate a device. Call this function from the driver if you just found
 * out that the read error wasn't quite that bad after all.
 * Device will be re-activated, and an event sent to the client. 
 */
_X_EXPORT void
xf86EnableDevice(DeviceIntPtr dev)
{
    EnableDevice(dev);
}

/* end of xf86Xinput.c */
