rootlessScreen.c revision 05b261ec
1/* 2 * Screen routines for generic rootless X server 3 */ 4/* 5 * Copyright (c) 2001 Greg Parker. All Rights Reserved. 6 * Copyright (c) 2002-2003 Torrey T. Lyons. All Rights Reserved. 7 * Copyright (c) 2002 Apple Computer, Inc. All rights reserved. 8 * 9 * Permission is hereby granted, free of charge, to any person obtaining a 10 * copy of this software and associated documentation files (the "Software"), 11 * to deal in the Software without restriction, including without limitation 12 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 13 * and/or sell copies of the Software, and to permit persons to whom the 14 * Software is furnished to do so, subject to the following conditions: 15 * 16 * The above copyright notice and this permission notice shall be included in 17 * all copies or substantial portions of the Software. 18 * 19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 22 * THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 23 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 24 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 * DEALINGS IN THE SOFTWARE. 26 * 27 * Except as contained in this notice, the name(s) of the above copyright 28 * holders shall not be used in advertising or otherwise to promote the sale, 29 * use or other dealings in this Software without prior written authorization. 30 */ 31 32 33#ifdef HAVE_DIX_CONFIG_H 34#include <dix-config.h> 35#endif 36 37#include "mi.h" 38#include "scrnintstr.h" 39#include "gcstruct.h" 40#include "pixmapstr.h" 41#include "windowstr.h" 42#include "propertyst.h" 43#include "mivalidate.h" 44#include "picturestr.h" 45 46#include <sys/types.h> 47#include <sys/stat.h> 48#include <fcntl.h> 49#include <string.h> 50 51#include "rootlessCommon.h" 52#include "rootlessWindow.h" 53 54/* In milliseconds */ 55#ifndef ROOTLESS_REDISPLAY_DELAY 56#define ROOTLESS_REDISPLAY_DELAY 10 57#endif 58 59extern int RootlessMiValidateTree(WindowPtr pRoot, WindowPtr pChild, 60 VTKind kind); 61extern Bool RootlessCreateGC(GCPtr pGC); 62 63// Initialize globals 64int rootlessGCPrivateIndex = -1; 65int rootlessScreenPrivateIndex = -1; 66int rootlessWindowPrivateIndex = -1; 67 68 69/* 70 * RootlessUpdateScreenPixmap 71 * miCreateScreenResources does not like a null framebuffer pointer, 72 * it leaves the screen pixmap with an uninitialized data pointer. 73 * Thus, rootless implementations typically set the framebuffer width 74 * to zero so that miCreateScreenResources does not allocate a screen 75 * pixmap for us. We allocate our own screen pixmap here since we need 76 * the screen pixmap to be valid (e.g. CopyArea from the root window). 77 */ 78void 79RootlessUpdateScreenPixmap(ScreenPtr pScreen) 80{ 81 RootlessScreenRec *s = SCREENREC(pScreen); 82 PixmapPtr pPix; 83 unsigned int rowbytes; 84 85 pPix = (*pScreen->GetScreenPixmap)(pScreen); 86 if (pPix == NULL) { 87 pPix = (*pScreen->CreatePixmap)(pScreen, 0, 0, pScreen->rootDepth); 88 (*pScreen->SetScreenPixmap)(pPix); 89 } 90 91 rowbytes = PixmapBytePad(pScreen->width, pScreen->rootDepth); 92 93 if (s->pixmap_data_size < rowbytes) { 94 if (s->pixmap_data != NULL) 95 xfree(s->pixmap_data); 96 97 s->pixmap_data_size = rowbytes; 98 s->pixmap_data = xalloc(s->pixmap_data_size); 99 if (s->pixmap_data == NULL) 100 return; 101 102 memset(s->pixmap_data, 0xFF, s->pixmap_data_size); 103 104 pScreen->ModifyPixmapHeader(pPix, pScreen->width, pScreen->height, 105 pScreen->rootDepth, 106 BitsPerPixel(pScreen->rootDepth), 107 0, s->pixmap_data); 108 /* ModifyPixmapHeader ignores zero arguments, so install rowbytes 109 by hand. */ 110 pPix->devKind = 0; 111 } 112} 113 114 115/* 116 * RootlessCreateScreenResources 117 * Rootless implementations typically set a null framebuffer pointer, which 118 * causes problems with miCreateScreenResources. We fix things up here. 119 */ 120static Bool 121RootlessCreateScreenResources(ScreenPtr pScreen) 122{ 123 Bool ret = TRUE; 124 125 SCREEN_UNWRAP(pScreen, CreateScreenResources); 126 127 if (pScreen->CreateScreenResources != NULL) 128 ret = (*pScreen->CreateScreenResources)(pScreen); 129 130 SCREEN_WRAP(pScreen, CreateScreenResources); 131 132 if (!ret) 133 return ret; 134 135 /* Make sure we have a valid screen pixmap. */ 136 137 RootlessUpdateScreenPixmap(pScreen); 138 139 return ret; 140} 141 142 143static Bool 144RootlessCloseScreen(int i, ScreenPtr pScreen) 145{ 146 RootlessScreenRec *s; 147 148 s = SCREENREC(pScreen); 149 150 // fixme unwrap everything that was wrapped? 151 pScreen->CloseScreen = s->CloseScreen; 152 153 if (s->pixmap_data != NULL) { 154 xfree (s->pixmap_data); 155 s->pixmap_data = NULL; 156 s->pixmap_data_size = 0; 157 } 158 159 xfree(s); 160 return pScreen->CloseScreen(i, pScreen); 161} 162 163 164static void 165RootlessGetImage(DrawablePtr pDrawable, int sx, int sy, int w, int h, 166 unsigned int format, unsigned long planeMask, char *pdstLine) 167{ 168 ScreenPtr pScreen = pDrawable->pScreen; 169 SCREEN_UNWRAP(pScreen, GetImage); 170 171 if (pDrawable->type == DRAWABLE_WINDOW) { 172 int x0, y0, x1, y1; 173 RootlessWindowRec *winRec; 174 175 // Many apps use GetImage to sync with the visible frame buffer 176 // FIXME: entire screen or just window or all screens? 177 RootlessRedisplayScreen(pScreen); 178 179 // RedisplayScreen stops drawing, so we need to start it again 180 RootlessStartDrawing((WindowPtr)pDrawable); 181 182 /* Check that we have some place to read from. */ 183 winRec = WINREC(TopLevelParent((WindowPtr) pDrawable)); 184 if (winRec == NULL) 185 goto out; 186 187 /* Clip to top-level window bounds. */ 188 /* FIXME: fbGetImage uses the width parameter to calculate the 189 stride of the destination pixmap. If w is clipped, the data 190 returned will be garbage, although we will not crash. */ 191 192 x0 = pDrawable->x + sx; 193 y0 = pDrawable->y + sy; 194 x1 = x0 + w; 195 y1 = y0 + h; 196 197 x0 = MAX (x0, winRec->x); 198 y0 = MAX (y0, winRec->y); 199 x1 = MIN (x1, winRec->x + winRec->width); 200 y1 = MIN (y1, winRec->y + winRec->height); 201 202 sx = x0 - pDrawable->x; 203 sy = y0 - pDrawable->y; 204 w = x1 - x0; 205 h = y1 - y0; 206 207 if (w <= 0 || h <= 0) 208 goto out; 209 } 210 211 pScreen->GetImage(pDrawable, sx, sy, w, h, format, planeMask, pdstLine); 212 213out: 214 SCREEN_WRAP(pScreen, GetImage); 215} 216 217 218/* 219 * RootlessSourceValidate 220 * CopyArea and CopyPlane use a GC tied to the destination drawable. 221 * StartDrawing/StopDrawing wrappers won't be called if source is 222 * a visible window but the destination isn't. So, we call StartDrawing 223 * here and leave StopDrawing for the block handler. 224 */ 225static void 226RootlessSourceValidate(DrawablePtr pDrawable, int x, int y, int w, int h) 227{ 228 SCREEN_UNWRAP(pDrawable->pScreen, SourceValidate); 229 if (pDrawable->type == DRAWABLE_WINDOW) { 230 WindowPtr pWin = (WindowPtr)pDrawable; 231 RootlessStartDrawing(pWin); 232 } 233 if (pDrawable->pScreen->SourceValidate) { 234 pDrawable->pScreen->SourceValidate(pDrawable, x, y, w, h); 235 } 236 SCREEN_WRAP(pDrawable->pScreen, SourceValidate); 237} 238 239#ifdef RENDER 240 241static void 242RootlessComposite(CARD8 op, PicturePtr pSrc, PicturePtr pMask, PicturePtr pDst, 243 INT16 xSrc, INT16 ySrc, INT16 xMask, INT16 yMask, 244 INT16 xDst, INT16 yDst, CARD16 width, CARD16 height) 245{ 246 ScreenPtr pScreen = pDst->pDrawable->pScreen; 247 PictureScreenPtr ps = GetPictureScreen(pScreen); 248 WindowPtr srcWin, dstWin, maskWin = NULL; 249 250 if (pMask) { // pMask can be NULL 251 maskWin = (pMask->pDrawable->type == DRAWABLE_WINDOW) ? 252 (WindowPtr)pMask->pDrawable : NULL; 253 } 254 srcWin = (pSrc->pDrawable->type == DRAWABLE_WINDOW) ? 255 (WindowPtr)pSrc->pDrawable : NULL; 256 dstWin = (pDst->pDrawable->type == DRAWABLE_WINDOW) ? 257 (WindowPtr)pDst->pDrawable : NULL; 258 259 // SCREEN_UNWRAP(ps, Composite); 260 ps->Composite = SCREENREC(pScreen)->Composite; 261 262 if (srcWin && IsFramedWindow(srcWin)) 263 RootlessStartDrawing(srcWin); 264 if (maskWin && IsFramedWindow(maskWin)) 265 RootlessStartDrawing(maskWin); 266 if (dstWin && IsFramedWindow(dstWin)) 267 RootlessStartDrawing(dstWin); 268 269 ps->Composite(op, pSrc, pMask, pDst, 270 xSrc, ySrc, xMask, yMask, 271 xDst, yDst, width, height); 272 273 if (dstWin && IsFramedWindow(dstWin)) { 274 RootlessDamageRect(dstWin, xDst, yDst, width, height); 275 } 276 277 ps->Composite = RootlessComposite; 278 // SCREEN_WRAP(ps, Composite); 279} 280 281 282static void 283RootlessGlyphs(CARD8 op, PicturePtr pSrc, PicturePtr pDst, 284 PictFormatPtr maskFormat, INT16 xSrc, INT16 ySrc, 285 int nlist, GlyphListPtr list, GlyphPtr *glyphs) 286{ 287 ScreenPtr pScreen = pDst->pDrawable->pScreen; 288 PictureScreenPtr ps = GetPictureScreen(pScreen); 289 int x, y; 290 int n; 291 GlyphPtr glyph; 292 WindowPtr srcWin, dstWin; 293 294 srcWin = (pSrc->pDrawable->type == DRAWABLE_WINDOW) ? 295 (WindowPtr)pSrc->pDrawable : NULL; 296 dstWin = (pDst->pDrawable->type == DRAWABLE_WINDOW) ? 297 (WindowPtr)pDst->pDrawable : NULL; 298 299 if (srcWin && IsFramedWindow(srcWin)) RootlessStartDrawing(srcWin); 300 if (dstWin && IsFramedWindow(dstWin)) RootlessStartDrawing(dstWin); 301 302 //SCREEN_UNWRAP(ps, Glyphs); 303 ps->Glyphs = SCREENREC(pScreen)->Glyphs; 304 ps->Glyphs(op, pSrc, pDst, maskFormat, xSrc, ySrc, nlist, list, glyphs); 305 ps->Glyphs = RootlessGlyphs; 306 //SCREEN_WRAP(ps, Glyphs); 307 308 if (dstWin && IsFramedWindow(dstWin)) { 309 x = xSrc; 310 y = ySrc; 311 312 while (nlist--) { 313 x += list->xOff; 314 y += list->yOff; 315 n = list->len; 316 317 /* Calling DamageRect for the bounding box of each glyph is 318 inefficient. So compute the union of all glyphs in a list 319 and damage that. */ 320 321 if (n > 0) { 322 BoxRec box; 323 324 glyph = *glyphs++; 325 326 box.x1 = x - glyph->info.x; 327 box.y1 = y - glyph->info.y; 328 box.x2 = box.x1 + glyph->info.width; 329 box.y2 = box.y2 + glyph->info.height; 330 331 x += glyph->info.xOff; 332 y += glyph->info.yOff; 333 334 while (--n > 0) { 335 short x1, y1, x2, y2; 336 337 glyph = *glyphs++; 338 339 x1 = x - glyph->info.x; 340 y1 = y - glyph->info.y; 341 x2 = x1 + glyph->info.width; 342 y2 = y1 + glyph->info.height; 343 344 box.x1 = MAX (box.x1, x1); 345 box.y1 = MAX (box.y1, y1); 346 box.x2 = MAX (box.x2, x2); 347 box.y2 = MAX (box.y2, y2); 348 349 x += glyph->info.xOff; 350 y += glyph->info.yOff; 351 } 352 353 RootlessDamageBox(dstWin, &box); 354 } 355 list++; 356 } 357 } 358} 359 360#endif // RENDER 361 362 363/* 364 * RootlessValidateTree 365 * ValidateTree is modified in two ways: 366 * - top-level windows don't clip each other 367 * - windows aren't clipped against root. 368 * These only matter when validating from the root. 369 */ 370static int 371RootlessValidateTree(WindowPtr pParent, WindowPtr pChild, VTKind kind) 372{ 373 int result; 374 RegionRec saveRoot; 375 ScreenPtr pScreen = pParent->drawable.pScreen; 376 377 SCREEN_UNWRAP(pScreen, ValidateTree); 378 RL_DEBUG_MSG("VALIDATETREE start "); 379 380 // Use our custom version to validate from root 381 if (IsRoot(pParent)) { 382 RL_DEBUG_MSG("custom "); 383 result = RootlessMiValidateTree(pParent, pChild, kind); 384 } else { 385 HUGE_ROOT(pParent); 386 result = pScreen->ValidateTree(pParent, pChild, kind); 387 NORMAL_ROOT(pParent); 388 } 389 390 SCREEN_WRAP(pScreen, ValidateTree); 391 RL_DEBUG_MSG("VALIDATETREE end\n"); 392 393 return result; 394} 395 396 397/* 398 * RootlessMarkOverlappedWindows 399 * MarkOverlappedWindows is modified to ignore overlapping 400 * top-level windows. 401 */ 402static Bool 403RootlessMarkOverlappedWindows(WindowPtr pWin, WindowPtr pFirst, 404 WindowPtr *ppLayerWin) 405{ 406 RegionRec saveRoot; 407 Bool result; 408 ScreenPtr pScreen = pWin->drawable.pScreen; 409 SCREEN_UNWRAP(pScreen, MarkOverlappedWindows); 410 RL_DEBUG_MSG("MARKOVERLAPPEDWINDOWS start "); 411 412 HUGE_ROOT(pWin); 413 if (IsRoot(pWin)) { 414 // root - mark nothing 415 RL_DEBUG_MSG("is root not marking "); 416 result = FALSE; 417 } 418 else if (! IsTopLevel(pWin)) { 419 // not top-level window - mark normally 420 result = pScreen->MarkOverlappedWindows(pWin, pFirst, ppLayerWin); 421 } 422 else { 423 //top-level window - mark children ONLY - NO overlaps with sibs (?) 424 // This code copied from miMarkOverlappedWindows() 425 426 register WindowPtr pChild; 427 Bool anyMarked = FALSE; 428 void (* MarkWindow)() = pScreen->MarkWindow; 429 430 RL_DEBUG_MSG("is top level! "); 431 /* single layered systems are easy */ 432 if (ppLayerWin) *ppLayerWin = pWin; 433 434 if (pWin == pFirst) { 435 /* Blindly mark pWin and all of its inferiors. This is a slight 436 * overkill if there are mapped windows that outside pWin's border, 437 * but it's better than wasting time on RectIn checks. 438 */ 439 pChild = pWin; 440 while (1) { 441 if (pChild->viewable) { 442 if (REGION_BROKEN (pScreen, &pChild->winSize)) 443 SetWinSize (pChild); 444 if (REGION_BROKEN (pScreen, &pChild->borderSize)) 445 SetBorderSize (pChild); 446 (* MarkWindow)(pChild); 447 if (pChild->firstChild) { 448 pChild = pChild->firstChild; 449 continue; 450 } 451 } 452 while (!pChild->nextSib && (pChild != pWin)) 453 pChild = pChild->parent; 454 if (pChild == pWin) 455 break; 456 pChild = pChild->nextSib; 457 } 458 anyMarked = TRUE; 459 pFirst = pFirst->nextSib; 460 } 461 if (anyMarked) 462 (* MarkWindow)(pWin->parent); 463 result = anyMarked; 464 } 465 NORMAL_ROOT(pWin); 466 SCREEN_WRAP(pScreen, MarkOverlappedWindows); 467 RL_DEBUG_MSG("MARKOVERLAPPEDWINDOWS end\n"); 468 469 return result; 470} 471 472 473static CARD32 474RootlessRedisplayCallback(OsTimerPtr timer, CARD32 time, void *arg) 475{ 476 RootlessScreenRec *screenRec = arg; 477 478 if (!screenRec->redisplay_queued) { 479 /* No update needed. Stop the timer. */ 480 481 screenRec->redisplay_timer_set = FALSE; 482 return 0; 483 } 484 485 screenRec->redisplay_queued = FALSE; 486 487 /* Mark that we should redisplay before waiting for I/O next time */ 488 screenRec->redisplay_expired = TRUE; 489 490 /* Reinstall the timer immediately, so we get as close to our 491 redisplay interval as possible. */ 492 493 return ROOTLESS_REDISPLAY_DELAY; 494} 495 496 497/* 498 * RootlessQueueRedisplay 499 * Queue a redisplay after a timer delay to ensure we do not redisplay 500 * too frequently. 501 */ 502void 503RootlessQueueRedisplay(ScreenPtr pScreen) 504{ 505 RootlessScreenRec *screenRec = SCREENREC(pScreen); 506 507 screenRec->redisplay_queued = TRUE; 508 509 if (screenRec->redisplay_timer_set) 510 return; 511 512 screenRec->redisplay_timer = TimerSet(screenRec->redisplay_timer, 513 0, ROOTLESS_REDISPLAY_DELAY, 514 RootlessRedisplayCallback, 515 screenRec); 516 screenRec->redisplay_timer_set = TRUE; 517} 518 519 520/* 521 * RootlessBlockHandler 522 * If the redisplay timer has expired, flush drawing before blocking 523 * on select(). 524 */ 525static void 526RootlessBlockHandler(pointer pbdata, OSTimePtr pTimeout, pointer pReadmask) 527{ 528 ScreenPtr pScreen = pbdata; 529 RootlessScreenRec *screenRec = SCREENREC(pScreen); 530 531 if (screenRec->redisplay_expired) { 532 screenRec->redisplay_expired = FALSE; 533 534 RootlessRedisplayScreen(pScreen); 535 } 536} 537 538 539static void 540RootlessWakeupHandler(pointer data, int i, pointer LastSelectMask) 541{ 542 // nothing here 543} 544 545 546static Bool 547RootlessAllocatePrivates(ScreenPtr pScreen) 548{ 549 RootlessScreenRec *s; 550 static unsigned long rootlessGeneration = 0; 551 552 if (rootlessGeneration != serverGeneration) { 553 rootlessScreenPrivateIndex = AllocateScreenPrivateIndex(); 554 if (rootlessScreenPrivateIndex == -1) return FALSE; 555 rootlessGCPrivateIndex = AllocateGCPrivateIndex(); 556 if (rootlessGCPrivateIndex == -1) return FALSE; 557 rootlessWindowPrivateIndex = AllocateWindowPrivateIndex(); 558 if (rootlessWindowPrivateIndex == -1) return FALSE; 559 rootlessGeneration = serverGeneration; 560 } 561 562 // no allocation needed for screen privates 563 if (!AllocateGCPrivate(pScreen, rootlessGCPrivateIndex, 564 sizeof(RootlessGCRec))) 565 return FALSE; 566 if (!AllocateWindowPrivate(pScreen, rootlessWindowPrivateIndex, 0)) 567 return FALSE; 568 569 s = xalloc(sizeof(RootlessScreenRec)); 570 if (! s) return FALSE; 571 SCREENREC(pScreen) = s; 572 573 s->pixmap_data = NULL; 574 s->pixmap_data_size = 0; 575 576 s->redisplay_timer = NULL; 577 s->redisplay_timer_set = FALSE; 578 579 return TRUE; 580} 581 582 583static void 584RootlessWrap(ScreenPtr pScreen) 585{ 586 RootlessScreenRec *s = (RootlessScreenRec*) 587 pScreen->devPrivates[rootlessScreenPrivateIndex].ptr; 588 589#define WRAP(a) \ 590 if (pScreen->a) { \ 591 s->a = pScreen->a; \ 592 } else { \ 593 RL_DEBUG_MSG("null screen fn " #a "\n"); \ 594 s->a = NULL; \ 595 } \ 596 pScreen->a = Rootless##a 597 598 WRAP(CreateScreenResources); 599 WRAP(CloseScreen); 600 WRAP(CreateGC); 601 WRAP(PaintWindowBackground); 602 WRAP(PaintWindowBorder); 603 WRAP(CopyWindow); 604 WRAP(GetImage); 605 WRAP(SourceValidate); 606 WRAP(CreateWindow); 607 WRAP(DestroyWindow); 608 WRAP(RealizeWindow); 609 WRAP(UnrealizeWindow); 610 WRAP(MoveWindow); 611 WRAP(PositionWindow); 612 WRAP(ResizeWindow); 613 WRAP(RestackWindow); 614 WRAP(ReparentWindow); 615 WRAP(ChangeBorderWidth); 616 WRAP(MarkOverlappedWindows); 617 WRAP(ValidateTree); 618 WRAP(ChangeWindowAttributes); 619 620#ifdef SHAPE 621 WRAP(SetShape); 622#endif 623 624#ifdef RENDER 625 { 626 // Composite and Glyphs don't use normal screen wrapping 627 PictureScreenPtr ps = GetPictureScreen(pScreen); 628 s->Composite = ps->Composite; 629 ps->Composite = RootlessComposite; 630 s->Glyphs = ps->Glyphs; 631 ps->Glyphs = RootlessGlyphs; 632 } 633#endif 634 635 // WRAP(ClearToBackground); fixme put this back? useful for shaped wins? 636 // WRAP(RestoreAreas); fixme put this back? 637 638#undef WRAP 639} 640 641 642/* 643 * RootlessInit 644 * Called by the rootless implementation to initialize the rootless layer. 645 * Rootless wraps lots of stuff and needs a bunch of devPrivates. 646 */ 647Bool RootlessInit(ScreenPtr pScreen, RootlessFrameProcsPtr procs) 648{ 649 RootlessScreenRec *s; 650 651 if (!RootlessAllocatePrivates(pScreen)) 652 return FALSE; 653 654 s = (RootlessScreenRec*) 655 pScreen->devPrivates[rootlessScreenPrivateIndex].ptr; 656 657 s->imp = procs; 658 659 RootlessWrap(pScreen); 660 661 if (!RegisterBlockAndWakeupHandlers(RootlessBlockHandler, 662 RootlessWakeupHandler, 663 (pointer) pScreen)) 664 { 665 return FALSE; 666 } 667 668 return TRUE; 669} 670