/*
 * Copyright © 2006 Nokia Corporation
 * Copyright © 2006-2007 Daniel Stone
 * Copyright © 2008 Red Hat, Inc.
 * Copyright © 2011 The Chromium Authors
 *
 * 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 (including the next
 * paragraph) 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 * Authors: Daniel Stone <daniel@fooishbar.org>
 *          Peter Hutterer <peter.hutterer@who-t.net>
 */

#ifdef HAVE_DIX_CONFIG_H
#include <dix-config.h>
#endif

#include <X11/X.h>
#include <X11/keysym.h>
#include <X11/Xproto.h>
#include <math.h>
#include <limits.h>

#include "misc.h"
#include "resource.h"
#include "inputstr.h"
#include "scrnintstr.h"
#include "cursorstr.h"
#include "dixstruct.h"
#include "globals.h"
#include "dixevents.h"
#include "mipointer.h"
#include "eventstr.h"
#include "eventconvert.h"
#include "inpututils.h"
#include "mi.h"
#include "windowstr.h"

#include <X11/extensions/XKBproto.h>
#include "xkbsrv.h"

#ifdef PANORAMIX
#include "panoramiX.h"
#include "panoramiXsrv.h"
#endif

#include <X11/extensions/XI.h>
#include <X11/extensions/XI2.h>
#include <X11/extensions/XIproto.h>
#include <pixman.h>
#include "exglobals.h"
#include "exevents.h"
#include "extnsionst.h"
#include "listdev.h"            /* for sizing up DeviceClassesChangedEvent */
#include "probes.h"

/* Number of motion history events to store. */
#define MOTION_HISTORY_SIZE 256

/**
 * InputEventList is the storage for input events generated by
 * QueuePointerEvents, QueueKeyboardEvents, and QueueProximityEvents.
 * This list is allocated on startup by the DIX.
 */
InternalEvent *InputEventList = NULL;

/**
 * Pick some arbitrary size for Xi motion history.
 */
int
GetMotionHistorySize(void)
{
    return MOTION_HISTORY_SIZE;
}

void
set_button_down(DeviceIntPtr pDev, int button, int type)
{
    if (type == BUTTON_PROCESSED)
        SetBit(pDev->button->down, button);
    else
        SetBit(pDev->button->postdown, button);
}

void
set_button_up(DeviceIntPtr pDev, int button, int type)
{
    if (type == BUTTON_PROCESSED)
        ClearBit(pDev->button->down, button);
    else
        ClearBit(pDev->button->postdown, button);
}

Bool
button_is_down(DeviceIntPtr pDev, int button, int type)
{
    Bool ret = FALSE;

    if (type & BUTTON_PROCESSED)
        ret = ret || BitIsOn(pDev->button->down, button);
    if (type & BUTTON_POSTED)
        ret = ret || BitIsOn(pDev->button->postdown, button);

    return ret;
}

void
set_key_down(DeviceIntPtr pDev, int key_code, int type)
{
    if (type == KEY_PROCESSED)
        SetBit(pDev->key->down, key_code);
    else
        SetBit(pDev->key->postdown, key_code);
}

void
set_key_up(DeviceIntPtr pDev, int key_code, int type)
{
    if (type == KEY_PROCESSED)
        ClearBit(pDev->key->down, key_code);
    else
        ClearBit(pDev->key->postdown, key_code);
}

Bool
key_is_down(DeviceIntPtr pDev, int key_code, int type)
{
    Bool ret = FALSE;

    if (type & KEY_PROCESSED)
        ret = ret || BitIsOn(pDev->key->down, key_code);
    if (type & KEY_POSTED)
        ret = ret || BitIsOn(pDev->key->postdown, key_code);

    return ret;
}

static Bool
key_autorepeats(DeviceIntPtr pDev, int key_code)
{
    return ! !(pDev->kbdfeed->ctrl.autoRepeats[key_code >> 3] &
               (1 << (key_code & 7)));
}

static void
init_touch_ownership(DeviceIntPtr dev, TouchOwnershipEvent *event, Time ms)
{
    memset(event, 0, sizeof(TouchOwnershipEvent));
    event->header = ET_Internal;
    event->type = ET_TouchOwnership;
    event->length = sizeof(TouchOwnershipEvent);
    event->time = ms;
    event->deviceid = dev->id;
}

static void
init_raw(DeviceIntPtr dev, RawDeviceEvent *event, Time ms, int type, int detail)
{
    memset(event, 0, sizeof(RawDeviceEvent));
    event->header = ET_Internal;
    event->length = sizeof(RawDeviceEvent);
    switch (type) {
    case MotionNotify:
        event->type = ET_RawMotion;
        break;
    case ButtonPress:
        event->type = ET_RawButtonPress;
        break;
    case ButtonRelease:
        event->type = ET_RawButtonRelease;
        break;
    case KeyPress:
        event->type = ET_RawKeyPress;
        break;
    case KeyRelease:
        event->type = ET_RawKeyRelease;
        break;
    case XI_TouchBegin:
        event->type = ET_RawTouchBegin;
        break;
    case XI_TouchUpdate:
        event->type = ET_RawTouchUpdate;
        break;
    case XI_TouchEnd:
        event->type = ET_RawTouchEnd;
        break;
    }
    event->time = ms;
    event->deviceid = dev->id;
    event->sourceid = dev->id;
    event->detail.button = detail;
}

static void
set_raw_valuators(RawDeviceEvent *event, ValuatorMask *mask,
                  BOOL use_unaccel, double *data)
{
    int i;

    use_unaccel = use_unaccel && valuator_mask_has_unaccelerated(mask);

    for (i = 0; i < valuator_mask_size(mask); i++) {
        if (valuator_mask_isset(mask, i)) {
            double v;

            SetBit(event->valuators.mask, i);

            if (use_unaccel)
                v = valuator_mask_get_unaccelerated(mask, i);
            else
                v = valuator_mask_get_double(mask, i);

            data[i] = v;
        }
    }
}

static void
set_valuators(DeviceIntPtr dev, DeviceEvent *event, ValuatorMask *mask)
{
    int i;

    /* Set the data to the previous value for unset absolute axes. The values
     * may be used when sent as part of an XI 1.x valuator event. */
    for (i = 0; i < valuator_mask_size(mask); i++) {
        if (valuator_mask_isset(mask, i)) {
            SetBit(event->valuators.mask, i);
            if (valuator_get_mode(dev, i) == Absolute)
                SetBit(event->valuators.mode, i);
            event->valuators.data[i] = valuator_mask_get_double(mask, i);
        }
        else
            event->valuators.data[i] = dev->valuator->axisVal[i];
    }
}

void
CreateClassesChangedEvent(InternalEvent *event,
                          DeviceIntPtr master, DeviceIntPtr slave, int flags)
{
    int i;
    DeviceChangedEvent *dce;
    CARD32 ms = GetTimeInMillis();

    dce = &event->changed_event;
    memset(dce, 0, sizeof(DeviceChangedEvent));
    dce->deviceid = slave->id;
    dce->masterid = master ? master->id : 0;
    dce->header = ET_Internal;
    dce->length = sizeof(DeviceChangedEvent);
    dce->type = ET_DeviceChanged;
    dce->time = ms;
    dce->flags = flags;
    dce->sourceid = slave->id;

    if (slave->button) {
        dce->buttons.num_buttons = slave->button->numButtons;
        for (i = 0; i < dce->buttons.num_buttons; i++)
            dce->buttons.names[i] = slave->button->labels[i];
    }
    if (slave->valuator) {
        dce->num_valuators = slave->valuator->numAxes;
        for (i = 0; i < dce->num_valuators; i++) {
            dce->valuators[i].min = slave->valuator->axes[i].min_value;
            dce->valuators[i].max = slave->valuator->axes[i].max_value;
            dce->valuators[i].resolution = slave->valuator->axes[i].resolution;
            dce->valuators[i].mode = slave->valuator->axes[i].mode;
            dce->valuators[i].name = slave->valuator->axes[i].label;
            dce->valuators[i].scroll = slave->valuator->axes[i].scroll;
            dce->valuators[i].value = slave->valuator->axisVal[i];
        }
    }
    if (slave->key) {
        dce->keys.min_keycode = slave->key->xkbInfo->desc->min_key_code;
        dce->keys.max_keycode = slave->key->xkbInfo->desc->max_key_code;
    }
}

/**
 * Rescale the coord between the two axis ranges.
 */
static double
rescaleValuatorAxis(double coord, AxisInfoPtr from, AxisInfoPtr to,
                    double defmin, double defmax)
{
    double fmin = defmin, fmax = defmax;
    double tmin = defmin, tmax = defmax;

    if (from && from->min_value < from->max_value) {
        fmin = from->min_value;
        fmax = from->max_value + 1;
    }
    if (to && to->min_value < to->max_value) {
        tmin = to->min_value;
        tmax = to->max_value + 1;
    }

    if (fmin == tmin && fmax == tmax)
        return coord;

    if (fmax == fmin)           /* avoid division by 0 */
        return 0.0;

    return (coord - fmin) * (tmax - tmin) / (fmax - fmin) + tmin;
}

/**
 * Update all coordinates when changing to a different SD
 * to ensure that relative reporting will work as expected
 * without loss of precision.
 *
 * pDev->last.valuators will be in absolute device coordinates after this
 * function.
 */
static void
updateSlaveDeviceCoords(DeviceIntPtr master, DeviceIntPtr pDev)
{
    /* master->last.valuators[0]/[1] is in desktop-wide coords and the actual
     * position of the pointer */
    pDev->last.valuators[0] = master->last.valuators[0];
    pDev->last.valuators[1] = master->last.valuators[1];

    if (!pDev->valuator)
        return;

    /* scale back to device coordinates */
    if (pDev->valuator->numAxes > 0) {
        pDev->last.valuators[0] = rescaleValuatorAxis(pDev->last.valuators[0],
                                                      NULL,
                                                      pDev->valuator->axes + 0,
                                                      screenInfo.x,
                                                      screenInfo.width);
    }
    if (pDev->valuator->numAxes > 1) {
        pDev->last.valuators[1] = rescaleValuatorAxis(pDev->last.valuators[1],
                                                      NULL,
                                                      pDev->valuator->axes + 1,
                                                      screenInfo.y,
                                                      screenInfo.height);
    }

    /* other axes are left as-is */
}

/**
 * Allocate the motion history buffer.
 */
void
AllocateMotionHistory(DeviceIntPtr pDev)
{
    int size;

    free(pDev->valuator->motion);

    if (pDev->valuator->numMotionEvents < 1)
        return;

    /* An MD must have a motion history size large enough to keep all
     * potential valuators, plus the respective range of the valuators.
     * 3 * INT32 for (min_val, max_val, curr_val))
     */
    if (IsMaster(pDev))
        size = sizeof(INT32) * 3 * MAX_VALUATORS;
    else {
        ValuatorClassPtr v = pDev->valuator;
        int numAxes;

        /* XI1 doesn't understand mixed mode devices */
        for (numAxes = 0; numAxes < v->numAxes; numAxes++)
            if (valuator_get_mode(pDev, numAxes) != valuator_get_mode(pDev, 0))
                break;
        size = sizeof(INT32) * numAxes;
    }

    size += sizeof(Time);

    pDev->valuator->motion = calloc(pDev->valuator->numMotionEvents, size);
    pDev->valuator->first_motion = 0;
    pDev->valuator->last_motion = 0;
    if (!pDev->valuator->motion)
        ErrorF("[dix] %s: Failed to alloc motion history (%d bytes).\n",
               pDev->name, size * pDev->valuator->numMotionEvents);
}

/**
 * Dump the motion history between start and stop into the supplied buffer.
 * Only records the event for a given screen in theory, but in practice, we
 * sort of ignore this.
 *
 * If core is set, we only generate x/y, in INT16, scaled to screen coords.
 */
int
GetMotionHistory(DeviceIntPtr pDev, xTimecoord ** buff, unsigned long start,
                 unsigned long stop, ScreenPtr pScreen, BOOL core)
{
    char *ibuff = NULL, *obuff;
    int i = 0, ret = 0;
    int j, coord;
    Time current;

    /* The size of a single motion event. */
    int size;
    AxisInfo from, *to;         /* for scaling */
    INT32 *ocbuf, *icbuf;       /* pointer to coordinates for copying */
    INT16 *corebuf;
    AxisInfo core_axis = { 0 };

    if (!pDev->valuator || !pDev->valuator->numMotionEvents)
        return 0;

    if (core && !pScreen)
        return 0;

    if (IsMaster(pDev))
        size = (sizeof(INT32) * 3 * MAX_VALUATORS) + sizeof(Time);
    else
        size = (sizeof(INT32) * pDev->valuator->numAxes) + sizeof(Time);

    *buff = malloc(size * pDev->valuator->numMotionEvents);
    if (!(*buff))
        return 0;
    obuff = (char *) *buff;

    for (i = pDev->valuator->first_motion;
         i != pDev->valuator->last_motion;
         i = (i + 1) % pDev->valuator->numMotionEvents) {
        /* We index the input buffer by which element we're accessing, which
         * is not monotonic, and the output buffer by how many events we've
         * written so far. */
        ibuff = (char *) pDev->valuator->motion + (i * size);
        memcpy(&current, ibuff, sizeof(Time));

        if (current > stop) {
            return ret;
        }
        else if (current >= start) {
            if (core) {
                memcpy(obuff, ibuff, sizeof(Time));     /* copy timestamp */

                icbuf = (INT32 *) (ibuff + sizeof(Time));
                corebuf = (INT16 *) (obuff + sizeof(Time));

                /* fetch x coordinate + range */
                memcpy(&from.min_value, icbuf++, sizeof(INT32));
                memcpy(&from.max_value, icbuf++, sizeof(INT32));
                memcpy(&coord, icbuf++, sizeof(INT32));

                /* scale to screen coords */
                to = &core_axis;
                to->max_value = pScreen->width;
                coord =
                    rescaleValuatorAxis(coord, &from, to, 0, pScreen->width);

                memcpy(corebuf, &coord, sizeof(INT16));
                corebuf++;

                /* fetch y coordinate + range */
                memcpy(&from.min_value, icbuf++, sizeof(INT32));
                memcpy(&from.max_value, icbuf++, sizeof(INT32));
                memcpy(&coord, icbuf++, sizeof(INT32));

                to->max_value = pScreen->height;
                coord =
                    rescaleValuatorAxis(coord, &from, to, 0, pScreen->height);
                memcpy(corebuf, &coord, sizeof(INT16));

            }
            else if (IsMaster(pDev)) {
                memcpy(obuff, ibuff, sizeof(Time));     /* copy timestamp */

                ocbuf = (INT32 *) (obuff + sizeof(Time));
                icbuf = (INT32 *) (ibuff + sizeof(Time));
                for (j = 0; j < MAX_VALUATORS; j++) {
                    if (j >= pDev->valuator->numAxes)
                        break;

                    /* fetch min/max/coordinate */
                    memcpy(&from.min_value, icbuf++, sizeof(INT32));
                    memcpy(&from.max_value, icbuf++, sizeof(INT32));
                    memcpy(&coord, icbuf++, sizeof(INT32));

                    to = (j <
                          pDev->valuator->numAxes) ? &pDev->valuator->
                        axes[j] : NULL;

                    /* x/y scaled to screen if no range is present */
                    if (j == 0 && (from.max_value < from.min_value))
                        from.max_value = pScreen->width;
                    else if (j == 1 && (from.max_value < from.min_value))
                        from.max_value = pScreen->height;

                    /* scale from stored range into current range */
                    coord = rescaleValuatorAxis(coord, &from, to, 0, 0);
                    memcpy(ocbuf, &coord, sizeof(INT32));
                    ocbuf++;
                }
            }
            else
                memcpy(obuff, ibuff, size);

            /* don't advance by size here. size may be different to the
             * actually written size if the MD has less valuators than MAX */
            if (core)
                obuff += sizeof(INT32) + sizeof(Time);
            else
                obuff +=
                    (sizeof(INT32) * pDev->valuator->numAxes) + sizeof(Time);
            ret++;
        }
    }

    return ret;
}

/**
 * Update the motion history for a specific device, with the list of
 * valuators.
 *
 * Layout of the history buffer:
 *   for SDs: [time] [val0] [val1] ... [valn]
 *   for MDs: [time] [min_val0] [max_val0] [val0] [min_val1] ... [valn]
 *
 * For events that have some valuators unset:
 *      min_val == max_val == val == 0.
 */
static void
updateMotionHistory(DeviceIntPtr pDev, CARD32 ms, ValuatorMask *mask,
                    double *valuators)
{
    char *buff = (char *) pDev->valuator->motion;
    ValuatorClassPtr v;
    int i;

    if (!pDev->valuator->numMotionEvents)
        return;

    v = pDev->valuator;
    if (IsMaster(pDev)) {
        buff += ((sizeof(INT32) * 3 * MAX_VALUATORS) + sizeof(CARD32)) *
            v->last_motion;

        memcpy(buff, &ms, sizeof(Time));
        buff += sizeof(Time);

        memset(buff, 0, sizeof(INT32) * 3 * MAX_VALUATORS);

        for (i = 0; i < v->numAxes; i++) {
            int val;

            /* XI1 doesn't support mixed mode devices */
            if (valuator_get_mode(pDev, i) != valuator_get_mode(pDev, 0))
                break;
            if (valuator_mask_size(mask) <= i || !valuator_mask_isset(mask, i)) {
                buff += 3 * sizeof(INT32);
                continue;
            }
            memcpy(buff, &v->axes[i].min_value, sizeof(INT32));
            buff += sizeof(INT32);
            memcpy(buff, &v->axes[i].max_value, sizeof(INT32));
            buff += sizeof(INT32);
            val = valuators[i];
            memcpy(buff, &val, sizeof(INT32));
            buff += sizeof(INT32);
        }
    }
    else {

        buff += ((sizeof(INT32) * pDev->valuator->numAxes) + sizeof(CARD32)) *
            pDev->valuator->last_motion;

        memcpy(buff, &ms, sizeof(Time));
        buff += sizeof(Time);

        memset(buff, 0, sizeof(INT32) * pDev->valuator->numAxes);

        for (i = 0; i < MAX_VALUATORS; i++) {
            int val;

            if (valuator_mask_size(mask) <= i || !valuator_mask_isset(mask, i)) {
                buff += sizeof(INT32);
                continue;
            }
            val = valuators[i];
            memcpy(buff, &val, sizeof(INT32));
            buff += sizeof(INT32);
        }
    }

    pDev->valuator->last_motion = (pDev->valuator->last_motion + 1) %
        pDev->valuator->numMotionEvents;
    /* If we're wrapping around, just keep the circular buffer going. */
    if (pDev->valuator->first_motion == pDev->valuator->last_motion)
        pDev->valuator->first_motion = (pDev->valuator->first_motion + 1) %
            pDev->valuator->numMotionEvents;

    return;
}

/**
 * Returns the maximum number of events GetKeyboardEvents
 * and GetPointerEvents will ever return.
 *
 * This MUST be absolutely constant, from init until exit.
 */
int
GetMaximumEventsNum(void)
{
    /* One raw event
     * One device event
     * One possible device changed event
     * Lots of possible separate button scroll events (horiz + vert)
     * Lots of possible separate raw button scroll events (horiz + vert)
     */
    return 100;
}

/**
 * Clip an axis to its bounds, which are declared in the call to
 * InitValuatorAxisClassStruct.
 */
static void
clipAxis(DeviceIntPtr pDev, int axisNum, double *val)
{
    AxisInfoPtr axis;

    if (axisNum >= pDev->valuator->numAxes)
        return;

    axis = pDev->valuator->axes + axisNum;

    /* If a value range is defined, clip. If not, do nothing */
    if (axis->max_value <= axis->min_value)
        return;

    if (*val < axis->min_value)
        *val = axis->min_value;
    if (*val > axis->max_value)
        *val = axis->max_value;
}

/**
 * Clip every axis in the list of valuators to its bounds.
 */
static void
clipValuators(DeviceIntPtr pDev, ValuatorMask *mask)
{
    int i;

    for (i = 0; i < valuator_mask_size(mask); i++)
        if (valuator_mask_isset(mask, i)) {
            double val = valuator_mask_get_double(mask, i);

            clipAxis(pDev, i, &val);
            valuator_mask_set_double(mask, i, val);
        }
}

/**
 * Create the DCCE event (does not update the master's device state yet, this
 * is done in the event processing).
 * Pull in the coordinates from the MD if necessary.
 *
 * @param events Pointer to a pre-allocated event array.
 * @param dev The slave device that generated an event.
 * @param type Either DEVCHANGE_POINTER_EVENT and/or DEVCHANGE_KEYBOARD_EVENT
 * @param num_events The current number of events, returns the number of
 *        events if a DCCE was generated.
 * @return The updated @events pointer.
 */
InternalEvent *
UpdateFromMaster(InternalEvent *events, DeviceIntPtr dev, int type,
                 int *num_events)
{
    DeviceIntPtr master;

    master =
        GetMaster(dev,
                  (type & DEVCHANGE_POINTER_EVENT) ? MASTER_POINTER :
                  MASTER_KEYBOARD);

    if (master && master->last.slave != dev) {
        CreateClassesChangedEvent(events, master, dev,
                                  type | DEVCHANGE_SLAVE_SWITCH);
        if (IsPointerDevice(master)) {
            updateSlaveDeviceCoords(master, dev);
            master->last.numValuators = dev->last.numValuators;
        }
        master->last.slave = dev;
        (*num_events)++;
        events++;
    }
    return events;
}

/**
 * Move the device's pointer to the position given in the valuators.
 *
 * @param dev The device whose pointer is to be moved.
 * @param mask Valuator data for this event.
 */
static void
clipAbsolute(DeviceIntPtr dev, ValuatorMask *mask)
{
    int i;

    for (i = 0; i < valuator_mask_size(mask); i++) {
        double val;

        if (!valuator_mask_isset(mask, i))
            continue;
        val = valuator_mask_get_double(mask, i);
        clipAxis(dev, i, &val);
        valuator_mask_set_double(mask, i, val);
    }
}

static void
add_to_scroll_valuator(DeviceIntPtr dev, ValuatorMask *mask, int valuator, double value)
{
    double v;

    if (!valuator_mask_fetch_double(mask, valuator, &v))
        return;

    /* protect against scrolling overflow. INT_MAX for double, because
     * we'll eventually write this as 32.32 fixed point */
    if ((value > 0 && v > INT_MAX - value) || (value < 0 && v < INT_MIN - value)) {
        v = 0;

        /* reset last.scroll to avoid a button storm */
        valuator_mask_set_double(dev->last.scroll, valuator, 0);
    }
    else
        v += value;

    valuator_mask_set_double(mask, valuator, v);
}


static void
scale_for_device_resolution(DeviceIntPtr dev, ValuatorMask *mask)
{
    double y;
    ValuatorClassPtr v = dev->valuator;
    int xrange = v->axes[0].max_value - v->axes[0].min_value + 1;
    int yrange = v->axes[1].max_value - v->axes[1].min_value + 1;

    double screen_ratio = 1.0 * screenInfo.width/screenInfo.height;
    double device_ratio = 1.0 * xrange/yrange;
    double resolution_ratio = 1.0;
    double ratio;

    if (!valuator_mask_fetch_double(mask, 1, &y))
        return;

    if (v->axes[0].resolution != 0 && v->axes[1].resolution != 0)
        resolution_ratio = 1.0 * v->axes[0].resolution/v->axes[1].resolution;

    ratio = device_ratio/resolution_ratio/screen_ratio;
    valuator_mask_set_double(mask, 1, y / ratio);
}

/**
 * Move the device's pointer by the values given in @valuators.
 *
 * @param dev The device whose pointer is to be moved.
 * @param[in,out] mask Valuator data for this event, modified in-place.
 */
static void
moveRelative(DeviceIntPtr dev, int flags, ValuatorMask *mask)
{
    int i;
    Bool clip_xy = IsMaster(dev) || !IsFloating(dev);
    ValuatorClassPtr v = dev->valuator;

    /* for abs devices in relative mode, we've just scaled wrong, since we
       mapped the device's shape into the screen shape. Undo this. */
    if ((flags & POINTER_ABSOLUTE) == 0 && v && v->numAxes > 1 &&
        v->axes[0].min_value < v->axes[0].max_value &&
        v->axes[1].min_value < v->axes[1].max_value) {
        scale_for_device_resolution(dev, mask);
    }

    /* calc other axes, clip, drop back into valuators */
    for (i = 0; i < valuator_mask_size(mask); i++) {
        double val = dev->last.valuators[i];

        if (!valuator_mask_isset(mask, i))
            continue;

        add_to_scroll_valuator(dev, mask, i, val);

        /* x & y need to go over the limits to cross screens if the SD
         * isn't currently attached; otherwise, clip to screen bounds. */
        if (valuator_get_mode(dev, i) == Absolute &&
            ((i != 0 && i != 1) || clip_xy)) {
            val = valuator_mask_get_double(mask, i);
            clipAxis(dev, i, &val);
            valuator_mask_set_double(mask, i, val);
        }
    }
}

/**
 * Accelerate the data in valuators based on the device's acceleration scheme.
 *
 * @param dev The device which's pointer is to be moved.
 * @param valuators Valuator mask
 * @param ms Current time.
 */
static void
accelPointer(DeviceIntPtr dev, ValuatorMask *valuators, CARD32 ms)
{
    if (dev->valuator->accelScheme.AccelSchemeProc)
        dev->valuator->accelScheme.AccelSchemeProc(dev, valuators, ms);
}

/**
 * Scale from absolute screen coordinates to absolute coordinates in the
 * device's coordinate range.
 *
 * @param dev The device to scale for.
 * @param[in, out] mask The mask in desktop/screen coordinates, modified in place
 * to contain device coordinate range.
 * @param flags If POINTER_SCREEN is set, mask is in per-screen coordinates.
 *              Otherwise, mask is in desktop coords.
 */
static void
scale_from_screen(DeviceIntPtr dev, ValuatorMask *mask, int flags)
{
    double scaled;
    ScreenPtr scr = miPointerGetScreen(dev);

    if (valuator_mask_isset(mask, 0)) {
        scaled = valuator_mask_get_double(mask, 0);
        if (flags & POINTER_SCREEN)
            scaled += scr->x;
        scaled = rescaleValuatorAxis(scaled,
                                     NULL, dev->valuator->axes + 0,
                                     screenInfo.x, screenInfo.width);
        valuator_mask_set_double(mask, 0, scaled);
    }
    if (valuator_mask_isset(mask, 1)) {
        scaled = valuator_mask_get_double(mask, 1);
        if (flags & POINTER_SCREEN)
            scaled += scr->y;
        scaled = rescaleValuatorAxis(scaled,
                                     NULL, dev->valuator->axes + 1,
                                     screenInfo.y, screenInfo.height);
        valuator_mask_set_double(mask, 1, scaled);
    }
}

/**
 * Scale from (absolute) device to screen coordinates here,
 *
 * The coordinates provided are always absolute. see fill_pointer_events for
 * information on coordinate systems.
 *
 * @param dev The device to be moved.
 * @param mask Mask of axis values for this event
 * @param[out] devx x desktop-wide coordinate in device coordinate system
 * @param[out] devy y desktop-wide coordinate in device coordinate system
 * @param[out] screenx x coordinate in desktop coordinate system
 * @param[out] screeny y coordinate in desktop coordinate system
 */
static ScreenPtr
scale_to_desktop(DeviceIntPtr dev, ValuatorMask *mask,
                 double *devx, double *devy, double *screenx, double *screeny)
{
    ScreenPtr scr = miPointerGetScreen(dev);
    double x, y;

    BUG_WARN(dev->valuator && dev->valuator->numAxes < 2);
    if (!dev->valuator || dev->valuator->numAxes < 2) {
        /* if we have no axes, last.valuators must be in screen coords
         * anyway */
        *devx = *screenx = dev->last.valuators[0];
        *devy = *screeny = dev->last.valuators[1];
        return scr;
    }

    if (valuator_mask_isset(mask, 0))
        x = valuator_mask_get_double(mask, 0);
    else
        x = dev->last.valuators[0];
    if (valuator_mask_isset(mask, 1))
        y = valuator_mask_get_double(mask, 1);
    else
        y = dev->last.valuators[1];

    /* scale x&y to desktop coordinates */
    *screenx = rescaleValuatorAxis(x, dev->valuator->axes + 0, NULL,
                                   screenInfo.x, screenInfo.width);
    *screeny = rescaleValuatorAxis(y, dev->valuator->axes + 1, NULL,
                                   screenInfo.y, screenInfo.height);

    *devx = x;
    *devy = y;

    return scr;
}

/**
 * If we have HW cursors, this actually moves the visible sprite. If not, we
 * just do all the screen crossing, etc.
 *
 * We use the screen coordinates here, call miPointerSetPosition() and then
 * scale back into device coordinates (if needed). miPSP will change x/y if
 * the screen was crossed.
 *
 * The coordinates provided are always absolute. The parameter mode
 * specifies whether it was relative or absolute movement that landed us at
 * those coordinates. see fill_pointer_events for information on coordinate
 * systems.
 *
 * @param dev The device to be moved.
 * @param mode Movement mode (Absolute or Relative)
 * @param[out] mask Mask of axis values for this event, returns the
 * per-screen device coordinates after confinement
 * @param[in,out] devx x desktop-wide coordinate in device coordinate system
 * @param[in,out] devy y desktop-wide coordinate in device coordinate system
 * @param[in,out] screenx x coordinate in desktop coordinate system
 * @param[in,out] screeny y coordinate in desktop coordinate system
 * @param[out] nevents Number of barrier events added to events
 * @param[in,out] events List of events barrier events are added to
 */
static ScreenPtr
positionSprite(DeviceIntPtr dev, int mode, ValuatorMask *mask,
               double *devx, double *devy, double *screenx, double *screeny,
               int *nevents, InternalEvent* events)
{
    ScreenPtr scr = miPointerGetScreen(dev);
    double tmpx, tmpy;

    if (!dev->valuator || dev->valuator->numAxes < 2)
        return scr;

    tmpx = *screenx;
    tmpy = *screeny;

    /* miPointerSetPosition takes care of crossing screens for us, as well as
     * clipping to the current screen. Coordinates returned are in desktop
     * coord system */
    scr = miPointerSetPosition(dev, mode, screenx, screeny, nevents, events);

    /* If we were constrained, rescale x/y from the screen coordinates so
     * the device valuators reflect the correct position. For screen
     * crossing this doesn't matter much, the coords would be 0 or max.
     */
    if (tmpx != *screenx)
        *devx = rescaleValuatorAxis(*screenx, NULL, dev->valuator->axes + 0,
                                    screenInfo.x, screenInfo.width);

    if (tmpy != *screeny)
        *devy = rescaleValuatorAxis(*screeny, NULL, dev->valuator->axes + 1,
                                    screenInfo.y, screenInfo.height);

    /* Recalculate the per-screen device coordinates */
    if (valuator_mask_isset(mask, 0)) {
        double x;

        x = rescaleValuatorAxis(*screenx - scr->x, NULL,
                                dev->valuator->axes + 0, 0, scr->width);
        valuator_mask_set_double(mask, 0, x);
    }
    if (valuator_mask_isset(mask, 1)) {
        double y;

        y = rescaleValuatorAxis(*screeny - scr->y, NULL,
                                dev->valuator->axes + 1, 0, scr->height);
        valuator_mask_set_double(mask, 1, y);
    }

    return scr;
}

/**
 * Update the motion history for the device and (if appropriate) for its
 * master device.
 * @param dev Slave device to update.
 * @param mask Bit mask of valid valuators to append to history.
 * @param num Total number of valuators to append to history.
 * @param ms Current time
 */
static void
updateHistory(DeviceIntPtr dev, ValuatorMask *mask, CARD32 ms)
{
    if (!dev->valuator)
        return;

    updateMotionHistory(dev, ms, mask, dev->last.valuators);
    if (!IsMaster(dev) && !IsFloating(dev)) {
        DeviceIntPtr master = GetMaster(dev, MASTER_POINTER);

        updateMotionHistory(master, ms, mask, dev->last.valuators);
    }
}

static void
queueEventList(DeviceIntPtr device, InternalEvent *events, int nevents)
{
    int i;

    for (i = 0; i < nevents; i++)
        mieqEnqueue(device, &events[i]);
}

static void
event_set_root_coordinates(DeviceEvent *event, double x, double y)
{
    event->root_x = trunc(x);
    event->root_y = trunc(y);
    event->root_x_frac = x - trunc(x);
    event->root_y_frac = y - trunc(y);
}

/**
 * Generate internal events representing this keyboard event and enqueue
 * them on the event queue.
 *
 * This function is not reentrant. Disable signals before calling.
 *
 * @param device The device to generate the event for
 * @param type Event type, one of KeyPress or KeyRelease
 * @param keycode Key code of the pressed/released key
 *
 */
void
QueueKeyboardEvents(DeviceIntPtr device, int type,
                    int keycode)
{
    int nevents;

    nevents = GetKeyboardEvents(InputEventList, device, type, keycode);
    queueEventList(device, InputEventList, nevents);
}

/**
 * Returns a set of InternalEvents for KeyPress/KeyRelease, optionally
 * also with valuator events.
 *
 * The DDX is responsible for allocating the event list in the first
 * place via InitEventList(), and for freeing it.
 *
 * @return the number of events written into events.
 */
int
GetKeyboardEvents(InternalEvent *events, DeviceIntPtr pDev, int type,
                  int key_code)
{
    int num_events = 0;
    CARD32 ms = 0;
    DeviceEvent *event;
    RawDeviceEvent *raw;
    enum DeviceEventSource source_type = EVENT_SOURCE_NORMAL;

#ifdef XSERVER_DTRACE
    if (XSERVER_INPUT_EVENT_ENABLED()) {
        XSERVER_INPUT_EVENT(pDev->id, type, key_code, 0, 0,
                            NULL, NULL);
    }
#endif

    if (type == EnterNotify) {
        source_type = EVENT_SOURCE_FOCUS;
        type = KeyPress;
    } else if (type == LeaveNotify) {
        source_type = EVENT_SOURCE_FOCUS;
        type = KeyRelease;
    }

    /* refuse events from disabled devices */
    if (!pDev->enabled)
        return 0;

    if (!events || !pDev->key || !pDev->focus || !pDev->kbdfeed ||
        (type != KeyPress && type != KeyRelease) ||
        (key_code < 8 || key_code > 255))
        return 0;

    num_events = 1;

    events =
        UpdateFromMaster(events, pDev, DEVCHANGE_KEYBOARD_EVENT, &num_events);

    /* Handle core repeating, via press/release/press/release. */
    if (type == KeyPress && key_is_down(pDev, key_code, KEY_POSTED)) {
        /* If autorepeating is disabled either globally or just for that key,
         * or we have a modifier, don't generate a repeat event. */
        if (!pDev->kbdfeed->ctrl.autoRepeat ||
            !key_autorepeats(pDev, key_code) ||
            pDev->key->xkbInfo->desc->map->modmap[key_code])
            return 0;
    }

    ms = GetTimeInMillis();

    if (source_type == EVENT_SOURCE_NORMAL) {
        raw = &events->raw_event;
        init_raw(pDev, raw, ms, type, key_code);
        events++;
        num_events++;
    }

    event = &events->device_event;
    init_device_event(event, pDev, ms, source_type);
    event->detail.key = key_code;

    if (type == KeyPress) {
        event->type = ET_KeyPress;
        set_key_down(pDev, key_code, KEY_POSTED);
    }
    else if (type == KeyRelease) {
        event->type = ET_KeyRelease;
        set_key_up(pDev, key_code, KEY_POSTED);
    }

    return num_events;
}

/**
 * Initialize an event array large enough for num_events arrays.
 * This event list is to be passed into GetPointerEvents() and
 * GetKeyboardEvents().
 *
 * @param num_events Number of elements in list.
 */
InternalEvent *
InitEventList(int num_events)
{
    InternalEvent *events = calloc(num_events, sizeof(InternalEvent));

    return events;
}

/**
 * Free an event list.
 *
 * @param list The list to be freed.
 * @param num_events Number of elements in list.
 */
void
FreeEventList(InternalEvent *list, int num_events)
{
    free(list);
}

/**
 * Transform vector x/y according to matrix m and drop the rounded coords
 * back into x/y.
 */
static void
transform(struct pixman_f_transform *m, double *x, double *y)
{
    struct pixman_f_vector p = {.v = {*x, *y, 1} };
    pixman_f_transform_point(m, &p);

    *x = p.v[0];
    *y = p.v[1];
}

static void
transformRelative(DeviceIntPtr dev, ValuatorMask *mask)
{
    double x = 0, y = 0;

    valuator_mask_fetch_double(mask, 0, &x);
    valuator_mask_fetch_double(mask, 1, &y);

    transform(&dev->relative_transform, &x, &y);

    if (x)
        valuator_mask_set_double(mask, 0, x);
    else
        valuator_mask_unset(mask, 0);

    if (y)
        valuator_mask_set_double(mask, 1, y);
    else
        valuator_mask_unset(mask, 1);
}

/**
 * Apply the device's transformation matrix to the valuator mask and replace
 * the scaled values in mask. This transformation only applies to valuators
 * 0 and 1, others will be untouched.
 *
 * @param dev The device the valuators came from
 * @param[in,out] mask The valuator mask.
 */
static void
transformAbsolute(DeviceIntPtr dev, ValuatorMask *mask)
{
    double x, y, ox = 0.0, oy = 0.0;
    int has_x, has_y;

    has_x = valuator_mask_isset(mask, 0);
    has_y = valuator_mask_isset(mask, 1);

    if (!has_x && !has_y)
        return;

    if (!has_x || !has_y) {
        struct pixman_f_transform invert;

        /* undo transformation from last event */
        ox = dev->last.valuators[0];
        oy = dev->last.valuators[1];

        pixman_f_transform_invert(&invert, &dev->scale_and_transform);
        transform(&invert, &ox, &oy);
    }

    if (has_x)
        ox = valuator_mask_get_double(mask, 0);

    if (has_y)
        oy = valuator_mask_get_double(mask, 1);

    x = ox;
    y = oy;

    transform(&dev->scale_and_transform, &x, &y);

    if (has_x || ox != x)
        valuator_mask_set_double(mask, 0, x);

    if (has_y || oy != y)
        valuator_mask_set_double(mask, 1, y);
}

static void
storeLastValuators(DeviceIntPtr dev, ValuatorMask *mask, double devx, double devy)
{
    int i;

    /* store desktop-wide in last.valuators */
    if (valuator_mask_isset(mask, 0))
        dev->last.valuators[0] = devx;
    if (valuator_mask_isset(mask, 1))
        dev->last.valuators[1] = devy;

    for (i = 0; i < valuator_mask_size(mask); i++) {
        if (i == 0 || i == 1)
            continue;

        if (valuator_mask_isset(mask, i))
            dev->last.valuators[i] = valuator_mask_get_double(mask, i);
    }

}

/**
 * Generate internal events representing this pointer event and enqueue them
 * on the event queue.
 *
 * This function is not reentrant. Disable signals before calling.
 *
 * @param device The device to generate the event for
 * @param type Event type, one of ButtonPress, ButtonRelease, MotionNotify
 * @param buttons Button number of the buttons modified. Must be 0 for
 * MotionNotify
 * @param flags Event modification flags
 * @param mask Valuator mask for valuators present for this event.
 */
void
QueuePointerEvents(DeviceIntPtr device, int type,
                   int buttons, int flags, const ValuatorMask *mask)
{
    int nevents;

    nevents =
        GetPointerEvents(InputEventList, device, type, buttons, flags, mask);
    queueEventList(device, InputEventList, nevents);
}

/**
 * Helper function for GetPointerEvents, which only generates motion and
 * raw motion events for the slave device: does not update the master device.
 *
 * Should not be called by anyone other than GetPointerEvents.
 *
 * We use several different coordinate systems and need to switch between
 * the three in fill_pointer_events, positionSprite and
 * miPointerSetPosition. "desktop" refers to the width/height of all
 * screenInfo.screens[n]->width/height added up. "screen" is ScreenRec, not
 * output.
 *
 * Coordinate systems:
 * - relative events have a mask_in in relative coordinates, mapped to
 *   pixels. These events are mapped to the current position±delta.
 * - absolute events have a mask_in in absolute device coordinates in
 *   device-specific range. This range is mapped to the desktop.
 * - POINTER_SCREEN absolute events (x86WarpCursor) are in screen-relative
 *   screen coordinate range.
 * - rootx/rooty in events must be be relative to the current screen's
 *   origin (screen coordinate system)
 * - XI2 valuators must be relative to the current screen's origin. On
 *   the protocol the device min/max range maps to the current screen.
 *
 * For screen switching we need to get the desktop coordinates for each
 * event, then map that to the respective position on each screen and
 * position the cursor there.
 * The device's last.valuator[] stores the last position in desktop-wide
 * coordinates (in device range for slave devices, desktop range for master
 * devices).
 *
 * screen-relative device coordinates requires scaling: A device coordinate
 * x/y of range [n..m] that maps to positions Sx/Sy on Screen S must be
 * rescaled to match Sx/Sy for [n..m]. In the simplest example, x of (m/2-1)
 * is the last coordinate on the first screen and must be rescaled for the
 * event to be m. XI2 clients that do their own coordinate mapping would
 * otherwise interpret the position of the device elsewhere to the cursor.
 * However, this scaling leads to losses:
 * if we have two ScreenRecs we scale from e.g. [0..44704]  (Wacom I4) to
 * [0..2048[. that gives us 2047.954 as desktop coord, or the per-screen
 * coordinate 1023.954. Scaling that back into the device coordinate range
 * gives us 44703. So off by one device unit. It's a bug, but we'll have to
 * live with it because with all this scaling, we just cannot win.
 *
 * @return the number of events written into events.
 */
static int
fill_pointer_events(InternalEvent *events, DeviceIntPtr pDev, int type,
                    int buttons, CARD32 ms, int flags,
                    const ValuatorMask *mask_in)
{
    int num_events = 0;
    DeviceEvent *event;
    RawDeviceEvent *raw = NULL;
    double screenx = 0.0, screeny = 0.0;        /* desktop coordinate system */
    double devx = 0.0, devy = 0.0;      /* desktop-wide in device coords */
    int sx = 0, sy = 0;                 /* for POINTER_SCREEN */
    ValuatorMask mask;
    ScreenPtr scr;
    int num_barrier_events = 0;

    switch (type) {
    case MotionNotify:
        if (!pDev->valuator) {
            ErrorF("[dix] motion events from device %d without valuators\n",
                   pDev->id);
            return 0;
        }
        if (!mask_in || valuator_mask_num_valuators(mask_in) <= 0)
            return 0;
        break;
    case ButtonPress:
    case ButtonRelease:
        if (!pDev->button || !buttons)
            return 0;
        if (mask_in && valuator_mask_size(mask_in) > 0 && !pDev->valuator) {
            ErrorF
                ("[dix] button event with valuator from device %d without valuators\n",
                 pDev->id);
            return 0;
        }
        break;
    default:
        return 0;
    }

    valuator_mask_copy(&mask, mask_in);

    if ((flags & POINTER_NORAW) == 0) {
        raw = &events->raw_event;
        events++;
        num_events++;

        init_raw(pDev, raw, ms, type, buttons);

        if (flags & POINTER_EMULATED)
            raw->flags = XIPointerEmulated;

        set_raw_valuators(raw, &mask, TRUE, raw->valuators.data_raw);
    }

    valuator_mask_drop_unaccelerated(&mask);

    /* valuators are in driver-native format (rel or abs) */

    if (flags & POINTER_ABSOLUTE) {
        if (flags & (POINTER_SCREEN | POINTER_DESKTOP)) {    /* valuators are in screen/desktop coords */
            sx = valuator_mask_get(&mask, 0);
            sy = valuator_mask_get(&mask, 1);
            scale_from_screen(pDev, &mask, flags);
        }

        transformAbsolute(pDev, &mask);
        clipAbsolute(pDev, &mask);
        if ((flags & POINTER_NORAW) == 0 && raw)
            set_raw_valuators(raw, &mask, FALSE, raw->valuators.data);
    }
    else {
        transformRelative(pDev, &mask);

        if (flags & POINTER_ACCELERATE)
            accelPointer(pDev, &mask, ms);
        if ((flags & POINTER_NORAW) == 0 && raw)
            set_raw_valuators(raw, &mask, FALSE, raw->valuators.data);

        moveRelative(pDev, flags, &mask);
    }

    /* valuators are in device coordinate system in absolute coordinates */
    scale_to_desktop(pDev, &mask, &devx, &devy, &screenx, &screeny);

    /* #53037 XWarpPointer's scaling back and forth between screen and
       device may leave us with rounding errors. End result is that the
       pointer doesn't end up on the pixel it should.
       Avoid this by forcing screenx/screeny back to what the input
       coordinates were.
     */
    if (flags & POINTER_SCREEN) {
        scr = miPointerGetScreen(pDev);
        screenx = sx + scr->x;
        screeny = sy + scr->y;
    }

    scr = positionSprite(pDev, (flags & POINTER_ABSOLUTE) ? Absolute : Relative,
                         &mask, &devx, &devy, &screenx, &screeny,
                         &num_barrier_events, events);
    num_events += num_barrier_events;
    events += num_barrier_events;

    /* screenx, screeny are in desktop coordinates,
       mask is in device coordinates per-screen (the event data)
       devx/devy is in device coordinate desktop-wide */
    updateHistory(pDev, &mask, ms);

    clipValuators(pDev, &mask);

    storeLastValuators(pDev, &mask, devx, devy);

    /* Update the MD's coordinates, which are always in desktop space. */
    if (!IsMaster(pDev) && !IsFloating(pDev)) {
        DeviceIntPtr master = GetMaster(pDev, MASTER_POINTER);

        master->last.valuators[0] = screenx;
        master->last.valuators[1] = screeny;
    }

    if ((flags & POINTER_RAWONLY) == 0) {
        num_events++;

        event = &events->device_event;
        init_device_event(event, pDev, ms, EVENT_SOURCE_NORMAL);

        if (type == MotionNotify) {
            event->type = ET_Motion;
            event->detail.button = 0;
        }
        else {
            if (type == ButtonPress) {
                event->type = ET_ButtonPress;
                set_button_down(pDev, buttons, BUTTON_POSTED);
            }
            else if (type == ButtonRelease) {
                event->type = ET_ButtonRelease;
                set_button_up(pDev, buttons, BUTTON_POSTED);
            }
            event->detail.button = buttons;
        }

        /* root_x and root_y must be in per-screen coordinates */
        event_set_root_coordinates(event, screenx - scr->x, screeny - scr->y);

        if (flags & POINTER_EMULATED)
            event->flags = XIPointerEmulated;

        set_valuators(pDev, event, &mask);
    }

    return num_events;
}

/**
 * Generate events for each scroll axis that changed between before/after
 * for the device.
 *
 * @param events The pointer to the event list to fill the events
 * @param dev The device to generate the events for
 * @param type The real type of the event
 * @param axis The axis number to generate events for
 * @param mask State before this event in absolute coords
 * @param last_valuators The device's last valuators value
 * @param[in,out] lastScroll Last scroll state posted in absolute coords (modified
 * in-place)
 * @param ms Current time in ms
 * @param max_events Max number of events to be generated
 * @return The number of events generated
 */
static int
emulate_scroll_button_events(InternalEvent *events,
                             DeviceIntPtr dev,
                             int type,
                             int axis,
                             const ValuatorMask *mask,
                             const ValuatorMask *last_valuators,
                             ValuatorMask *lastScroll, CARD32 ms, int max_events)
{
    AxisInfoPtr ax;
    double delta;
    double incr;
    int direction = 0; /* -1 for up, 1 for down */
    int num_events = 0;
    int b;
    int flags = 0;
    double last_val,    /* abs axis value from previous event */
           current_val, /* abs axis value for this event */
           last_scroll_val; /* abs axis value we sent out the last scroll button for */

    if (dev->valuator->axes[axis].scroll.type == SCROLL_TYPE_NONE)
        return 0;

    if (!valuator_mask_isset(mask, axis))
        return 0;

    ax = &dev->valuator->axes[axis];
    incr = ax->scroll.increment;

    BUG_WARN_MSG(incr == 0, "for device %s\n", dev->name);
    if (incr == 0)
        return 0;

    if (type != ButtonPress && type != ButtonRelease)
        flags |= POINTER_EMULATED;

    if (!valuator_mask_isset(lastScroll, axis))
        valuator_mask_set_double(lastScroll, axis, 0);

    /* The delta between the last value we sent a scroll button event for
     * and the current event value (which has been applied already in
     * fill_pointer_events). This tells us the scroll direction. */
    delta = valuator_mask_get_double(mask, axis) - valuator_mask_get_double(lastScroll, axis);
    direction = delta * incr > 0 ? 1 : -1;

    b = (ax->scroll.type == SCROLL_TYPE_VERTICAL) ? 5 : 7;
    if (direction < 0)
        b--;                    /* we're scrolling up or left → button 4 or 6 */

    /* Note: we emulate scroll on multiples of the increment, regardless of the
     * current delta, mostly for the benefit of Xwayland which doesn't (cannot)
     * distinguish between devices, see #1339 and #1414.
     *
     * Where a device scrolls a fraction of an increment, a subsequent scroll in
     * the other direction did not trigger a scroll event. For example, where
     * the increment is 1.0, the current axis value is 3.0 and a device scrolls
     * down by 0.7 (a), then up by -1.0 (b), no scroll event was emitted:
     *      -----|------b--|------a--|----
     *          2.0       3.0       4.0
     * For both events, the last button was sent at 3.0 and since the delta
     * from that is never a full increment, no events were generated.
     * With Xwayland this can happen when we switch between smooth-scroll
     * devices and discrete devices.
     *
     * To avoid this, we now emulate button events whenever we cross a multiple
     * of the scroll increment. For example, for a scroll increment of 1.0
     * we expect events at -2.0, -1.0, 0.0, 1.0, 2.0,...
     *
     * In the above example, we go from 3.0 to 3.7 (no scroll button event),
     * then from 3.7 to 2.7 which triggers a scroll button event because we
     * cross 3.0.
     *
     * This trades off one bug for another. Previously, the first scroll button
     * event after changing direction was always between
     * [increment, 2 * increment). Above example again: the first event would be
     * emulated at 2.0 so the full movement before a button event was actually
     * -1.7.
     *
     * Now, the first scroll button event is always between (0.0, increment).
     * Above example again: the first event would be emulated at 3.0
     * so the full movement before a button event was actually -0.7.
     *
     * This only affects changes of directions. Above example again: the next
     * button event in-direction would've been emulated at 4.0 so only 0.3
     * from the current position.
     */
    last_val = valuator_mask_get_double(last_valuators, axis);
    last_scroll_val = valuator_mask_get_double(lastScroll, axis);
    current_val = valuator_mask_get_double(mask, axis);

    /* We're crossing an increment multiple? */
    if ((current_val < last_scroll_val && last_scroll_val < last_val) ||
        (last_val < last_scroll_val && last_scroll_val < current_val)) {
        last_scroll_val -= direction * incr;
    }

    while (TRUE) {
        /* The next value we want to send out a button event for */
        double next_val = last_scroll_val + direction * incr;

        if ((((direction > 0 && incr > 0) || (direction < 0 && incr < 0)) && (next_val > current_val)) ||
            (((direction > 0 && incr < 0) || (direction < 0 && incr > 0)) && (next_val < current_val)))
            break;

        /* fill_pointer_events() generates four events: one normal and one raw
         * event for button press and button release.
         * We may get a bigger scroll delta than we can generate events
         * for. In that case, we keep decreasing delta, but skip events.
         */
        if (num_events + 4 < max_events) {
            int nev_tmp;

            if (type != ButtonRelease) {
                nev_tmp = fill_pointer_events(events, dev, ButtonPress, b, ms,
                                              flags, NULL);
                events += nev_tmp;
                num_events += nev_tmp;
            }
            if (type != ButtonPress) {
                nev_tmp = fill_pointer_events(events, dev, ButtonRelease, b, ms,
                                              flags, NULL);
                events += nev_tmp;
                num_events += nev_tmp;
            }
        }
        /* send out scroll event */
        last_scroll_val = next_val;
    }

    valuator_mask_set_double(lastScroll, axis, last_scroll_val);

    return num_events;
}


/**
 * Generate a complete series of InternalEvents (filled into the EventList)
 * representing pointer motion, or button presses.  If the device is a slave
 * device, also potentially generate a DeviceClassesChangedEvent to update
 * the master device.
 *
 * events is not NULL-terminated; the return value is the number of events.
 * The DDX is responsible for allocating the event structure in the first
 * place via InitEventList() and GetMaximumEventsNum(), and for freeing it.
 *
 * In the generated events rootX/Y will be in absolute screen coords and
 * the valuator information in the absolute or relative device coords.
 *
 * last.valuators[x] of the device is always in absolute device coords.
 * last.valuators[x] of the master device is in absolute screen coords.
 *
 * master->last.valuators[x] for x > 2 is undefined.
 */
int
GetPointerEvents(InternalEvent *events, DeviceIntPtr pDev, int type,
                 int buttons, int flags, const ValuatorMask *mask_in)
{
    CARD32 ms = GetTimeInMillis();
    int num_events = 0, nev_tmp;
    ValuatorMask last_valuators;
    ValuatorMask mask;
    ValuatorMask scroll;
    int i;
    int realtype = type;

#ifdef XSERVER_DTRACE
    if (XSERVER_INPUT_EVENT_ENABLED()) {
        XSERVER_INPUT_EVENT(pDev->id, type, buttons, flags,
                            mask_in ? mask_in->last_bit + 1 : 0,
                            mask_in ? mask_in->mask : NULL,
                            mask_in ? mask_in->valuators : NULL);
    }
#endif

    BUG_RETURN_VAL(buttons >= MAX_BUTTONS, 0);

    /* refuse events from disabled devices */
    if (!pDev->enabled)
        return 0;

    if (!miPointerGetScreen(pDev))
        return 0;

    events = UpdateFromMaster(events, pDev, DEVCHANGE_POINTER_EVENT,
                              &num_events);

    valuator_mask_copy(&mask, mask_in);

    /* Back up the current value of last.valuators. fill_pointer_events()
     * overwrites those but we need them for scroll button emulation */
    valuator_mask_zero(&last_valuators);
    for (size_t idx = 0; idx < pDev->last.numValuators; idx++)
        valuator_mask_set_double(&last_valuators, idx, pDev->last.valuators[idx]);

    /* Turn a scroll button press into a smooth-scrolling event if
     * necessary. This only needs to cater for the XIScrollFlagPreferred
     * axis (if more than one scrolling axis is present) */
    if (type == ButtonPress) {
        double adj;
        int axis;
        int h_scroll_axis = -1;
        int v_scroll_axis = -1;

        if (pDev->valuator) {
            h_scroll_axis = pDev->valuator->h_scroll_axis;
            v_scroll_axis = pDev->valuator->v_scroll_axis;
        }

        /* Up is negative on valuators, down positive */
        switch (buttons) {
        case 4:
            adj = -1.0;
            axis = v_scroll_axis;
            break;
        case 5:
            adj = 1.0;
            axis = v_scroll_axis;
            break;
        case 6:
            adj = -1.0;
            axis = h_scroll_axis;
            break;
        case 7:
            adj = 1.0;
            axis = h_scroll_axis;
            break;
        default:
            adj = 0.0;
            axis = -1;
            break;
        }

        if (adj != 0.0 && axis != -1) {
            adj *= pDev->valuator->axes[axis].scroll.increment;
            if (!valuator_mask_isset(&mask, axis))
                valuator_mask_set(&mask, axis, 0);
            add_to_scroll_valuator(pDev, &mask, axis, adj);
            type = MotionNotify;
            buttons = 0;
            flags |= POINTER_EMULATED;
        }
    }

    /* First fill out the original event set, with smooth-scrolling axes. */
    nev_tmp = fill_pointer_events(events, pDev, type, buttons, ms, flags,
                                  &mask);
    events += nev_tmp;
    num_events += nev_tmp;

    valuator_mask_zero(&scroll);

    /* Now turn the smooth-scrolling axes back into emulated button presses
     * for legacy clients, based on the integer delta between before and now */
    for (i = 0; i < valuator_mask_size(&mask); i++) {
        if ( !pDev->valuator || (i >= pDev->valuator->numAxes))
            break;

        if (!valuator_mask_isset(&mask, i))
            continue;

        valuator_mask_set_double(&scroll, i, pDev->last.valuators[i]);

        nev_tmp =
            emulate_scroll_button_events(events, pDev, realtype, i, &scroll,
                                         &last_valuators, pDev->last.scroll, ms,
                                         GetMaximumEventsNum() - num_events);
        events += nev_tmp;
        num_events += nev_tmp;
    }

    return num_events;
}

/**
 * Generate internal events representing this proximity event and enqueue
 * them on the event queue.
 *
 * This function is not reentrant. Disable signals before calling.
 *
 * @param device The device to generate the event for
 * @param type Event type, one of ProximityIn or ProximityOut
 * @param keycode Key code of the pressed/released key
 * @param mask Valuator mask for valuators present for this event.
 *
 */
void
QueueProximityEvents(DeviceIntPtr device, int type, const ValuatorMask *mask)
{
    int nevents;

    nevents = GetProximityEvents(InputEventList, device, type, mask);
    queueEventList(device, InputEventList, nevents);
}

/**
 * Generate ProximityIn/ProximityOut InternalEvents, accompanied by
 * valuators.
 *
 * The DDX is responsible for allocating the events in the first place via
 * InitEventList(), and for freeing it.
 *
 * @return the number of events written into events.
 */
int
GetProximityEvents(InternalEvent *events, DeviceIntPtr pDev, int type,
                   const ValuatorMask *mask_in)
{
    int num_events = 1, i;
    DeviceEvent *event;
    ValuatorMask mask;

#ifdef XSERVER_DTRACE
    if (XSERVER_INPUT_EVENT_ENABLED()) {
        XSERVER_INPUT_EVENT(pDev->id, type, 0, 0,
                            mask_in ? mask_in->last_bit + 1 : 0,
                            mask_in ? mask_in->mask : NULL,
                            mask_in ? mask_in->valuators : NULL);
    }
#endif

    /* refuse events from disabled devices */
    if (!pDev->enabled)
        return 0;

    /* Sanity checks. */
    if ((type != ProximityIn && type != ProximityOut) || !mask_in)
        return 0;
    if (!pDev->valuator || !pDev->proximity)
        return 0;

    valuator_mask_copy(&mask, mask_in);

    /* ignore relative axes for proximity. */
    for (i = 0; i < valuator_mask_size(&mask); i++) {
        if (valuator_mask_isset(&mask, i) &&
            valuator_get_mode(pDev, i) == Relative)
            valuator_mask_unset(&mask, i);
    }

    /* FIXME: posting proximity events with relative valuators only results
     * in an empty event, EventToXI() will fail to convert → no event sent
     * to client. */

    events =
        UpdateFromMaster(events, pDev, DEVCHANGE_POINTER_EVENT, &num_events);

    event = &events->device_event;
    init_device_event(event, pDev, GetTimeInMillis(), EVENT_SOURCE_NORMAL);
    event->type = (type == ProximityIn) ? ET_ProximityIn : ET_ProximityOut;

    clipValuators(pDev, &mask);

    set_valuators(pDev, event, &mask);

    return num_events;
}

int
GetTouchOwnershipEvents(InternalEvent *events, DeviceIntPtr pDev,
                        TouchPointInfoPtr ti, uint8_t reason, XID resource,
                        uint32_t flags)
{
    TouchClassPtr t = pDev->touch;
    TouchOwnershipEvent *event;
    CARD32 ms = GetTimeInMillis();

    if (!pDev->enabled || !t || !ti)
        return 0;

    event = &events->touch_ownership_event;
    init_touch_ownership(pDev, event, ms);

    event->touchid = ti->client_id;
    event->sourceid = ti->sourceid;
    event->resource = resource;
    event->flags = flags;
    event->reason = reason;

    return 1;
}

/**
 * Generate internal events representing this touch event and enqueue them
 * on the event queue.
 *
 * This function is not reentrant. Disable signals before calling.
 *
 * @param device The device to generate the event for
 * @param type Event type, one of XI_TouchBegin, XI_TouchUpdate, XI_TouchEnd
 * @param touchid Touch point ID
 * @param flags Event modification flags
 * @param mask Valuator mask for valuators present for this event.
 */
void
QueueTouchEvents(DeviceIntPtr device, int type,
                 uint32_t ddx_touchid, int flags, const ValuatorMask *mask)
{
    int nevents;

    nevents =
        GetTouchEvents(InputEventList, device, ddx_touchid, type, flags, mask);
    queueEventList(device, InputEventList, nevents);
}

/**
 * Get events for a touch. Generates a TouchBegin event if end is not set and
 * the touch id is not active. Generates a TouchUpdate event if end is not set
 * and the touch id is active. Generates a TouchEnd event if end is set and the
 * touch id is active.
 *
 * events is not NULL-terminated; the return value is the number of events.
 * The DDX is responsible for allocating the event structure in the first
 * place via GetMaximumEventsNum(), and for freeing it.
 *
 * @param[out] events The list of events generated
 * @param dev The device to generate the events for
 * @param ddx_touchid The touch ID as assigned by the DDX
 * @param type XI_TouchBegin, XI_TouchUpdate or XI_TouchEnd
 * @param flags Event flags
 * @param mask_in Valuator information for this event
 */
int
GetTouchEvents(InternalEvent *events, DeviceIntPtr dev, uint32_t ddx_touchid,
               uint16_t type, uint32_t flags, const ValuatorMask *mask_in)
{
    ScreenPtr scr;
    TouchClassPtr t = dev->touch;
    ValuatorClassPtr v = dev->valuator;
    DeviceEvent *event;
    CARD32 ms = GetTimeInMillis();
    ValuatorMask mask;
    double screenx = 0.0, screeny = 0.0;        /* desktop coordinate system */
    double devx = 0.0, devy = 0.0;      /* desktop-wide in device coords */
    int i;
    int num_events = 0;
    RawDeviceEvent *raw;
    DDXTouchPointInfoPtr ti;
    int need_rawevent = TRUE;
    Bool emulate_pointer = FALSE;
    int client_id = 0;

#ifdef XSERVER_DTRACE
    if (XSERVER_INPUT_EVENT_ENABLED()) {
        XSERVER_INPUT_EVENT(dev->id, type, ddx_touchid, flags,
                            mask_in ? mask_in->last_bit + 1 : 0,
                            mask_in ? mask_in->mask : NULL,
                            mask_in ? mask_in->valuators : NULL);
    }
#endif

    if (!dev->enabled || !t || !v)
        return 0;

    /* Find and/or create the DDX touch info */

    ti = TouchFindByDDXID(dev, ddx_touchid, (type == XI_TouchBegin));
    if (!ti) {
        ErrorFSigSafe("[dix] %s: unable to %s touch point %u\n", dev->name,
                      type == XI_TouchBegin ? "begin" : "find", ddx_touchid);
        return 0;
    }
    client_id = ti->client_id;

    emulate_pointer = ti->emulate_pointer;

    if (!IsMaster(dev))
        events =
            UpdateFromMaster(events, dev, DEVCHANGE_POINTER_EVENT, &num_events);

    valuator_mask_copy(&mask, mask_in);

    if (need_rawevent) {
        raw = &events->raw_event;
        events++;
        num_events++;
        init_raw(dev, raw, ms, type, client_id);
        set_raw_valuators(raw, &mask, TRUE, raw->valuators.data_raw);
    }

    event = &events->device_event;
    num_events++;

    init_device_event(event, dev, ms, EVENT_SOURCE_NORMAL);

    switch (type) {
    case XI_TouchBegin:
        event->type = ET_TouchBegin;
        /* If we're starting a touch, we must have x & y coordinates. */
        if (!mask_in ||
            !valuator_mask_isset(mask_in, 0) ||
            !valuator_mask_isset(mask_in, 1)) {
            ErrorFSigSafe("%s: Attempted to start touch without x/y "
                          "(driver bug)\n", dev->name);
            return 0;
        }
        break;
    case XI_TouchUpdate:
        event->type = ET_TouchUpdate;
        if (!mask_in || valuator_mask_num_valuators(mask_in) <= 0) {
            ErrorFSigSafe("%s: TouchUpdate with no valuators? Driver bug\n",
                          dev->name);
        }
        break;
    case XI_TouchEnd:
        event->type = ET_TouchEnd;
        /* We can end the DDX touch here, since we don't use the active
         * field below */
        TouchEndDDXTouch(dev, ti);
        break;
    default:
        return 0;
    }

    /* Get our screen event coordinates (root_x/root_y/event_x/event_y):
     * these come from the touchpoint in Absolute mode, or the sprite in
     * Relative. */
    if (t->mode == XIDirectTouch) {
        for (i = 0; i < max(valuator_mask_size(&mask), 2); i++) {
            double val;

            if (valuator_mask_fetch_double(&mask, i, &val))
                valuator_mask_set_double(ti->valuators, i, val);
            /* If the device doesn't post new X and Y axis values,
             * use the last values posted.
             */
            else if (i < 2 &&
                valuator_mask_fetch_double(ti->valuators, i, &val))
                valuator_mask_set_double(&mask, i, val);
        }

        transformAbsolute(dev, &mask);
        clipAbsolute(dev, &mask);
    }
    else {
        screenx = dev->spriteInfo->sprite->hotPhys.x;
        screeny = dev->spriteInfo->sprite->hotPhys.y;
    }
    if (need_rawevent)
        set_raw_valuators(raw, &mask, FALSE, raw->valuators.data);

    scr = dev->spriteInfo->sprite->hotPhys.pScreen;

    /* Indirect device touch coordinates are not used for cursor positioning.
     * They are merely informational, and are provided in device coordinates.
     * The device sprite is used for positioning instead, and it is already
     * scaled. */
    if (t->mode == XIDirectTouch)
        scr = scale_to_desktop(dev, &mask, &devx, &devy, &screenx, &screeny);
    if (emulate_pointer)
        scr = positionSprite(dev, Absolute, &mask,
                             &devx, &devy, &screenx, &screeny, NULL, NULL);

    /* see fill_pointer_events for coordinate systems */
    if (emulate_pointer)
        updateHistory(dev, &mask, ms);

    clipValuators(dev, &mask);

    if (emulate_pointer)
        storeLastValuators(dev, &mask, devx, devy);

    /* Update the MD's coordinates, which are always in desktop space. */
    if (emulate_pointer && !IsMaster(dev) && !IsFloating(dev)) {
	    DeviceIntPtr master = GetMaster(dev, MASTER_POINTER);

	    master->last.valuators[0] = screenx;
	    master->last.valuators[1] = screeny;
    }

    event->root = scr->root->drawable.id;

    event_set_root_coordinates(event, screenx - scr->x, screeny - scr->y);
    event->touchid = client_id;
    event->flags = flags;

    if (emulate_pointer) {
        event->flags |= TOUCH_POINTER_EMULATED;
        event->detail.button = 1;
    }

    set_valuators(dev, event, &mask);
    for (i = 0; i < v->numAxes; i++) {
        if (valuator_mask_isset(&mask, i))
            v->axisVal[i] = valuator_mask_get(&mask, i);
    }

    return num_events;
}

void
GetDixTouchEnd(InternalEvent *ievent, DeviceIntPtr dev, TouchPointInfoPtr ti,
               uint32_t flags)
{
    ScreenPtr scr = dev->spriteInfo->sprite->hotPhys.pScreen;
    DeviceEvent *event = &ievent->device_event;
    CARD32 ms = GetTimeInMillis();

    BUG_WARN(!dev->enabled);

    init_device_event(event, dev, ms, EVENT_SOURCE_NORMAL);

    event->sourceid = ti->sourceid;
    event->type = ET_TouchEnd;

    event->root = scr->root->drawable.id;

    /* Get screen event coordinates from the sprite.  Is this really the best
     * we can do? */
    event_set_root_coordinates(event,
                               dev->last.valuators[0] - scr->x,
                               dev->last.valuators[1] - scr->y);
    event->touchid = ti->client_id;
    event->flags = flags;

    if (flags & TOUCH_POINTER_EMULATED) {
        event->flags |= TOUCH_POINTER_EMULATED;
        event->detail.button = 1;
    }
}

/**
 * Synthesize a single motion event for the core pointer.
 *
 * Used in cursor functions, e.g. when cursor confinement changes, and we need
 * to shift the pointer to get it inside the new bounds.
 */
void
PostSyntheticMotion(DeviceIntPtr pDev,
                    int x, int y, int screen, unsigned long time)
{
    DeviceEvent ev;

#ifdef PANORAMIX
    /* Translate back to the sprite screen since processInputProc
       will translate from sprite screen to screen 0 upon reentry
       to the DIX layer. */
    if (!noPanoramiXExtension) {
        x += screenInfo.screens[0]->x - screenInfo.screens[screen]->x;
        y += screenInfo.screens[0]->y - screenInfo.screens[screen]->y;
    }
#endif

    memset(&ev, 0, sizeof(DeviceEvent));
    init_device_event(&ev, pDev, time, EVENT_SOURCE_NORMAL);
    ev.root_x = x;
    ev.root_y = y;
    ev.type = ET_Motion;
    ev.time = time;

    /* FIXME: MD/SD considerations? */
    (*pDev->public.processInputProc) ((InternalEvent *) &ev, pDev);
}

void
InitGestureEvent(InternalEvent *ievent, DeviceIntPtr dev, CARD32 ms,
                 int type, uint16_t num_touches, uint32_t flags,
                 double delta_x, double delta_y,
                 double delta_unaccel_x, double delta_unaccel_y,
                 double scale, double delta_angle)
{
    ScreenPtr scr = dev->spriteInfo->sprite->hotPhys.pScreen;
    GestureEvent *event = &ievent->gesture_event;
    double screenx = 0.0, screeny = 0.0;        /* desktop coordinate system */

    init_gesture_event(event, dev, ms);

    screenx = dev->spriteInfo->sprite->hotPhys.x;
    screeny = dev->spriteInfo->sprite->hotPhys.y;

    event->type = type;
    event->root = scr->root->drawable.id;
    event->root_x = screenx - scr->x;
    event->root_y = screeny - scr->y;
    event->num_touches = num_touches;
    event->flags = flags;

    event->delta_x = delta_x;
    event->delta_y = delta_y;
    event->delta_unaccel_x = delta_unaccel_x;
    event->delta_unaccel_y = delta_unaccel_y;
    event->scale = scale;
    event->delta_angle = delta_angle;
}

/**
 * Get events for a pinch or swipe gesture.
 *
 * events is not NULL-terminated; the return value is the number of events.
 * The DDX is responsible for allocating the event structure in the first
 * place via GetMaximumEventsNum(), and for freeing it.
 *
 * @param[out] events The list of events generated
 * @param dev The device to generate the events for
 * @param type XI_Gesture{Pinch,Swipe}{Begin,Update,End}
 * @prama num_touches The number of touches in the gesture
 * @param flags Event flags
 * @param delta_x,delta_y accelerated relative motion delta
 * @param delta_unaccel_x,delta_unaccel_y unaccelerated relative motion delta
 * @param scale (valid only to pinch events) absolute scale of a pinch gesture
 * @param delta_angle (valid only to pinch events) the ange delta in degrees between the last and
 *        the current pinch event.
 */
int
GetGestureEvents(InternalEvent *events, DeviceIntPtr dev,
                 uint16_t type, uint16_t num_touches, uint32_t flags,
                 double delta_x, double delta_y,
                 double delta_unaccel_x, double delta_unaccel_y,
                 double scale, double delta_angle)

{
    GestureClassPtr g = dev->gesture;
    CARD32 ms = GetTimeInMillis();
    enum EventType evtype;
    int num_events = 0;
    uint32_t evflags = 0;

    if (!dev->enabled || !g)
        return 0;

    if (!IsMaster(dev))
        events = UpdateFromMaster(events, dev, DEVCHANGE_POINTER_EVENT,
                                  &num_events);

    switch (type) {
    case XI_GesturePinchBegin:
        evtype = ET_GesturePinchBegin;
        break;
    case XI_GesturePinchUpdate:
        evtype = ET_GesturePinchUpdate;
        break;
    case XI_GesturePinchEnd:
        evtype = ET_GesturePinchEnd;
        if (flags & XIGesturePinchEventCancelled)
            evflags |= GESTURE_CANCELLED;
        break;
    case XI_GestureSwipeBegin:
        evtype = ET_GestureSwipeBegin;
        break;
    case XI_GestureSwipeUpdate:
        evtype = ET_GestureSwipeUpdate;
        break;
    case XI_GestureSwipeEnd:
        evtype = ET_GestureSwipeEnd;
        if (flags & XIGestureSwipeEventCancelled)
            evflags |= GESTURE_CANCELLED;
        break;
    default:
        return 0;
    }

    InitGestureEvent(events, dev, ms, evtype, num_touches, evflags,
                     delta_x, delta_y, delta_unaccel_x, delta_unaccel_y,
                     scale, delta_angle);
    num_events++;

    return num_events;
}

void
QueueGesturePinchEvents(DeviceIntPtr dev, uint16_t type,
                        uint16_t num_touches, uint32_t flags,
                        double delta_x, double delta_y,
                        double delta_unaccel_x,
                        double delta_unaccel_y,
                        double scale, double delta_angle)
{
    int nevents;
    nevents = GetGestureEvents(InputEventList, dev, type, num_touches, flags,
                               delta_x, delta_y,
                               delta_unaccel_x, delta_unaccel_y,
                               scale, delta_angle);
    queueEventList(dev, InputEventList, nevents);
}

void
QueueGestureSwipeEvents(DeviceIntPtr dev, uint16_t type,
                        uint16_t num_touches, uint32_t flags,
                        double delta_x, double delta_y,
                        double delta_unaccel_x,
                        double delta_unaccel_y)
{
    int nevents;
    nevents = GetGestureEvents(InputEventList, dev, type, num_touches, flags,
                               delta_x, delta_y,
                               delta_unaccel_x, delta_unaccel_y,
                               0.0, 0.0);
    queueEventList(dev, InputEventList, nevents);
}
