1100 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			1100 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			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"
 | |
| #include "listdev.h" /* for sizing up DeviceClassesChangedEvent */
 | |
| 
 | |
| /* 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
 | |
| 
 | |
| /* InputEventList is the container list for all input events generated by the
 | |
|  * DDX. The DDX is expected to call GetEventList() and then pass the list into
 | |
|  * Get{Pointer|Keyboard}Events.
 | |
|  */
 | |
| EventListPtr InputEventList = NULL;
 | |
| int InputEventListLen = 0;
 | |
| 
 | |
| _X_EXPORT int
 | |
| GetEventList(EventListPtr* list)
 | |
| {
 | |
|     *list = InputEventList;
 | |
|     return InputEventListLen;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * 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)));
 | |
| }
 | |
| 
 | |
| void
 | |
| CreateClassesChangedEvent(EventList* event,
 | |
|                           DeviceIntPtr master,
 | |
|                           DeviceIntPtr slave)
 | |
| {
 | |
|     deviceClassesChangedEvent *dcce;
 | |
|     int len = sizeof(xEvent);
 | |
|     CARD32 ms = GetTimeInMillis();
 | |
|     int namelen = 0; /* dummy */
 | |
| 
 | |
|     /* XXX: ok, this is a bit weird. We need to alloc enough size for the
 | |
|      * event so it can be filled in in POE lateron. Reason being that if
 | |
|      * we realloc the event in POE we can get SIGABRT when we try to free
 | |
|      * or realloc the original pointer.
 | |
|      * We can only do it here as we don't have the EventList in the event
 | |
|      * processing any more.
 | |
|      */
 | |
|     SizeDeviceInfo(slave, &namelen, &len);
 | |
| 
 | |
|     if (event->evlen < len)
 | |
|     {
 | |
|         event->event = realloc(event->event, len);
 | |
|         if (!event->event)
 | |
|             FatalError("[dix] Cannot allocate memory for "
 | |
|                     "DeviceClassesChangedEvent.\n");
 | |
|         event->evlen = len;
 | |
|     }
 | |
| 
 | |
|     dcce = (deviceClassesChangedEvent*)event->event;
 | |
|     dcce->type = GenericEvent;
 | |
|     dcce->extension = IReqCode;
 | |
|     dcce->evtype = XI_DeviceClassesChangedNotify;
 | |
|     dcce->time = ms;
 | |
|     dcce->new_slave = slave->id;
 | |
|     dcce->length = (len - sizeof(xEvent))/4;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Rescale the coord between the two axis ranges.
 | |
|  */
 | |
| static int
 | |
| rescaleValuatorAxis(int coord, AxisInfoPtr from, AxisInfoPtr to,
 | |
|                     int defmax)
 | |
| {
 | |
|     int fmin = 0, tmin = 0, fmax = defmax, tmax = defmax;
 | |
| 
 | |
|     if(from && from->min_value < from->max_value) {
 | |
|         fmin = from->min_value;
 | |
|         fmax = from->max_value;
 | |
|     }
 | |
|     if(to && to->min_value < to->max_value) {
 | |
|         tmin = to->min_value;
 | |
|         tmax = to->max_value;
 | |
|     }
 | |
| 
 | |
|     if(fmin == tmin && fmax == tmax)
 | |
|         return coord;
 | |
| 
 | |
|     if(fmax == fmin) /* avoid division by 0 */
 | |
|         return 0;
 | |
| 
 | |
|     return roundf(((float)(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)
 | |
| {
 | |
|     ScreenPtr scr = miPointerGetScreen(pDev);
 | |
|     int i;
 | |
|     DeviceIntPtr lastSlave;
 | |
| 
 | |
|     /* master->last.valuators[0]/[1] is in screen 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, scr->width);
 | |
|     if(pDev->valuator->numAxes > 1)
 | |
|         pDev->last.valuators[1] = rescaleValuatorAxis(pDev->last.valuators[1], NULL, pDev->valuator->axes + 1, scr->height);
 | |
| 
 | |
|     /* calculate the other axis as well based on info from the old
 | |
|      * slave-device. If the old slave had less axes than this one,
 | |
|      * last.valuators is reset to 0.
 | |
|      */
 | |
|     if ((lastSlave = master->u.lastSlave) && lastSlave->valuator) {
 | |
|         for (i = 2; i < pDev->valuator->numAxes; i++) {
 | |
|             if (i >= lastSlave->valuator->numAxes)
 | |
|                 pDev->last.valuators[i] = 0;
 | |
|             else
 | |
|                 pDev->last.valuators[i] =
 | |
|                     rescaleValuatorAxis(pDev->last.valuators[i],
 | |
|                             lastSlave->valuator->axes + i,
 | |
|                             pDev->valuator->axes + i, 0);
 | |
|         }
 | |
|     }
 | |
| 
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Allocate the motion history buffer.
 | |
|  */
 | |
| _X_EXPORT void
 | |
| AllocateMotionHistory(DeviceIntPtr pDev)
 | |
| {
 | |
|     int size;
 | |
|     if (pDev->valuator->motion)
 | |
|         xfree(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 (pDev->isMaster)
 | |
|         size = sizeof(INT32) * 3 * MAX_VALUATORS;
 | |
|     else
 | |
|         size = sizeof(INT32) * pDev->valuator->numAxes;
 | |
| 
 | |
|     size += sizeof(Time);
 | |
| 
 | |
|     pDev->valuator->motion = xcalloc(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.
 | |
|  */
 | |
| _X_EXPORT 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;
 | |
|     int dflt;
 | |
|     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 (pDev->isMaster)
 | |
|         size = (sizeof(INT32) * 3 * MAX_VALUATORS) + sizeof(Time);
 | |
|     else
 | |
|         size = (sizeof(INT32) * pDev->valuator->numAxes) + sizeof(Time);
 | |
| 
 | |
|     *buff = xalloc(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(¤t, 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, 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, pScreen->height);
 | |
|                 memcpy(corebuf, &coord, sizeof(INT16));
 | |
| 
 | |
|             } else if (pDev->isMaster)
 | |
|             {
 | |
|                 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;
 | |
| 
 | |
|                     if (j == 0 && (to->max_value < to->min_value))
 | |
|                         dflt = pScreen->width;
 | |
|                     else if (j == 1 && (to->max_value < to->min_value))
 | |
|                         dflt = pScreen->height;
 | |
|                     else
 | |
|                         dflt = 0;
 | |
| 
 | |
|                     /* scale from stored range into current range */
 | |
|                     coord = rescaleValuatorAxis(coord, &from, to, 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 (first_valuator > 0):
 | |
|  *      min_val == max_val == val == 0.
 | |
|  */
 | |
| static void
 | |
| updateMotionHistory(DeviceIntPtr pDev, CARD32 ms, int first_valuator,
 | |
|                     int num_valuators, int *valuators)
 | |
| {
 | |
|     char *buff = (char *) pDev->valuator->motion;
 | |
|     ValuatorClassPtr v;
 | |
|     int i;
 | |
| 
 | |
|     if (!pDev->valuator->numMotionEvents)
 | |
|         return;
 | |
| 
 | |
|     v = pDev->valuator;
 | |
|     if (pDev->isMaster)
 | |
|     {
 | |
|         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);
 | |
|         buff += 3 * sizeof(INT32) * first_valuator;
 | |
| 
 | |
|         for (i = first_valuator; i < first_valuator + num_valuators; i++)
 | |
|         {
 | |
|             if (i >= v->numAxes)
 | |
|                 break;
 | |
|             memcpy(buff, &v->axes[i].min_value, sizeof(INT32));
 | |
|             buff += sizeof(INT32);
 | |
|             memcpy(buff, &v->axes[i].max_value, sizeof(INT32));
 | |
|             buff += sizeof(INT32);
 | |
|             memcpy(buff, &valuators[i - first_valuator], 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);
 | |
|         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());
 | |
|  *
 | |
|  * This MUST be absolutely constant, from init until exit.
 | |
|  */
 | |
| _X_EXPORT int
 | |
| GetMaximumEventsNum(void) {
 | |
|     /* One base event -- device, plus valuator events.
 | |
|      *  Multiply by two if we're doing non-XKB key repeats. */
 | |
|     int ret = 1 + MAX_VALUATOR_EVENTS;
 | |
| 
 | |
| #ifdef XKB
 | |
|     if (noXkbExtension)
 | |
| #endif
 | |
|         ret *= 2;
 | |
| 
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * Clip an axis to its bounds, which are declared in the call to
 | |
|  * InitValuatorAxisClassStruct.
 | |
|  */
 | |
| static void
 | |
| clipAxis(DeviceIntPtr pDev, int axisNum, int *val)
 | |
| {
 | |
|     AxisInfoPtr axis = pDev->valuator->axes + axisNum;
 | |
|     /* InitValuatoraAxisStruct ensures that (min < max). */
 | |
| 
 | |
|     /* 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, 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.
 | |
|  */
 | |
| static EventList *
 | |
| getValuatorEvents(EventList *events, DeviceIntPtr pDev,
 | |
|         int first_valuator, int num_valuators, int *valuators) {
 | |
|     deviceValuator *xv;
 | |
|     int i;
 | |
| 
 | |
|     for (i = 0; i < num_valuators; i += 6, events++) {
 | |
|         xv = (deviceValuator*)events->event;
 | |
|         xv->type = DeviceValuator;
 | |
|         xv->first_valuator = first_valuator + i;
 | |
|         xv->num_valuators = ((num_valuators - i) > 6) ? 6 : (num_valuators - i);
 | |
|         xv->deviceid = pDev->id;
 | |
|         switch (num_valuators - 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 + 0];
 | |
|         }
 | |
| 
 | |
|         if (i + 6 < num_valuators)
 | |
|             xv->deviceid |= MORE_EVENTS;
 | |
|     }
 | |
| 
 | |
|     return events;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * Convenience wrapper around GetKeyboardValuatorEvents, that takes no
 | |
|  * valuators.
 | |
|  */
 | |
| _X_EXPORT int
 | |
| GetKeyboardEvents(EventList *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.
 | |
|  *
 | |
|  * DOES NOT GENERATE CORE EVENTS! Core events are created when processing the
 | |
|  * event (ProcessOtherEvent).
 | |
|  *
 | |
|  * 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(EventList *events, DeviceIntPtr pDev, int type,
 | |
|                           int key_code, int first_valuator,
 | |
|                           int num_valuators, int *valuators) {
 | |
|     int numEvents = 0;
 | |
|     CARD32 ms = 0;
 | |
|     KeySym *map;
 | |
|     KeySym sym;
 | |
|     deviceKeyButtonPointer *kbp = NULL;
 | |
|     DeviceIntPtr master;
 | |
| 
 | |
|     if (!events)
 | |
|         return 0;
 | |
| 
 | |
|     /* DO NOT WANT */
 | |
|     if (type != KeyPress && type != KeyRelease)
 | |
|         return 0;
 | |
| 
 | |
|     if (!pDev->key || !pDev->focus || !pDev->kbdfeed)
 | |
|         return 0;
 | |
| 
 | |
|     numEvents = 1;
 | |
| 
 | |
|     if (key_code < 8 || key_code > 255)
 | |
|         return 0;
 | |
| 
 | |
|     map = pDev->key->curKeySyms.map;
 | |
|     sym = map[(key_code - pDev->key->curKeySyms.minKeyCode)
 | |
|               * pDev->key->curKeySyms.mapWidth];
 | |
| 
 | |
|     master = pDev->u.master;
 | |
|     if (master && master->u.lastSlave != pDev)
 | |
|     {
 | |
|         CreateClassesChangedEvent(events, master, pDev);
 | |
|         updateSlaveDeviceCoords(master, pDev);
 | |
|         master->u.lastSlave = pDev;
 | |
|         master->last.numValuators = pDev->last.numValuators;
 | |
|         numEvents++;
 | |
|         events++;
 | |
|     }
 | |
| 
 | |
|     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();
 | |
| 
 | |
|     kbp = (deviceKeyButtonPointer *) events->event;
 | |
|     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;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Initialize an event list and fill with 32 byte sized events.
 | |
|  * This event list is to be passed into GetPointerEvents() and
 | |
|  * GetKeyboardEvents().
 | |
|  *
 | |
|  * @param num_events Number of elements in list.
 | |
|  */
 | |
| EventListPtr
 | |
| InitEventList(int num_events)
 | |
| {
 | |
|     EventListPtr events;
 | |
|     int i;
 | |
| 
 | |
|     events = (EventListPtr)xcalloc(num_events, sizeof(EventList));
 | |
|     if (!events)
 | |
|         return NULL;
 | |
| 
 | |
|     for (i = 0; i < num_events; i++)
 | |
|     {
 | |
|         events[i].evlen = sizeof(xEvent);
 | |
|         events[i].event = xcalloc(1, sizeof(xEvent));
 | |
|         if (!events[i].event)
 | |
|         {
 | |
|             /* rollback */
 | |
|             while(i--)
 | |
|                 xfree(events[i].event);
 | |
|             xfree(events);
 | |
|             events = NULL;
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return events;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Allocs min_size memory for each event in the list.
 | |
|  */
 | |
| _X_EXPORT void
 | |
| SetMinimumEventSize(EventListPtr list, int num_events, int min_size)
 | |
| {
 | |
|     if (!list)
 | |
|         return;
 | |
| 
 | |
|     while(num_events--)
 | |
|     {
 | |
|         if (list[num_events].evlen < min_size)
 | |
|         {
 | |
|             list[num_events].evlen = min_size;
 | |
|             list[num_events].event = realloc(list[num_events].event, min_size);
 | |
|             if (!list[num_events].event)
 | |
|             {
 | |
|                 FatalError("[dix] Failed to set event list's "
 | |
|                         "min_size to %d.\n", min_size);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Free an event list.
 | |
|  *
 | |
|  * @param list The list to be freed.
 | |
|  * @param num_events Number of elements in list.
 | |
|  */
 | |
| _X_EXPORT void
 | |
| FreeEventList(EventListPtr list, int num_events)
 | |
| {
 | |
|     if (!list)
 | |
|         return;
 | |
|     while(num_events--)
 | |
|         xfree(list[num_events].event);
 | |
|     xfree(list);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Generate a series of xEvents (filled into the EventList) representing
 | |
|  * pointer motion, or button presses.  Xi and XKB-aware.
 | |
|  *
 | |
|  * DOES NOT GENERATE CORE EVENTS! Core events are created when processing the
 | |
|  * event (ProcessOtherEvent).
 | |
|  *
 | |
|  * 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.
 | |
|  */
 | |
| _X_EXPORT int
 | |
| GetPointerEvents(EventList *events, DeviceIntPtr pDev, int type, int buttons,
 | |
|                  int flags, int first_valuator, int num_valuators,
 | |
|                  int *valuators) {
 | |
|     int num_events = 1;
 | |
|     CARD32 ms;
 | |
|     deviceKeyButtonPointer *kbp = NULL;
 | |
|     DeviceIntPtr master;
 | |
|     int x, y, /* switches between device and screen coords */
 | |
|         cx, cy; /* only screen coordinates */
 | |
|     ScreenPtr scr = miPointerGetScreen(pDev);
 | |
|     int *v0 = NULL, *v1 = NULL;
 | |
|     int i;
 | |
| 
 | |
|     ms = GetTimeInMillis(); /* before pointer update to help precision */
 | |
| 
 | |
|     /* Sanity checks. */
 | |
|     if (!scr) /* can happen during server shutdown */
 | |
|         return 0;
 | |
|     if (type != MotionNotify && type != ButtonPress && type != ButtonRelease)
 | |
|         return 0;
 | |
|     if (type != MotionNotify && !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 (type == MotionNotify && num_valuators <= 0)
 | |
|         return 0;
 | |
| 
 | |
|     /* Do we need to send a DeviceValuator event? */
 | |
|     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;
 | |
| 
 | |
|     master = pDev->u.master;
 | |
|     if (master && master->u.lastSlave != pDev)
 | |
|     {
 | |
|         CreateClassesChangedEvent(events, master, pDev);
 | |
|         updateSlaveDeviceCoords(master, pDev);
 | |
|         master->u.lastSlave = pDev;
 | |
|         master->last.numValuators = pDev->last.numValuators;
 | |
|         num_events++;
 | |
|         events++;
 | |
|     }
 | |
| 
 | |
|     /* Fetch pointers into the valuator array for more easy to read code */
 | |
|     if (num_valuators >= 1 && first_valuator == 0)
 | |
|         v0 = valuators + 0;
 | |
|     if (first_valuator <= 1 && num_valuators >= (2 - first_valuator))
 | |
|         v1 = valuators + 1 - first_valuator;
 | |
| 
 | |
|     /* Set x and y based on whether this is absolute or relative, and
 | |
|      * accelerate if we need to. */
 | |
|     x = pDev->last.valuators[0];
 | |
|     y = pDev->last.valuators[1];
 | |
|     if (flags & POINTER_ABSOLUTE) {
 | |
|         if(v0) x = *v0;
 | |
|         if(v1) y = *v1;
 | |
| 
 | |
|         /* Clip both x and y to the defined limits (usually co-ord space limit). */
 | |
|         clipAxis(pDev, 0, &x);
 | |
|         clipAxis(pDev, 1, &y);
 | |
| 
 | |
|         i = (first_valuator > 2) ? 0 : 2;
 | |
|         for (; i < num_valuators; i++)
 | |
|         {
 | |
|             pDev->last.valuators[i + first_valuator] = valuators[i];
 | |
|             clipAxis(pDev, i, &pDev->last.valuators[i + first_valuator]);
 | |
|         }
 | |
|     }
 | |
|     else {
 | |
|         if (flags & POINTER_ACCELERATE &&
 | |
|             pDev->valuator->accelScheme.AccelSchemeProc){
 | |
|             pDev->valuator->accelScheme.AccelSchemeProc(
 | |
|                       pDev, first_valuator, num_valuators, valuators, ms);
 | |
|         }
 | |
| 
 | |
|         if(v0) x += *v0;
 | |
|         if(v1) y += *v1;
 | |
| 
 | |
|         /* if attached, clip both x and y to the defined limits (usually
 | |
|          * co-ord space limit). If it is attached, we need x/y to go over the
 | |
|          * limits to be able to change screens. */
 | |
|         if(master) {
 | |
|             clipAxis(pDev, 0, &x);
 | |
|             clipAxis(pDev, 1, &y);
 | |
|         }
 | |
| 
 | |
|         /* calc other axes, clip, drop back into valuators */
 | |
|         i = (first_valuator > 2) ? 0 : 2;
 | |
|         for (; i < num_valuators; i++)
 | |
|         {
 | |
|             pDev->last.valuators[i + first_valuator] += valuators[i];
 | |
|             clipAxis(pDev, i, &pDev->last.valuators[i + first_valuator]);
 | |
|             valuators[i] = pDev->last.valuators[i + first_valuator];
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* scale x&y to screen */
 | |
|     pDev->last.valuators[0] = cx = rescaleValuatorAxis(x, pDev->valuator->axes + 0,
 | |
|                                            NULL, scr->width);
 | |
|     pDev->last.valuators[1] = cy = rescaleValuatorAxis(y, pDev->valuator->axes + 1,
 | |
|                                            NULL, scr->height);
 | |
| 
 | |
|     /* 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, &pDev->last.valuators[0], &pDev->last.valuators[1]);
 | |
| 
 | |
|     if (master) {
 | |
|         master->last.valuators[0] = pDev->last.valuators[0];
 | |
|         master->last.valuators[1] = pDev->last.valuators[1];
 | |
|     }
 | |
| 
 | |
|     /* Crossed screen? Scale back to device coordiantes */
 | |
|     if(cx != pDev->last.valuators[0])
 | |
|     {
 | |
|         scr = miPointerGetScreen(pDev);
 | |
|         x = rescaleValuatorAxis(pDev->last.valuators[0], NULL,
 | |
|                                 pDev->valuator->axes + 0, scr->width);
 | |
|         cx = pDev->last.valuators[0];
 | |
|     }
 | |
|     if(cy != pDev->last.valuators[1])
 | |
|     {
 | |
|         scr = miPointerGetScreen(pDev);
 | |
|         cy = pDev->last.valuators[1];
 | |
|         y = rescaleValuatorAxis(pDev->last.valuators[1], NULL,
 | |
|                                 pDev->valuator->axes + 1, scr->height);
 | |
|     }
 | |
| 
 | |
| 
 | |
|     updateMotionHistory(pDev, ms, first_valuator, num_valuators,
 | |
|             &pDev->last.valuators[first_valuator]);
 | |
|     if (master)
 | |
|         updateMotionHistory(master, ms, first_valuator, num_valuators,
 | |
|                 &pDev->last.valuators[first_valuator]);
 | |
| 
 | |
|     /* Update the valuators with the true value sent to the client*/
 | |
|     if(v0) *v0 = x;
 | |
|     if(v1) *v1 = y;
 | |
| 
 | |
|     /* dropy x/y (device coordinates) back into valuators for next event */
 | |
|     pDev->last.valuators[0] = x;
 | |
|     pDev->last.valuators[1] = y;
 | |
| 
 | |
|     kbp = (deviceKeyButtonPointer *) events->event;
 | |
|     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 = buttons;
 | |
|     }
 | |
| 
 | |
|     kbp->root_x = cx; /* root_x/y always in screen coords */
 | |
|     kbp->root_y = cy;
 | |
| 
 | |
|     events++;
 | |
|     if (num_valuators) {
 | |
|         kbp->deviceid |= MORE_EVENTS;
 | |
|         if (flags & POINTER_ABSOLUTE)
 | |
|             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(EventList *events, DeviceIntPtr pDev, int type,
 | |
|                    int first_valuator, int num_valuators, int *valuators)
 | |
| {
 | |
|     int num_events = 1;
 | |
|     deviceKeyButtonPointer *kbp;
 | |
|     DeviceIntPtr master;
 | |
| 
 | |
|     /* 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;
 | |
| 
 | |
|     master = pDev->u.master;
 | |
|     if (master && master->u.lastSlave != pDev)
 | |
|     {
 | |
|         CreateClassesChangedEvent(events, master, pDev);
 | |
|         updateSlaveDeviceCoords(master, pDev);
 | |
|         master->u.lastSlave = pDev;
 | |
|         master->last.numValuators = pDev->last.numValuators;
 | |
|         num_events++;
 | |
|         events++;
 | |
|     }
 | |
| 
 | |
|     kbp = (deviceKeyButtonPointer *) events->event;
 | |
|     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 function to send a core pointer event.
 | |
|  * Currently a no-op.
 | |
|  *
 | |
|  * Call this just before processInputProc.
 | |
|  */
 | |
| _X_EXPORT void
 | |
| SwitchCorePointer(DeviceIntPtr pDev)
 | |
| {
 | |
|     if (pDev != dixLookupPrivate(&inputInfo.pointer->devPrivates,
 | |
| 				 CoreDevicePrivateKey))
 | |
| 	dixSetPrivate(&inputInfo.pointer->devPrivates,
 | |
| 		      CoreDevicePrivateKey, 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(DeviceIntPtr pDev,
 | |
|                     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;
 | |
| 
 | |
|     (*pDev->public.processInputProc)(&xE, pDev, 1);
 | |
| }
 |