Edit

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

Branch :

  • Show log

    Commit

  • Author : matthieu
    Date : 2007-12-13 21:06:02
    Hash : 2fcd9ee6
    Message : xserver 1.4.0.90

  • xserver/dix/getevents.c
  • /*
     * Copyright © 2006 Nokia Corporation
     * Copyright © 2006-2007 Daniel Stone
     *
     * 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.
     *
     * Author: Daniel Stone <daniel@fooishbar.org>
     */
    
    #ifdef HAVE_DIX_CONFIG_H
    #include <dix-config.h>
    #endif
    
    #include <X11/X.h>
    #include <X11/keysym.h>
    #define NEED_EVENTS
    #define NEED_REPLIES
    #include <X11/Xproto.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"
    
    #ifdef XKB
    #include <X11/extensions/XKBproto.h>
    #include <xkbsrv.h>
    extern Bool XkbCopyKeymap(XkbDescPtr src, XkbDescPtr dst, Bool sendNotifies);
    #endif
    
    #ifdef PANORAMIX
    #include "panoramiX.h"
    #include "panoramiXsrv.h"
    #endif
    
    #include <X11/extensions/XI.h>
    #include <X11/extensions/XIproto.h>
    #include "exglobals.h"
    #include "exevents.h"
    #include "exglobals.h"
    #include "extnsionst.h"
    
    
    /* Maximum number of valuators, divided by six, rounded up, to get number
     * of events. */
    #define MAX_VALUATOR_EVENTS 6
    
    /* Number of motion history events to store. */
    #define MOTION_HISTORY_SIZE 256
    
    
    /**
     * Pick some arbitrary size for Xi motion history.
     */
    _X_EXPORT int
    GetMotionHistorySize(void)
    {
        return MOTION_HISTORY_SIZE;
    }
    
    static void
    set_key_down(DeviceIntPtr pDev, int key_code)
    {
        pDev->key->postdown[key_code >> 3] |= (1 << (key_code & 7));
    }
    
    static void
    set_key_up(DeviceIntPtr pDev, int key_code)
    {
        pDev->key->postdown[key_code >> 3] &= ~(1 << (key_code & 7));
    }
    
    static Bool
    key_is_down(DeviceIntPtr pDev, int key_code)
    {
        return !!(pDev->key->postdown[key_code >> 3] & (1 << (key_code & 7)));
    }
    
    static Bool
    key_autorepeats(DeviceIntPtr pDev, int key_code)
    {
        return !!(pDev->kbdfeed->ctrl.autoRepeats[key_code >> 3] &
                  (1 << (key_code & 7)));
    }
    
    /**
     * Allocate the motion history buffer.
     */
    _X_EXPORT void
    AllocateMotionHistory(DeviceIntPtr pDev)
    {
        if (pDev->valuator->motion)
            xfree(pDev->valuator->motion);
    
        if (pDev->valuator->numMotionEvents < 1)
            return;
    
        pDev->valuator->motion = xalloc(((sizeof(INT32) * pDev->valuator->numAxes) +
                                         sizeof(Time)) *
                                        pDev->valuator->numMotionEvents);
        pDev->valuator->first_motion = 0;
        pDev->valuator->last_motion = 0;
    }
    
    
    /**
     * 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.
     */
    _X_EXPORT int
    GetMotionHistory(DeviceIntPtr pDev, xTimecoord *buff, unsigned long start,
                     unsigned long stop, ScreenPtr pScreen)
    {
        char *ibuff = NULL, *obuff = (char *) buff;
        int i = 0, ret = 0;
        Time current;
        /* The size of a single motion event. */
        int size = (sizeof(INT32) * pDev->valuator->numAxes) + sizeof(Time);
    
        if (!pDev->valuator || !pDev->valuator->numMotionEvents)
            return 0;
    
        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) {
                memcpy(obuff, ibuff, size);
                obuff += size;
                ret++;
            }
        }
    
        return ret;
    }
    
    
    /**
     * Update the motion history for a specific device, with the list of
     * valuators.
     */
    static void
    updateMotionHistory(DeviceIntPtr pDev, CARD32 ms, int first_valuator,
                        int num_valuators, int *valuators)
    {
        char *buff = (char *) pDev->valuator->motion;
    
        if (!pDev->valuator->numMotionEvents)
            return;
    
        buff += ((sizeof(INT32) * pDev->valuator->numAxes) + sizeof(CARD32)) *
                pDev->valuator->last_motion;
        memcpy(buff, &ms, sizeof(Time));
    
        buff += sizeof(Time);
        bzero(buff, sizeof(INT32) * pDev->valuator->numAxes);
    
        buff += sizeof(INT32) * first_valuator;
        memcpy(buff, valuators, sizeof(INT32) * num_valuators);
    
        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,
     * GetKeyboardValuatorEvents, and GetPointerEvents will ever return.
     *
     * Should be used in DIX as:
     * xEvent *events = xcalloc(sizeof(xEvent), GetMaximumEventsNum());
     */
    _X_EXPORT int
    GetMaximumEventsNum(void) {
        /* Two base events -- core and device, plus valuator events.  Multiply
         * by two if we're doing key repeats. */
        int ret = 2 + MAX_VALUATOR_EVENTS;
    
    #ifdef XKB
        if (noXkbExtension)
    #endif
            ret *= 2;
    
        return ret;
    }
    
    
    /* Originally a part of xf86PostMotionEvent; modifies valuators
     * in-place. */
    static void
    acceleratePointer(DeviceIntPtr pDev, int first_valuator, int num_valuators,
                      int *valuators)
    {
        float mult = 0.0;
        int dx = 0, dy = 0;
        int *px = NULL, *py = NULL;
    
        if (!num_valuators || !valuators)
            return;
    
        if (first_valuator == 0) {
            dx = valuators[0];
            px = &valuators[0];
        }
        if (first_valuator <= 1 && num_valuators >= (2 - first_valuator)) {
            dy = valuators[1 - first_valuator];
            py = &valuators[1 - first_valuator];
        }
    
        if (!dx && !dy)
            return;
    
        if (pDev->ptrfeed && pDev->ptrfeed->ctrl.num) {
            /* modeled from xf86Events.c */
            if (pDev->ptrfeed->ctrl.threshold) {
                if ((abs(dx) + abs(dy)) >= pDev->ptrfeed->ctrl.threshold) {
                    pDev->valuator->dxremaind = ((float)dx *
                                                 (float)(pDev->ptrfeed->ctrl.num)) /
                                                 (float)(pDev->ptrfeed->ctrl.den) +
                                                pDev->valuator->dxremaind;
                    if (px) {
                        *px = (int)pDev->valuator->dxremaind;
                        pDev->valuator->dxremaind = pDev->valuator->dxremaind -
                                                    (float)(*px);
                    }
    
                    pDev->valuator->dyremaind = ((float)dy *
                                                 (float)(pDev->ptrfeed->ctrl.num)) /
                                                 (float)(pDev->ptrfeed->ctrl.den) +
                                                pDev->valuator->dyremaind;
                    if (py) {
                        *py = (int)pDev->valuator->dyremaind;
                        pDev->valuator->dyremaind = pDev->valuator->dyremaind -
                                                    (float)(*py);
                    }
                }
            }
            else {
    		mult = pow((float)dx * (float)dx + (float)dy * (float)dy,
                           ((float)(pDev->ptrfeed->ctrl.num) /
                            (float)(pDev->ptrfeed->ctrl.den) - 1.0) /
                           2.0) / 2.0;
                if (dx) {
                    pDev->valuator->dxremaind = mult * (float)dx +
                                                pDev->valuator->dxremaind;
                    *px = (int)pDev->valuator->dxremaind;
                    pDev->valuator->dxremaind = pDev->valuator->dxremaind -
                                                (float)(*px);
                }
                if (dy) {
                    pDev->valuator->dyremaind = mult * (float)dy +
                                                pDev->valuator->dyremaind;
                    *py = (int)pDev->valuator->dyremaind;
                    pDev->valuator->dyremaind = pDev->valuator->dyremaind -
                                                (float)(*py);
                }
            }
        }
    }
    
    
    /**
     * Clip an axis to its bounds, which are declared in the call to
     * InitValuatorAxisClassStruct.
     */
    static void
    clipAxis(DeviceIntPtr pDev, int axisNum, int *val)
    {
        AxisInfoPtr axes = pDev->valuator->axes + axisNum;
    
        if (*val < axes->min_value)
            *val = axes->min_value;
        if (axes->max_value >= 0 && *val > axes->max_value)
            *val = axes->max_value;
    }
    
    /**
     * Clip every axis in the list of valuators to its bounds.
     */
    static void
    clipValuators(DeviceIntPtr pDev, int first_valuator, int num_valuators,
                  int *valuators)
    {
        AxisInfoPtr axes = pDev->valuator->axes + first_valuator;
        int i;
    
        for (i = 0; i < num_valuators; i++, axes++)
            clipAxis(pDev, i + first_valuator, &(valuators[i]));
    }
    
    
    /**
     * Fills events with valuator events for pDev, as given by the other
     * parameters.
     *
     * FIXME: Need to fix ValuatorClassRec to store all the valuators as
     *        last posted, not just x and y; otherwise relative non-x/y
     *        valuators, though a very narrow use case, will be broken.
     */
    static xEvent *
    getValuatorEvents(xEvent *events, DeviceIntPtr pDev, int first_valuator,
                      int num_valuators, int *valuators) {
        deviceValuator *xv = (deviceValuator *) events;
        int i = 0, final_valuator = first_valuator + num_valuators;
    
        for (i = first_valuator; i < final_valuator; i += 6, xv++, events++) {
            xv->type = DeviceValuator;
            xv->first_valuator = i;
            xv->num_valuators = num_valuators;
            xv->deviceid = pDev->id;
            switch (final_valuator - i) {
            case 6:
                xv->valuator5 = valuators[i + 5];
            case 5:
                xv->valuator4 = valuators[i + 4];
            case 4:
                xv->valuator3 = valuators[i + 3];
            case 3:
                xv->valuator2 = valuators[i + 2];
            case 2:
                xv->valuator1 = valuators[i + 1];
            case 1:
                xv->valuator0 = valuators[i];
            }
    
            if (i + 6 < final_valuator)
                xv->deviceid |= MORE_EVENTS;
        }
    
        return events;
    }
    
    
    /**
     * Convenience wrapper around GetKeyboardValuatorEvents, that takes no
     * valuators.
     */
    _X_EXPORT int
    GetKeyboardEvents(xEvent *events, DeviceIntPtr pDev, int type, int key_code) {
        return GetKeyboardValuatorEvents(events, pDev, type, key_code, 0, 0, NULL);
    }
    
    
    /**
     * Returns a set of keyboard events for KeyPress/KeyRelease, optionally
     * also with valuator events.  Handles Xi and XKB.
     *
     * 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.
     *
     * This function does not change the core keymap to that of the device;
     * that is done by SwitchCoreKeyboard, which is called from
     * mieqProcessInputEvents.  If replacing that function, take care to call
     * SetCoreKeyboard before processInputProc, so keymaps are altered to suit.
     *
     * Note that this function recurses!  If called for non-XKB, a repeating
     * key press will trigger a matching KeyRelease, as well as the
     * KeyPresses.
     */
    _X_EXPORT int
    GetKeyboardValuatorEvents(xEvent *events, DeviceIntPtr pDev, int type,
                              int key_code, int first_valuator,
                              int num_valuators, int *valuators) {
        int numEvents = 0;
        CARD32 ms = 0;
        KeySym *map = pDev->key->curKeySyms.map;
        KeySym sym = map[key_code * pDev->key->curKeySyms.mapWidth];
        deviceKeyButtonPointer *kbp = NULL;
    
        if (!events)
            return 0;
    
        /* DO NOT WANT */
        if (type != KeyPress && type != KeyRelease)
            return 0;
    
        if (!pDev->key || !pDev->focus || !pDev->kbdfeed ||
            (pDev->coreEvents && !inputInfo.keyboard->key))
            return 0;
    
        if (key_code < 8 || key_code > 255)
            return 0;
    
        if (pDev->coreEvents)
            numEvents = 2;
        else
            numEvents = 1;
    
        if (num_valuators) {
            if ((num_valuators / 6) + 1 > MAX_VALUATOR_EVENTS)
                num_valuators = MAX_VALUATOR_EVENTS;
            numEvents += (num_valuators / 6) + 1;
        }
    
    #ifdef XKB
        if (noXkbExtension)
    #endif
        {
            switch (sym) {
                case XK_Num_Lock:
                case XK_Caps_Lock:
                case XK_Scroll_Lock:
                case XK_Shift_Lock:
                    if (type == KeyRelease)
                        return 0;
                    else if (type == KeyPress && key_is_down(pDev, key_code))
                        type = KeyRelease;
            }
        }
    
        /* Handle core repeating, via press/release/press/release.
         * FIXME: In theory, if you're repeating with two keyboards in non-XKB,
         *        you could get unbalanced events here. */
        if (type == KeyPress && key_is_down(pDev, key_code)) {
            /* 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->modifierMap[key_code])
                return 0;
    
    #ifdef XKB
            if (noXkbExtension)
    #endif
            {
                numEvents += GetKeyboardValuatorEvents(events, pDev,
                                                       KeyRelease, key_code,
                                                       first_valuator, num_valuators,
                                                       valuators);
                events += numEvents;
            }
        }
    
        ms = GetTimeInMillis();
    
        if (pDev->coreEvents) {
            events->u.keyButtonPointer.time = ms;
            events->u.u.type = type;
            events->u.u.detail = key_code;
            if (type == KeyPress)
    	    set_key_down(inputInfo.keyboard, key_code);
            else if (type == KeyRelease)
    	    set_key_up(inputInfo.keyboard, key_code);
            events++;
        }
    
        kbp = (deviceKeyButtonPointer *) events;
        kbp->time = ms;
        kbp->deviceid = pDev->id;
        kbp->detail = key_code;
        if (type == KeyPress) {
            kbp->type = DeviceKeyPress;
    	set_key_down(pDev, key_code);
        }
        else if (type == KeyRelease) {
            kbp->type = DeviceKeyRelease;
    	set_key_up(pDev, key_code);
        }
    
        events++;
        if (num_valuators) {
            kbp->deviceid |= MORE_EVENTS;
            clipValuators(pDev, first_valuator, num_valuators, valuators);
            events = getValuatorEvents(events, pDev, first_valuator,
                                       num_valuators, valuators);
        }
    
        return numEvents;
    }
    
    
    /**
     * Generate a series of xEvents (returned in xE) representing pointer
     * motion, or button presses.  Xi and XKB-aware.
     *
     * 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.
     */
    _X_EXPORT int
    GetPointerEvents(xEvent *events, DeviceIntPtr pDev, int type, int buttons,
                     int flags, int first_valuator, int num_valuators,
                     int *valuators) {
        int num_events = 0, final_valuator = 0;
        CARD32 ms = 0;
        deviceKeyButtonPointer *kbp = NULL;
        /* Thanks to a broken lib, we _always_ have to chase DeviceMotionNotifies
         * with DeviceValuators. */
        Bool sendValuators = (type == MotionNotify || flags & POINTER_ABSOLUTE);
        DeviceIntPtr cp = inputInfo.pointer;
        int x = 0, y = 0;
        Bool coreOnly = (pDev == inputInfo.pointer);
    
        /* Sanity checks. */
        if (type != MotionNotify && type != ButtonPress && type != ButtonRelease)
            return 0;
    
        if ((type == ButtonPress || type == ButtonRelease) && !pDev->button)
            return 0;
    
        /* FIXME: I guess it should, in theory, be possible to post button events
         *        from devices without valuators. */
        if (!pDev->valuator)
            return 0;
    
        if (!coreOnly && pDev->coreEvents)
            num_events = 2;
        else
            num_events = 1;
    
        if (type == MotionNotify && num_valuators <= 0)
            return 0;
    
        /* Do we need to send a DeviceValuator event? */
        if (!coreOnly && sendValuators) {
            if ((((num_valuators - 1) / 6) + 1) > MAX_VALUATOR_EVENTS)
                num_valuators = MAX_VALUATOR_EVENTS * 6;
            num_events += ((num_valuators - 1) / 6) + 1;
        }
    
        final_valuator = num_valuators + first_valuator;
    
        /* You fail. */
        if (first_valuator < 0 || final_valuator > pDev->valuator->numAxes)
            return 0;
    
        ms = GetTimeInMillis();
    
        /* Set x and y based on whether this is absolute or relative, and
         * accelerate if we need to. */
        if (flags & POINTER_ABSOLUTE) {
            if (num_valuators >= 1 && first_valuator == 0) {
                x = valuators[0];
            }
            else {
                if (pDev->coreEvents)
                    x = cp->valuator->lastx;
                else
                    x = pDev->valuator->lastx;
            }
    
            if (first_valuator <= 1 && num_valuators >= (2 - first_valuator)) {
                y = valuators[1 - first_valuator];
            }
            else {
                if (pDev->coreEvents)
                    y = cp->valuator->lasty;
                else
                    y = pDev->valuator->lasty;
            }
        }
        else {
            if (flags & POINTER_ACCELERATE)
                acceleratePointer(pDev, first_valuator, num_valuators,
                                  valuators);
    
            if (pDev->coreEvents) {
                if (first_valuator == 0 && num_valuators >= 1)
                    x = cp->valuator->lastx + valuators[0];
                else
                    x = cp->valuator->lastx;
    
                if (first_valuator <= 1 && num_valuators >= (2 - first_valuator))
                    y = cp->valuator->lasty + valuators[1 - first_valuator];
                else
                    y = cp->valuator->lasty;
            }
            else {
                if (first_valuator == 0 && num_valuators >= 1)
                    x = pDev->valuator->lastx + valuators[0];
                else
                    x = pDev->valuator->lastx;
    
                if (first_valuator <= 1 && num_valuators >= (2 - first_valuator))
                    y = pDev->valuator->lasty + valuators[1 - first_valuator];
                else
                    y = pDev->valuator->lasty;
            }
        }
    
        /* Clip both x and y to the defined limits (usually co-ord space limit). */
        clipAxis(pDev, 0, &x);
        clipAxis(pDev, 1, &y);
    
        /* This takes care of crossing screens for us, as well as clipping
         * to the current screen.  Right now, we only have one history buffer,
         * so we don't set this for both the device and core.*/
        miPointerSetPosition(pDev, &x, &y, ms);
    
        /* Drop x and y back into the valuators list, if they were originally
         * present. */
        if (first_valuator == 0 && num_valuators >= 1)
            valuators[0] = x;
        if (first_valuator <= 1 && num_valuators >= (2 - first_valuator))
            valuators[1 - first_valuator] = y;
    
        updateMotionHistory(pDev, ms, first_valuator, num_valuators, valuators);
    
        if (pDev->coreEvents) {
            cp->valuator->lastx = x;
            cp->valuator->lasty = y;
        }
        pDev->valuator->lastx = x;
        pDev->valuator->lasty = y;
    
        /* for some reason inputInfo.pointer does not have coreEvents set */
        if (coreOnly || pDev->coreEvents) {
            events->u.u.type = type;
            events->u.keyButtonPointer.time = ms;
            events->u.keyButtonPointer.rootX = x;
            events->u.keyButtonPointer.rootY = y;
    
            if (type == ButtonPress || type == ButtonRelease) {
                /* We hijack SetPointerMapping to work on all core-sending
                 * devices, so we use the device-specific map here instead of
                 * the core one. */
                events->u.u.detail = pDev->button->map[buttons];
            }
            else {
                events->u.u.detail = 0;
            }
    
            events++;
        }
    
        if (!coreOnly) {
            kbp = (deviceKeyButtonPointer *) events;
            kbp->time = ms;
            kbp->deviceid = pDev->id;
    
            if (type == MotionNotify) {
                kbp->type = DeviceMotionNotify;
            }
            else {
                if (type == ButtonPress)
                    kbp->type = DeviceButtonPress;
                else if (type == ButtonRelease)
                    kbp->type = DeviceButtonRelease;
                kbp->detail = pDev->button->map[buttons];
            }
    
            kbp->root_x = x;
            kbp->root_y = y;
    
            events++;
            if (sendValuators) {
                kbp->deviceid |= MORE_EVENTS;
                clipValuators(pDev, first_valuator, num_valuators, valuators);
                events = getValuatorEvents(events, pDev, first_valuator,
                                           num_valuators, valuators);
            }
        }
    
        return num_events;
    }
    
    
    /**
     * Post ProximityIn/ProximityOut events, accompanied by valuators.
     *
     * 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.
     */
    _X_EXPORT int
    GetProximityEvents(xEvent *events, DeviceIntPtr pDev, int type,
                       int first_valuator, int num_valuators, int *valuators)
    {
        int num_events = 1;
        deviceKeyButtonPointer *kbp = (deviceKeyButtonPointer *) events;
    
        /* Sanity checks. */
        if (type != ProximityIn && type != ProximityOut)
            return 0;
    
        if (!pDev->valuator)
            return 0;
    
        /* Do we need to send a DeviceValuator event? */
        if ((pDev->valuator->mode & 1) == Relative)
            num_valuators = 0;
    
        if (num_valuators) {
            if ((((num_valuators - 1) / 6) + 1) > MAX_VALUATOR_EVENTS)
                num_valuators = MAX_VALUATOR_EVENTS * 6;
            num_events += ((num_valuators - 1) / 6) + 1;
        }
    
        /* You fail. */
        if (first_valuator < 0 ||
            (num_valuators + first_valuator) > pDev->valuator->numAxes)
            return 0;
    
        kbp->type = type;
        kbp->deviceid = pDev->id;
        kbp->detail = 0;
        kbp->time = GetTimeInMillis();
    
        if (num_valuators) {
            kbp->deviceid |= MORE_EVENTS;
            events++;
            clipValuators(pDev, first_valuator, num_valuators, valuators);
            events = getValuatorEvents(events, pDev, first_valuator,
                                       num_valuators, valuators);
        }
    
        return num_events;
    }
    
    
    /**
     * Note that pDev was the last device to send a core event.  This function
     * copies the complete keymap from the originating device to the core
     * device, and makes sure the appropriate notifications are generated.
     *
     * Call this just before processInputProc.
     */
    _X_EXPORT void
    SwitchCoreKeyboard(DeviceIntPtr pDev)
    {
        KeyClassPtr ckeyc = inputInfo.keyboard->key;
        int i = 0;
    
        if (inputInfo.keyboard->devPrivates[CoreDevicePrivatesIndex].ptr != pDev) {
            memcpy(ckeyc->modifierMap, pDev->key->modifierMap, MAP_LENGTH);
            if (ckeyc->modifierKeyMap)
                xfree(ckeyc->modifierKeyMap);
            ckeyc->modifierKeyMap = xalloc(8 * pDev->key->maxKeysPerModifier);
            memcpy(ckeyc->modifierKeyMap, pDev->key->modifierKeyMap,
                    (8 * pDev->key->maxKeysPerModifier));
    
            ckeyc->maxKeysPerModifier = pDev->key->maxKeysPerModifier;
            ckeyc->curKeySyms.minKeyCode = pDev->key->curKeySyms.minKeyCode;
            ckeyc->curKeySyms.maxKeyCode = pDev->key->curKeySyms.maxKeyCode;
            SetKeySymsMap(&ckeyc->curKeySyms, &pDev->key->curKeySyms);
    
            /*
             * Copy state from the extended keyboard to core.  If you omit this,
             * holding Ctrl on keyboard one, and pressing Q on keyboard two, will
             * cause your app to quit.  This feels wrong to me, hence the below
             * code.
             *
             * XXX: If you synthesise core modifier events, the state will get
             *      clobbered here.  You'll have to work out something sensible
             *      to fix that.  Good luck.
             */
    
    #define KEYBOARD_MASK (ShiftMask | LockMask | ControlMask | Mod1Mask | \
                           Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask)
            ckeyc->state &= ~(KEYBOARD_MASK);
            ckeyc->state |= (pDev->key->state & KEYBOARD_MASK);
    #undef KEYBOARD_MASK
            for (i = 0; i < 8; i++)
                ckeyc->modifierKeyCount[i] = pDev->key->modifierKeyCount[i];
    
    #ifdef XKB
            if (!noXkbExtension && pDev->key->xkbInfo && pDev->key->xkbInfo->desc) {
                if (!XkbCopyKeymap(pDev->key->xkbInfo->desc, ckeyc->xkbInfo->desc,
                                   True))
                    FatalError("Couldn't pivot keymap from device to core!\n");
            }
    #endif
    
            SendMappingNotify(MappingKeyboard, ckeyc->curKeySyms.minKeyCode,
                              (ckeyc->curKeySyms.maxKeyCode -
                               ckeyc->curKeySyms.minKeyCode),
                              serverClient);
            inputInfo.keyboard->devPrivates[CoreDevicePrivatesIndex].ptr = pDev;
        }
    }
    
    
    /**
     * Note that pDev was the last function to send a core pointer event.
     * Currently a no-op.
     *
     * Call this just before processInputProc.
     */
    _X_EXPORT void
    SwitchCorePointer(DeviceIntPtr pDev)
    {
        if (inputInfo.pointer->devPrivates[CoreDevicePrivatesIndex].ptr != pDev)
            inputInfo.pointer->devPrivates[CoreDevicePrivatesIndex].ptr = pDev;
    }
    
    
    /**
     * 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(int x, int y, int screen, unsigned long time)
    {
        xEvent xE;
    
    #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 += panoramiXdataPtr[0].x - panoramiXdataPtr[screen].x;
            y += panoramiXdataPtr[0].y - panoramiXdataPtr[screen].y;
        }
    #endif
    
        memset(&xE, 0, sizeof(xEvent));
        xE.u.u.type = MotionNotify;
        xE.u.keyButtonPointer.rootX = x;
        xE.u.keyButtonPointer.rootY = y;
        xE.u.keyButtonPointer.time = time;
    
        (*inputInfo.pointer->public.processInputProc)(&xE, inputInfo.pointer, 1);
    }