Edit

IABSD.fr/xenocara/xserver/dix/eventconvert.c

Branch :

  • Show log

    Commit

  • Author : matthieu
    Date : 2025-03-02 09:09:28
    Hash : 3cfba106
    Message : Update to xserver 21.1.16. The security fixes were committed earlier. This is the rest of the 21.1.16 update.

  • xserver/dix/eventconvert.c
  • /*
     * Copyright © 2009 Red Hat, 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 (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.
     *
     */
    
    /**
     * @file eventconvert.c
     * This file contains event conversion routines from InternalEvent to the
     * matching protocol events.
     */
    
    #ifdef HAVE_DIX_CONFIG_H
    #include <dix-config.h>
    #endif
    
    #include <stdint.h>
    #include <X11/X.h>
    #include <X11/extensions/XIproto.h>
    #include <X11/extensions/XI2proto.h>
    #include <X11/extensions/XI.h>
    #include <X11/extensions/XI2.h>
    
    #include "dix.h"
    #include "inputstr.h"
    #include "misc.h"
    #include "eventstr.h"
    #include "exevents.h"
    #include "exglobals.h"
    #include "eventconvert.h"
    #include "inpututils.h"
    #include "xiquerydevice.h"
    #include "xkbsrv.h"
    #include "inpututils.h"
    
    static int countValuators(DeviceEvent *ev, int *first);
    static int getValuatorEvents(DeviceEvent *ev, deviceValuator * xv);
    static int eventToKeyButtonPointer(DeviceEvent *ev, xEvent **xi, int *count);
    static int eventToDeviceChanged(DeviceChangedEvent *ev, xEvent **dcce);
    static int eventToDeviceEvent(DeviceEvent *ev, xEvent **xi);
    static int eventToRawEvent(RawDeviceEvent *ev, xEvent **xi);
    static int eventToBarrierEvent(BarrierEvent *ev, xEvent **xi);
    static int eventToTouchOwnershipEvent(TouchOwnershipEvent *ev, xEvent **xi);
    static int eventToGestureSwipeEvent(GestureEvent *ev, xEvent **xi);
    static int eventToGesturePinchEvent(GestureEvent *ev, xEvent **xi);
    
    /* Do not use, read comments below */
    BOOL EventIsKeyRepeat(xEvent *event);
    
    /**
     * Hack to allow detectable autorepeat for core and XI1 events.
     * The sequence number is unused until we send to the client and can be
     * misused to store data. More or less, anyway.
     *
     * Do not use this. It may change any time without warning, eat your babies
     * and piss on your cat.
     */
    static void
    EventSetKeyRepeatFlag(xEvent *event, BOOL on)
    {
        event->u.u.sequenceNumber = on;
    }
    
    /**
     * Check if the event was marked as a repeat event before.
     * NOTE: This is a nasty hack and should NOT be used by anyone else but
     * TryClientEvents.
     */
    BOOL
    EventIsKeyRepeat(xEvent *event)
    {
        return ! !event->u.u.sequenceNumber;
    }
    
    /**
     * Convert the given event to the respective core event.
     *
     * Return values:
     * Success ... core contains the matching core event.
     * BadValue .. One or more values in the internal event are invalid.
     * BadMatch .. The event has no core equivalent.
     *
     * @param[in] event The event to convert into a core event.
     * @param[in] core The memory location to store the core event at.
     * @return Success or the matching error code.
     */
    int
    EventToCore(InternalEvent *event, xEvent **core_out, int *count_out)
    {
        xEvent *core = NULL;
        int count = 0;
        int ret = BadImplementation;
    
        switch (event->any.type) {
        case ET_Motion:
        {
            DeviceEvent *e = &event->device_event;
    
            /* Don't create core motion event if neither x nor y are
             * present */
            if (!BitIsOn(e->valuators.mask, 0) && !BitIsOn(e->valuators.mask, 1)) {
                ret = BadMatch;
                goto out;
            }
        }
            /* fallthrough */
        case ET_ButtonPress:
        case ET_ButtonRelease:
        case ET_KeyPress:
        case ET_KeyRelease:
        {
            DeviceEvent *e = &event->device_event;
    
            if (e->detail.key > 0xFF) {
                ret = BadMatch;
                goto out;
            }
    
            core = calloc(1, sizeof(*core));
            if (!core)
                return BadAlloc;
            count = 1;
            core->u.u.type = e->type - ET_KeyPress + KeyPress;
            core->u.u.detail = e->detail.key & 0xFF;
            core->u.keyButtonPointer.time = e->time;
            core->u.keyButtonPointer.rootX = e->root_x;
            core->u.keyButtonPointer.rootY = e->root_y;
            core->u.keyButtonPointer.state = e->corestate;
            core->u.keyButtonPointer.root = e->root;
            EventSetKeyRepeatFlag(core, (e->type == ET_KeyPress && e->key_repeat));
            ret = Success;
        }
            break;
        case ET_ProximityIn:
        case ET_ProximityOut:
        case ET_RawKeyPress:
        case ET_RawKeyRelease:
        case ET_RawButtonPress:
        case ET_RawButtonRelease:
        case ET_RawMotion:
        case ET_RawTouchBegin:
        case ET_RawTouchUpdate:
        case ET_RawTouchEnd:
        case ET_TouchBegin:
        case ET_TouchUpdate:
        case ET_TouchEnd:
        case ET_TouchOwnership:
        case ET_BarrierHit:
        case ET_BarrierLeave:
        case ET_GesturePinchBegin:
        case ET_GesturePinchUpdate:
        case ET_GesturePinchEnd:
        case ET_GestureSwipeBegin:
        case ET_GestureSwipeUpdate:
        case ET_GestureSwipeEnd:
            ret = BadMatch;
            break;
        default:
            /* XXX: */
            ErrorF("[dix] EventToCore: Not implemented yet \n");
            ret = BadImplementation;
        }
    
     out:
        *core_out = core;
        *count_out = count;
        return ret;
    }
    
    /**
     * Convert the given event to the respective XI 1.x event and store it in
     * xi. xi is allocated on demand and must be freed by the caller.
     * count returns the number of events in xi. If count is 1, and the type of
     * xi is GenericEvent, then xi may be larger than 32 bytes.
     *
     * Return values:
     * Success ... core contains the matching core event.
     * BadValue .. One or more values in the internal event are invalid.
     * BadMatch .. The event has no XI equivalent.
     *
     * @param[in] ev The event to convert into an XI 1 event.
     * @param[out] xi Future memory location for the XI event.
     * @param[out] count Number of elements in xi.
     *
     * @return Success or the error code.
     */
    int
    EventToXI(InternalEvent *ev, xEvent **xi, int *count)
    {
        switch (ev->any.type) {
        case ET_Motion:
        case ET_ButtonPress:
        case ET_ButtonRelease:
        case ET_KeyPress:
        case ET_KeyRelease:
        case ET_ProximityIn:
        case ET_ProximityOut:
            return eventToKeyButtonPointer(&ev->device_event, xi, count);
        case ET_DeviceChanged:
        case ET_RawKeyPress:
        case ET_RawKeyRelease:
        case ET_RawButtonPress:
        case ET_RawButtonRelease:
        case ET_RawMotion:
        case ET_RawTouchBegin:
        case ET_RawTouchUpdate:
        case ET_RawTouchEnd:
        case ET_TouchBegin:
        case ET_TouchUpdate:
        case ET_TouchEnd:
        case ET_TouchOwnership:
        case ET_BarrierHit:
        case ET_BarrierLeave:
        case ET_GesturePinchBegin:
        case ET_GesturePinchUpdate:
        case ET_GesturePinchEnd:
        case ET_GestureSwipeBegin:
        case ET_GestureSwipeUpdate:
        case ET_GestureSwipeEnd:
            *count = 0;
            *xi = NULL;
            return BadMatch;
        default:
            break;
        }
    
        ErrorF("[dix] EventToXI: Not implemented for %d \n", ev->any.type);
        return BadImplementation;
    }
    
    /**
     * Convert the given event to the respective XI 2.x event and store it in xi.
     * xi is allocated on demand and must be freed by the caller.
     *
     * Return values:
     * Success ... core contains the matching core event.
     * BadValue .. One or more values in the internal event are invalid.
     * BadMatch .. The event has no XI2 equivalent.
     *
     * @param[in] ev The event to convert into an XI2 event
     * @param[out] xi Future memory location for the XI2 event.
     *
     * @return Success or the error code.
     */
    int
    EventToXI2(InternalEvent *ev, xEvent **xi)
    {
        switch (ev->any.type) {
            /* Enter/FocusIn are for grabs. We don't need an actual event, since
             * the real events delivered are triggered elsewhere */
        case ET_Enter:
        case ET_FocusIn:
            *xi = NULL;
            return Success;
        case ET_Motion:
        case ET_ButtonPress:
        case ET_ButtonRelease:
        case ET_KeyPress:
        case ET_KeyRelease:
        case ET_TouchBegin:
        case ET_TouchUpdate:
        case ET_TouchEnd:
            return eventToDeviceEvent(&ev->device_event, xi);
        case ET_TouchOwnership:
            return eventToTouchOwnershipEvent(&ev->touch_ownership_event, xi);
        case ET_ProximityIn:
        case ET_ProximityOut:
            *xi = NULL;
            return BadMatch;
        case ET_DeviceChanged:
            return eventToDeviceChanged(&ev->changed_event, xi);
        case ET_RawKeyPress:
        case ET_RawKeyRelease:
        case ET_RawButtonPress:
        case ET_RawButtonRelease:
        case ET_RawMotion:
        case ET_RawTouchBegin:
        case ET_RawTouchUpdate:
        case ET_RawTouchEnd:
            return eventToRawEvent(&ev->raw_event, xi);
        case ET_BarrierHit:
        case ET_BarrierLeave:
            return eventToBarrierEvent(&ev->barrier_event, xi);
        case ET_GesturePinchBegin:
        case ET_GesturePinchUpdate:
        case ET_GesturePinchEnd:
            return eventToGesturePinchEvent(&ev->gesture_event, xi);
        case ET_GestureSwipeBegin:
        case ET_GestureSwipeUpdate:
        case ET_GestureSwipeEnd:
            return eventToGestureSwipeEvent(&ev->gesture_event, xi);
        default:
            break;
        }
    
        ErrorF("[dix] EventToXI2: Not implemented for %d \n", ev->any.type);
        return BadImplementation;
    }
    
    static int
    eventToKeyButtonPointer(DeviceEvent *ev, xEvent **xi, int *count)
    {
        int num_events;
        int first;                  /* dummy */
        deviceKeyButtonPointer *kbp;
    
        /* Sorry, XI 1.x protocol restrictions. */
        if (ev->detail.button > 0xFF || ev->deviceid >= 0x80) {
            *count = 0;
            return Success;
        }
    
        num_events = (countValuators(ev, &first) + 5) / 6;  /* valuator ev */
        if (num_events <= 0) {
            switch (ev->type) {
            case ET_KeyPress:
            case ET_KeyRelease:
            case ET_ButtonPress:
            case ET_ButtonRelease:
                /* no axes is ok */
                break;
            case ET_Motion:
            case ET_ProximityIn:
            case ET_ProximityOut:
                *count = 0;
                return BadMatch;
            default:
                *count = 0;
                return BadImplementation;
            }
        }
    
        num_events++;               /* the actual event event */
    
        *xi = calloc(num_events, sizeof(xEvent));
        if (!(*xi)) {
            return BadAlloc;
        }
    
        kbp = (deviceKeyButtonPointer *) (*xi);
        kbp->detail = ev->detail.button;
        kbp->time = ev->time;
        kbp->root = ev->root;
        kbp->root_x = ev->root_x;
        kbp->root_y = ev->root_y;
        kbp->deviceid = ev->deviceid;
        kbp->state = ev->corestate;
        EventSetKeyRepeatFlag((xEvent *) kbp,
                              (ev->type == ET_KeyPress && ev->key_repeat));
    
        if (num_events > 1)
            kbp->deviceid |= MORE_EVENTS;
    
        switch (ev->type) {
        case ET_Motion:
            kbp->type = DeviceMotionNotify;
            break;
        case ET_ButtonPress:
            kbp->type = DeviceButtonPress;
            break;
        case ET_ButtonRelease:
            kbp->type = DeviceButtonRelease;
            break;
        case ET_KeyPress:
            kbp->type = DeviceKeyPress;
            break;
        case ET_KeyRelease:
            kbp->type = DeviceKeyRelease;
            break;
        case ET_ProximityIn:
            kbp->type = ProximityIn;
            break;
        case ET_ProximityOut:
            kbp->type = ProximityOut;
            break;
        default:
            break;
        }
    
        if (num_events > 1) {
            getValuatorEvents(ev, (deviceValuator *) (kbp + 1));
        }
    
        *count = num_events;
        return Success;
    }
    
    /**
     * Set first to the first valuator in the event ev and return the number of
     * valuators from first to the last set valuator.
     */
    static int
    countValuators(DeviceEvent *ev, int *first)
    {
        int first_valuator = -1, last_valuator = -1, num_valuators = 0;
        int i;
    
        for (i = 0; i < sizeof(ev->valuators.mask) * 8; i++) {
            if (BitIsOn(ev->valuators.mask, i)) {
                if (first_valuator == -1)
                    first_valuator = i;
                last_valuator = i;
            }
        }
    
        if (first_valuator != -1) {
            num_valuators = last_valuator - first_valuator + 1;
            *first = first_valuator;
        }
    
        return num_valuators;
    }
    
    static int
    getValuatorEvents(DeviceEvent *ev, deviceValuator * xv)
    {
        int i;
        int state = 0;
        int first_valuator, num_valuators;
    
        num_valuators = countValuators(ev, &first_valuator);
        if (num_valuators > 0) {
            DeviceIntPtr dev = NULL;
    
            dixLookupDevice(&dev, ev->deviceid, serverClient, DixUseAccess);
            /* State needs to be assembled BEFORE the device is updated. */
            state = (dev &&
                     dev->key) ? XkbStateFieldFromRec(&dev->key->xkbInfo->
                                                      state) : 0;
            state |= (dev && dev->button) ? (dev->button->state) : 0;
        }
    
        for (i = 0; i < num_valuators; i += 6, xv++) {
            INT32 *valuators = &xv->valuator0;      // Treat all 6 vals as an array
            int j;
    
            xv->type = DeviceValuator;
            xv->first_valuator = first_valuator + i;
            xv->num_valuators = ((num_valuators - i) > 6) ? 6 : (num_valuators - i);
            xv->deviceid = ev->deviceid;
            xv->device_state = state;
    
            /* Unset valuators in masked valuator events have the proper data values
             * in the case of an absolute axis in between two set valuators. */
            for (j = 0; j < xv->num_valuators; j++)
                valuators[j] = ev->valuators.data[xv->first_valuator + j];
    
            if (i + 6 < num_valuators)
                xv->deviceid |= MORE_EVENTS;
        }
    
        return (num_valuators + 5) / 6;
    }
    
    static int
    appendKeyInfo(DeviceChangedEvent *dce, xXIKeyInfo * info)
    {
        uint32_t *kc;
        int i;
    
        info->type = XIKeyClass;
        info->num_keycodes = dce->keys.max_keycode - dce->keys.min_keycode + 1;
        info->length = sizeof(xXIKeyInfo) / 4 + info->num_keycodes;
        info->sourceid = dce->sourceid;
    
        kc = (uint32_t *) &info[1];
        for (i = 0; i < info->num_keycodes; i++)
            *kc++ = i + dce->keys.min_keycode;
    
        return info->length * 4;
    }
    
    static int
    appendButtonInfo(DeviceChangedEvent *dce, xXIButtonInfo * info)
    {
        unsigned char *bits;
        int mask_len;
    
        mask_len = bytes_to_int32(bits_to_bytes(dce->buttons.num_buttons));
    
        info->type = XIButtonClass;
        info->num_buttons = dce->buttons.num_buttons;
        info->length = bytes_to_int32(sizeof(xXIButtonInfo)) +
            info->num_buttons + mask_len;
        info->sourceid = dce->sourceid;
    
        bits = (unsigned char *) &info[1];
        memset(bits, 0, mask_len * 4);
        /* FIXME: is_down? */
    
        bits += mask_len * 4;
        memcpy(bits, dce->buttons.names, dce->buttons.num_buttons * sizeof(Atom));
    
        return info->length * 4;
    }
    
    static int
    appendValuatorInfo(DeviceChangedEvent *dce, xXIValuatorInfo * info,
                       int axisnumber)
    {
        info->type = XIValuatorClass;
        info->length = sizeof(xXIValuatorInfo) / 4;
        info->label = dce->valuators[axisnumber].name;
        info->min.integral = dce->valuators[axisnumber].min;
        info->min.frac = 0;
        info->max.integral = dce->valuators[axisnumber].max;
        info->max.frac = 0;
        info->value = double_to_fp3232(dce->valuators[axisnumber].value);
        info->resolution = dce->valuators[axisnumber].resolution;
        info->number = axisnumber;
        info->mode = dce->valuators[axisnumber].mode;
        info->sourceid = dce->sourceid;
    
        return info->length * 4;
    }
    
    static int
    appendScrollInfo(DeviceChangedEvent *dce, xXIScrollInfo * info, int axisnumber)
    {
        if (dce->valuators[axisnumber].scroll.type == SCROLL_TYPE_NONE)
            return 0;
    
        info->type = XIScrollClass;
        info->length = sizeof(xXIScrollInfo) / 4;
        info->number = axisnumber;
        switch (dce->valuators[axisnumber].scroll.type) {
        case SCROLL_TYPE_VERTICAL:
            info->scroll_type = XIScrollTypeVertical;
            break;
        case SCROLL_TYPE_HORIZONTAL:
            info->scroll_type = XIScrollTypeHorizontal;
            break;
        default:
            ErrorF("[Xi] Unknown scroll type %d. This is a bug.\n",
                   dce->valuators[axisnumber].scroll.type);
            break;
        }
        info->increment =
            double_to_fp3232(dce->valuators[axisnumber].scroll.increment);
        info->sourceid = dce->sourceid;
    
        info->flags = 0;
    
        if (dce->valuators[axisnumber].scroll.flags & SCROLL_FLAG_DONT_EMULATE)
            info->flags |= XIScrollFlagNoEmulation;
        if (dce->valuators[axisnumber].scroll.flags & SCROLL_FLAG_PREFERRED)
            info->flags |= XIScrollFlagPreferred;
    
        return info->length * 4;
    }
    
    static int
    eventToDeviceChanged(DeviceChangedEvent *dce, xEvent **xi)
    {
        xXIDeviceChangedEvent *dcce;
        int len = sizeof(xXIDeviceChangedEvent);
        int nkeys;
        char *ptr;
    
        if (dce->buttons.num_buttons) {
            len += sizeof(xXIButtonInfo);
            len += dce->buttons.num_buttons * sizeof(Atom); /* button names */
            len += pad_to_int32(bits_to_bytes(dce->buttons.num_buttons));
        }
        if (dce->num_valuators) {
            int i;
    
            len += sizeof(xXIValuatorInfo) * dce->num_valuators;
    
            for (i = 0; i < dce->num_valuators; i++)
                if (dce->valuators[i].scroll.type != SCROLL_TYPE_NONE)
                    len += sizeof(xXIScrollInfo);
        }
    
        nkeys = (dce->keys.max_keycode > 0) ?
            dce->keys.max_keycode - dce->keys.min_keycode + 1 : 0;
        if (nkeys > 0) {
            len += sizeof(xXIKeyInfo);
            len += sizeof(CARD32) * nkeys;  /* keycodes */
        }
    
        dcce = calloc(1, len);
        if (!dcce) {
            ErrorF("[Xi] BadAlloc in SendDeviceChangedEvent.\n");
            return BadAlloc;
        }
    
        dcce->type = GenericEvent;
        dcce->extension = IReqCode;
        dcce->evtype = XI_DeviceChanged;
        dcce->time = dce->time;
        dcce->deviceid = dce->deviceid;
        dcce->sourceid = dce->sourceid;
        dcce->reason =
            (dce->flags & DEVCHANGE_DEVICE_CHANGE) ? XIDeviceChange : XISlaveSwitch;
        dcce->num_classes = 0;
        dcce->length = bytes_to_int32(len - sizeof(xEvent));
    
        ptr = (char *) &dcce[1];
        if (dce->buttons.num_buttons) {
            dcce->num_classes++;
            ptr += appendButtonInfo(dce, (xXIButtonInfo *) ptr);
        }
    
        if (nkeys) {
            dcce->num_classes++;
            ptr += appendKeyInfo(dce, (xXIKeyInfo *) ptr);
        }
    
        if (dce->num_valuators) {
            int i;
    
            dcce->num_classes += dce->num_valuators;
            for (i = 0; i < dce->num_valuators; i++)
                ptr += appendValuatorInfo(dce, (xXIValuatorInfo *) ptr, i);
    
            for (i = 0; i < dce->num_valuators; i++) {
                if (dce->valuators[i].scroll.type != SCROLL_TYPE_NONE) {
                    dcce->num_classes++;
                    ptr += appendScrollInfo(dce, (xXIScrollInfo *) ptr, i);
                }
            }
        }
    
        *xi = (xEvent *) dcce;
    
        return Success;
    }
    
    static int
    count_bits(unsigned char *ptr, int len)
    {
        int bits = 0;
        unsigned int i;
        unsigned char x;
    
        for (i = 0; i < len; i++) {
            x = ptr[i];
            while (x > 0) {
                bits += (x & 0x1);
                x >>= 1;
            }
        }
        return bits;
    }
    
    static int
    eventToDeviceEvent(DeviceEvent *ev, xEvent **xi)
    {
        int len = sizeof(xXIDeviceEvent);
        xXIDeviceEvent *xde;
        int i, btlen, vallen;
        char *ptr;
        FP3232 *axisval;
    
        /* FIXME: this should just send the buttons we have, not MAX_BUTTONs. Same
         * with MAX_VALUATORS below */
        /* btlen is in 4 byte units */
        btlen = bytes_to_int32(bits_to_bytes(MAX_BUTTONS));
        len += btlen * 4;           /* buttonmask len */
    
        vallen = count_bits(ev->valuators.mask, ARRAY_SIZE(ev->valuators.mask));
        len += vallen * 2 * sizeof(uint32_t);       /* axisvalues */
        vallen = bytes_to_int32(bits_to_bytes(MAX_VALUATORS));
        len += vallen * 4;          /* valuators mask */
    
        *xi = calloc(1, len);
        if (*xi == NULL)
            return BadAlloc;
        xde = (xXIDeviceEvent *) * xi;
        xde->type = GenericEvent;
        xde->extension = IReqCode;
        xde->evtype = GetXI2Type(ev->type);
        xde->time = ev->time;
        xde->length = bytes_to_int32(len - sizeof(xEvent));
        if (IsTouchEvent((InternalEvent *) ev))
            xde->detail = ev->touchid;
        else
            xde->detail = ev->detail.button;
    
        xde->root = ev->root;
        xde->buttons_len = btlen;
        xde->valuators_len = vallen;
        xde->deviceid = ev->deviceid;
        xde->sourceid = ev->sourceid;
        xde->root_x = double_to_fp1616(ev->root_x + ev->root_x_frac);
        xde->root_y = double_to_fp1616(ev->root_y + ev->root_y_frac);
    
        if (IsTouchEvent((InternalEvent *)ev)) {
            if (ev->type == ET_TouchUpdate)
                xde->flags |= (ev->flags & TOUCH_PENDING_END) ? XITouchPendingEnd : 0;
    
            if (ev->flags & TOUCH_POINTER_EMULATED)
                xde->flags |= XITouchEmulatingPointer;
        } else {
            xde->flags = ev->flags;
    
            if (ev->key_repeat)
                xde->flags |= XIKeyRepeat;
        }
    
        xde->mods.base_mods = ev->mods.base;
        xde->mods.latched_mods = ev->mods.latched;
        xde->mods.locked_mods = ev->mods.locked;
        xde->mods.effective_mods = ev->mods.effective;
    
        xde->group.base_group = ev->group.base;
        xde->group.latched_group = ev->group.latched;
        xde->group.locked_group = ev->group.locked;
        xde->group.effective_group = ev->group.effective;
    
        ptr = (char *) &xde[1];
        for (i = 0; i < sizeof(ev->buttons) * 8; i++) {
            if (BitIsOn(ev->buttons, i))
                SetBit(ptr, i);
        }
    
        ptr += xde->buttons_len * 4;
        axisval = (FP3232 *) (ptr + xde->valuators_len * 4);
        for (i = 0; i < MAX_VALUATORS; i++) {
            if (BitIsOn(ev->valuators.mask, i)) {
                SetBit(ptr, i);
                *axisval = double_to_fp3232(ev->valuators.data[i]);
                axisval++;
            }
        }
    
        return Success;
    }
    
    static int
    eventToTouchOwnershipEvent(TouchOwnershipEvent *ev, xEvent **xi)
    {
        int len = sizeof(xXITouchOwnershipEvent);
        xXITouchOwnershipEvent *xtoe;
    
        *xi = calloc(1, len);
        if (*xi == NULL)
            return BadAlloc;
        xtoe = (xXITouchOwnershipEvent *) * xi;
        xtoe->type = GenericEvent;
        xtoe->extension = IReqCode;
        xtoe->length = bytes_to_int32(len - sizeof(xEvent));
        xtoe->evtype = GetXI2Type(ev->type);
        xtoe->deviceid = ev->deviceid;
        xtoe->time = ev->time;
        xtoe->sourceid = ev->sourceid;
        xtoe->touchid = ev->touchid;
        xtoe->flags = 0;            /* we don't have wire flags for ownership yet */
    
        return Success;
    }
    
    static int
    eventToRawEvent(RawDeviceEvent *ev, xEvent **xi)
    {
        xXIRawEvent *raw;
        int vallen, nvals;
        int i, len = sizeof(xXIRawEvent);
        char *ptr;
        FP3232 *axisval, *axisval_raw;
    
        nvals = count_bits(ev->valuators.mask, sizeof(ev->valuators.mask));
        len += nvals * sizeof(FP3232) * 2;  /* 8 byte per valuator, once
                                               raw, once processed */
        vallen = bytes_to_int32(bits_to_bytes(MAX_VALUATORS));
        len += vallen * 4;          /* valuators mask */
    
        *xi = calloc(1, len);
        if (*xi == NULL)
            return BadAlloc;
        raw = (xXIRawEvent *) * xi;
        raw->type = GenericEvent;
        raw->extension = IReqCode;
        raw->evtype = GetXI2Type(ev->type);
        raw->time = ev->time;
        raw->length = bytes_to_int32(len - sizeof(xEvent));
        raw->detail = ev->detail.button;
        raw->deviceid = ev->deviceid;
        raw->sourceid = ev->sourceid;
        raw->valuators_len = vallen;
        raw->flags = ev->flags;
    
        ptr = (char *) &raw[1];
        axisval = (FP3232 *) (ptr + raw->valuators_len * 4);
        axisval_raw = axisval + nvals;
        for (i = 0; i < MAX_VALUATORS; i++) {
            if (BitIsOn(ev->valuators.mask, i)) {
                SetBit(ptr, i);
                *axisval = double_to_fp3232(ev->valuators.data[i]);
                *axisval_raw = double_to_fp3232(ev->valuators.data_raw[i]);
                axisval++;
                axisval_raw++;
            }
        }
    
        return Success;
    }
    
    static int
    eventToBarrierEvent(BarrierEvent *ev, xEvent **xi)
    {
        xXIBarrierEvent *barrier;
        int len = sizeof(xXIBarrierEvent);
    
        *xi = calloc(1, len);
        if (*xi == NULL)
            return BadAlloc;
        barrier = (xXIBarrierEvent*) *xi;
        barrier->type = GenericEvent;
        barrier->extension = IReqCode;
        barrier->evtype = GetXI2Type(ev->type);
        barrier->length = bytes_to_int32(len - sizeof(xEvent));
        barrier->deviceid = ev->deviceid;
        barrier->sourceid = ev->sourceid;
        barrier->time = ev->time;
        barrier->event = ev->window;
        barrier->root = ev->root;
        barrier->dx = double_to_fp3232(ev->dx);
        barrier->dy = double_to_fp3232(ev->dy);
        barrier->dtime = ev->dt;
        barrier->flags = ev->flags;
        barrier->eventid = ev->event_id;
        barrier->barrier = ev->barrierid;
        barrier->root_x = double_to_fp1616(ev->root_x);
        barrier->root_y = double_to_fp1616(ev->root_y);
    
        return Success;
    }
    
    int
    eventToGesturePinchEvent(GestureEvent *ev, xEvent **xi)
    {
        int len = sizeof(xXIGesturePinchEvent);
        xXIGesturePinchEvent *xpe;
    
        *xi = calloc(1, len);
        if (*xi == NULL)
            return BadAlloc;
        xpe = (xXIGesturePinchEvent *) * xi;
        xpe->type = GenericEvent;
        xpe->extension = IReqCode;
        xpe->evtype = GetXI2Type(ev->type);
        xpe->time = ev->time;
        xpe->length = bytes_to_int32(len - sizeof(xEvent));
        xpe->detail = ev->num_touches;
    
        xpe->root = ev->root;
        xpe->deviceid = ev->deviceid;
        xpe->sourceid = ev->sourceid;
        xpe->root_x = double_to_fp1616(ev->root_x);
        xpe->root_y = double_to_fp1616(ev->root_y);
        xpe->flags |= (ev->flags & GESTURE_CANCELLED) ? XIGesturePinchEventCancelled : 0;
    
        xpe->delta_x = double_to_fp1616(ev->delta_x);
        xpe->delta_y = double_to_fp1616(ev->delta_y);
        xpe->delta_unaccel_x = double_to_fp1616(ev->delta_unaccel_x);
        xpe->delta_unaccel_y = double_to_fp1616(ev->delta_unaccel_y);
        xpe->scale = double_to_fp1616(ev->scale);
        xpe->delta_angle = double_to_fp1616(ev->delta_angle);
    
        xpe->mods.base_mods = ev->mods.base;
        xpe->mods.latched_mods = ev->mods.latched;
        xpe->mods.locked_mods = ev->mods.locked;
        xpe->mods.effective_mods = ev->mods.effective;
    
        xpe->group.base_group = ev->group.base;
        xpe->group.latched_group = ev->group.latched;
        xpe->group.locked_group = ev->group.locked;
        xpe->group.effective_group = ev->group.effective;
    
        return Success;
    }
    
    int
    eventToGestureSwipeEvent(GestureEvent *ev, xEvent **xi)
    {
        int len = sizeof(xXIGestureSwipeEvent);
        xXIGestureSwipeEvent *xde;
    
        *xi = calloc(1, len);
        if (*xi == NULL)
            return BadAlloc;
        xde = (xXIGestureSwipeEvent *) * xi;
        xde->type = GenericEvent;
        xde->extension = IReqCode;
        xde->evtype = GetXI2Type(ev->type);
        xde->time = ev->time;
        xde->length = bytes_to_int32(len - sizeof(xEvent));
        xde->detail = ev->num_touches;
    
        xde->root = ev->root;
        xde->deviceid = ev->deviceid;
        xde->sourceid = ev->sourceid;
        xde->root_x = double_to_fp1616(ev->root_x);
        xde->root_y = double_to_fp1616(ev->root_y);
        xde->flags |= (ev->flags & GESTURE_CANCELLED) ? XIGestureSwipeEventCancelled : 0;
    
        xde->delta_x = double_to_fp1616(ev->delta_x);
        xde->delta_y = double_to_fp1616(ev->delta_y);
        xde->delta_unaccel_x = double_to_fp1616(ev->delta_unaccel_x);
        xde->delta_unaccel_y = double_to_fp1616(ev->delta_unaccel_y);
    
        xde->mods.base_mods = ev->mods.base;
        xde->mods.latched_mods = ev->mods.latched;
        xde->mods.locked_mods = ev->mods.locked;
        xde->mods.effective_mods = ev->mods.effective;
    
        xde->group.base_group = ev->group.base;
        xde->group.latched_group = ev->group.latched;
        xde->group.locked_group = ev->group.locked;
        xde->group.effective_group = ev->group.effective;
    
        return Success;
    }
    
    /**
     * Return the corresponding core type for the given event or 0 if no core
     * equivalent exists.
     */
    int
    GetCoreType(enum EventType type)
    {
        int coretype = 0;
    
        switch (type) {
        case ET_Motion:
            coretype = MotionNotify;
            break;
        case ET_ButtonPress:
            coretype = ButtonPress;
            break;
        case ET_ButtonRelease:
            coretype = ButtonRelease;
            break;
        case ET_KeyPress:
            coretype = KeyPress;
            break;
        case ET_KeyRelease:
            coretype = KeyRelease;
            break;
        default:
            break;
        }
        return coretype;
    }
    
    /**
     * Return the corresponding XI 1.x type for the given event or 0 if no
     * equivalent exists.
     */
    int
    GetXIType(enum EventType type)
    {
        int xitype = 0;
    
        switch (type) {
        case ET_Motion:
            xitype = DeviceMotionNotify;
            break;
        case ET_ButtonPress:
            xitype = DeviceButtonPress;
            break;
        case ET_ButtonRelease:
            xitype = DeviceButtonRelease;
            break;
        case ET_KeyPress:
            xitype = DeviceKeyPress;
            break;
        case ET_KeyRelease:
            xitype = DeviceKeyRelease;
            break;
        case ET_ProximityIn:
            xitype = ProximityIn;
            break;
        case ET_ProximityOut:
            xitype = ProximityOut;
            break;
        default:
            break;
        }
        return xitype;
    }
    
    /**
     * Return the corresponding XI 2.x type for the given event or 0 if no
     * equivalent exists.
     */
    int
    GetXI2Type(enum EventType type)
    {
        int xi2type = 0;
    
        switch (type) {
        case ET_Motion:
            xi2type = XI_Motion;
            break;
        case ET_ButtonPress:
            xi2type = XI_ButtonPress;
            break;
        case ET_ButtonRelease:
            xi2type = XI_ButtonRelease;
            break;
        case ET_KeyPress:
            xi2type = XI_KeyPress;
            break;
        case ET_KeyRelease:
            xi2type = XI_KeyRelease;
            break;
        case ET_Enter:
            xi2type = XI_Enter;
            break;
        case ET_Leave:
            xi2type = XI_Leave;
            break;
        case ET_Hierarchy:
            xi2type = XI_HierarchyChanged;
            break;
        case ET_DeviceChanged:
            xi2type = XI_DeviceChanged;
            break;
        case ET_RawKeyPress:
            xi2type = XI_RawKeyPress;
            break;
        case ET_RawKeyRelease:
            xi2type = XI_RawKeyRelease;
            break;
        case ET_RawButtonPress:
            xi2type = XI_RawButtonPress;
            break;
        case ET_RawButtonRelease:
            xi2type = XI_RawButtonRelease;
            break;
        case ET_RawMotion:
            xi2type = XI_RawMotion;
            break;
        case ET_RawTouchBegin:
            xi2type = XI_RawTouchBegin;
            break;
        case ET_RawTouchUpdate:
            xi2type = XI_RawTouchUpdate;
            break;
        case ET_RawTouchEnd:
            xi2type = XI_RawTouchEnd;
            break;
        case ET_FocusIn:
            xi2type = XI_FocusIn;
            break;
        case ET_FocusOut:
            xi2type = XI_FocusOut;
            break;
        case ET_TouchBegin:
            xi2type = XI_TouchBegin;
            break;
        case ET_TouchEnd:
            xi2type = XI_TouchEnd;
            break;
        case ET_TouchUpdate:
            xi2type = XI_TouchUpdate;
            break;
        case ET_TouchOwnership:
            xi2type = XI_TouchOwnership;
            break;
        case ET_BarrierHit:
            xi2type = XI_BarrierHit;
            break;
        case ET_BarrierLeave:
            xi2type = XI_BarrierLeave;
            break;
        case ET_GesturePinchBegin:
            xi2type = XI_GesturePinchBegin;
            break;
        case ET_GesturePinchUpdate:
            xi2type = XI_GesturePinchUpdate;
            break;
        case ET_GesturePinchEnd:
            xi2type = XI_GesturePinchEnd;
            break;
        case ET_GestureSwipeBegin:
            xi2type = XI_GestureSwipeBegin;
            break;
        case ET_GestureSwipeUpdate:
            xi2type = XI_GestureSwipeUpdate;
            break;
        case ET_GestureSwipeEnd:
            xi2type = XI_GestureSwipeEnd;
            break;
        default:
            break;
        }
        return xi2type;
    }
    
    /**
     * Converts a gesture type to corresponding Gesture{Pinch,Swipe}Begin.
     * Returns 0 if the input type is not a gesture.
     */
    enum EventType
    GestureTypeToBegin(enum EventType type)
    {
        switch (type) {
        case ET_GesturePinchBegin:
        case ET_GesturePinchUpdate:
        case ET_GesturePinchEnd:
            return ET_GesturePinchBegin;
        case ET_GestureSwipeBegin:
        case ET_GestureSwipeUpdate:
        case ET_GestureSwipeEnd:
            return ET_GestureSwipeBegin;
        default:
            return 0;
        }
    }
    
    /**
     * Converts a gesture type to corresponding Gesture{Pinch,Swipe}End.
     * Returns 0 if the input type is not a gesture.
     */
    enum EventType
    GestureTypeToEnd(enum EventType type)
    {
        switch (type) {
        case ET_GesturePinchBegin:
        case ET_GesturePinchUpdate:
        case ET_GesturePinchEnd:
            return ET_GesturePinchEnd;
        case ET_GestureSwipeBegin:
        case ET_GestureSwipeUpdate:
        case ET_GestureSwipeEnd:
            return ET_GestureSwipeEnd;
        default:
            return 0;
        }
    }