xichangehierarchy.c revision 7e31ba66
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 _X_COLD 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 for (int i = 0; i < currentMaxClients; i++) 198 XIBarrierNewMasterDevice(clients[i], ptr->id); 199 200 unwind: 201 free(name); 202 return rc; 203} 204 205static void 206disable_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 218static int 219remove_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 332static int 333detach_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 363static int 364attach_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 412int 413ProcXIChangeHierarchy(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)stuff->length << 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