686 lines
19 KiB
C
686 lines
19 KiB
C
/*
|
|
|
|
Copyright 1992, 1998 The Open Group
|
|
|
|
Permission to use, copy, modify, distribute, and sell this software and its
|
|
documentation for any purpose is hereby granted without fee, provided that
|
|
the above copyright notice appear in all copies and that both that
|
|
copyright notice and this permission notice appear in supporting
|
|
documentation.
|
|
|
|
The above copyright notice and this permission notice shall be included
|
|
in all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
IN NO EVENT SHALL THE OPEN GROUP 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.
|
|
|
|
Except as contained in this notice, the name of The Open Group shall
|
|
not be used in advertising or otherwise to promote the sale, use or
|
|
other dealings in this Software without prior written authorization
|
|
from The Open Group.
|
|
|
|
*/
|
|
|
|
#ifdef HAVE_DIX_CONFIG_H
|
|
#include <dix-config.h>
|
|
#endif
|
|
|
|
#include <X11/X.h>
|
|
#include <X11/Xproto.h>
|
|
#include <X11/Xatom.h>
|
|
#include "misc.h"
|
|
#include "os.h"
|
|
#include "dixstruct.h"
|
|
#include "extnsionst.h"
|
|
#include "windowstr.h"
|
|
#include "inputstr.h"
|
|
#include "scrnintstr.h"
|
|
#include "dixevents.h"
|
|
#include "sleepuntil.h"
|
|
#include "mi.h"
|
|
#include "xkbsrv.h"
|
|
#include "xkbstr.h"
|
|
#include <X11/extensions/xtestproto.h>
|
|
#include <X11/extensions/XI.h>
|
|
#include <X11/extensions/XIproto.h>
|
|
#include "exglobals.h"
|
|
#include "mipointer.h"
|
|
#include "xserver-properties.h"
|
|
#include "exevents.h"
|
|
#include "eventstr.h"
|
|
#include "inpututils.h"
|
|
|
|
#include "modinit.h"
|
|
|
|
extern int DeviceValuator;
|
|
|
|
/* XTest events are sent during request processing and may be interruped by
|
|
* a SIGIO. We need a separate event list to avoid events overwriting each
|
|
* other's memory */
|
|
static InternalEvent *xtest_evlist;
|
|
|
|
/**
|
|
* xtestpointer
|
|
* is the virtual pointer for XTest. It is the first slave
|
|
* device of the VCP.
|
|
* xtestkeyboard
|
|
* is the virtual keyboard for XTest. It is the first slave
|
|
* device of the VCK
|
|
*
|
|
* Neither of these devices can be deleted.
|
|
*/
|
|
DeviceIntPtr xtestpointer, xtestkeyboard;
|
|
|
|
#ifdef PANORAMIX
|
|
#include "panoramiX.h"
|
|
#include "panoramiXsrv.h"
|
|
#endif
|
|
|
|
static int XTestSwapFakeInput(ClientPtr /* client */ ,
|
|
xReq * /* req */
|
|
);
|
|
|
|
static int
|
|
ProcXTestGetVersion(ClientPtr client)
|
|
{
|
|
xXTestGetVersionReply rep;
|
|
|
|
REQUEST_SIZE_MATCH(xXTestGetVersionReq);
|
|
rep.type = X_Reply;
|
|
rep.length = 0;
|
|
rep.sequenceNumber = client->sequence;
|
|
rep.majorVersion = XTestMajorVersion;
|
|
rep.minorVersion = XTestMinorVersion;
|
|
if (client->swapped) {
|
|
swaps(&rep.sequenceNumber);
|
|
swaps(&rep.minorVersion);
|
|
}
|
|
WriteToClient(client, sizeof(xXTestGetVersionReply), (char *) &rep);
|
|
return Success;
|
|
}
|
|
|
|
static int
|
|
ProcXTestCompareCursor(ClientPtr client)
|
|
{
|
|
REQUEST(xXTestCompareCursorReq);
|
|
xXTestCompareCursorReply rep;
|
|
WindowPtr pWin;
|
|
CursorPtr pCursor;
|
|
int rc;
|
|
DeviceIntPtr ptr = PickPointer(client);
|
|
|
|
REQUEST_SIZE_MATCH(xXTestCompareCursorReq);
|
|
rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess);
|
|
if (rc != Success)
|
|
return rc;
|
|
|
|
if (!ptr)
|
|
return BadAccess;
|
|
|
|
if (stuff->cursor == None)
|
|
pCursor = NullCursor;
|
|
else if (stuff->cursor == XTestCurrentCursor)
|
|
pCursor = GetSpriteCursor(ptr);
|
|
else {
|
|
rc = dixLookupResourceByType((pointer *) &pCursor, stuff->cursor,
|
|
RT_CURSOR, client, DixReadAccess);
|
|
if (rc != Success) {
|
|
client->errorValue = stuff->cursor;
|
|
return rc;
|
|
}
|
|
}
|
|
rep.type = X_Reply;
|
|
rep.length = 0;
|
|
rep.sequenceNumber = client->sequence;
|
|
rep.same = (wCursor(pWin) == pCursor);
|
|
if (client->swapped) {
|
|
swaps(&rep.sequenceNumber);
|
|
}
|
|
WriteToClient(client, sizeof(xXTestCompareCursorReply), (char *) &rep);
|
|
return Success;
|
|
}
|
|
|
|
static int
|
|
ProcXTestFakeInput(ClientPtr client)
|
|
{
|
|
REQUEST(xXTestFakeInputReq);
|
|
int nev, n, type, rc;
|
|
xEvent *ev;
|
|
DeviceIntPtr dev = NULL;
|
|
WindowPtr root;
|
|
Bool extension = FALSE;
|
|
deviceValuator *dv = NULL;
|
|
ValuatorMask mask;
|
|
int valuators[MAX_VALUATORS] = { 0 };
|
|
int numValuators = 0;
|
|
int firstValuator = 0;
|
|
int nevents = 0;
|
|
int i;
|
|
int base = 0;
|
|
int flags = 0;
|
|
int need_ptr_update = 1;
|
|
|
|
nev = (stuff->length << 2) - sizeof(xReq);
|
|
if ((nev % sizeof(xEvent)) || !nev)
|
|
return BadLength;
|
|
nev /= sizeof(xEvent);
|
|
UpdateCurrentTime();
|
|
ev = (xEvent *) &((xReq *) stuff)[1];
|
|
type = ev->u.u.type & 0177;
|
|
|
|
if (type >= EXTENSION_EVENT_BASE) {
|
|
extension = TRUE;
|
|
|
|
/* check device */
|
|
rc = dixLookupDevice(&dev, stuff->deviceid & 0177, client,
|
|
DixWriteAccess);
|
|
if (rc != Success) {
|
|
client->errorValue = stuff->deviceid & 0177;
|
|
return rc;
|
|
}
|
|
|
|
/* check type */
|
|
type -= DeviceValuator;
|
|
switch (type) {
|
|
case XI_DeviceKeyPress:
|
|
case XI_DeviceKeyRelease:
|
|
if (!dev->key) {
|
|
client->errorValue = ev->u.u.type;
|
|
return BadValue;
|
|
}
|
|
break;
|
|
case XI_DeviceButtonPress:
|
|
case XI_DeviceButtonRelease:
|
|
if (!dev->button) {
|
|
client->errorValue = ev->u.u.type;
|
|
return BadValue;
|
|
}
|
|
break;
|
|
case XI_DeviceMotionNotify:
|
|
if (!dev->valuator) {
|
|
client->errorValue = ev->u.u.type;
|
|
return BadValue;
|
|
}
|
|
break;
|
|
case XI_ProximityIn:
|
|
case XI_ProximityOut:
|
|
if (!dev->proximity) {
|
|
client->errorValue = ev->u.u.type;
|
|
return BadValue;
|
|
}
|
|
break;
|
|
default:
|
|
client->errorValue = ev->u.u.type;
|
|
return BadValue;
|
|
}
|
|
|
|
/* check validity */
|
|
if (nev == 1 && type == XI_DeviceMotionNotify)
|
|
return BadLength; /* DevMotion must be followed by DevValuator */
|
|
|
|
if (type == XI_DeviceMotionNotify) {
|
|
firstValuator = ((deviceValuator *) (ev + 1))->first_valuator;
|
|
if (firstValuator > dev->valuator->numAxes) {
|
|
client->errorValue = ev->u.u.type;
|
|
return BadValue;
|
|
}
|
|
|
|
if (ev->u.u.detail == xFalse)
|
|
flags |= POINTER_ABSOLUTE;
|
|
}
|
|
else {
|
|
firstValuator = 0;
|
|
flags |= POINTER_ABSOLUTE;
|
|
}
|
|
|
|
if (nev > 1 && !dev->valuator) {
|
|
client->errorValue = dv->first_valuator;
|
|
return BadValue;
|
|
}
|
|
|
|
/* check validity of valuator events */
|
|
base = firstValuator;
|
|
for (n = 1; n < nev; n++) {
|
|
dv = (deviceValuator *) (ev + n);
|
|
if (dv->type != DeviceValuator) {
|
|
client->errorValue = dv->type;
|
|
return BadValue;
|
|
}
|
|
if (dv->first_valuator != base) {
|
|
client->errorValue = dv->first_valuator;
|
|
return BadValue;
|
|
}
|
|
switch (dv->num_valuators) {
|
|
case 6:
|
|
valuators[base + 5] = dv->valuator5;
|
|
case 5:
|
|
valuators[base + 4] = dv->valuator4;
|
|
case 4:
|
|
valuators[base + 3] = dv->valuator3;
|
|
case 3:
|
|
valuators[base + 2] = dv->valuator2;
|
|
case 2:
|
|
valuators[base + 1] = dv->valuator1;
|
|
case 1:
|
|
valuators[base] = dv->valuator0;
|
|
break;
|
|
default:
|
|
client->errorValue = dv->num_valuators;
|
|
return BadValue;
|
|
}
|
|
|
|
base += dv->num_valuators;
|
|
numValuators += dv->num_valuators;
|
|
|
|
if (firstValuator + numValuators > dev->valuator->numAxes) {
|
|
client->errorValue = dv->num_valuators;
|
|
return BadValue;
|
|
}
|
|
}
|
|
type = type - XI_DeviceKeyPress + KeyPress;
|
|
|
|
}
|
|
else {
|
|
if (nev != 1)
|
|
return BadLength;
|
|
switch (type) {
|
|
case KeyPress:
|
|
case KeyRelease:
|
|
dev = PickKeyboard(client);
|
|
break;
|
|
case ButtonPress:
|
|
case ButtonRelease:
|
|
dev = PickPointer(client);
|
|
break;
|
|
case MotionNotify:
|
|
dev = PickPointer(client);
|
|
valuators[0] = ev->u.keyButtonPointer.rootX;
|
|
valuators[1] = ev->u.keyButtonPointer.rootY;
|
|
numValuators = 2;
|
|
firstValuator = 0;
|
|
if (ev->u.u.detail == xFalse)
|
|
flags = POINTER_ABSOLUTE | POINTER_SCREEN;
|
|
break;
|
|
default:
|
|
client->errorValue = ev->u.u.type;
|
|
return BadValue;
|
|
}
|
|
|
|
/* Technically the protocol doesn't allow for BadAccess here but
|
|
* this can only happen when all MDs are disabled. */
|
|
if (!dev)
|
|
return BadAccess;
|
|
|
|
dev = GetXTestDevice(dev);
|
|
}
|
|
|
|
|
|
/* If the event has a time set, wait for it to pass */
|
|
if (ev->u.keyButtonPointer.time) {
|
|
TimeStamp activateTime;
|
|
CARD32 ms;
|
|
|
|
activateTime = currentTime;
|
|
ms = activateTime.milliseconds + ev->u.keyButtonPointer.time;
|
|
if (ms < activateTime.milliseconds)
|
|
activateTime.months++;
|
|
activateTime.milliseconds = ms;
|
|
ev->u.keyButtonPointer.time = 0;
|
|
|
|
/* see mbuf.c:QueueDisplayRequest (from the deprecated Multibuffer
|
|
* extension) for code similar to this */
|
|
|
|
if (!ClientSleepUntil(client, &activateTime, NULL, NULL)) {
|
|
return BadAlloc;
|
|
}
|
|
/* swap the request back so we can simply re-execute it */
|
|
if (client->swapped) {
|
|
(void) XTestSwapFakeInput(client, (xReq *) stuff);
|
|
swaps(&stuff->length);
|
|
}
|
|
ResetCurrentRequest(client);
|
|
client->sequence--;
|
|
return Success;
|
|
}
|
|
|
|
switch (type) {
|
|
case KeyPress:
|
|
case KeyRelease:
|
|
if (!dev->key)
|
|
return BadDevice;
|
|
|
|
if (ev->u.u.detail < dev->key->xkbInfo->desc->min_key_code ||
|
|
ev->u.u.detail > dev->key->xkbInfo->desc->max_key_code) {
|
|
client->errorValue = ev->u.u.detail;
|
|
return BadValue;
|
|
}
|
|
|
|
need_ptr_update = 0;
|
|
break;
|
|
case MotionNotify:
|
|
if (!dev->valuator)
|
|
return BadDevice;
|
|
|
|
if (!(extension || ev->u.keyButtonPointer.root == None)) {
|
|
rc = dixLookupWindow(&root, ev->u.keyButtonPointer.root,
|
|
client, DixGetAttrAccess);
|
|
if (rc != Success)
|
|
return rc;
|
|
if (root->parent) {
|
|
client->errorValue = ev->u.keyButtonPointer.root;
|
|
return BadValue;
|
|
}
|
|
}
|
|
if (ev->u.u.detail != xTrue && ev->u.u.detail != xFalse) {
|
|
client->errorValue = ev->u.u.detail;
|
|
return BadValue;
|
|
}
|
|
|
|
/* FIXME: Xinerama! */
|
|
|
|
break;
|
|
case ButtonPress:
|
|
case ButtonRelease:
|
|
if (!dev->button)
|
|
return BadDevice;
|
|
|
|
if (!ev->u.u.detail || ev->u.u.detail > dev->button->numButtons) {
|
|
client->errorValue = ev->u.u.detail;
|
|
return BadValue;
|
|
}
|
|
break;
|
|
}
|
|
if (screenIsSaved == SCREEN_SAVER_ON)
|
|
dixSaveScreens(serverClient, SCREEN_SAVER_OFF, ScreenSaverReset);
|
|
|
|
switch (type) {
|
|
case MotionNotify:
|
|
valuator_mask_set_range(&mask, firstValuator, numValuators, valuators);
|
|
nevents = GetPointerEvents(xtest_evlist, dev, type, 0, flags, &mask);
|
|
break;
|
|
case ButtonPress:
|
|
case ButtonRelease:
|
|
valuator_mask_set_range(&mask, firstValuator, numValuators, valuators);
|
|
nevents = GetPointerEvents(xtest_evlist, dev, type, ev->u.u.detail,
|
|
flags, &mask);
|
|
break;
|
|
case KeyPress:
|
|
case KeyRelease:
|
|
nevents =
|
|
GetKeyboardEvents(xtest_evlist, dev, type, ev->u.u.detail, NULL);
|
|
break;
|
|
}
|
|
|
|
for (i = 0; i < nevents; i++)
|
|
mieqProcessDeviceEvent(dev, &xtest_evlist[i], NULL);
|
|
|
|
if (need_ptr_update)
|
|
miPointerUpdateSprite(dev);
|
|
return Success;
|
|
}
|
|
|
|
static int
|
|
ProcXTestGrabControl(ClientPtr client)
|
|
{
|
|
REQUEST(xXTestGrabControlReq);
|
|
|
|
REQUEST_SIZE_MATCH(xXTestGrabControlReq);
|
|
if ((stuff->impervious != xTrue) && (stuff->impervious != xFalse)) {
|
|
client->errorValue = stuff->impervious;
|
|
return BadValue;
|
|
}
|
|
if (stuff->impervious)
|
|
MakeClientGrabImpervious(client);
|
|
else
|
|
MakeClientGrabPervious(client);
|
|
return Success;
|
|
}
|
|
|
|
static int
|
|
ProcXTestDispatch(ClientPtr client)
|
|
{
|
|
REQUEST(xReq);
|
|
switch (stuff->data) {
|
|
case X_XTestGetVersion:
|
|
return ProcXTestGetVersion(client);
|
|
case X_XTestCompareCursor:
|
|
return ProcXTestCompareCursor(client);
|
|
case X_XTestFakeInput:
|
|
return ProcXTestFakeInput(client);
|
|
case X_XTestGrabControl:
|
|
return ProcXTestGrabControl(client);
|
|
default:
|
|
return BadRequest;
|
|
}
|
|
}
|
|
|
|
static int
|
|
SProcXTestGetVersion(ClientPtr client)
|
|
{
|
|
REQUEST(xXTestGetVersionReq);
|
|
|
|
swaps(&stuff->length);
|
|
REQUEST_SIZE_MATCH(xXTestGetVersionReq);
|
|
swaps(&stuff->minorVersion);
|
|
return ProcXTestGetVersion(client);
|
|
}
|
|
|
|
static int
|
|
SProcXTestCompareCursor(ClientPtr client)
|
|
{
|
|
REQUEST(xXTestCompareCursorReq);
|
|
|
|
swaps(&stuff->length);
|
|
REQUEST_SIZE_MATCH(xXTestCompareCursorReq);
|
|
swapl(&stuff->window);
|
|
swapl(&stuff->cursor);
|
|
return ProcXTestCompareCursor(client);
|
|
}
|
|
|
|
static int
|
|
XTestSwapFakeInput(ClientPtr client, xReq * req)
|
|
{
|
|
int nev;
|
|
xEvent *ev;
|
|
xEvent sev;
|
|
EventSwapPtr proc;
|
|
|
|
nev = ((req->length << 2) - sizeof(xReq)) / sizeof(xEvent);
|
|
for (ev = (xEvent *) &req[1]; --nev >= 0; ev++) {
|
|
/* Swap event */
|
|
proc = EventSwapVector[ev->u.u.type & 0177];
|
|
/* no swapping proc; invalid event type? */
|
|
if (!proc || proc == NotImplemented) {
|
|
client->errorValue = ev->u.u.type;
|
|
return BadValue;
|
|
}
|
|
(*proc) (ev, &sev);
|
|
*ev = sev;
|
|
}
|
|
return Success;
|
|
}
|
|
|
|
static int
|
|
SProcXTestFakeInput(ClientPtr client)
|
|
{
|
|
int n;
|
|
|
|
REQUEST(xReq);
|
|
|
|
swaps(&stuff->length);
|
|
n = XTestSwapFakeInput(client, stuff);
|
|
if (n != Success)
|
|
return n;
|
|
return ProcXTestFakeInput(client);
|
|
}
|
|
|
|
static int
|
|
SProcXTestGrabControl(ClientPtr client)
|
|
{
|
|
REQUEST(xXTestGrabControlReq);
|
|
|
|
swaps(&stuff->length);
|
|
REQUEST_SIZE_MATCH(xXTestGrabControlReq);
|
|
return ProcXTestGrabControl(client);
|
|
}
|
|
|
|
static int
|
|
SProcXTestDispatch(ClientPtr client)
|
|
{
|
|
REQUEST(xReq);
|
|
switch (stuff->data) {
|
|
case X_XTestGetVersion:
|
|
return SProcXTestGetVersion(client);
|
|
case X_XTestCompareCursor:
|
|
return SProcXTestCompareCursor(client);
|
|
case X_XTestFakeInput:
|
|
return SProcXTestFakeInput(client);
|
|
case X_XTestGrabControl:
|
|
return SProcXTestGrabControl(client);
|
|
default:
|
|
return BadRequest;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Allocate an virtual slave device for xtest events, this
|
|
* is a slave device to inputInfo master devices
|
|
*/
|
|
void
|
|
InitXTestDevices(void)
|
|
{
|
|
if (AllocXTestDevice(serverClient, "Virtual core",
|
|
&xtestpointer, &xtestkeyboard,
|
|
inputInfo.pointer, inputInfo.keyboard) != Success)
|
|
FatalError("Failed to allocate XTest devices");
|
|
|
|
if (ActivateDevice(xtestpointer, TRUE) != Success ||
|
|
ActivateDevice(xtestkeyboard, TRUE) != Success)
|
|
FatalError("Failed to activate XTest core devices.");
|
|
if (!EnableDevice(xtestpointer, TRUE) || !EnableDevice(xtestkeyboard, TRUE))
|
|
FatalError("Failed to enable XTest core devices.");
|
|
|
|
AttachDevice(NULL, xtestpointer, inputInfo.pointer);
|
|
|
|
AttachDevice(NULL, xtestkeyboard, inputInfo.keyboard);
|
|
}
|
|
|
|
/**
|
|
* Don't allow changing the XTest property.
|
|
*/
|
|
static int
|
|
DeviceSetXTestProperty(DeviceIntPtr dev, Atom property,
|
|
XIPropertyValuePtr prop, BOOL checkonly)
|
|
{
|
|
if (property == XIGetKnownProperty(XI_PROP_XTEST_DEVICE))
|
|
return BadAccess;
|
|
|
|
return Success;
|
|
}
|
|
|
|
/**
|
|
* Allocate a device pair that is initialised as a slave
|
|
* device with properties that identify the devices as belonging
|
|
* to XTest subsystem.
|
|
* This only creates the pair, Activate/Enable Device
|
|
* still need to be called.
|
|
*/
|
|
int
|
|
AllocXTestDevice(ClientPtr client, const char *name,
|
|
DeviceIntPtr *ptr, DeviceIntPtr *keybd,
|
|
DeviceIntPtr master_ptr, DeviceIntPtr master_keybd)
|
|
{
|
|
int retval;
|
|
char *xtestname;
|
|
char dummy = 1;
|
|
|
|
if (asprintf(&xtestname, "%s XTEST", name) == -1)
|
|
return BadAlloc;
|
|
|
|
retval =
|
|
AllocDevicePair(client, xtestname, ptr, keybd, CorePointerProc,
|
|
CoreKeyboardProc, FALSE);
|
|
if (retval == Success) {
|
|
(*ptr)->xtest_master_id = master_ptr->id;
|
|
(*keybd)->xtest_master_id = master_keybd->id;
|
|
|
|
XIChangeDeviceProperty(*ptr, XIGetKnownProperty(XI_PROP_XTEST_DEVICE),
|
|
XA_INTEGER, 8, PropModeReplace, 1, &dummy,
|
|
FALSE);
|
|
XISetDevicePropertyDeletable(*ptr,
|
|
XIGetKnownProperty(XI_PROP_XTEST_DEVICE),
|
|
FALSE);
|
|
XIRegisterPropertyHandler(*ptr, DeviceSetXTestProperty, NULL, NULL);
|
|
XIChangeDeviceProperty(*keybd, XIGetKnownProperty(XI_PROP_XTEST_DEVICE),
|
|
XA_INTEGER, 8, PropModeReplace, 1, &dummy,
|
|
FALSE);
|
|
XISetDevicePropertyDeletable(*keybd,
|
|
XIGetKnownProperty(XI_PROP_XTEST_DEVICE),
|
|
FALSE);
|
|
XIRegisterPropertyHandler(*keybd, DeviceSetXTestProperty, NULL, NULL);
|
|
}
|
|
|
|
free(xtestname);
|
|
|
|
return retval;
|
|
}
|
|
|
|
/**
|
|
* If master is NULL, return TRUE if the given device is an xtest device or
|
|
* FALSE otherwise.
|
|
* If master is not NULL, return TRUE if the given device is this master's
|
|
* xtest device.
|
|
*/
|
|
BOOL
|
|
IsXTestDevice(DeviceIntPtr dev, DeviceIntPtr master)
|
|
{
|
|
if (IsMaster(dev))
|
|
return FALSE;
|
|
|
|
/* deviceid 0 is reserved for XIAllDevices, non-zero mid means XTest
|
|
* device */
|
|
if (master)
|
|
return dev->xtest_master_id == master->id;
|
|
|
|
return dev->xtest_master_id != 0;
|
|
}
|
|
|
|
/**
|
|
* @return The X Test virtual device for the given master.
|
|
*/
|
|
DeviceIntPtr
|
|
GetXTestDevice(DeviceIntPtr master)
|
|
{
|
|
DeviceIntPtr it;
|
|
|
|
for (it = inputInfo.devices; it; it = it->next) {
|
|
if (IsXTestDevice(it, master))
|
|
return it;
|
|
}
|
|
|
|
/* This only happens if master is a slave device. don't do that */
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
XTestExtensionTearDown(ExtensionEntry * e)
|
|
{
|
|
FreeEventList(xtest_evlist, GetMaximumEventsNum());
|
|
xtest_evlist = NULL;
|
|
}
|
|
|
|
void
|
|
XTestExtensionInit(INITARGS)
|
|
{
|
|
AddExtension(XTestExtensionName, 0, 0,
|
|
ProcXTestDispatch, SProcXTestDispatch,
|
|
XTestExtensionTearDown, StandardMinorOpcode);
|
|
|
|
xtest_evlist = InitEventList(GetMaximumEventsNum());
|
|
}
|