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