xichangehierarchy.c revision 9ace9065
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 int
204remove_master(ClientPtr client, xXIRemoveMasterInfo *r,
205              int flags[MAXDEVICES])
206{
207    DeviceIntPtr ptr, keybd, XTestptr, XTestkeybd;
208    int rc = Success;
209
210    if (r->return_mode != XIAttachToMaster &&
211        r->return_mode != XIFloating)
212        return BadValue;
213
214    rc = dixLookupDevice(&ptr, r->deviceid, client, DixDestroyAccess);
215    if (rc != Success)
216        goto unwind;
217
218    if (!IsMaster(ptr))
219    {
220        client->errorValue = r->deviceid;
221        rc = BadDevice;
222        goto unwind;
223    }
224
225    /* XXX: For now, don't allow removal of VCP, VCK */
226    if (ptr == inputInfo.pointer || ptr == inputInfo.keyboard)
227    {
228        rc = BadDevice;
229        goto unwind;
230    }
231
232
233    ptr = GetMaster(ptr, MASTER_POINTER);
234    rc = dixLookupDevice(&ptr, ptr->id, client, DixDestroyAccess);
235    if (rc != Success)
236        goto unwind;
237    keybd = GetMaster(ptr, MASTER_KEYBOARD);
238    rc = dixLookupDevice(&keybd, keybd->id, client, DixDestroyAccess);
239    if (rc != Success)
240        goto unwind;
241
242    XTestptr = GetXTestDevice(ptr);
243    rc = dixLookupDevice(&XTestptr, XTestptr->id, client, DixDestroyAccess);
244    if (rc != Success)
245        goto unwind;
246
247    XTestkeybd = GetXTestDevice(keybd);
248    rc = dixLookupDevice(&XTestkeybd, XTestkeybd->id, client,
249                         DixDestroyAccess);
250    if (rc != Success)
251        goto unwind;
252
253    /* Disabling sends the devices floating, reattach them if
254     * desired. */
255    if (r->return_mode == XIAttachToMaster)
256    {
257        DeviceIntPtr attached,
258                     newptr,
259                     newkeybd;
260
261        rc = dixLookupDevice(&newptr, r->return_pointer, client, DixAddAccess);
262        if (rc != Success)
263            goto unwind;
264
265        if (!IsMaster(newptr))
266        {
267            client->errorValue = r->return_pointer;
268            rc = BadDevice;
269            goto unwind;
270        }
271
272        rc = dixLookupDevice(&newkeybd, r->return_keyboard,
273                             client, DixAddAccess);
274        if (rc != Success)
275            goto unwind;
276
277        if (!IsMaster(newkeybd))
278        {
279            client->errorValue = r->return_keyboard;
280            rc = BadDevice;
281            goto unwind;
282        }
283
284        for (attached = inputInfo.devices; attached; attached = attached->next)
285        {
286            if (!IsMaster(attached)) {
287                if (attached->u.master == ptr)
288                {
289                    AttachDevice(client, attached, newptr);
290                    flags[attached->id] |= XISlaveAttached;
291                }
292                if (attached->u.master == keybd)
293                {
294                    AttachDevice(client, attached, newkeybd);
295                    flags[attached->id] |= XISlaveAttached;
296                }
297            }
298        }
299    }
300
301    /* can't disable until we removed pairing */
302    keybd->spriteInfo->paired = NULL;
303    ptr->spriteInfo->paired = NULL;
304    XTestptr->spriteInfo->paired = NULL;
305    XTestkeybd->spriteInfo->paired = NULL;
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    RemoveDevice(XTestptr, FALSE);
319    RemoveDevice(XTestkeybd, FALSE);
320    RemoveDevice(keybd, FALSE);
321    RemoveDevice(ptr, FALSE);
322    flags[XTestptr->id] |= XISlaveRemoved;
323    flags[XTestkeybd->id] |= XISlaveRemoved;
324    flags[keybd->id] |= XIMasterRemoved;
325    flags[ptr->id] |= XIMasterRemoved;
326
327unwind:
328    return rc;
329}
330
331static int
332detach_slave(ClientPtr client, xXIDetachSlaveInfo *c, int flags[MAXDEVICES])
333{
334    DeviceIntPtr dev;
335    int rc;
336
337    rc = dixLookupDevice(&dev, c->deviceid, client, DixManageAccess);
338    if (rc != Success)
339        goto unwind;
340
341    if (IsMaster(dev))
342    {
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    {
351        client->errorValue = c->deviceid;
352        rc = BadDevice;
353        goto unwind;
354    }
355
356    ReleaseButtonsAndKeys(dev);
357    AttachDevice(client, dev, NULL);
358    flags[dev->id] |= XISlaveDetached;
359
360unwind:
361    return rc;
362}
363
364static int
365attach_slave(ClientPtr client, xXIAttachSlaveInfo *c,
366             int flags[MAXDEVICES])
367{
368    DeviceIntPtr dev;
369    DeviceIntPtr newmaster;
370    int rc;
371
372    rc = dixLookupDevice(&dev, c->deviceid, client, DixManageAccess);
373    if (rc != Success)
374        goto unwind;
375
376    if (IsMaster(dev))
377    {
378        client->errorValue = c->deviceid;
379        rc = BadDevice;
380        goto unwind;
381    }
382
383    /* Don't allow changes to XTest Devices, these are fixed */
384    if (IsXTestDevice(dev, NULL))
385    {
386        client->errorValue = c->deviceid;
387        rc = BadDevice;
388        goto unwind;
389    }
390
391    rc = dixLookupDevice(&newmaster, c->new_master, client, DixAddAccess);
392    if (rc != Success)
393        goto unwind;
394    if (!IsMaster(newmaster))
395    {
396        client->errorValue = c->new_master;
397        rc = BadDevice;
398        goto unwind;
399    }
400
401    if (!((IsPointerDevice(newmaster) && IsPointerDevice(dev)) ||
402        (IsKeyboardDevice(newmaster) && IsKeyboardDevice(dev))))
403    {
404        rc = BadDevice;
405        goto unwind;
406    }
407
408    ReleaseButtonsAndKeys(dev);
409    AttachDevice(client, dev, newmaster);
410    flags[dev->id] |= XISlaveAttached;
411
412unwind:
413    return rc;
414}
415
416
417
418#define SWAPIF(cmd) if (client->swapped) { cmd; }
419
420int
421ProcXIChangeHierarchy(ClientPtr client)
422{
423    xXIAnyHierarchyChangeInfo *any;
424    int required_len = sizeof(xXIChangeHierarchyReq);
425    char n;
426    int rc = Success;
427    int flags[MAXDEVICES] = {0};
428
429    REQUEST(xXIChangeHierarchyReq);
430    REQUEST_AT_LEAST_SIZE(xXIChangeHierarchyReq);
431
432    if (!stuff->num_changes)
433        return rc;
434
435    any = (xXIAnyHierarchyChangeInfo*)&stuff[1];
436    while(stuff->num_changes--)
437    {
438        SWAPIF(swapl(&any->type, n));
439        SWAPIF(swaps(&any->length, n));
440
441        required_len += any->length;
442        if ((stuff->length * 4) < required_len)
443            return BadLength;
444
445        switch(any->type)
446        {
447            case XIAddMaster:
448                {
449                    xXIAddMasterInfo* c = (xXIAddMasterInfo*)any;
450                    SWAPIF(swaps(&c->name_len, n));
451
452                    rc = add_master(client, c, flags);
453                    if (rc != Success)
454                        goto unwind;
455                }
456                break;
457            case XIRemoveMaster:
458                {
459                    xXIRemoveMasterInfo* r = (xXIRemoveMasterInfo*)any;
460
461                    rc = remove_master(client, r, flags);
462                    if (rc != Success)
463                        goto unwind;
464                }
465                break;
466            case XIDetachSlave:
467                {
468                    xXIDetachSlaveInfo* c = (xXIDetachSlaveInfo*)any;
469
470                    rc = detach_slave(client, c, flags);
471                    if (rc != Success)
472                       goto unwind;
473                }
474                break;
475            case XIAttachSlave:
476                {
477                    xXIAttachSlaveInfo* c = (xXIAttachSlaveInfo*)any;
478
479                    rc = attach_slave(client, c, flags);
480                    if (rc != Success)
481                       goto unwind;
482                }
483                break;
484        }
485
486        any = (xXIAnyHierarchyChangeInfo*)((char*)any + any->length * 4);
487    }
488
489unwind:
490
491    XISendDeviceHierarchyEvent(flags);
492    return rc;
493}
494
495