xichangehierarchy.c revision d566a54b
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 "misc.h"
50#include "xace.h"
51#include "xiquerydevice.h"      /* for GetDeviceUse */
52
53#include "xkbsrv.h"
54
55#include "xichangehierarchy.h"
56#include "xibarriers.h"
57
58/**
59 * Send the current state of the device hierarchy to all clients.
60 */
61void
62XISendDeviceHierarchyEvent(int flags[MAXDEVICES])
63{
64    xXIHierarchyEvent *ev;
65    xXIHierarchyInfo *info;
66    DeviceIntRec dummyDev;
67    DeviceIntPtr dev;
68    int i;
69
70    if (!flags)
71        return;
72
73    ev = calloc(1, sizeof(xXIHierarchyEvent) +
74                MAXDEVICES * sizeof(xXIHierarchyInfo));
75    if (!ev)
76        return;
77    ev->type = GenericEvent;
78    ev->extension = IReqCode;
79    ev->evtype = XI_HierarchyChanged;
80    ev->time = GetTimeInMillis();
81    ev->flags = 0;
82    ev->num_info = inputInfo.numDevices;
83
84    info = (xXIHierarchyInfo *) &ev[1];
85    for (dev = inputInfo.devices; dev; dev = dev->next) {
86        info->deviceid = dev->id;
87        info->enabled = dev->enabled;
88        info->use = GetDeviceUse(dev, &info->attachment);
89        info->flags = flags[dev->id];
90        ev->flags |= info->flags;
91        info++;
92    }
93    for (dev = inputInfo.off_devices; dev; dev = dev->next) {
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    for (i = 0; i < MAXDEVICES; i++) {
103        if (flags[i] & (XIMasterRemoved | XISlaveRemoved)) {
104            info->deviceid = i;
105            info->enabled = FALSE;
106            info->flags = flags[i];
107            info->use = 0;
108            ev->flags |= info->flags;
109            ev->num_info++;
110            info++;
111        }
112    }
113
114    ev->length = bytes_to_int32(ev->num_info * sizeof(xXIHierarchyInfo));
115
116    memset(&dummyDev, 0, sizeof(dummyDev));
117    dummyDev.id = XIAllDevices;
118    dummyDev.type = SLAVE;
119    SendEventToAllWindows(&dummyDev, (XI_HierarchyChangedMask >> 8),
120                          (xEvent *) ev, 1);
121    free(ev);
122}
123
124/***********************************************************************
125 *
126 * This procedure allows a client to change the device hierarchy through
127 * adding new master devices, removing them, etc.
128 *
129 */
130
131int _X_COLD
132SProcXIChangeHierarchy(ClientPtr client)
133{
134    REQUEST(xXIChangeHierarchyReq);
135    swaps(&stuff->length);
136    return (ProcXIChangeHierarchy(client));
137}
138
139static int
140add_master(ClientPtr client, xXIAddMasterInfo * c, int flags[MAXDEVICES])
141{
142    DeviceIntPtr ptr, keybd, XTestptr, XTestkeybd;
143    char *name;
144    int rc;
145
146    name = calloc(c->name_len + 1, sizeof(char));
147    if (name == NULL) {
148        rc = BadAlloc;
149        goto unwind;
150    }
151    strncpy(name, (char *) &c[1], c->name_len);
152
153    rc = AllocDevicePair(client, name, &ptr, &keybd,
154                         CorePointerProc, CoreKeyboardProc, TRUE);
155    if (rc != Success)
156        goto unwind;
157
158    if (!c->send_core)
159        ptr->coreEvents = keybd->coreEvents = FALSE;
160
161    /* Allocate virtual slave devices for xtest events */
162    rc = AllocXTestDevice(client, name, &XTestptr, &XTestkeybd, ptr, keybd);
163    if (rc != Success) {
164        DeleteInputDeviceRequest(ptr);
165        DeleteInputDeviceRequest(keybd);
166        goto unwind;
167    }
168
169    ActivateDevice(ptr, FALSE);
170    ActivateDevice(keybd, FALSE);
171    flags[ptr->id] |= XIMasterAdded;
172    flags[keybd->id] |= XIMasterAdded;
173
174    ActivateDevice(XTestptr, FALSE);
175    ActivateDevice(XTestkeybd, FALSE);
176    flags[XTestptr->id] |= XISlaveAdded;
177    flags[XTestkeybd->id] |= XISlaveAdded;
178
179    if (c->enable) {
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
198    for (int i = 0; i < currentMaxClients; i++)
199        XIBarrierNewMasterDevice(clients[i], ptr->id);
200
201 unwind:
202    free(name);
203    return rc;
204}
205
206static void
207disable_clientpointer(DeviceIntPtr dev)
208{
209    int i;
210
211    for (i = 0; i < currentMaxClients; i++) {
212        ClientPtr client = clients[i];
213
214        if (client && client->clientPtr == dev)
215            client->clientPtr = NULL;
216    }
217}
218
219static DeviceIntPtr
220find_disabled_master(int type)
221{
222    DeviceIntPtr dev;
223
224    /* Once a master device is disabled it loses the pairing, so returning the first
225     * match is good enough */
226    for (dev = inputInfo.off_devices; dev; dev = dev->next) {
227        if (dev->type == type)
228            return dev;
229    }
230
231    return NULL;
232}
233
234static int
235remove_master(ClientPtr client, xXIRemoveMasterInfo * r, int flags[MAXDEVICES])
236{
237    DeviceIntPtr dev, ptr, keybd, XTestptr, XTestkeybd;
238    int rc = Success;
239
240    if (r->return_mode != XIAttachToMaster && r->return_mode != XIFloating)
241        return BadValue;
242
243    rc = dixLookupDevice(&dev, r->deviceid, client, DixDestroyAccess);
244    if (rc != Success)
245        goto unwind;
246
247    if (!IsMaster(dev)) {
248        client->errorValue = r->deviceid;
249        rc = BadDevice;
250        goto unwind;
251    }
252
253    /* XXX: For now, don't allow removal of VCP, VCK */
254    if (dev == inputInfo.pointer || dev == inputInfo.keyboard) {
255        rc = BadDevice;
256        goto unwind;
257    }
258
259    if ((ptr = GetMaster(dev, MASTER_POINTER)) == NULL)
260        ptr = find_disabled_master(MASTER_POINTER);
261    BUG_RETURN_VAL(ptr == NULL, BadDevice);
262    rc = dixLookupDevice(&ptr, ptr->id, client, DixDestroyAccess);
263    if (rc != Success)
264        goto unwind;
265
266    if ((keybd = GetMaster(dev, MASTER_KEYBOARD)) == NULL)
267        keybd = find_disabled_master(MASTER_KEYBOARD);
268    BUG_RETURN_VAL(keybd == NULL, BadDevice);
269    rc = dixLookupDevice(&keybd, keybd->id, client, DixDestroyAccess);
270    if (rc != Success)
271        goto unwind;
272
273    XTestptr = GetXTestDevice(ptr);
274    BUG_RETURN_VAL(XTestptr == NULL, BadDevice);
275    rc = dixLookupDevice(&XTestptr, XTestptr->id, client, DixDestroyAccess);
276    if (rc != Success)
277        goto unwind;
278
279    XTestkeybd = GetXTestDevice(keybd);
280    BUG_RETURN_VAL(XTestkeybd == NULL, BadDevice);
281    rc = dixLookupDevice(&XTestkeybd, XTestkeybd->id, client, DixDestroyAccess);
282    if (rc != Success)
283        goto unwind;
284
285    disable_clientpointer(ptr);
286
287    /* Disabling sends the devices floating, reattach them if
288     * desired. */
289    if (r->return_mode == XIAttachToMaster) {
290        DeviceIntPtr attached, newptr, newkeybd;
291
292        rc = dixLookupDevice(&newptr, r->return_pointer, client, DixAddAccess);
293        if (rc != Success)
294            goto unwind;
295
296        if (!IsMaster(newptr)) {
297            client->errorValue = r->return_pointer;
298            rc = BadDevice;
299            goto unwind;
300        }
301
302        rc = dixLookupDevice(&newkeybd, r->return_keyboard,
303                             client, DixAddAccess);
304        if (rc != Success)
305            goto unwind;
306
307        if (!IsMaster(newkeybd)) {
308            client->errorValue = r->return_keyboard;
309            rc = BadDevice;
310            goto unwind;
311        }
312
313        for (attached = inputInfo.devices; attached; attached = attached->next) {
314            if (!IsMaster(attached)) {
315                if (GetMaster(attached, MASTER_ATTACHED) == ptr) {
316                    AttachDevice(client, attached, newptr);
317                    flags[attached->id] |= XISlaveAttached;
318                }
319                if (GetMaster(attached, MASTER_ATTACHED) == keybd) {
320                    AttachDevice(client, attached, newkeybd);
321                    flags[attached->id] |= XISlaveAttached;
322                }
323            }
324        }
325    }
326
327    for (int i = 0; i < currentMaxClients; i++)
328        XIBarrierRemoveMasterDevice(clients[i], ptr->id);
329
330    /* disable the remove the devices, XTest devices must be done first
331       else the sprites they rely on will be destroyed  */
332    DisableDevice(XTestptr, FALSE);
333    DisableDevice(XTestkeybd, FALSE);
334    DisableDevice(keybd, FALSE);
335    DisableDevice(ptr, FALSE);
336    flags[XTestptr->id] |= XIDeviceDisabled | XISlaveDetached;
337    flags[XTestkeybd->id] |= XIDeviceDisabled | XISlaveDetached;
338    flags[keybd->id] |= XIDeviceDisabled;
339    flags[ptr->id] |= XIDeviceDisabled;
340
341    flags[XTestptr->id] |= XISlaveRemoved;
342    flags[XTestkeybd->id] |= XISlaveRemoved;
343    flags[keybd->id] |= XIMasterRemoved;
344    flags[ptr->id] |= XIMasterRemoved;
345
346    RemoveDevice(XTestptr, FALSE);
347    RemoveDevice(XTestkeybd, FALSE);
348    RemoveDevice(keybd, FALSE);
349    RemoveDevice(ptr, FALSE);
350
351 unwind:
352    return rc;
353}
354
355static int
356detach_slave(ClientPtr client, xXIDetachSlaveInfo * c, int flags[MAXDEVICES])
357{
358    DeviceIntPtr dev;
359    int rc;
360
361    rc = dixLookupDevice(&dev, c->deviceid, client, DixManageAccess);
362    if (rc != Success)
363        goto unwind;
364
365    if (IsMaster(dev)) {
366        client->errorValue = c->deviceid;
367        rc = BadDevice;
368        goto unwind;
369    }
370
371    /* Don't allow changes to XTest Devices, these are fixed */
372    if (IsXTestDevice(dev, NULL)) {
373        client->errorValue = c->deviceid;
374        rc = BadDevice;
375        goto unwind;
376    }
377
378    ReleaseButtonsAndKeys(dev);
379    AttachDevice(client, dev, NULL);
380    flags[dev->id] |= XISlaveDetached;
381
382 unwind:
383    return rc;
384}
385
386static int
387attach_slave(ClientPtr client, xXIAttachSlaveInfo * c, int flags[MAXDEVICES])
388{
389    DeviceIntPtr dev;
390    DeviceIntPtr newmaster;
391    int rc;
392
393    rc = dixLookupDevice(&dev, c->deviceid, client, DixManageAccess);
394    if (rc != Success)
395        goto unwind;
396
397    if (IsMaster(dev)) {
398        client->errorValue = c->deviceid;
399        rc = BadDevice;
400        goto unwind;
401    }
402
403    /* Don't allow changes to XTest Devices, these are fixed */
404    if (IsXTestDevice(dev, NULL)) {
405        client->errorValue = c->deviceid;
406        rc = BadDevice;
407        goto unwind;
408    }
409
410    rc = dixLookupDevice(&newmaster, c->new_master, client, DixAddAccess);
411    if (rc != Success)
412        goto unwind;
413    if (!IsMaster(newmaster)) {
414        client->errorValue = c->new_master;
415        rc = BadDevice;
416        goto unwind;
417    }
418
419    if (!((IsPointerDevice(newmaster) && IsPointerDevice(dev)) ||
420          (IsKeyboardDevice(newmaster) && IsKeyboardDevice(dev)))) {
421        rc = BadDevice;
422        goto unwind;
423    }
424
425    ReleaseButtonsAndKeys(dev);
426    AttachDevice(client, dev, newmaster);
427    flags[dev->id] |= XISlaveAttached;
428
429 unwind:
430    return rc;
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    int rc = Success;
441    int flags[MAXDEVICES] = { 0 };
442    enum {
443        NO_CHANGE,
444        FLUSH,
445        CHANGED,
446    } changes = NO_CHANGE;
447
448    REQUEST(xXIChangeHierarchyReq);
449    REQUEST_AT_LEAST_SIZE(xXIChangeHierarchyReq);
450
451    if (!stuff->num_changes)
452        return rc;
453
454    len = ((size_t)client->req_len << 2) - sizeof(xXIChangeHierarchyReq);
455
456    any = (xXIAnyHierarchyChangeInfo *) &stuff[1];
457    while (stuff->num_changes--) {
458        if (len < sizeof(xXIAnyHierarchyChangeInfo)) {
459            rc = BadLength;
460            goto unwind;
461        }
462
463        SWAPIF(swaps(&any->type));
464        SWAPIF(swaps(&any->length));
465
466        if (len < ((size_t)any->length << 2))
467            return BadLength;
468
469#define CHANGE_SIZE_MATCH(type) \
470    do { \
471        if ((len < sizeof(type)) || (any->length != (sizeof(type) >> 2))) { \
472            rc = BadLength; \
473            goto unwind; \
474        } \
475    } while(0)
476
477        switch (any->type) {
478        case XIAddMaster:
479        {
480            xXIAddMasterInfo *c = (xXIAddMasterInfo *) any;
481
482            /* Variable length, due to appended name string */
483            if (len < sizeof(xXIAddMasterInfo)) {
484                rc = BadLength;
485                goto unwind;
486            }
487            SWAPIF(swaps(&c->name_len));
488            if (c->name_len > (len - sizeof(xXIAddMasterInfo))) {
489                rc = BadLength;
490                goto unwind;
491            }
492
493            rc = add_master(client, c, flags);
494            if (rc != Success)
495                goto unwind;
496            changes = FLUSH;
497            break;
498        }
499        case XIRemoveMaster:
500        {
501            xXIRemoveMasterInfo *r = (xXIRemoveMasterInfo *) any;
502
503            CHANGE_SIZE_MATCH(xXIRemoveMasterInfo);
504            rc = remove_master(client, r, flags);
505            if (rc != Success)
506                goto unwind;
507            changes = FLUSH;
508            break;
509        }
510        case XIDetachSlave:
511        {
512            xXIDetachSlaveInfo *c = (xXIDetachSlaveInfo *) any;
513
514            CHANGE_SIZE_MATCH(xXIDetachSlaveInfo);
515            rc = detach_slave(client, c, flags);
516            if (rc != Success)
517                goto unwind;
518            changes = CHANGED;
519            break;
520        }
521        case XIAttachSlave:
522        {
523            xXIAttachSlaveInfo *c = (xXIAttachSlaveInfo *) any;
524
525            CHANGE_SIZE_MATCH(xXIAttachSlaveInfo);
526            rc = attach_slave(client, c, flags);
527            if (rc != Success)
528                goto unwind;
529            changes = CHANGED;
530            break;
531        }
532        default:
533            break;
534        }
535
536        if (changes == FLUSH) {
537            XISendDeviceHierarchyEvent(flags);
538            memset(flags, 0, sizeof(flags));
539            changes = NO_CHANGE;
540        }
541
542        len -= any->length * 4;
543        any = (xXIAnyHierarchyChangeInfo *) ((char *) any + any->length * 4);
544    }
545
546 unwind:
547    if (changes != NO_CHANGE)
548        XISendDeviceHierarchyEvent(flags);
549    return rc;
550}
551