compalloc.c revision 4642e01f
1/* 2 * Copyright © 2006 Sun Microsystems 3 * 4 * Permission to use, copy, modify, distribute, and sell this software and its 5 * documentation for any purpose is hereby granted without fee, provided that 6 * the above copyright notice appear in all copies and that both that 7 * copyright notice and this permission notice appear in supporting 8 * documentation, and that the name of Sun Microsystems not be used in 9 * advertising or publicity pertaining to distribution of the software without 10 * specific, written prior permission. Sun Microsystems makes no 11 * representations about the suitability of this software for any purpose. It 12 * is provided "as is" without express or implied warranty. 13 * 14 * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 16 * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 20 * PERFORMANCE OF THIS SOFTWARE. 21 * 22 * Copyright © 2003 Keith Packard 23 * 24 * Permission to use, copy, modify, distribute, and sell this software and its 25 * documentation for any purpose is hereby granted without fee, provided that 26 * the above copyright notice appear in all copies and that both that 27 * copyright notice and this permission notice appear in supporting 28 * documentation, and that the name of Keith Packard not be used in 29 * advertising or publicity pertaining to distribution of the software without 30 * specific, written prior permission. Keith Packard makes no 31 * representations about the suitability of this software for any purpose. It 32 * is provided "as is" without express or implied warranty. 33 * 34 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 35 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 36 * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR 37 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 38 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 39 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 40 * PERFORMANCE OF THIS SOFTWARE. 41 */ 42 43#ifdef HAVE_DIX_CONFIG_H 44#include <dix-config.h> 45#endif 46 47#include "compint.h" 48 49static void 50compReportDamage (DamagePtr pDamage, RegionPtr pRegion, void *closure) 51{ 52 WindowPtr pWin = (WindowPtr) closure; 53 ScreenPtr pScreen = pWin->drawable.pScreen; 54 CompScreenPtr cs = GetCompScreen (pScreen); 55 CompWindowPtr cw = GetCompWindow (pWin); 56 57 cs->damaged = TRUE; 58 cw->damaged = TRUE; 59} 60 61static void 62compDestroyDamage (DamagePtr pDamage, void *closure) 63{ 64 WindowPtr pWin = (WindowPtr) closure; 65 CompWindowPtr cw = GetCompWindow (pWin); 66 67 cw->damage = 0; 68} 69 70/* 71 * Redirect one window for one client 72 */ 73int 74compRedirectWindow (ClientPtr pClient, WindowPtr pWin, int update) 75{ 76 CompWindowPtr cw = GetCompWindow (pWin); 77 CompClientWindowPtr ccw; 78 Bool wasMapped = pWin->mapped; 79 CompScreenPtr cs = GetCompScreen(pWin->drawable.pScreen); 80 81 if (pWin == cs->pOverlayWin) { 82 return Success; 83 } 84 85 if (!pWin->parent) 86 return BadMatch; 87 88 /* 89 * Only one Manual update is allowed 90 */ 91 if (cw && update == CompositeRedirectManual) 92 for (ccw = cw->clients; ccw; ccw = ccw->next) 93 if (ccw->update == CompositeRedirectManual) 94 return BadAccess; 95 96 /* 97 * Allocate per-client per-window structure 98 * The client *could* allocate multiple, but while supported, 99 * it is not expected to be common 100 */ 101 ccw = xalloc (sizeof (CompClientWindowRec)); 102 if (!ccw) 103 return BadAlloc; 104 ccw->id = FakeClientID (pClient->index); 105 ccw->update = update; 106 /* 107 * Now make sure there's a per-window structure to hang this from 108 */ 109 if (!cw) 110 { 111 cw = xalloc (sizeof (CompWindowRec)); 112 if (!cw) 113 { 114 xfree (ccw); 115 return BadAlloc; 116 } 117 cw->damage = DamageCreate (compReportDamage, 118 compDestroyDamage, 119 DamageReportNonEmpty, 120 FALSE, 121 pWin->drawable.pScreen, 122 pWin); 123 if (!cw->damage) 124 { 125 xfree (ccw); 126 xfree (cw); 127 return BadAlloc; 128 } 129 if (wasMapped) 130 { 131 DisableMapUnmapEvents (pWin); 132 UnmapWindow (pWin, FALSE); 133 EnableMapUnmapEvents (pWin); 134 } 135 136 REGION_NULL (pScreen, &cw->borderClip); 137 cw->update = CompositeRedirectAutomatic; 138 cw->clients = 0; 139 cw->oldx = COMP_ORIGIN_INVALID; 140 cw->oldy = COMP_ORIGIN_INVALID; 141 cw->damageRegistered = FALSE; 142 cw->damaged = FALSE; 143 dixSetPrivate(&pWin->devPrivates, CompWindowPrivateKey, cw); 144 } 145 ccw->next = cw->clients; 146 cw->clients = ccw; 147 if (!AddResource (ccw->id, CompositeClientWindowType, pWin)) 148 return BadAlloc; 149 if (ccw->update == CompositeRedirectManual) 150 { 151 /* If the window was CompositeRedirectAutomatic, then 152 * unmap the window so that the parent clip list will 153 * be correctly recomputed. 154 */ 155 if (pWin->mapped) 156 { 157 DisableMapUnmapEvents (pWin); 158 UnmapWindow (pWin, FALSE); 159 EnableMapUnmapEvents (pWin); 160 } 161 if (cw->damageRegistered) 162 { 163 DamageUnregister (&pWin->drawable, cw->damage); 164 cw->damageRegistered = FALSE; 165 } 166 cw->update = CompositeRedirectManual; 167 } 168 169 if (!compCheckRedirect (pWin)) 170 { 171 FreeResource (ccw->id, RT_NONE); 172 return BadAlloc; 173 } 174 if (wasMapped && !pWin->mapped) 175 { 176 Bool overrideRedirect = pWin->overrideRedirect; 177 pWin->overrideRedirect = TRUE; 178 DisableMapUnmapEvents (pWin); 179 MapWindow (pWin, pClient); 180 EnableMapUnmapEvents (pWin); 181 pWin->overrideRedirect = overrideRedirect; 182 } 183 184 return Success; 185} 186 187/* 188 * Free one of the per-client per-window resources, clearing 189 * redirect and the per-window pointer as appropriate 190 */ 191void 192compFreeClientWindow (WindowPtr pWin, XID id) 193{ 194 CompWindowPtr cw = GetCompWindow (pWin); 195 CompClientWindowPtr ccw, *prev; 196 Bool wasMapped = pWin->mapped; 197 198 if (!cw) 199 return; 200 for (prev = &cw->clients; (ccw = *prev); prev = &ccw->next) 201 { 202 if (ccw->id == id) 203 { 204 *prev = ccw->next; 205 if (ccw->update == CompositeRedirectManual) 206 cw->update = CompositeRedirectAutomatic; 207 xfree (ccw); 208 break; 209 } 210 } 211 if (!cw->clients) 212 { 213 if (wasMapped) 214 { 215 DisableMapUnmapEvents (pWin); 216 UnmapWindow (pWin, FALSE); 217 EnableMapUnmapEvents (pWin); 218 } 219 220 if (pWin->redirectDraw != RedirectDrawNone) 221 compFreePixmap (pWin); 222 223 if (cw->damage) 224 DamageDestroy (cw->damage); 225 226 REGION_UNINIT (pScreen, &cw->borderClip); 227 228 dixSetPrivate(&pWin->devPrivates, CompWindowPrivateKey, NULL); 229 xfree (cw); 230 } 231 else if (cw->update == CompositeRedirectAutomatic && 232 !cw->damageRegistered && pWin->redirectDraw != RedirectDrawNone) 233 { 234 DamageRegister (&pWin->drawable, cw->damage); 235 cw->damageRegistered = TRUE; 236 pWin->redirectDraw = RedirectDrawAutomatic; 237 DamageRegionAppend(&pWin->drawable, &pWin->borderSize); 238 } 239 if (wasMapped && !pWin->mapped) 240 { 241 Bool overrideRedirect = pWin->overrideRedirect; 242 pWin->overrideRedirect = TRUE; 243 DisableMapUnmapEvents (pWin); 244 MapWindow (pWin, clients[CLIENT_ID(id)]); 245 EnableMapUnmapEvents (pWin); 246 pWin->overrideRedirect = overrideRedirect; 247 } 248} 249 250/* 251 * This is easy, just free the appropriate resource. 252 */ 253 254int 255compUnredirectWindow (ClientPtr pClient, WindowPtr pWin, int update) 256{ 257 CompWindowPtr cw = GetCompWindow (pWin); 258 CompClientWindowPtr ccw; 259 260 if (!cw) 261 return BadValue; 262 263 for (ccw = cw->clients; ccw; ccw = ccw->next) 264 if (ccw->update == update && CLIENT_ID(ccw->id) == pClient->index) 265 { 266 FreeResource (ccw->id, RT_NONE); 267 return Success; 268 } 269 return BadValue; 270} 271 272/* 273 * Redirect all subwindows for one client 274 */ 275 276int 277compRedirectSubwindows (ClientPtr pClient, WindowPtr pWin, int update) 278{ 279 CompSubwindowsPtr csw = GetCompSubwindows (pWin); 280 CompClientWindowPtr ccw; 281 WindowPtr pChild; 282 283 /* 284 * Only one Manual update is allowed 285 */ 286 if (csw && update == CompositeRedirectManual) 287 for (ccw = csw->clients; ccw; ccw = ccw->next) 288 if (ccw->update == CompositeRedirectManual) 289 return BadAccess; 290 /* 291 * Allocate per-client per-window structure 292 * The client *could* allocate multiple, but while supported, 293 * it is not expected to be common 294 */ 295 ccw = xalloc (sizeof (CompClientWindowRec)); 296 if (!ccw) 297 return BadAlloc; 298 ccw->id = FakeClientID (pClient->index); 299 ccw->update = update; 300 /* 301 * Now make sure there's a per-window structure to hang this from 302 */ 303 if (!csw) 304 { 305 csw = xalloc (sizeof (CompSubwindowsRec)); 306 if (!csw) 307 { 308 xfree (ccw); 309 return BadAlloc; 310 } 311 csw->update = CompositeRedirectAutomatic; 312 csw->clients = 0; 313 dixSetPrivate(&pWin->devPrivates, CompSubwindowsPrivateKey, csw); 314 } 315 /* 316 * Redirect all existing windows 317 */ 318 for (pChild = pWin->lastChild; pChild; pChild = pChild->prevSib) 319 { 320 int ret = compRedirectWindow (pClient, pChild, update); 321 if (ret != Success) 322 { 323 for (pChild = pChild->nextSib; pChild; pChild = pChild->nextSib) 324 (void) compUnredirectWindow (pClient, pChild, update); 325 if (!csw->clients) 326 { 327 xfree (csw); 328 dixSetPrivate(&pWin->devPrivates, CompSubwindowsPrivateKey, 0); 329 } 330 xfree (ccw); 331 return ret; 332 } 333 } 334 /* 335 * Hook into subwindows list 336 */ 337 ccw->next = csw->clients; 338 csw->clients = ccw; 339 if (!AddResource (ccw->id, CompositeClientSubwindowsType, pWin)) 340 return BadAlloc; 341 if (ccw->update == CompositeRedirectManual) 342 { 343 csw->update = CompositeRedirectManual; 344 /* 345 * tell damage extension that damage events for this client are 346 * critical output 347 */ 348 DamageExtSetCritical (pClient, TRUE); 349 } 350 return Success; 351} 352 353/* 354 * Free one of the per-client per-subwindows resources, 355 * which frees one redirect per subwindow 356 */ 357void 358compFreeClientSubwindows (WindowPtr pWin, XID id) 359{ 360 CompSubwindowsPtr csw = GetCompSubwindows (pWin); 361 CompClientWindowPtr ccw, *prev; 362 WindowPtr pChild; 363 364 if (!csw) 365 return; 366 for (prev = &csw->clients; (ccw = *prev); prev = &ccw->next) 367 { 368 if (ccw->id == id) 369 { 370 ClientPtr pClient = clients[CLIENT_ID(id)]; 371 372 *prev = ccw->next; 373 if (ccw->update == CompositeRedirectManual) 374 { 375 /* 376 * tell damage extension that damage events for this client are 377 * critical output 378 */ 379 DamageExtSetCritical (pClient, FALSE); 380 csw->update = CompositeRedirectAutomatic; 381 if (pWin->mapped) 382 (*pWin->drawable.pScreen->ClearToBackground)(pWin, 0, 0, 0, 0, TRUE); 383 } 384 385 /* 386 * Unredirect all existing subwindows 387 */ 388 for (pChild = pWin->lastChild; pChild; pChild = pChild->prevSib) 389 (void) compUnredirectWindow (pClient, pChild, ccw->update); 390 391 xfree (ccw); 392 break; 393 } 394 } 395 396 /* 397 * Check if all of the per-client records are gone 398 */ 399 if (!csw->clients) 400 { 401 dixSetPrivate(&pWin->devPrivates, CompSubwindowsPrivateKey, NULL); 402 xfree (csw); 403 } 404} 405 406/* 407 * This is easy, just free the appropriate resource. 408 */ 409 410int 411compUnredirectSubwindows (ClientPtr pClient, WindowPtr pWin, int update) 412{ 413 CompSubwindowsPtr csw = GetCompSubwindows (pWin); 414 CompClientWindowPtr ccw; 415 416 if (!csw) 417 return BadValue; 418 for (ccw = csw->clients; ccw; ccw = ccw->next) 419 if (ccw->update == update && CLIENT_ID(ccw->id) == pClient->index) 420 { 421 FreeResource (ccw->id, RT_NONE); 422 return Success; 423 } 424 return BadValue; 425} 426 427/* 428 * Add redirection information for one subwindow (during reparent) 429 */ 430 431int 432compRedirectOneSubwindow (WindowPtr pParent, WindowPtr pWin) 433{ 434 CompSubwindowsPtr csw = GetCompSubwindows (pParent); 435 CompClientWindowPtr ccw; 436 437 if (!csw) 438 return Success; 439 for (ccw = csw->clients; ccw; ccw = ccw->next) 440 { 441 int ret = compRedirectWindow (clients[CLIENT_ID(ccw->id)], 442 pWin, ccw->update); 443 if (ret != Success) 444 return ret; 445 } 446 return Success; 447} 448 449/* 450 * Remove redirection information for one subwindow (during reparent) 451 */ 452 453int 454compUnredirectOneSubwindow (WindowPtr pParent, WindowPtr pWin) 455{ 456 CompSubwindowsPtr csw = GetCompSubwindows (pParent); 457 CompClientWindowPtr ccw; 458 459 if (!csw) 460 return Success; 461 for (ccw = csw->clients; ccw; ccw = ccw->next) 462 { 463 int ret = compUnredirectWindow (clients[CLIENT_ID(ccw->id)], 464 pWin, ccw->update); 465 if (ret != Success) 466 return ret; 467 } 468 return Success; 469} 470 471static PixmapPtr 472compNewPixmap (WindowPtr pWin, int x, int y, int w, int h) 473{ 474 ScreenPtr pScreen = pWin->drawable.pScreen; 475 WindowPtr pParent = pWin->parent; 476 PixmapPtr pPixmap; 477 478 pPixmap = (*pScreen->CreatePixmap) (pScreen, w, h, pWin->drawable.depth, 479 CREATE_PIXMAP_USAGE_BACKING_PIXMAP); 480 481 if (!pPixmap) 482 return 0; 483 484 pPixmap->screen_x = x; 485 pPixmap->screen_y = y; 486 487 if (pParent->drawable.depth == pWin->drawable.depth) 488 { 489 GCPtr pGC = GetScratchGC (pWin->drawable.depth, pScreen); 490 491 /* 492 * Copy bits from the parent into the new pixmap so that it will 493 * have "reasonable" contents in case for background None areas. 494 */ 495 if (pGC) 496 { 497 XID val = IncludeInferiors; 498 499 ValidateGC(&pPixmap->drawable, pGC); 500 dixChangeGC (serverClient, pGC, GCSubwindowMode, &val, NULL); 501 (*pGC->ops->CopyArea) (&pParent->drawable, 502 &pPixmap->drawable, 503 pGC, 504 x - pParent->drawable.x, 505 y - pParent->drawable.y, 506 w, h, 0, 0); 507 FreeScratchGC (pGC); 508 } 509 } 510 else 511 { 512 PictFormatPtr pSrcFormat = compWindowFormat (pParent); 513 PictFormatPtr pDstFormat = compWindowFormat (pWin); 514 XID inferiors = IncludeInferiors; 515 int error; 516 517 PicturePtr pSrcPicture = CreatePicture (None, 518 &pParent->drawable, 519 pSrcFormat, 520 CPSubwindowMode, 521 &inferiors, 522 serverClient, &error); 523 524 PicturePtr pDstPicture = CreatePicture (None, 525 &pPixmap->drawable, 526 pDstFormat, 527 0, 0, 528 serverClient, &error); 529 530 if (pSrcPicture && pDstPicture) 531 { 532 CompositePicture (PictOpSrc, 533 pSrcPicture, 534 NULL, 535 pDstPicture, 536 x - pParent->drawable.x, 537 y - pParent->drawable.y, 538 0, 0, 0, 0, w, h); 539 } 540 if (pSrcPicture) 541 FreePicture (pSrcPicture, 0); 542 if (pDstPicture) 543 FreePicture (pDstPicture, 0); 544 } 545 return pPixmap; 546} 547 548Bool 549compAllocPixmap (WindowPtr pWin) 550{ 551 int bw = (int) pWin->borderWidth; 552 int x = pWin->drawable.x - bw; 553 int y = pWin->drawable.y - bw; 554 int w = pWin->drawable.width + (bw << 1); 555 int h = pWin->drawable.height + (bw << 1); 556 PixmapPtr pPixmap = compNewPixmap (pWin, x, y, w, h); 557 CompWindowPtr cw = GetCompWindow (pWin); 558 559 if (!pPixmap) 560 return FALSE; 561 if (cw->update == CompositeRedirectAutomatic) 562 pWin->redirectDraw = RedirectDrawAutomatic; 563 else 564 pWin->redirectDraw = RedirectDrawManual; 565 566 compSetPixmap (pWin, pPixmap); 567 cw->oldx = COMP_ORIGIN_INVALID; 568 cw->oldy = COMP_ORIGIN_INVALID; 569 cw->damageRegistered = FALSE; 570 if (cw->update == CompositeRedirectAutomatic) 571 { 572 DamageRegister (&pWin->drawable, cw->damage); 573 cw->damageRegistered = TRUE; 574 } 575 return TRUE; 576} 577 578void 579compFreePixmap (WindowPtr pWin) 580{ 581 ScreenPtr pScreen = pWin->drawable.pScreen; 582 PixmapPtr pRedirectPixmap, pParentPixmap; 583 CompWindowPtr cw = GetCompWindow (pWin); 584 585 if (cw->damageRegistered) 586 { 587 DamageUnregister (&pWin->drawable, cw->damage); 588 cw->damageRegistered = FALSE; 589 DamageEmpty (cw->damage); 590 } 591 /* 592 * Move the parent-constrained border clip region back into 593 * the window so that ValidateTree will handle the unmap 594 * case correctly. Unmap adds the window borderClip to the 595 * parent exposed area; regions beyond the parent cause crashes 596 */ 597 REGION_COPY (pScreen, &pWin->borderClip, &cw->borderClip); 598 pRedirectPixmap = (*pScreen->GetWindowPixmap) (pWin); 599 pParentPixmap = (*pScreen->GetWindowPixmap) (pWin->parent); 600 pWin->redirectDraw = RedirectDrawNone; 601 compSetPixmap (pWin, pParentPixmap); 602 (*pScreen->DestroyPixmap) (pRedirectPixmap); 603} 604 605/* 606 * Make sure the pixmap is the right size and offset. Allocate a new 607 * pixmap to change size, adjust origin to change offset, leaving the 608 * old pixmap in cw->pOldPixmap so bits can be recovered 609 */ 610Bool 611compReallocPixmap (WindowPtr pWin, int draw_x, int draw_y, 612 unsigned int w, unsigned int h, int bw) 613{ 614 ScreenPtr pScreen = pWin->drawable.pScreen; 615 PixmapPtr pOld = (*pScreen->GetWindowPixmap) (pWin); 616 PixmapPtr pNew; 617 CompWindowPtr cw = GetCompWindow (pWin); 618 int pix_x, pix_y; 619 int pix_w, pix_h; 620 621 assert (cw && pWin->redirectDraw != RedirectDrawNone); 622 cw->oldx = pOld->screen_x; 623 cw->oldy = pOld->screen_y; 624 pix_x = draw_x - bw; 625 pix_y = draw_y - bw; 626 pix_w = w + (bw << 1); 627 pix_h = h + (bw << 1); 628 if (pix_w != pOld->drawable.width || pix_h != pOld->drawable.height) 629 { 630 pNew = compNewPixmap (pWin, pix_x, pix_y, pix_w, pix_h); 631 if (!pNew) 632 return FALSE; 633 cw->pOldPixmap = pOld; 634 compSetPixmap (pWin, pNew); 635 } 636 else 637 { 638 pNew = pOld; 639 cw->pOldPixmap = 0; 640 } 641 pNew->screen_x = pix_x; 642 pNew->screen_y = pix_y; 643 return TRUE; 644} 645