xichangehierarchy.c revision d566a54b
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 "misc.h" 50#include "xace.h" 51#include "xiquerydevice.h" /* for GetDeviceUse */ 52 53#include "xkbsrv.h" 54 55#include "xichangehierarchy.h" 56#include "xibarriers.h" 57 58/** 59 * Send the current state of the device hierarchy to all clients. 60 */ 61void 62XISendDeviceHierarchyEvent(int flags[MAXDEVICES]) 63{ 64 xXIHierarchyEvent *ev; 65 xXIHierarchyInfo *info; 66 DeviceIntRec dummyDev; 67 DeviceIntPtr dev; 68 int i; 69 70 if (!flags) 71 return; 72 73 ev = calloc(1, sizeof(xXIHierarchyEvent) + 74 MAXDEVICES * sizeof(xXIHierarchyInfo)); 75 if (!ev) 76 return; 77 ev->type = GenericEvent; 78 ev->extension = IReqCode; 79 ev->evtype = XI_HierarchyChanged; 80 ev->time = GetTimeInMillis(); 81 ev->flags = 0; 82 ev->num_info = inputInfo.numDevices; 83 84 info = (xXIHierarchyInfo *) &ev[1]; 85 for (dev = inputInfo.devices; dev; dev = dev->next) { 86 info->deviceid = dev->id; 87 info->enabled = dev->enabled; 88 info->use = GetDeviceUse(dev, &info->attachment); 89 info->flags = flags[dev->id]; 90 ev->flags |= info->flags; 91 info++; 92 } 93 for (dev = inputInfo.off_devices; dev; dev = dev->next) { 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 for (i = 0; i < MAXDEVICES; i++) { 103 if (flags[i] & (XIMasterRemoved | XISlaveRemoved)) { 104 info->deviceid = i; 105 info->enabled = FALSE; 106 info->flags = flags[i]; 107 info->use = 0; 108 ev->flags |= info->flags; 109 ev->num_info++; 110 info++; 111 } 112 } 113 114 ev->length = bytes_to_int32(ev->num_info * sizeof(xXIHierarchyInfo)); 115 116 memset(&dummyDev, 0, sizeof(dummyDev)); 117 dummyDev.id = XIAllDevices; 118 dummyDev.type = SLAVE; 119 SendEventToAllWindows(&dummyDev, (XI_HierarchyChangedMask >> 8), 120 (xEvent *) ev, 1); 121 free(ev); 122} 123 124/*********************************************************************** 125 * 126 * This procedure allows a client to change the device hierarchy through 127 * adding new master devices, removing them, etc. 128 * 129 */ 130 131int _X_COLD 132SProcXIChangeHierarchy(ClientPtr client) 133{ 134 REQUEST(xXIChangeHierarchyReq); 135 swaps(&stuff->length); 136 return (ProcXIChangeHierarchy(client)); 137} 138 139static int 140add_master(ClientPtr client, xXIAddMasterInfo * c, int flags[MAXDEVICES]) 141{ 142 DeviceIntPtr ptr, keybd, XTestptr, XTestkeybd; 143 char *name; 144 int rc; 145 146 name = calloc(c->name_len + 1, sizeof(char)); 147 if (name == NULL) { 148 rc = BadAlloc; 149 goto unwind; 150 } 151 strncpy(name, (char *) &c[1], c->name_len); 152 153 rc = AllocDevicePair(client, name, &ptr, &keybd, 154 CorePointerProc, CoreKeyboardProc, TRUE); 155 if (rc != Success) 156 goto unwind; 157 158 if (!c->send_core) 159 ptr->coreEvents = keybd->coreEvents = FALSE; 160 161 /* Allocate virtual slave devices for xtest events */ 162 rc = AllocXTestDevice(client, name, &XTestptr, &XTestkeybd, ptr, keybd); 163 if (rc != Success) { 164 DeleteInputDeviceRequest(ptr); 165 DeleteInputDeviceRequest(keybd); 166 goto unwind; 167 } 168 169 ActivateDevice(ptr, FALSE); 170 ActivateDevice(keybd, FALSE); 171 flags[ptr->id] |= XIMasterAdded; 172 flags[keybd->id] |= XIMasterAdded; 173 174 ActivateDevice(XTestptr, FALSE); 175 ActivateDevice(XTestkeybd, FALSE); 176 flags[XTestptr->id] |= XISlaveAdded; 177 flags[XTestkeybd->id] |= XISlaveAdded; 178 179 if (c->enable) { 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 198 for (int i = 0; i < currentMaxClients; i++) 199 XIBarrierNewMasterDevice(clients[i], ptr->id); 200 201 unwind: 202 free(name); 203 return rc; 204} 205 206static void 207disable_clientpointer(DeviceIntPtr dev) 208{ 209 int i; 210 211 for (i = 0; i < currentMaxClients; i++) { 212 ClientPtr client = clients[i]; 213 214 if (client && client->clientPtr == dev) 215 client->clientPtr = NULL; 216 } 217} 218 219static DeviceIntPtr 220find_disabled_master(int type) 221{ 222 DeviceIntPtr dev; 223 224 /* Once a master device is disabled it loses the pairing, so returning the first 225 * match is good enough */ 226 for (dev = inputInfo.off_devices; dev; dev = dev->next) { 227 if (dev->type == type) 228 return dev; 229 } 230 231 return NULL; 232} 233 234static int 235remove_master(ClientPtr client, xXIRemoveMasterInfo * r, int flags[MAXDEVICES]) 236{ 237 DeviceIntPtr dev, ptr, keybd, XTestptr, XTestkeybd; 238 int rc = Success; 239 240 if (r->return_mode != XIAttachToMaster && r->return_mode != XIFloating) 241 return BadValue; 242 243 rc = dixLookupDevice(&dev, r->deviceid, client, DixDestroyAccess); 244 if (rc != Success) 245 goto unwind; 246 247 if (!IsMaster(dev)) { 248 client->errorValue = r->deviceid; 249 rc = BadDevice; 250 goto unwind; 251 } 252 253 /* XXX: For now, don't allow removal of VCP, VCK */ 254 if (dev == inputInfo.pointer || dev == inputInfo.keyboard) { 255 rc = BadDevice; 256 goto unwind; 257 } 258 259 if ((ptr = GetMaster(dev, MASTER_POINTER)) == NULL) 260 ptr = find_disabled_master(MASTER_POINTER); 261 BUG_RETURN_VAL(ptr == NULL, BadDevice); 262 rc = dixLookupDevice(&ptr, ptr->id, client, DixDestroyAccess); 263 if (rc != Success) 264 goto unwind; 265 266 if ((keybd = GetMaster(dev, MASTER_KEYBOARD)) == NULL) 267 keybd = find_disabled_master(MASTER_KEYBOARD); 268 BUG_RETURN_VAL(keybd == NULL, BadDevice); 269 rc = dixLookupDevice(&keybd, keybd->id, client, DixDestroyAccess); 270 if (rc != Success) 271 goto unwind; 272 273 XTestptr = GetXTestDevice(ptr); 274 BUG_RETURN_VAL(XTestptr == NULL, BadDevice); 275 rc = dixLookupDevice(&XTestptr, XTestptr->id, client, DixDestroyAccess); 276 if (rc != Success) 277 goto unwind; 278 279 XTestkeybd = GetXTestDevice(keybd); 280 BUG_RETURN_VAL(XTestkeybd == NULL, BadDevice); 281 rc = dixLookupDevice(&XTestkeybd, XTestkeybd->id, client, DixDestroyAccess); 282 if (rc != Success) 283 goto unwind; 284 285 disable_clientpointer(ptr); 286 287 /* Disabling sends the devices floating, reattach them if 288 * desired. */ 289 if (r->return_mode == XIAttachToMaster) { 290 DeviceIntPtr attached, newptr, newkeybd; 291 292 rc = dixLookupDevice(&newptr, r->return_pointer, client, DixAddAccess); 293 if (rc != Success) 294 goto unwind; 295 296 if (!IsMaster(newptr)) { 297 client->errorValue = r->return_pointer; 298 rc = BadDevice; 299 goto unwind; 300 } 301 302 rc = dixLookupDevice(&newkeybd, r->return_keyboard, 303 client, DixAddAccess); 304 if (rc != Success) 305 goto unwind; 306 307 if (!IsMaster(newkeybd)) { 308 client->errorValue = r->return_keyboard; 309 rc = BadDevice; 310 goto unwind; 311 } 312 313 for (attached = inputInfo.devices; attached; attached = attached->next) { 314 if (!IsMaster(attached)) { 315 if (GetMaster(attached, MASTER_ATTACHED) == ptr) { 316 AttachDevice(client, attached, newptr); 317 flags[attached->id] |= XISlaveAttached; 318 } 319 if (GetMaster(attached, MASTER_ATTACHED) == keybd) { 320 AttachDevice(client, attached, newkeybd); 321 flags[attached->id] |= XISlaveAttached; 322 } 323 } 324 } 325 } 326 327 for (int i = 0; i < currentMaxClients; i++) 328 XIBarrierRemoveMasterDevice(clients[i], ptr->id); 329 330 /* disable the remove the devices, XTest devices must be done first 331 else the sprites they rely on will be destroyed */ 332 DisableDevice(XTestptr, FALSE); 333 DisableDevice(XTestkeybd, FALSE); 334 DisableDevice(keybd, FALSE); 335 DisableDevice(ptr, FALSE); 336 flags[XTestptr->id] |= XIDeviceDisabled | XISlaveDetached; 337 flags[XTestkeybd->id] |= XIDeviceDisabled | XISlaveDetached; 338 flags[keybd->id] |= XIDeviceDisabled; 339 flags[ptr->id] |= XIDeviceDisabled; 340 341 flags[XTestptr->id] |= XISlaveRemoved; 342 flags[XTestkeybd->id] |= XISlaveRemoved; 343 flags[keybd->id] |= XIMasterRemoved; 344 flags[ptr->id] |= XIMasterRemoved; 345 346 RemoveDevice(XTestptr, FALSE); 347 RemoveDevice(XTestkeybd, FALSE); 348 RemoveDevice(keybd, FALSE); 349 RemoveDevice(ptr, FALSE); 350 351 unwind: 352 return rc; 353} 354 355static int 356detach_slave(ClientPtr client, xXIDetachSlaveInfo * c, int flags[MAXDEVICES]) 357{ 358 DeviceIntPtr dev; 359 int rc; 360 361 rc = dixLookupDevice(&dev, c->deviceid, client, DixManageAccess); 362 if (rc != Success) 363 goto unwind; 364 365 if (IsMaster(dev)) { 366 client->errorValue = c->deviceid; 367 rc = BadDevice; 368 goto unwind; 369 } 370 371 /* Don't allow changes to XTest Devices, these are fixed */ 372 if (IsXTestDevice(dev, NULL)) { 373 client->errorValue = c->deviceid; 374 rc = BadDevice; 375 goto unwind; 376 } 377 378 ReleaseButtonsAndKeys(dev); 379 AttachDevice(client, dev, NULL); 380 flags[dev->id] |= XISlaveDetached; 381 382 unwind: 383 return rc; 384} 385 386static int 387attach_slave(ClientPtr client, xXIAttachSlaveInfo * c, int flags[MAXDEVICES]) 388{ 389 DeviceIntPtr dev; 390 DeviceIntPtr newmaster; 391 int rc; 392 393 rc = dixLookupDevice(&dev, c->deviceid, client, DixManageAccess); 394 if (rc != Success) 395 goto unwind; 396 397 if (IsMaster(dev)) { 398 client->errorValue = c->deviceid; 399 rc = BadDevice; 400 goto unwind; 401 } 402 403 /* Don't allow changes to XTest Devices, these are fixed */ 404 if (IsXTestDevice(dev, NULL)) { 405 client->errorValue = c->deviceid; 406 rc = BadDevice; 407 goto unwind; 408 } 409 410 rc = dixLookupDevice(&newmaster, c->new_master, client, DixAddAccess); 411 if (rc != Success) 412 goto unwind; 413 if (!IsMaster(newmaster)) { 414 client->errorValue = c->new_master; 415 rc = BadDevice; 416 goto unwind; 417 } 418 419 if (!((IsPointerDevice(newmaster) && IsPointerDevice(dev)) || 420 (IsKeyboardDevice(newmaster) && IsKeyboardDevice(dev)))) { 421 rc = BadDevice; 422 goto unwind; 423 } 424 425 ReleaseButtonsAndKeys(dev); 426 AttachDevice(client, dev, newmaster); 427 flags[dev->id] |= XISlaveAttached; 428 429 unwind: 430 return rc; 431} 432 433#define SWAPIF(cmd) if (client->swapped) { cmd; } 434 435int 436ProcXIChangeHierarchy(ClientPtr client) 437{ 438 xXIAnyHierarchyChangeInfo *any; 439 size_t len; /* length of data remaining in request */ 440 int rc = Success; 441 int flags[MAXDEVICES] = { 0 }; 442 enum { 443 NO_CHANGE, 444 FLUSH, 445 CHANGED, 446 } changes = NO_CHANGE; 447 448 REQUEST(xXIChangeHierarchyReq); 449 REQUEST_AT_LEAST_SIZE(xXIChangeHierarchyReq); 450 451 if (!stuff->num_changes) 452 return rc; 453 454 len = ((size_t)client->req_len << 2) - sizeof(xXIChangeHierarchyReq); 455 456 any = (xXIAnyHierarchyChangeInfo *) &stuff[1]; 457 while (stuff->num_changes--) { 458 if (len < sizeof(xXIAnyHierarchyChangeInfo)) { 459 rc = BadLength; 460 goto unwind; 461 } 462 463 SWAPIF(swaps(&any->type)); 464 SWAPIF(swaps(&any->length)); 465 466 if (len < ((size_t)any->length << 2)) 467 return BadLength; 468 469#define CHANGE_SIZE_MATCH(type) \ 470 do { \ 471 if ((len < sizeof(type)) || (any->length != (sizeof(type) >> 2))) { \ 472 rc = BadLength; \ 473 goto unwind; \ 474 } \ 475 } while(0) 476 477 switch (any->type) { 478 case XIAddMaster: 479 { 480 xXIAddMasterInfo *c = (xXIAddMasterInfo *) any; 481 482 /* Variable length, due to appended name string */ 483 if (len < sizeof(xXIAddMasterInfo)) { 484 rc = BadLength; 485 goto unwind; 486 } 487 SWAPIF(swaps(&c->name_len)); 488 if (c->name_len > (len - sizeof(xXIAddMasterInfo))) { 489 rc = BadLength; 490 goto unwind; 491 } 492 493 rc = add_master(client, c, flags); 494 if (rc != Success) 495 goto unwind; 496 changes = FLUSH; 497 break; 498 } 499 case XIRemoveMaster: 500 { 501 xXIRemoveMasterInfo *r = (xXIRemoveMasterInfo *) any; 502 503 CHANGE_SIZE_MATCH(xXIRemoveMasterInfo); 504 rc = remove_master(client, r, flags); 505 if (rc != Success) 506 goto unwind; 507 changes = FLUSH; 508 break; 509 } 510 case XIDetachSlave: 511 { 512 xXIDetachSlaveInfo *c = (xXIDetachSlaveInfo *) any; 513 514 CHANGE_SIZE_MATCH(xXIDetachSlaveInfo); 515 rc = detach_slave(client, c, flags); 516 if (rc != Success) 517 goto unwind; 518 changes = CHANGED; 519 break; 520 } 521 case XIAttachSlave: 522 { 523 xXIAttachSlaveInfo *c = (xXIAttachSlaveInfo *) any; 524 525 CHANGE_SIZE_MATCH(xXIAttachSlaveInfo); 526 rc = attach_slave(client, c, flags); 527 if (rc != Success) 528 goto unwind; 529 changes = CHANGED; 530 break; 531 } 532 default: 533 break; 534 } 535 536 if (changes == FLUSH) { 537 XISendDeviceHierarchyEvent(flags); 538 memset(flags, 0, sizeof(flags)); 539 changes = NO_CHANGE; 540 } 541 542 len -= any->length * 4; 543 any = (xXIAnyHierarchyChangeInfo *) ((char *) any + any->length * 4); 544 } 545 546 unwind: 547 if (changes != NO_CHANGE) 548 XISendDeviceHierarchyEvent(flags); 549 return rc; 550} 551