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