xichangehierarchy.c revision 475c125c
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    int required_len = sizeof(xXIChangeHierarchyReq);
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    any = (xXIAnyHierarchyChangeInfo*)&stuff[1];
451    while(stuff->num_changes--)
452    {
453        SWAPIF(swapl(&any->type, n));
454        SWAPIF(swaps(&any->length, n));
455
456        required_len += any->length;
457        if ((stuff->length * 4) < required_len)
458            return BadLength;
459
460        switch(any->type)
461        {
462            case XIAddMaster:
463                {
464                    xXIAddMasterInfo* c = (xXIAddMasterInfo*)any;
465                    SWAPIF(swaps(&c->name_len, n));
466
467                    rc = add_master(client, c, flags);
468                    if (rc != Success)
469                        goto unwind;
470                }
471                break;
472            case XIRemoveMaster:
473                {
474                    xXIRemoveMasterInfo* r = (xXIRemoveMasterInfo*)any;
475
476                    rc = remove_master(client, r, flags);
477                    if (rc != Success)
478                        goto unwind;
479                }
480                break;
481            case XIDetachSlave:
482                {
483                    xXIDetachSlaveInfo* c = (xXIDetachSlaveInfo*)any;
484
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                    rc = attach_slave(client, c, flags);
495                    if (rc != Success)
496                       goto unwind;
497                }
498                break;
499        }
500
501        any = (xXIAnyHierarchyChangeInfo*)((char*)any + any->length * 4);
502    }
503
504unwind:
505
506    XISendDeviceHierarchyEvent(flags);
507    return rc;
508}
509
510