16747b715Smrg/*
26747b715Smrg * Copyright 2007-2008 Peter Hutterer
36747b715Smrg * Copyright 2009 Red Hat, Inc.
46747b715Smrg *
56747b715Smrg * Permission is hereby granted, free of charge, to any person obtaining a
66747b715Smrg * copy of this software and associated documentation files (the "Software"),
76747b715Smrg * to deal in the Software without restriction, including without limitation
86747b715Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
96747b715Smrg * and/or sell copies of the Software, and to permit persons to whom the
106747b715Smrg * Software is furnished to do so, subject to the following conditions:
116747b715Smrg *
126747b715Smrg * The above copyright notice and this permission notice (including the next
136747b715Smrg * paragraph) shall be included in all copies or substantial portions of the
146747b715Smrg * Software.
156747b715Smrg *
166747b715Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
176747b715Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
186747b715Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
196747b715Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
206747b715Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
216747b715Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
226747b715Smrg * DEALINGS IN THE SOFTWARE.
236747b715Smrg *
246747b715Smrg * Author: Peter Hutterer, University of South Australia, NICTA
256747b715Smrg */
266747b715Smrg
276747b715Smrg/***********************************************************************
286747b715Smrg *
296747b715Smrg * Request change in the device hierarchy.
306747b715Smrg *
316747b715Smrg */
326747b715Smrg
336747b715Smrg#ifdef HAVE_DIX_CONFIG_H
346747b715Smrg#include <dix-config.h>
356747b715Smrg#endif
366747b715Smrg
37f7df2e56Smrg#include <X11/X.h>              /* for inputstr.h    */
38f7df2e56Smrg#include <X11/Xproto.h>         /* Request macro     */
39f7df2e56Smrg#include "inputstr.h"           /* DeviceIntPtr      */
40f7df2e56Smrg#include "windowstr.h"          /* window structure  */
41f7df2e56Smrg#include "scrnintstr.h"         /* screen structure  */
426747b715Smrg#include <X11/extensions/XI.h>
436747b715Smrg#include <X11/extensions/XI2proto.h>
446747b715Smrg#include <X11/extensions/geproto.h>
456747b715Smrg#include "extnsionst.h"
466747b715Smrg#include "exevents.h"
476747b715Smrg#include "exglobals.h"
486747b715Smrg#include "geext.h"
49d566a54bSmrg#include "misc.h"
506747b715Smrg#include "xace.h"
51f7df2e56Smrg#include "xiquerydevice.h"      /* for GetDeviceUse */
526747b715Smrg
536747b715Smrg#include "xkbsrv.h"
546747b715Smrg
556747b715Smrg#include "xichangehierarchy.h"
56f7df2e56Smrg#include "xibarriers.h"
576747b715Smrg
586747b715Smrg/**
596747b715Smrg * Send the current state of the device hierarchy to all clients.
606747b715Smrg */
61f7df2e56Smrgvoid
62f7df2e56SmrgXISendDeviceHierarchyEvent(int flags[MAXDEVICES])
636747b715Smrg{
646747b715Smrg    xXIHierarchyEvent *ev;
656747b715Smrg    xXIHierarchyInfo *info;
666747b715Smrg    DeviceIntRec dummyDev;
676747b715Smrg    DeviceIntPtr dev;
686747b715Smrg    int i;
696747b715Smrg
706747b715Smrg    if (!flags)
716747b715Smrg        return;
726747b715Smrg
736747b715Smrg    ev = calloc(1, sizeof(xXIHierarchyEvent) +
74f7df2e56Smrg                MAXDEVICES * sizeof(xXIHierarchyInfo));
759ace9065Smrg    if (!ev)
769ace9065Smrg        return;
776747b715Smrg    ev->type = GenericEvent;
786747b715Smrg    ev->extension = IReqCode;
796747b715Smrg    ev->evtype = XI_HierarchyChanged;
806747b715Smrg    ev->time = GetTimeInMillis();
816747b715Smrg    ev->flags = 0;
826747b715Smrg    ev->num_info = inputInfo.numDevices;
836747b715Smrg
84f7df2e56Smrg    info = (xXIHierarchyInfo *) &ev[1];
85f7df2e56Smrg    for (dev = inputInfo.devices; dev; dev = dev->next) {
866747b715Smrg        info->deviceid = dev->id;
876747b715Smrg        info->enabled = dev->enabled;
886747b715Smrg        info->use = GetDeviceUse(dev, &info->attachment);
896747b715Smrg        info->flags = flags[dev->id];
906747b715Smrg        ev->flags |= info->flags;
916747b715Smrg        info++;
926747b715Smrg    }
93f7df2e56Smrg    for (dev = inputInfo.off_devices; dev; dev = dev->next) {
946747b715Smrg        info->deviceid = dev->id;
956747b715Smrg        info->enabled = dev->enabled;
966747b715Smrg        info->use = GetDeviceUse(dev, &info->attachment);
976747b715Smrg        info->flags = flags[dev->id];
986747b715Smrg        ev->flags |= info->flags;
996747b715Smrg        info++;
1006747b715Smrg    }
1016747b715Smrg
102f7df2e56Smrg    for (i = 0; i < MAXDEVICES; i++) {
103f7df2e56Smrg        if (flags[i] & (XIMasterRemoved | XISlaveRemoved)) {
1046747b715Smrg            info->deviceid = i;
1056747b715Smrg            info->enabled = FALSE;
1066747b715Smrg            info->flags = flags[i];
1076747b715Smrg            info->use = 0;
1086747b715Smrg            ev->flags |= info->flags;
1096747b715Smrg            ev->num_info++;
1106747b715Smrg            info++;
1116747b715Smrg        }
1126747b715Smrg    }
1136747b715Smrg
1146747b715Smrg    ev->length = bytes_to_int32(ev->num_info * sizeof(xXIHierarchyInfo));
1156747b715Smrg
116f7df2e56Smrg    memset(&dummyDev, 0, sizeof(dummyDev));
1176747b715Smrg    dummyDev.id = XIAllDevices;
118f7df2e56Smrg    dummyDev.type = SLAVE;
119f7df2e56Smrg    SendEventToAllWindows(&dummyDev, (XI_HierarchyChangedMask >> 8),
120f7df2e56Smrg                          (xEvent *) ev, 1);
1216747b715Smrg    free(ev);
1226747b715Smrg}
1236747b715Smrg
1246747b715Smrg/***********************************************************************
1256747b715Smrg *
1266747b715Smrg * This procedure allows a client to change the device hierarchy through
1276747b715Smrg * adding new master devices, removing them, etc.
1286747b715Smrg *
1296747b715Smrg */
1306747b715Smrg
1317e31ba66Smrgint _X_COLD
132f7df2e56SmrgSProcXIChangeHierarchy(ClientPtr client)
1336747b715Smrg{
1346747b715Smrg    REQUEST(xXIChangeHierarchyReq);
135f7df2e56Smrg    swaps(&stuff->length);
1366747b715Smrg    return (ProcXIChangeHierarchy(client));
1376747b715Smrg}
1386747b715Smrg
1399ace9065Smrgstatic int
140f7df2e56Smrgadd_master(ClientPtr client, xXIAddMasterInfo * c, int flags[MAXDEVICES])
1419ace9065Smrg{
1429ace9065Smrg    DeviceIntPtr ptr, keybd, XTestptr, XTestkeybd;
143f7df2e56Smrg    char *name;
1449ace9065Smrg    int rc;
1459ace9065Smrg
1469ace9065Smrg    name = calloc(c->name_len + 1, sizeof(char));
147f7df2e56Smrg    if (name == NULL) {
148f7df2e56Smrg        rc = BadAlloc;
149f7df2e56Smrg        goto unwind;
150f7df2e56Smrg    }
151f7df2e56Smrg    strncpy(name, (char *) &c[1], c->name_len);
1529ace9065Smrg
1539ace9065Smrg    rc = AllocDevicePair(client, name, &ptr, &keybd,
1549ace9065Smrg                         CorePointerProc, CoreKeyboardProc, TRUE);
1559ace9065Smrg    if (rc != Success)
1569ace9065Smrg        goto unwind;
1579ace9065Smrg
1589ace9065Smrg    if (!c->send_core)
159f7df2e56Smrg        ptr->coreEvents = keybd->coreEvents = FALSE;
1609ace9065Smrg
1619ace9065Smrg    /* Allocate virtual slave devices for xtest events */
1629ace9065Smrg    rc = AllocXTestDevice(client, name, &XTestptr, &XTestkeybd, ptr, keybd);
163f7df2e56Smrg    if (rc != Success) {
1649ace9065Smrg        DeleteInputDeviceRequest(ptr);
1659ace9065Smrg        DeleteInputDeviceRequest(keybd);
1669ace9065Smrg        goto unwind;
1679ace9065Smrg    }
1689ace9065Smrg
1699ace9065Smrg    ActivateDevice(ptr, FALSE);
1709ace9065Smrg    ActivateDevice(keybd, FALSE);
1719ace9065Smrg    flags[ptr->id] |= XIMasterAdded;
1729ace9065Smrg    flags[keybd->id] |= XIMasterAdded;
1739ace9065Smrg
1749ace9065Smrg    ActivateDevice(XTestptr, FALSE);
1759ace9065Smrg    ActivateDevice(XTestkeybd, FALSE);
1769ace9065Smrg    flags[XTestptr->id] |= XISlaveAdded;
1779ace9065Smrg    flags[XTestkeybd->id] |= XISlaveAdded;
1789ace9065Smrg
179f7df2e56Smrg    if (c->enable) {
1809ace9065Smrg        EnableDevice(ptr, FALSE);
1819ace9065Smrg        EnableDevice(keybd, FALSE);
1829ace9065Smrg        flags[ptr->id] |= XIDeviceEnabled;
1839ace9065Smrg        flags[keybd->id] |= XIDeviceEnabled;
1849ace9065Smrg
1859ace9065Smrg        EnableDevice(XTestptr, FALSE);
1869ace9065Smrg        EnableDevice(XTestkeybd, FALSE);
1879ace9065Smrg        flags[XTestptr->id] |= XIDeviceEnabled;
1889ace9065Smrg        flags[XTestkeybd->id] |= XIDeviceEnabled;
1899ace9065Smrg    }
1909ace9065Smrg
1919ace9065Smrg    /* Attach the XTest virtual devices to the newly
1929ace9065Smrg       created master device */
1939ace9065Smrg    AttachDevice(NULL, XTestptr, ptr);
1949ace9065Smrg    AttachDevice(NULL, XTestkeybd, keybd);
1959ace9065Smrg    flags[XTestptr->id] |= XISlaveAttached;
1969ace9065Smrg    flags[XTestkeybd->id] |= XISlaveAttached;
1979ace9065Smrg
1987e31ba66Smrg    for (int i = 0; i < currentMaxClients; i++)
1997e31ba66Smrg        XIBarrierNewMasterDevice(clients[i], ptr->id);
200f7df2e56Smrg
201f7df2e56Smrg unwind:
2029ace9065Smrg    free(name);
2039ace9065Smrg    return rc;
2049ace9065Smrg}
2059ace9065Smrg
206475c125cSmrgstatic void
207475c125cSmrgdisable_clientpointer(DeviceIntPtr dev)
208475c125cSmrg{
209475c125cSmrg    int i;
210475c125cSmrg
211f7df2e56Smrg    for (i = 0; i < currentMaxClients; i++) {
212475c125cSmrg        ClientPtr client = clients[i];
213f7df2e56Smrg
214475c125cSmrg        if (client && client->clientPtr == dev)
215475c125cSmrg            client->clientPtr = NULL;
216475c125cSmrg    }
217475c125cSmrg}
218475c125cSmrg
219d566a54bSmrgstatic DeviceIntPtr
220d566a54bSmrgfind_disabled_master(int type)
221d566a54bSmrg{
222d566a54bSmrg    DeviceIntPtr dev;
223d566a54bSmrg
224d566a54bSmrg    /* Once a master device is disabled it loses the pairing, so returning the first
225d566a54bSmrg     * match is good enough */
226d566a54bSmrg    for (dev = inputInfo.off_devices; dev; dev = dev->next) {
227d566a54bSmrg        if (dev->type == type)
228d566a54bSmrg            return dev;
229d566a54bSmrg    }
230d566a54bSmrg
231d566a54bSmrg    return NULL;
232d566a54bSmrg}
233d566a54bSmrg
2349ace9065Smrgstatic int
235f7df2e56Smrgremove_master(ClientPtr client, xXIRemoveMasterInfo * r, int flags[MAXDEVICES])
2369ace9065Smrg{
237d566a54bSmrg    DeviceIntPtr dev, ptr, keybd, XTestptr, XTestkeybd;
2389ace9065Smrg    int rc = Success;
2399ace9065Smrg
240f7df2e56Smrg    if (r->return_mode != XIAttachToMaster && r->return_mode != XIFloating)
2419ace9065Smrg        return BadValue;
2429ace9065Smrg
243d566a54bSmrg    rc = dixLookupDevice(&dev, r->deviceid, client, DixDestroyAccess);
2449ace9065Smrg    if (rc != Success)
2459ace9065Smrg        goto unwind;
2469ace9065Smrg
247d566a54bSmrg    if (!IsMaster(dev)) {
2489ace9065Smrg        client->errorValue = r->deviceid;
2499ace9065Smrg        rc = BadDevice;
2509ace9065Smrg        goto unwind;
2519ace9065Smrg    }
2529ace9065Smrg
2539ace9065Smrg    /* XXX: For now, don't allow removal of VCP, VCK */
254d566a54bSmrg    if (dev == inputInfo.pointer || dev == inputInfo.keyboard) {
2559ace9065Smrg        rc = BadDevice;
2569ace9065Smrg        goto unwind;
2579ace9065Smrg    }
2589ace9065Smrg
259d566a54bSmrg    if ((ptr = GetMaster(dev, MASTER_POINTER)) == NULL)
260d566a54bSmrg        ptr = find_disabled_master(MASTER_POINTER);
261d566a54bSmrg    BUG_RETURN_VAL(ptr == NULL, BadDevice);
2629ace9065Smrg    rc = dixLookupDevice(&ptr, ptr->id, client, DixDestroyAccess);
2639ace9065Smrg    if (rc != Success)
2649ace9065Smrg        goto unwind;
265d566a54bSmrg
266d566a54bSmrg    if ((keybd = GetMaster(dev, MASTER_KEYBOARD)) == NULL)
267d566a54bSmrg        keybd = find_disabled_master(MASTER_KEYBOARD);
268d566a54bSmrg    BUG_RETURN_VAL(keybd == NULL, BadDevice);
2699ace9065Smrg    rc = dixLookupDevice(&keybd, keybd->id, client, DixDestroyAccess);
2709ace9065Smrg    if (rc != Success)
2719ace9065Smrg        goto unwind;
2729ace9065Smrg
2739ace9065Smrg    XTestptr = GetXTestDevice(ptr);
274d566a54bSmrg    BUG_RETURN_VAL(XTestptr == NULL, BadDevice);
2759ace9065Smrg    rc = dixLookupDevice(&XTestptr, XTestptr->id, client, DixDestroyAccess);
2769ace9065Smrg    if (rc != Success)
2779ace9065Smrg        goto unwind;
2789ace9065Smrg
2799ace9065Smrg    XTestkeybd = GetXTestDevice(keybd);
280d566a54bSmrg    BUG_RETURN_VAL(XTestkeybd == NULL, BadDevice);
281f7df2e56Smrg    rc = dixLookupDevice(&XTestkeybd, XTestkeybd->id, client, DixDestroyAccess);
2829ace9065Smrg    if (rc != Success)
2839ace9065Smrg        goto unwind;
2849ace9065Smrg
285475c125cSmrg    disable_clientpointer(ptr);
286475c125cSmrg
2879ace9065Smrg    /* Disabling sends the devices floating, reattach them if
2889ace9065Smrg     * desired. */
289f7df2e56Smrg    if (r->return_mode == XIAttachToMaster) {
290f7df2e56Smrg        DeviceIntPtr attached, newptr, newkeybd;
2919ace9065Smrg
2929ace9065Smrg        rc = dixLookupDevice(&newptr, r->return_pointer, client, DixAddAccess);
2939ace9065Smrg        if (rc != Success)
2949ace9065Smrg            goto unwind;
2959ace9065Smrg
296f7df2e56Smrg        if (!IsMaster(newptr)) {
2979ace9065Smrg            client->errorValue = r->return_pointer;
2989ace9065Smrg            rc = BadDevice;
2999ace9065Smrg            goto unwind;
3009ace9065Smrg        }
3019ace9065Smrg
3029ace9065Smrg        rc = dixLookupDevice(&newkeybd, r->return_keyboard,
3039ace9065Smrg                             client, DixAddAccess);
3049ace9065Smrg        if (rc != Success)
3059ace9065Smrg            goto unwind;
3069ace9065Smrg
307f7df2e56Smrg        if (!IsMaster(newkeybd)) {
3089ace9065Smrg            client->errorValue = r->return_keyboard;
3099ace9065Smrg            rc = BadDevice;
3109ace9065Smrg            goto unwind;
3119ace9065Smrg        }
3129ace9065Smrg
313f7df2e56Smrg        for (attached = inputInfo.devices; attached; attached = attached->next) {
3149ace9065Smrg            if (!IsMaster(attached)) {
315f7df2e56Smrg                if (GetMaster(attached, MASTER_ATTACHED) == ptr) {
3169ace9065Smrg                    AttachDevice(client, attached, newptr);
3179ace9065Smrg                    flags[attached->id] |= XISlaveAttached;
3189ace9065Smrg                }
319f7df2e56Smrg                if (GetMaster(attached, MASTER_ATTACHED) == keybd) {
3209ace9065Smrg                    AttachDevice(client, attached, newkeybd);
3219ace9065Smrg                    flags[attached->id] |= XISlaveAttached;
3229ace9065Smrg                }
3239ace9065Smrg            }
3249ace9065Smrg        }
3259ace9065Smrg    }
3269ace9065Smrg
3277e31ba66Smrg    for (int i = 0; i < currentMaxClients; i++)
3287e31ba66Smrg        XIBarrierRemoveMasterDevice(clients[i], ptr->id);
3299ace9065Smrg
3309ace9065Smrg    /* disable the remove the devices, XTest devices must be done first
3319ace9065Smrg       else the sprites they rely on will be destroyed  */
3329ace9065Smrg    DisableDevice(XTestptr, FALSE);
3339ace9065Smrg    DisableDevice(XTestkeybd, FALSE);
3349ace9065Smrg    DisableDevice(keybd, FALSE);
3359ace9065Smrg    DisableDevice(ptr, FALSE);
3369ace9065Smrg    flags[XTestptr->id] |= XIDeviceDisabled | XISlaveDetached;
3379ace9065Smrg    flags[XTestkeybd->id] |= XIDeviceDisabled | XISlaveDetached;
3389ace9065Smrg    flags[keybd->id] |= XIDeviceDisabled;
3399ace9065Smrg    flags[ptr->id] |= XIDeviceDisabled;
3409ace9065Smrg
3419ace9065Smrg    flags[XTestptr->id] |= XISlaveRemoved;
3429ace9065Smrg    flags[XTestkeybd->id] |= XISlaveRemoved;
3439ace9065Smrg    flags[keybd->id] |= XIMasterRemoved;
3449ace9065Smrg    flags[ptr->id] |= XIMasterRemoved;
3459ace9065Smrg
346f7df2e56Smrg    RemoveDevice(XTestptr, FALSE);
347f7df2e56Smrg    RemoveDevice(XTestkeybd, FALSE);
348f7df2e56Smrg    RemoveDevice(keybd, FALSE);
349f7df2e56Smrg    RemoveDevice(ptr, FALSE);
350f7df2e56Smrg
351f7df2e56Smrg unwind:
3529ace9065Smrg    return rc;
3539ace9065Smrg}
3549ace9065Smrg
3559ace9065Smrgstatic int
356f7df2e56Smrgdetach_slave(ClientPtr client, xXIDetachSlaveInfo * c, int flags[MAXDEVICES])
3579ace9065Smrg{
3589ace9065Smrg    DeviceIntPtr dev;
3599ace9065Smrg    int rc;
3609ace9065Smrg
3619ace9065Smrg    rc = dixLookupDevice(&dev, c->deviceid, client, DixManageAccess);
3629ace9065Smrg    if (rc != Success)
3639ace9065Smrg        goto unwind;
3649ace9065Smrg
365f7df2e56Smrg    if (IsMaster(dev)) {
3669ace9065Smrg        client->errorValue = c->deviceid;
3679ace9065Smrg        rc = BadDevice;
3689ace9065Smrg        goto unwind;
3699ace9065Smrg    }
3709ace9065Smrg
3719ace9065Smrg    /* Don't allow changes to XTest Devices, these are fixed */
372f7df2e56Smrg    if (IsXTestDevice(dev, NULL)) {
3739ace9065Smrg        client->errorValue = c->deviceid;
3749ace9065Smrg        rc = BadDevice;
3759ace9065Smrg        goto unwind;
3769ace9065Smrg    }
3779ace9065Smrg
3789ace9065Smrg    ReleaseButtonsAndKeys(dev);
3799ace9065Smrg    AttachDevice(client, dev, NULL);
3809ace9065Smrg    flags[dev->id] |= XISlaveDetached;
3819ace9065Smrg
382f7df2e56Smrg unwind:
3839ace9065Smrg    return rc;
3849ace9065Smrg}
3859ace9065Smrg
3869ace9065Smrgstatic int
387f7df2e56Smrgattach_slave(ClientPtr client, xXIAttachSlaveInfo * c, int flags[MAXDEVICES])
3889ace9065Smrg{
3899ace9065Smrg    DeviceIntPtr dev;
3909ace9065Smrg    DeviceIntPtr newmaster;
3919ace9065Smrg    int rc;
3929ace9065Smrg
3939ace9065Smrg    rc = dixLookupDevice(&dev, c->deviceid, client, DixManageAccess);
3949ace9065Smrg    if (rc != Success)
3959ace9065Smrg        goto unwind;
3969ace9065Smrg
397f7df2e56Smrg    if (IsMaster(dev)) {
3989ace9065Smrg        client->errorValue = c->deviceid;
3999ace9065Smrg        rc = BadDevice;
4009ace9065Smrg        goto unwind;
4019ace9065Smrg    }
4029ace9065Smrg
4039ace9065Smrg    /* Don't allow changes to XTest Devices, these are fixed */
404f7df2e56Smrg    if (IsXTestDevice(dev, NULL)) {
4059ace9065Smrg        client->errorValue = c->deviceid;
4069ace9065Smrg        rc = BadDevice;
4079ace9065Smrg        goto unwind;
4089ace9065Smrg    }
4099ace9065Smrg
4109ace9065Smrg    rc = dixLookupDevice(&newmaster, c->new_master, client, DixAddAccess);
4119ace9065Smrg    if (rc != Success)
4129ace9065Smrg        goto unwind;
413f7df2e56Smrg    if (!IsMaster(newmaster)) {
4149ace9065Smrg        client->errorValue = c->new_master;
4159ace9065Smrg        rc = BadDevice;
4169ace9065Smrg        goto unwind;
4179ace9065Smrg    }
4189ace9065Smrg
4199ace9065Smrg    if (!((IsPointerDevice(newmaster) && IsPointerDevice(dev)) ||
420f7df2e56Smrg          (IsKeyboardDevice(newmaster) && IsKeyboardDevice(dev)))) {
4219ace9065Smrg        rc = BadDevice;
4229ace9065Smrg        goto unwind;
4239ace9065Smrg    }
4249ace9065Smrg
4259ace9065Smrg    ReleaseButtonsAndKeys(dev);
4269ace9065Smrg    AttachDevice(client, dev, newmaster);
4279ace9065Smrg    flags[dev->id] |= XISlaveAttached;
4289ace9065Smrg
429f7df2e56Smrg unwind:
4309ace9065Smrg    return rc;
4319ace9065Smrg}
4329ace9065Smrg
4336747b715Smrg#define SWAPIF(cmd) if (client->swapped) { cmd; }
4346747b715Smrg
4356747b715Smrgint
4366747b715SmrgProcXIChangeHierarchy(ClientPtr client)
4376747b715Smrg{
4386747b715Smrg    xXIAnyHierarchyChangeInfo *any;
4390b0d8713Smrg    size_t len;			/* length of data remaining in request */
4406747b715Smrg    int rc = Success;
441f7df2e56Smrg    int flags[MAXDEVICES] = { 0 };
442875c6e4fSmrg    enum {
443875c6e4fSmrg        NO_CHANGE,
444875c6e4fSmrg        FLUSH,
445875c6e4fSmrg        CHANGED,
446875c6e4fSmrg    } changes = NO_CHANGE;
4476747b715Smrg
4486747b715Smrg    REQUEST(xXIChangeHierarchyReq);
4496747b715Smrg    REQUEST_AT_LEAST_SIZE(xXIChangeHierarchyReq);
4506747b715Smrg
4516747b715Smrg    if (!stuff->num_changes)
4526747b715Smrg        return rc;
4536747b715Smrg
454806e81e9Smrg    len = ((size_t)client->req_len << 2) - sizeof(xXIChangeHierarchyReq);
4550b0d8713Smrg
456f7df2e56Smrg    any = (xXIAnyHierarchyChangeInfo *) &stuff[1];
457f7df2e56Smrg    while (stuff->num_changes--) {
4580b0d8713Smrg        if (len < sizeof(xXIAnyHierarchyChangeInfo)) {
4590b0d8713Smrg            rc = BadLength;
4600b0d8713Smrg            goto unwind;
4610b0d8713Smrg        }
4620b0d8713Smrg
463f7df2e56Smrg        SWAPIF(swaps(&any->type));
464f7df2e56Smrg        SWAPIF(swaps(&any->length));
4656747b715Smrg
466f7df2e56Smrg        if (len < ((size_t)any->length << 2))
4676747b715Smrg            return BadLength;
4686747b715Smrg
4690b0d8713Smrg#define CHANGE_SIZE_MATCH(type) \
4700b0d8713Smrg    do { \
4710b0d8713Smrg        if ((len < sizeof(type)) || (any->length != (sizeof(type) >> 2))) { \
4720b0d8713Smrg            rc = BadLength; \
4730b0d8713Smrg            goto unwind; \
4740b0d8713Smrg        } \
4750b0d8713Smrg    } while(0)
4760b0d8713Smrg
477f7df2e56Smrg        switch (any->type) {
478f7df2e56Smrg        case XIAddMaster:
4796747b715Smrg        {
480f7df2e56Smrg            xXIAddMasterInfo *c = (xXIAddMasterInfo *) any;
481f7df2e56Smrg
482f7df2e56Smrg            /* Variable length, due to appended name string */
483f7df2e56Smrg            if (len < sizeof(xXIAddMasterInfo)) {
484f7df2e56Smrg                rc = BadLength;
485f7df2e56Smrg                goto unwind;
486f7df2e56Smrg            }
487f7df2e56Smrg            SWAPIF(swaps(&c->name_len));
488f7df2e56Smrg            if (c->name_len > (len - sizeof(xXIAddMasterInfo))) {
489f7df2e56Smrg                rc = BadLength;
490f7df2e56Smrg                goto unwind;
491f7df2e56Smrg            }
492f7df2e56Smrg
493f7df2e56Smrg            rc = add_master(client, c, flags);
494f7df2e56Smrg            if (rc != Success)
495f7df2e56Smrg                goto unwind;
496875c6e4fSmrg            changes = FLUSH;
497f7df2e56Smrg            break;
498875c6e4fSmrg        }
499f7df2e56Smrg        case XIRemoveMaster:
500f7df2e56Smrg        {
501f7df2e56Smrg            xXIRemoveMasterInfo *r = (xXIRemoveMasterInfo *) any;
502f7df2e56Smrg
503f7df2e56Smrg            CHANGE_SIZE_MATCH(xXIRemoveMasterInfo);
504f7df2e56Smrg            rc = remove_master(client, r, flags);
505f7df2e56Smrg            if (rc != Success)
506f7df2e56Smrg                goto unwind;
507875c6e4fSmrg            changes = FLUSH;
508f7df2e56Smrg            break;
509875c6e4fSmrg        }
510f7df2e56Smrg        case XIDetachSlave:
511f7df2e56Smrg        {
512f7df2e56Smrg            xXIDetachSlaveInfo *c = (xXIDetachSlaveInfo *) any;
513f7df2e56Smrg
514f7df2e56Smrg            CHANGE_SIZE_MATCH(xXIDetachSlaveInfo);
515f7df2e56Smrg            rc = detach_slave(client, c, flags);
516f7df2e56Smrg            if (rc != Success)
517f7df2e56Smrg                goto unwind;
518875c6e4fSmrg            changes = CHANGED;
519f7df2e56Smrg            break;
520875c6e4fSmrg        }
521f7df2e56Smrg        case XIAttachSlave:
522f7df2e56Smrg        {
523f7df2e56Smrg            xXIAttachSlaveInfo *c = (xXIAttachSlaveInfo *) any;
524f7df2e56Smrg
525f7df2e56Smrg            CHANGE_SIZE_MATCH(xXIAttachSlaveInfo);
526f7df2e56Smrg            rc = attach_slave(client, c, flags);
527f7df2e56Smrg            if (rc != Success)
528f7df2e56Smrg                goto unwind;
529875c6e4fSmrg            changes = CHANGED;
530875c6e4fSmrg            break;
531f7df2e56Smrg        }
532875c6e4fSmrg        default:
533f7df2e56Smrg            break;
5346747b715Smrg        }
5356747b715Smrg
536875c6e4fSmrg        if (changes == FLUSH) {
537875c6e4fSmrg            XISendDeviceHierarchyEvent(flags);
538875c6e4fSmrg            memset(flags, 0, sizeof(flags));
539875c6e4fSmrg            changes = NO_CHANGE;
540875c6e4fSmrg        }
541875c6e4fSmrg
5420b0d8713Smrg        len -= any->length * 4;
543f7df2e56Smrg        any = (xXIAnyHierarchyChangeInfo *) ((char *) any + any->length * 4);
5446747b715Smrg    }
5456747b715Smrg
546f7df2e56Smrg unwind:
547875c6e4fSmrg    if (changes != NO_CHANGE)
548875c6e4fSmrg        XISendDeviceHierarchyEvent(flags);
5496747b715Smrg    return rc;
5506747b715Smrg}
551