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