xichangehierarchy.c revision 7e31ba66
1/*
2 * Copyright 2007-2008 Peter Hutterer
3 * Copyright 2009 Red Hat, Inc.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *
24 * Author: Peter Hutterer, University of South Australia, NICTA
25 */
26
27/***********************************************************************
28 *
29 * Request change in the device hierarchy.
30 *
31 */
32
33#ifdef HAVE_DIX_CONFIG_H
34#include <dix-config.h>
35#endif
36
37#include <X11/X.h>              /* for inputstr.h    */
38#include <X11/Xproto.h>         /* Request macro     */
39#include "inputstr.h"           /* DeviceIntPtr      */
40#include "windowstr.h"          /* window structure  */
41#include "scrnintstr.h"         /* screen structure  */
42#include <X11/extensions/XI.h>
43#include <X11/extensions/XI2proto.h>
44#include <X11/extensions/geproto.h>
45#include "extnsionst.h"
46#include "exevents.h"
47#include "exglobals.h"
48#include "geext.h"
49#include "xace.h"
50#include "xiquerydevice.h"      /* for GetDeviceUse */
51
52#include "xkbsrv.h"
53
54#include "xichangehierarchy.h"
55#include "xibarriers.h"
56
57/**
58 * Send the current state of the device hierarchy to all clients.
59 */
60void
61XISendDeviceHierarchyEvent(int flags[MAXDEVICES])
62{
63    xXIHierarchyEvent *ev;
64    xXIHierarchyInfo *info;
65    DeviceIntRec dummyDev;
66    DeviceIntPtr dev;
67    int i;
68
69    if (!flags)
70        return;
71
72    ev = calloc(1, sizeof(xXIHierarchyEvent) +
73                MAXDEVICES * sizeof(xXIHierarchyInfo));
74    if (!ev)
75        return;
76    ev->type = GenericEvent;
77    ev->extension = IReqCode;
78    ev->evtype = XI_HierarchyChanged;
79    ev->time = GetTimeInMillis();
80    ev->flags = 0;
81    ev->num_info = inputInfo.numDevices;
82
83    info = (xXIHierarchyInfo *) &ev[1];
84    for (dev = inputInfo.devices; dev; dev = dev->next) {
85        info->deviceid = dev->id;
86        info->enabled = dev->enabled;
87        info->use = GetDeviceUse(dev, &info->attachment);
88        info->flags = flags[dev->id];
89        ev->flags |= info->flags;
90        info++;
91    }
92    for (dev = inputInfo.off_devices; dev; dev = dev->next) {
93        info->deviceid = dev->id;
94        info->enabled = dev->enabled;
95        info->use = GetDeviceUse(dev, &info->attachment);
96        info->flags = flags[dev->id];
97        ev->flags |= info->flags;
98        info++;
99    }
100
101    for (i = 0; i < MAXDEVICES; i++) {
102        if (flags[i] & (XIMasterRemoved | XISlaveRemoved)) {
103            info->deviceid = i;
104            info->enabled = FALSE;
105            info->flags = flags[i];
106            info->use = 0;
107            ev->flags |= info->flags;
108            ev->num_info++;
109            info++;
110        }
111    }
112
113    ev->length = bytes_to_int32(ev->num_info * sizeof(xXIHierarchyInfo));
114
115    memset(&dummyDev, 0, sizeof(dummyDev));
116    dummyDev.id = XIAllDevices;
117    dummyDev.type = SLAVE;
118    SendEventToAllWindows(&dummyDev, (XI_HierarchyChangedMask >> 8),
119                          (xEvent *) ev, 1);
120    free(ev);
121}
122
123/***********************************************************************
124 *
125 * This procedure allows a client to change the device hierarchy through
126 * adding new master devices, removing them, etc.
127 *
128 */
129
130int _X_COLD
131SProcXIChangeHierarchy(ClientPtr client)
132{
133    REQUEST(xXIChangeHierarchyReq);
134    swaps(&stuff->length);
135    return (ProcXIChangeHierarchy(client));
136}
137
138static int
139add_master(ClientPtr client, xXIAddMasterInfo * c, int flags[MAXDEVICES])
140{
141    DeviceIntPtr ptr, keybd, XTestptr, XTestkeybd;
142    char *name;
143    int rc;
144
145    name = calloc(c->name_len + 1, sizeof(char));
146    if (name == NULL) {
147        rc = BadAlloc;
148        goto unwind;
149    }
150    strncpy(name, (char *) &c[1], c->name_len);
151
152    rc = AllocDevicePair(client, name, &ptr, &keybd,
153                         CorePointerProc, CoreKeyboardProc, TRUE);
154    if (rc != Success)
155        goto unwind;
156
157    if (!c->send_core)
158        ptr->coreEvents = keybd->coreEvents = FALSE;
159
160    /* Allocate virtual slave devices for xtest events */
161    rc = AllocXTestDevice(client, name, &XTestptr, &XTestkeybd, ptr, keybd);
162    if (rc != Success) {
163        DeleteInputDeviceRequest(ptr);
164        DeleteInputDeviceRequest(keybd);
165        goto unwind;
166    }
167
168    ActivateDevice(ptr, FALSE);
169    ActivateDevice(keybd, FALSE);
170    flags[ptr->id] |= XIMasterAdded;
171    flags[keybd->id] |= XIMasterAdded;
172
173    ActivateDevice(XTestptr, FALSE);
174    ActivateDevice(XTestkeybd, FALSE);
175    flags[XTestptr->id] |= XISlaveAdded;
176    flags[XTestkeybd->id] |= XISlaveAdded;
177
178    if (c->enable) {
179        EnableDevice(ptr, FALSE);
180        EnableDevice(keybd, FALSE);
181        flags[ptr->id] |= XIDeviceEnabled;
182        flags[keybd->id] |= XIDeviceEnabled;
183
184        EnableDevice(XTestptr, FALSE);
185        EnableDevice(XTestkeybd, FALSE);
186        flags[XTestptr->id] |= XIDeviceEnabled;
187        flags[XTestkeybd->id] |= XIDeviceEnabled;
188    }
189
190    /* Attach the XTest virtual devices to the newly
191       created master device */
192    AttachDevice(NULL, XTestptr, ptr);
193    AttachDevice(NULL, XTestkeybd, keybd);
194    flags[XTestptr->id] |= XISlaveAttached;
195    flags[XTestkeybd->id] |= XISlaveAttached;
196
197    for (int i = 0; i < currentMaxClients; i++)
198        XIBarrierNewMasterDevice(clients[i], ptr->id);
199
200 unwind:
201    free(name);
202    return rc;
203}
204
205static void
206disable_clientpointer(DeviceIntPtr dev)
207{
208    int i;
209
210    for (i = 0; i < currentMaxClients; i++) {
211        ClientPtr client = clients[i];
212
213        if (client && client->clientPtr == dev)
214            client->clientPtr = NULL;
215    }
216}
217
218static int
219remove_master(ClientPtr client, xXIRemoveMasterInfo * r, int flags[MAXDEVICES])
220{
221    DeviceIntPtr ptr, keybd, XTestptr, XTestkeybd;
222    int rc = Success;
223
224    if (r->return_mode != XIAttachToMaster && r->return_mode != XIFloating)
225        return BadValue;
226
227    rc = dixLookupDevice(&ptr, r->deviceid, client, DixDestroyAccess);
228    if (rc != Success)
229        goto unwind;
230
231    if (!IsMaster(ptr)) {
232        client->errorValue = r->deviceid;
233        rc = BadDevice;
234        goto unwind;
235    }
236
237    /* XXX: For now, don't allow removal of VCP, VCK */
238    if (ptr == inputInfo.pointer ||ptr == inputInfo.keyboard) {
239        rc = BadDevice;
240        goto unwind;
241    }
242
243    ptr = GetMaster(ptr, MASTER_POINTER);
244    rc = dixLookupDevice(&ptr, ptr->id, client, DixDestroyAccess);
245    if (rc != Success)
246        goto unwind;
247    keybd = GetMaster(ptr, MASTER_KEYBOARD);
248    rc = dixLookupDevice(&keybd, keybd->id, client, DixDestroyAccess);
249    if (rc != Success)
250        goto unwind;
251
252    XTestptr = GetXTestDevice(ptr);
253    rc = dixLookupDevice(&XTestptr, XTestptr->id, client, DixDestroyAccess);
254    if (rc != Success)
255        goto unwind;
256
257    XTestkeybd = GetXTestDevice(keybd);
258    rc = dixLookupDevice(&XTestkeybd, XTestkeybd->id, client, DixDestroyAccess);
259    if (rc != Success)
260        goto unwind;
261
262    disable_clientpointer(ptr);
263
264    /* Disabling sends the devices floating, reattach them if
265     * desired. */
266    if (r->return_mode == XIAttachToMaster) {
267        DeviceIntPtr attached, newptr, newkeybd;
268
269        rc = dixLookupDevice(&newptr, r->return_pointer, client, DixAddAccess);
270        if (rc != Success)
271            goto unwind;
272
273        if (!IsMaster(newptr)) {
274            client->errorValue = r->return_pointer;
275            rc = BadDevice;
276            goto unwind;
277        }
278
279        rc = dixLookupDevice(&newkeybd, r->return_keyboard,
280                             client, DixAddAccess);
281        if (rc != Success)
282            goto unwind;
283
284        if (!IsMaster(newkeybd)) {
285            client->errorValue = r->return_keyboard;
286            rc = BadDevice;
287            goto unwind;
288        }
289
290        for (attached = inputInfo.devices; attached; attached = attached->next) {
291            if (!IsMaster(attached)) {
292                if (GetMaster(attached, MASTER_ATTACHED) == ptr) {
293                    AttachDevice(client, attached, newptr);
294                    flags[attached->id] |= XISlaveAttached;
295                }
296                if (GetMaster(attached, MASTER_ATTACHED) == keybd) {
297                    AttachDevice(client, attached, newkeybd);
298                    flags[attached->id] |= XISlaveAttached;
299                }
300            }
301        }
302    }
303
304    for (int i = 0; i < currentMaxClients; i++)
305        XIBarrierRemoveMasterDevice(clients[i], ptr->id);
306
307    /* disable the remove the devices, XTest devices must be done first
308       else the sprites they rely on will be destroyed  */
309    DisableDevice(XTestptr, FALSE);
310    DisableDevice(XTestkeybd, FALSE);
311    DisableDevice(keybd, FALSE);
312    DisableDevice(ptr, FALSE);
313    flags[XTestptr->id] |= XIDeviceDisabled | XISlaveDetached;
314    flags[XTestkeybd->id] |= XIDeviceDisabled | XISlaveDetached;
315    flags[keybd->id] |= XIDeviceDisabled;
316    flags[ptr->id] |= XIDeviceDisabled;
317
318    flags[XTestptr->id] |= XISlaveRemoved;
319    flags[XTestkeybd->id] |= XISlaveRemoved;
320    flags[keybd->id] |= XIMasterRemoved;
321    flags[ptr->id] |= XIMasterRemoved;
322
323    RemoveDevice(XTestptr, FALSE);
324    RemoveDevice(XTestkeybd, FALSE);
325    RemoveDevice(keybd, FALSE);
326    RemoveDevice(ptr, FALSE);
327
328 unwind:
329    return rc;
330}
331
332static int
333detach_slave(ClientPtr client, xXIDetachSlaveInfo * c, int flags[MAXDEVICES])
334{
335    DeviceIntPtr dev;
336    int rc;
337
338    rc = dixLookupDevice(&dev, c->deviceid, client, DixManageAccess);
339    if (rc != Success)
340        goto unwind;
341
342    if (IsMaster(dev)) {
343        client->errorValue = c->deviceid;
344        rc = BadDevice;
345        goto unwind;
346    }
347
348    /* Don't allow changes to XTest Devices, these are fixed */
349    if (IsXTestDevice(dev, NULL)) {
350        client->errorValue = c->deviceid;
351        rc = BadDevice;
352        goto unwind;
353    }
354
355    ReleaseButtonsAndKeys(dev);
356    AttachDevice(client, dev, NULL);
357    flags[dev->id] |= XISlaveDetached;
358
359 unwind:
360    return rc;
361}
362
363static int
364attach_slave(ClientPtr client, xXIAttachSlaveInfo * c, int flags[MAXDEVICES])
365{
366    DeviceIntPtr dev;
367    DeviceIntPtr newmaster;
368    int rc;
369
370    rc = dixLookupDevice(&dev, c->deviceid, client, DixManageAccess);
371    if (rc != Success)
372        goto unwind;
373
374    if (IsMaster(dev)) {
375        client->errorValue = c->deviceid;
376        rc = BadDevice;
377        goto unwind;
378    }
379
380    /* Don't allow changes to XTest Devices, these are fixed */
381    if (IsXTestDevice(dev, NULL)) {
382        client->errorValue = c->deviceid;
383        rc = BadDevice;
384        goto unwind;
385    }
386
387    rc = dixLookupDevice(&newmaster, c->new_master, client, DixAddAccess);
388    if (rc != Success)
389        goto unwind;
390    if (!IsMaster(newmaster)) {
391        client->errorValue = c->new_master;
392        rc = BadDevice;
393        goto unwind;
394    }
395
396    if (!((IsPointerDevice(newmaster) && IsPointerDevice(dev)) ||
397          (IsKeyboardDevice(newmaster) && IsKeyboardDevice(dev)))) {
398        rc = BadDevice;
399        goto unwind;
400    }
401
402    ReleaseButtonsAndKeys(dev);
403    AttachDevice(client, dev, newmaster);
404    flags[dev->id] |= XISlaveAttached;
405
406 unwind:
407    return rc;
408}
409
410#define SWAPIF(cmd) if (client->swapped) { cmd; }
411
412int
413ProcXIChangeHierarchy(ClientPtr client)
414{
415    xXIAnyHierarchyChangeInfo *any;
416    size_t len;			/* length of data remaining in request */
417    int rc = Success;
418    int flags[MAXDEVICES] = { 0 };
419
420    REQUEST(xXIChangeHierarchyReq);
421    REQUEST_AT_LEAST_SIZE(xXIChangeHierarchyReq);
422
423    if (!stuff->num_changes)
424        return rc;
425
426    len = ((size_t)stuff->length << 2) - sizeof(xXIChangeHierarchyReq);
427
428    any = (xXIAnyHierarchyChangeInfo *) &stuff[1];
429    while (stuff->num_changes--) {
430        if (len < sizeof(xXIAnyHierarchyChangeInfo)) {
431            rc = BadLength;
432            goto unwind;
433        }
434
435        SWAPIF(swaps(&any->type));
436        SWAPIF(swaps(&any->length));
437
438        if (len < ((size_t)any->length << 2))
439            return BadLength;
440
441#define CHANGE_SIZE_MATCH(type) \
442    do { \
443        if ((len < sizeof(type)) || (any->length != (sizeof(type) >> 2))) { \
444            rc = BadLength; \
445            goto unwind; \
446        } \
447    } while(0)
448
449        switch (any->type) {
450        case XIAddMaster:
451        {
452            xXIAddMasterInfo *c = (xXIAddMasterInfo *) any;
453
454            /* Variable length, due to appended name string */
455            if (len < sizeof(xXIAddMasterInfo)) {
456                rc = BadLength;
457                goto unwind;
458            }
459            SWAPIF(swaps(&c->name_len));
460            if (c->name_len > (len - sizeof(xXIAddMasterInfo))) {
461                rc = BadLength;
462                goto unwind;
463            }
464
465            rc = add_master(client, c, flags);
466            if (rc != Success)
467                goto unwind;
468        }
469            break;
470        case XIRemoveMaster:
471        {
472            xXIRemoveMasterInfo *r = (xXIRemoveMasterInfo *) any;
473
474            CHANGE_SIZE_MATCH(xXIRemoveMasterInfo);
475            rc = remove_master(client, r, flags);
476            if (rc != Success)
477                goto unwind;
478        }
479            break;
480        case XIDetachSlave:
481        {
482            xXIDetachSlaveInfo *c = (xXIDetachSlaveInfo *) any;
483
484            CHANGE_SIZE_MATCH(xXIDetachSlaveInfo);
485            rc = detach_slave(client, c, flags);
486            if (rc != Success)
487                goto unwind;
488        }
489            break;
490        case XIAttachSlave:
491        {
492            xXIAttachSlaveInfo *c = (xXIAttachSlaveInfo *) any;
493
494            CHANGE_SIZE_MATCH(xXIAttachSlaveInfo);
495            rc = attach_slave(client, c, flags);
496            if (rc != Success)
497                goto unwind;
498        }
499            break;
500        }
501
502        len -= any->length * 4;
503        any = (xXIAnyHierarchyChangeInfo *) ((char *) any + any->length * 4);
504    }
505
506 unwind:
507
508    XISendDeviceHierarchyEvent(flags);
509    return rc;
510}
511