xichangehierarchy.c revision 706f2543
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
34#ifdef HAVE_DIX_CONFIG_H
35#include <dix-config.h>
36#endif
37
38#include <X11/X.h>	/* for inputstr.h    */
39#include <X11/Xproto.h>	/* Request macro     */
40#include "inputstr.h"	/* DeviceIntPtr      */
41#include "windowstr.h"	/* window structure  */
42#include "scrnintstr.h"	/* screen structure  */
43#include <X11/extensions/XI.h>
44#include <X11/extensions/XI2proto.h>
45#include <X11/extensions/geproto.h>
46#include "extnsionst.h"
47#include "exevents.h"
48#include "exglobals.h"
49#include "geext.h"
50#include "xace.h"
51#include "xiquerydevice.h" /* for GetDeviceUse */
52
53#include "xkbsrv.h"
54
55#include "xichangehierarchy.h"
56
57/**
58 * Send the current state of the device hierarchy to all clients.
59 */
60void XISendDeviceHierarchyEvent(int flags[MAXDEVICES])
61{
62    xXIHierarchyEvent *ev;
63    xXIHierarchyInfo *info;
64    DeviceIntRec dummyDev;
65    DeviceIntPtr dev;
66    int i;
67
68    if (!flags)
69        return;
70
71    ev = calloc(1, sizeof(xXIHierarchyEvent) +
72                 MAXDEVICES * sizeof(xXIHierarchyInfo));
73    if (!ev)
74        return;
75    ev->type = GenericEvent;
76    ev->extension = IReqCode;
77    ev->evtype = XI_HierarchyChanged;
78    ev->time = GetTimeInMillis();
79    ev->flags = 0;
80    ev->num_info = inputInfo.numDevices;
81
82    info = (xXIHierarchyInfo*)&ev[1];
83    for (dev = inputInfo.devices; dev; dev = dev->next)
84    {
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    {
94        info->deviceid = dev->id;
95        info->enabled = dev->enabled;
96        info->use = GetDeviceUse(dev, &info->attachment);
97        info->flags = flags[dev->id];
98        ev->flags |= info->flags;
99        info++;
100    }
101
102
103    for (i = 0; i < MAXDEVICES; i++)
104    {
105        if (flags[i] & (XIMasterRemoved | XISlaveRemoved))
106        {
107            info->deviceid = i;
108            info->enabled = FALSE;
109            info->flags = flags[i];
110            info->use = 0;
111            ev->flags |= info->flags;
112            ev->num_info++;
113            info++;
114        }
115    }
116
117    ev->length = bytes_to_int32(ev->num_info * sizeof(xXIHierarchyInfo));
118
119    dummyDev.id = XIAllDevices;
120    SendEventToAllWindows(&dummyDev, (XI_HierarchyChangedMask >> 8), (xEvent*)ev, 1);
121    free(ev);
122}
123
124
125/***********************************************************************
126 *
127 * This procedure allows a client to change the device hierarchy through
128 * adding new master devices, removing them, etc.
129 *
130 */
131
132int SProcXIChangeHierarchy(ClientPtr client)
133{
134    char n;
135
136    REQUEST(xXIChangeHierarchyReq);
137    swaps(&stuff->length, n);
138    return (ProcXIChangeHierarchy(client));
139}
140
141static int
142add_master(ClientPtr client, xXIAddMasterInfo *c, int flags[MAXDEVICES])
143{
144    DeviceIntPtr ptr, keybd, XTestptr, XTestkeybd;
145    char* name;
146    int rc;
147
148    name = calloc(c->name_len + 1, sizeof(char));
149    strncpy(name, (char*)&c[1], c->name_len);
150
151    rc = AllocDevicePair(client, name, &ptr, &keybd,
152                         CorePointerProc, CoreKeyboardProc, TRUE);
153    if (rc != Success)
154        goto unwind;
155
156    if (!c->send_core)
157        ptr->coreEvents = keybd->coreEvents =  FALSE;
158
159    /* Allocate virtual slave devices for xtest events */
160    rc = AllocXTestDevice(client, name, &XTestptr, &XTestkeybd, ptr, keybd);
161    if (rc != Success)
162    {
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    {
180        EnableDevice(ptr, FALSE);
181        EnableDevice(keybd, FALSE);
182        flags[ptr->id] |= XIDeviceEnabled;
183        flags[keybd->id] |= XIDeviceEnabled;
184
185        EnableDevice(XTestptr, FALSE);
186        EnableDevice(XTestkeybd, FALSE);
187        flags[XTestptr->id] |= XIDeviceEnabled;
188        flags[XTestkeybd->id] |= XIDeviceEnabled;
189    }
190
191    /* Attach the XTest virtual devices to the newly
192       created master device */
193    AttachDevice(NULL, XTestptr, ptr);
194    AttachDevice(NULL, XTestkeybd, keybd);
195    flags[XTestptr->id] |= XISlaveAttached;
196    flags[XTestkeybd->id] |= XISlaveAttached;
197
198unwind:
199    free(name);
200    return rc;
201}
202
203static void
204disable_clientpointer(DeviceIntPtr dev)
205{
206    int i;
207
208    for (i = 0; i < currentMaxClients; i++)
209    {
210        ClientPtr client = clients[i];
211        if (client && client->clientPtr == dev)
212            client->clientPtr = NULL;
213    }
214}
215
216static int
217remove_master(ClientPtr client, xXIRemoveMasterInfo *r,
218              int flags[MAXDEVICES])
219{
220    DeviceIntPtr ptr, keybd, XTestptr, XTestkeybd;
221    int rc = Success;
222
223    if (r->return_mode != XIAttachToMaster &&
224        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    {
233        client->errorValue = r->deviceid;
234        rc = BadDevice;
235        goto unwind;
236    }
237
238    /* XXX: For now, don't allow removal of VCP, VCK */
239    if (ptr == inputInfo.pointer || ptr == inputInfo.keyboard)
240    {
241        rc = BadDevice;
242        goto unwind;
243    }
244
245
246    ptr = GetMaster(ptr, MASTER_POINTER);
247    rc = dixLookupDevice(&ptr, ptr->id, client, DixDestroyAccess);
248    if (rc != Success)
249        goto unwind;
250    keybd = GetMaster(ptr, MASTER_KEYBOARD);
251    rc = dixLookupDevice(&keybd, keybd->id, client, DixDestroyAccess);
252    if (rc != Success)
253        goto unwind;
254
255    XTestptr = GetXTestDevice(ptr);
256    rc = dixLookupDevice(&XTestptr, XTestptr->id, client, DixDestroyAccess);
257    if (rc != Success)
258        goto unwind;
259
260    XTestkeybd = GetXTestDevice(keybd);
261    rc = dixLookupDevice(&XTestkeybd, XTestkeybd->id, client,
262                         DixDestroyAccess);
263    if (rc != Success)
264        goto unwind;
265
266    disable_clientpointer(ptr);
267
268    /* Disabling sends the devices floating, reattach them if
269     * desired. */
270    if (r->return_mode == XIAttachToMaster)
271    {
272        DeviceIntPtr attached,
273                     newptr,
274                     newkeybd;
275
276        rc = dixLookupDevice(&newptr, r->return_pointer, client, DixAddAccess);
277        if (rc != Success)
278            goto unwind;
279
280        if (!IsMaster(newptr))
281        {
282            client->errorValue = r->return_pointer;
283            rc = BadDevice;
284            goto unwind;
285        }
286
287        rc = dixLookupDevice(&newkeybd, r->return_keyboard,
288                             client, DixAddAccess);
289        if (rc != Success)
290            goto unwind;
291
292        if (!IsMaster(newkeybd))
293        {
294            client->errorValue = r->return_keyboard;
295            rc = BadDevice;
296            goto unwind;
297        }
298
299        for (attached = inputInfo.devices; attached; attached = attached->next)
300        {
301            if (!IsMaster(attached)) {
302                if (attached->u.master == ptr)
303                {
304                    AttachDevice(client, attached, newptr);
305                    flags[attached->id] |= XISlaveAttached;
306                }
307                if (attached->u.master == keybd)
308                {
309                    AttachDevice(client, attached, newkeybd);
310                    flags[attached->id] |= XISlaveAttached;
311                }
312            }
313        }
314    }
315
316    /* can't disable until we removed pairing */
317    keybd->spriteInfo->paired = NULL;
318    ptr->spriteInfo->paired = NULL;
319    XTestptr->spriteInfo->paired = NULL;
320    XTestkeybd->spriteInfo->paired = NULL;
321
322    /* disable the remove the devices, XTest devices must be done first
323       else the sprites they rely on will be destroyed  */
324    DisableDevice(XTestptr, FALSE);
325    DisableDevice(XTestkeybd, FALSE);
326    DisableDevice(keybd, FALSE);
327    DisableDevice(ptr, FALSE);
328    flags[XTestptr->id] |= XIDeviceDisabled | XISlaveDetached;
329    flags[XTestkeybd->id] |= XIDeviceDisabled | XISlaveDetached;
330    flags[keybd->id] |= XIDeviceDisabled;
331    flags[ptr->id] |= XIDeviceDisabled;
332
333    RemoveDevice(XTestptr, FALSE);
334    RemoveDevice(XTestkeybd, FALSE);
335    RemoveDevice(keybd, FALSE);
336    RemoveDevice(ptr, FALSE);
337    flags[XTestptr->id] |= XISlaveRemoved;
338    flags[XTestkeybd->id] |= XISlaveRemoved;
339    flags[keybd->id] |= XIMasterRemoved;
340    flags[ptr->id] |= XIMasterRemoved;
341
342unwind:
343    return rc;
344}
345
346static int
347detach_slave(ClientPtr client, xXIDetachSlaveInfo *c, int flags[MAXDEVICES])
348{
349    DeviceIntPtr dev;
350    int rc;
351
352    rc = dixLookupDevice(&dev, c->deviceid, client, DixManageAccess);
353    if (rc != Success)
354        goto unwind;
355
356    if (IsMaster(dev))
357    {
358        client->errorValue = c->deviceid;
359        rc = BadDevice;
360        goto unwind;
361    }
362
363    /* Don't allow changes to XTest Devices, these are fixed */
364    if (IsXTestDevice(dev, NULL))
365    {
366        client->errorValue = c->deviceid;
367        rc = BadDevice;
368        goto unwind;
369    }
370
371    ReleaseButtonsAndKeys(dev);
372    AttachDevice(client, dev, NULL);
373    flags[dev->id] |= XISlaveDetached;
374
375unwind:
376    return rc;
377}
378
379static int
380attach_slave(ClientPtr client, xXIAttachSlaveInfo *c,
381             int flags[MAXDEVICES])
382{
383    DeviceIntPtr dev;
384    DeviceIntPtr newmaster;
385    int rc;
386
387    rc = dixLookupDevice(&dev, c->deviceid, client, DixManageAccess);
388    if (rc != Success)
389        goto unwind;
390
391    if (IsMaster(dev))
392    {
393        client->errorValue = c->deviceid;
394        rc = BadDevice;
395        goto unwind;
396    }
397
398    /* Don't allow changes to XTest Devices, these are fixed */
399    if (IsXTestDevice(dev, NULL))
400    {
401        client->errorValue = c->deviceid;
402        rc = BadDevice;
403        goto unwind;
404    }
405
406    rc = dixLookupDevice(&newmaster, c->new_master, client, DixAddAccess);
407    if (rc != Success)
408        goto unwind;
409    if (!IsMaster(newmaster))
410    {
411        client->errorValue = c->new_master;
412        rc = BadDevice;
413        goto unwind;
414    }
415
416    if (!((IsPointerDevice(newmaster) && IsPointerDevice(dev)) ||
417        (IsKeyboardDevice(newmaster) && IsKeyboardDevice(dev))))
418    {
419        rc = BadDevice;
420        goto unwind;
421    }
422
423    ReleaseButtonsAndKeys(dev);
424    AttachDevice(client, dev, newmaster);
425    flags[dev->id] |= XISlaveAttached;
426
427unwind:
428    return rc;
429}
430
431
432
433#define SWAPIF(cmd) if (client->swapped) { cmd; }
434
435int
436ProcXIChangeHierarchy(ClientPtr client)
437{
438    xXIAnyHierarchyChangeInfo *any;
439    size_t len;			/* length of data remaining in request */
440    char n;
441    int rc = Success;
442    int flags[MAXDEVICES] = {0};
443
444    REQUEST(xXIChangeHierarchyReq);
445    REQUEST_AT_LEAST_SIZE(xXIChangeHierarchyReq);
446
447    if (!stuff->num_changes)
448        return rc;
449
450    if (stuff->length > (INT_MAX >> 2))
451        return BadAlloc;
452    len = (stuff->length << 2) - sizeof(xXIAnyHierarchyChangeInfo);
453
454    any = (xXIAnyHierarchyChangeInfo*)&stuff[1];
455    while(stuff->num_changes--)
456    {
457        if (len < sizeof(xXIAnyHierarchyChangeInfo)) {
458            rc = BadLength;
459            goto unwind;
460        }
461
462        SWAPIF(swapl(&any->type, n));
463        SWAPIF(swaps(&any->length, n));
464
465        if ((any->length > (INT_MAX >> 2)) || (len < (any->length << 2)))
466            return BadLength;
467
468#define CHANGE_SIZE_MATCH(type) \
469    do { \
470        if ((len < sizeof(type)) || (any->length != (sizeof(type) >> 2))) { \
471            rc = BadLength; \
472            goto unwind; \
473        } \
474    } while(0)
475
476        switch(any->type)
477        {
478            case XIAddMaster:
479                {
480                    xXIAddMasterInfo* c = (xXIAddMasterInfo*)any;
481	            /* Variable length, due to appended name string */
482	            if (len < sizeof(xXIAddMasterInfo)) {
483	                rc = BadLength;
484	                goto unwind;
485	            }
486                    SWAPIF(swaps(&c->name_len, n));
487	            if (c->name_len > (len - sizeof(xXIAddMasterInfo))) {
488	                rc = BadLength;
489	                goto unwind;
490	            }
491
492                    rc = add_master(client, c, flags);
493                    if (rc != Success)
494                        goto unwind;
495                }
496                break;
497            case XIRemoveMaster:
498                {
499                    xXIRemoveMasterInfo* r = (xXIRemoveMasterInfo*)any;
500
501	            CHANGE_SIZE_MATCH(xXIRemoveMasterInfo);
502                    rc = remove_master(client, r, flags);
503                    if (rc != Success)
504                        goto unwind;
505                }
506                break;
507            case XIDetachSlave:
508                {
509                    xXIDetachSlaveInfo* c = (xXIDetachSlaveInfo*)any;
510
511	            CHANGE_SIZE_MATCH(xXIDetachSlaveInfo);
512                    rc = detach_slave(client, c, flags);
513                    if (rc != Success)
514                       goto unwind;
515                }
516                break;
517            case XIAttachSlave:
518                {
519                    xXIAttachSlaveInfo* c = (xXIAttachSlaveInfo*)any;
520
521	            CHANGE_SIZE_MATCH(xXIAttachSlaveInfo);
522                    rc = attach_slave(client, c, flags);
523                    if (rc != Success)
524                       goto unwind;
525                }
526                break;
527        }
528
529        len -= any->length * 4;
530        any = (xXIAnyHierarchyChangeInfo*)((char*)any + any->length * 4);
531    }
532
533unwind:
534
535    XISendDeviceHierarchyEvent(flags);
536    return rc;
537}
538
539