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  */
60 void
XISendDeviceHierarchyEvent(int flags[MAXDEVICES])61 XISendDeviceHierarchyEvent(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 
130 int _X_COLD
SProcXIChangeHierarchy(ClientPtr client)131 SProcXIChangeHierarchy(ClientPtr client)
132 {
133     REQUEST(xXIChangeHierarchyReq);
134     swaps(&stuff->length);
135     return (ProcXIChangeHierarchy(client));
136 }
137 
138 static int
add_master(ClientPtr client,xXIAddMasterInfo * c,int flags[MAXDEVICES])139 add_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 
205 static void
disable_clientpointer(DeviceIntPtr dev)206 disable_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 
218 static int
remove_master(ClientPtr client,xXIRemoveMasterInfo * r,int flags[MAXDEVICES])219 remove_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 
332 static int
detach_slave(ClientPtr client,xXIDetachSlaveInfo * c,int flags[MAXDEVICES])333 detach_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 
363 static int
attach_slave(ClientPtr client,xXIAttachSlaveInfo * c,int flags[MAXDEVICES])364 attach_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 
412 int
ProcXIChangeHierarchy(ClientPtr client)413 ProcXIChangeHierarchy(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)client->req_len << 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