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