xichangehierarchy.c revision 6e78d31f
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 */ 60void 61XISendDeviceHierarchyEvent(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 130int 131SProcXIChangeHierarchy(ClientPtr client) 132{ 133 REQUEST(xXIChangeHierarchyReq); 134 swaps(&stuff->length); 135 return (ProcXIChangeHierarchy(client)); 136} 137 138static int 139add_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 204static void 205disable_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 217static int 218remove_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 330static int 331detach_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 361static int 362attach_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 410int 411ProcXIChangeHierarchy(ClientPtr client) 412{ 413 xXIAnyHierarchyChangeInfo *any; 414 size_t len; /* length of data remaining in request */ 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#if 0 425 if (stuff->length > (INT_MAX >> 2)) 426 return BadAlloc; 427#endif 428 len = ((size_t)stuff->length << 2) - sizeof(xXIChangeHierarchyReq); 429 430 any = (xXIAnyHierarchyChangeInfo *) &stuff[1]; 431 while (stuff->num_changes--) { 432 if (len < sizeof(xXIAnyHierarchyChangeInfo)) { 433 rc = BadLength; 434 goto unwind; 435 } 436 437 SWAPIF(swaps(&any->type)); 438 SWAPIF(swaps(&any->length)); 439 440 if (len < ((size_t)any->length << 2)) 441 return BadLength; 442 443#define CHANGE_SIZE_MATCH(type) \ 444 do { \ 445 if ((len < sizeof(type)) || (any->length != (sizeof(type) >> 2))) { \ 446 rc = BadLength; \ 447 goto unwind; \ 448 } \ 449 } while(0) 450 451 switch (any->type) { 452 case XIAddMaster: 453 { 454 xXIAddMasterInfo *c = (xXIAddMasterInfo *) any; 455 456 /* Variable length, due to appended name string */ 457 if (len < sizeof(xXIAddMasterInfo)) { 458 rc = BadLength; 459 goto unwind; 460 } 461 SWAPIF(swaps(&c->name_len)); 462 if (c->name_len > (len - sizeof(xXIAddMasterInfo))) { 463 rc = BadLength; 464 goto unwind; 465 } 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 CHANGE_SIZE_MATCH(xXIRemoveMasterInfo); 477 rc = remove_master(client, r, flags); 478 if (rc != Success) 479 goto unwind; 480 } 481 break; 482 case XIDetachSlave: 483 { 484 xXIDetachSlaveInfo *c = (xXIDetachSlaveInfo *) any; 485 486 CHANGE_SIZE_MATCH(xXIDetachSlaveInfo); 487 rc = detach_slave(client, c, flags); 488 if (rc != Success) 489 goto unwind; 490 } 491 break; 492 case XIAttachSlave: 493 { 494 xXIAttachSlaveInfo *c = (xXIAttachSlaveInfo *) any; 495 496 CHANGE_SIZE_MATCH(xXIAttachSlaveInfo); 497 rc = attach_slave(client, c, flags); 498 if (rc != Success) 499 goto unwind; 500 } 501 break; 502 } 503 504 len -= any->length * 4; 505 any = (xXIAnyHierarchyChangeInfo *) ((char *) any + any->length * 4); 506 } 507 508 unwind: 509 510 XISendDeviceHierarchyEvent(flags); 511 return rc; 512} 513