panoramiX.c revision 7e31ba66
1/***************************************************************** 2Copyright (c) 1991, 1997 Digital Equipment Corporation, Maynard, Massachusetts. 3Permission is hereby granted, free of charge, to any person obtaining a copy 4of this software and associated documentation files (the "Software"), to deal 5in the Software without restriction, including without limitation the rights 6to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7copies of the Software. 8 9The above copyright notice and this permission notice shall be included in 10all copies or substantial portions of the Software. 11 12THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 14FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 15DIGITAL EQUIPMENT CORPORATION BE LIABLE FOR ANY CLAIM, DAMAGES, INCLUDING, 16BUT NOT LIMITED TO CONSEQUENTIAL OR INCIDENTAL DAMAGES, OR OTHER LIABILITY, 17WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 18IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 20Except as contained in this notice, the name of Digital Equipment Corporation 21shall not be used in advertising or otherwise to promote the sale, use or other 22dealings in this Software without prior written authorization from Digital 23Equipment Corporation. 24******************************************************************/ 25 26#ifdef HAVE_DIX_CONFIG_H 27#include <dix-config.h> 28#endif 29 30#ifdef HAVE_DMX_CONFIG_H 31#include <dmx-config.h> 32#endif 33 34#include <stdio.h> 35#include <X11/X.h> 36#include <X11/Xproto.h> 37#include <X11/Xarch.h> 38#include "misc.h" 39#include "cursor.h" 40#include "cursorstr.h" 41#include "extnsionst.h" 42#include "dixstruct.h" 43#include "gc.h" 44#include "gcstruct.h" 45#include "scrnintstr.h" 46#include "window.h" 47#include "windowstr.h" 48#include "pixmapstr.h" 49#include "panoramiX.h" 50#include <X11/extensions/panoramiXproto.h> 51#include "panoramiXsrv.h" 52#include "globals.h" 53#include "servermd.h" 54#include "resource.h" 55#include "picturestr.h" 56#include "xfixesint.h" 57#include "damageextint.h" 58#ifdef COMPOSITE 59#include "compint.h" 60#endif 61#include "extinit.h" 62#include "protocol-versions.h" 63 64#ifdef GLXPROXY 65extern VisualPtr glxMatchVisual(ScreenPtr pScreen, 66 VisualPtr pVisual, ScreenPtr pMatchScreen); 67#endif 68 69/* 70 * PanoramiX data declarations 71 */ 72 73int PanoramiXPixWidth = 0; 74int PanoramiXPixHeight = 0; 75int PanoramiXNumScreens = 0; 76 77_X_EXPORT RegionRec PanoramiXScreenRegion = { {0, 0, 0, 0}, NULL }; 78 79static int PanoramiXNumDepths; 80static DepthPtr PanoramiXDepths; 81static int PanoramiXNumVisuals; 82static VisualPtr PanoramiXVisuals; 83 84RESTYPE XRC_DRAWABLE; 85RESTYPE XRT_WINDOW; 86RESTYPE XRT_PIXMAP; 87RESTYPE XRT_GC; 88RESTYPE XRT_COLORMAP; 89 90static Bool VisualsEqual(VisualPtr, ScreenPtr, VisualPtr); 91XineramaVisualsEqualProcPtr XineramaVisualsEqualPtr = &VisualsEqual; 92 93/* 94 * Function prototypes 95 */ 96 97static int panoramiXGeneration; 98static int ProcPanoramiXDispatch(ClientPtr client); 99 100static void PanoramiXResetProc(ExtensionEntry *); 101 102/* 103 * External references for functions and data variables 104 */ 105 106#include "panoramiXh.h" 107 108int (*SavedProcVector[256]) (ClientPtr client) = { 109NULL,}; 110 111static DevPrivateKeyRec PanoramiXGCKeyRec; 112 113#define PanoramiXGCKey (&PanoramiXGCKeyRec) 114static DevPrivateKeyRec PanoramiXScreenKeyRec; 115 116#define PanoramiXScreenKey (&PanoramiXScreenKeyRec) 117 118typedef struct { 119 DDXPointRec clipOrg; 120 DDXPointRec patOrg; 121 const GCFuncs *wrapFuncs; 122} PanoramiXGCRec, *PanoramiXGCPtr; 123 124typedef struct { 125 CreateGCProcPtr CreateGC; 126 CloseScreenProcPtr CloseScreen; 127} PanoramiXScreenRec, *PanoramiXScreenPtr; 128 129static void XineramaValidateGC(GCPtr, unsigned long, DrawablePtr); 130static void XineramaChangeGC(GCPtr, unsigned long); 131static void XineramaCopyGC(GCPtr, unsigned long, GCPtr); 132static void XineramaDestroyGC(GCPtr); 133static void XineramaChangeClip(GCPtr, int, void *, int); 134static void XineramaDestroyClip(GCPtr); 135static void XineramaCopyClip(GCPtr, GCPtr); 136 137static const GCFuncs XineramaGCFuncs = { 138 XineramaValidateGC, XineramaChangeGC, XineramaCopyGC, XineramaDestroyGC, 139 XineramaChangeClip, XineramaDestroyClip, XineramaCopyClip 140}; 141 142#define Xinerama_GC_FUNC_PROLOGUE(pGC)\ 143 PanoramiXGCPtr pGCPriv = (PanoramiXGCPtr) \ 144 dixLookupPrivate(&(pGC)->devPrivates, PanoramiXGCKey); \ 145 (pGC)->funcs = pGCPriv->wrapFuncs; 146 147#define Xinerama_GC_FUNC_EPILOGUE(pGC)\ 148 pGCPriv->wrapFuncs = (pGC)->funcs;\ 149 (pGC)->funcs = &XineramaGCFuncs; 150 151static Bool 152XineramaCloseScreen(ScreenPtr pScreen) 153{ 154 PanoramiXScreenPtr pScreenPriv = (PanoramiXScreenPtr) 155 dixLookupPrivate(&pScreen->devPrivates, PanoramiXScreenKey); 156 157 pScreen->CloseScreen = pScreenPriv->CloseScreen; 158 pScreen->CreateGC = pScreenPriv->CreateGC; 159 160 if (pScreen->myNum == 0) 161 RegionUninit(&PanoramiXScreenRegion); 162 163 free(pScreenPriv); 164 165 return (*pScreen->CloseScreen) (pScreen); 166} 167 168static Bool 169XineramaCreateGC(GCPtr pGC) 170{ 171 ScreenPtr pScreen = pGC->pScreen; 172 PanoramiXScreenPtr pScreenPriv = (PanoramiXScreenPtr) 173 dixLookupPrivate(&pScreen->devPrivates, PanoramiXScreenKey); 174 Bool ret; 175 176 pScreen->CreateGC = pScreenPriv->CreateGC; 177 if ((ret = (*pScreen->CreateGC) (pGC))) { 178 PanoramiXGCPtr pGCPriv = (PanoramiXGCPtr) 179 dixLookupPrivate(&pGC->devPrivates, PanoramiXGCKey); 180 181 pGCPriv->wrapFuncs = pGC->funcs; 182 pGC->funcs = &XineramaGCFuncs; 183 184 pGCPriv->clipOrg.x = pGC->clipOrg.x; 185 pGCPriv->clipOrg.y = pGC->clipOrg.y; 186 pGCPriv->patOrg.x = pGC->patOrg.x; 187 pGCPriv->patOrg.y = pGC->patOrg.y; 188 } 189 pScreen->CreateGC = XineramaCreateGC; 190 191 return ret; 192} 193 194static void 195XineramaValidateGC(GCPtr pGC, unsigned long changes, DrawablePtr pDraw) 196{ 197 Xinerama_GC_FUNC_PROLOGUE(pGC); 198 199 if ((pDraw->type == DRAWABLE_WINDOW) && !(((WindowPtr) pDraw)->parent)) { 200 /* the root window */ 201 int x_off = pGC->pScreen->x; 202 int y_off = pGC->pScreen->y; 203 int new_val; 204 205 new_val = pGCPriv->clipOrg.x - x_off; 206 if (pGC->clipOrg.x != new_val) { 207 pGC->clipOrg.x = new_val; 208 changes |= GCClipXOrigin; 209 } 210 new_val = pGCPriv->clipOrg.y - y_off; 211 if (pGC->clipOrg.y != new_val) { 212 pGC->clipOrg.y = new_val; 213 changes |= GCClipYOrigin; 214 } 215 new_val = pGCPriv->patOrg.x - x_off; 216 if (pGC->patOrg.x != new_val) { 217 pGC->patOrg.x = new_val; 218 changes |= GCTileStipXOrigin; 219 } 220 new_val = pGCPriv->patOrg.y - y_off; 221 if (pGC->patOrg.y != new_val) { 222 pGC->patOrg.y = new_val; 223 changes |= GCTileStipYOrigin; 224 } 225 } 226 else { 227 if (pGC->clipOrg.x != pGCPriv->clipOrg.x) { 228 pGC->clipOrg.x = pGCPriv->clipOrg.x; 229 changes |= GCClipXOrigin; 230 } 231 if (pGC->clipOrg.y != pGCPriv->clipOrg.y) { 232 pGC->clipOrg.y = pGCPriv->clipOrg.y; 233 changes |= GCClipYOrigin; 234 } 235 if (pGC->patOrg.x != pGCPriv->patOrg.x) { 236 pGC->patOrg.x = pGCPriv->patOrg.x; 237 changes |= GCTileStipXOrigin; 238 } 239 if (pGC->patOrg.y != pGCPriv->patOrg.y) { 240 pGC->patOrg.y = pGCPriv->patOrg.y; 241 changes |= GCTileStipYOrigin; 242 } 243 } 244 245 (*pGC->funcs->ValidateGC) (pGC, changes, pDraw); 246 Xinerama_GC_FUNC_EPILOGUE(pGC); 247} 248 249static void 250XineramaDestroyGC(GCPtr pGC) 251{ 252 Xinerama_GC_FUNC_PROLOGUE(pGC); 253 (*pGC->funcs->DestroyGC) (pGC); 254 Xinerama_GC_FUNC_EPILOGUE(pGC); 255} 256 257static void 258XineramaChangeGC(GCPtr pGC, unsigned long mask) 259{ 260 Xinerama_GC_FUNC_PROLOGUE(pGC); 261 262 if (mask & GCTileStipXOrigin) 263 pGCPriv->patOrg.x = pGC->patOrg.x; 264 if (mask & GCTileStipYOrigin) 265 pGCPriv->patOrg.y = pGC->patOrg.y; 266 if (mask & GCClipXOrigin) 267 pGCPriv->clipOrg.x = pGC->clipOrg.x; 268 if (mask & GCClipYOrigin) 269 pGCPriv->clipOrg.y = pGC->clipOrg.y; 270 271 (*pGC->funcs->ChangeGC) (pGC, mask); 272 Xinerama_GC_FUNC_EPILOGUE(pGC); 273} 274 275static void 276XineramaCopyGC(GCPtr pGCSrc, unsigned long mask, GCPtr pGCDst) 277{ 278 PanoramiXGCPtr pSrcPriv = (PanoramiXGCPtr) 279 dixLookupPrivate(&pGCSrc->devPrivates, PanoramiXGCKey); 280 281 Xinerama_GC_FUNC_PROLOGUE(pGCDst); 282 283 if (mask & GCTileStipXOrigin) 284 pGCPriv->patOrg.x = pSrcPriv->patOrg.x; 285 if (mask & GCTileStipYOrigin) 286 pGCPriv->patOrg.y = pSrcPriv->patOrg.y; 287 if (mask & GCClipXOrigin) 288 pGCPriv->clipOrg.x = pSrcPriv->clipOrg.x; 289 if (mask & GCClipYOrigin) 290 pGCPriv->clipOrg.y = pSrcPriv->clipOrg.y; 291 292 (*pGCDst->funcs->CopyGC) (pGCSrc, mask, pGCDst); 293 Xinerama_GC_FUNC_EPILOGUE(pGCDst); 294} 295 296static void 297XineramaChangeClip(GCPtr pGC, int type, void *pvalue, int nrects) 298{ 299 Xinerama_GC_FUNC_PROLOGUE(pGC); 300 (*pGC->funcs->ChangeClip) (pGC, type, pvalue, nrects); 301 Xinerama_GC_FUNC_EPILOGUE(pGC); 302} 303 304static void 305XineramaCopyClip(GCPtr pgcDst, GCPtr pgcSrc) 306{ 307 Xinerama_GC_FUNC_PROLOGUE(pgcDst); 308 (*pgcDst->funcs->CopyClip) (pgcDst, pgcSrc); 309 Xinerama_GC_FUNC_EPILOGUE(pgcDst); 310} 311 312static void 313XineramaDestroyClip(GCPtr pGC) 314{ 315 Xinerama_GC_FUNC_PROLOGUE(pGC); 316 (*pGC->funcs->DestroyClip) (pGC); 317 Xinerama_GC_FUNC_EPILOGUE(pGC); 318} 319 320int 321XineramaDeleteResource(void *data, XID id) 322{ 323 free(data); 324 return 1; 325} 326 327typedef struct { 328 int screen; 329 int id; 330} PanoramiXSearchData; 331 332static Bool 333XineramaFindIDByScrnum(void *resource, XID id, void *privdata) 334{ 335 PanoramiXRes *res = (PanoramiXRes *) resource; 336 PanoramiXSearchData *data = (PanoramiXSearchData *) privdata; 337 338 return res->info[data->screen].id == data->id; 339} 340 341PanoramiXRes * 342PanoramiXFindIDByScrnum(RESTYPE type, XID id, int screen) 343{ 344 PanoramiXSearchData data; 345 void *val; 346 347 if (!screen) { 348 dixLookupResourceByType(&val, id, type, serverClient, DixReadAccess); 349 return val; 350 } 351 352 data.screen = screen; 353 data.id = id; 354 355 return LookupClientResourceComplex(clients[CLIENT_ID(id)], type, 356 XineramaFindIDByScrnum, &data); 357} 358 359typedef struct _connect_callback_list { 360 void (*func) (void); 361 struct _connect_callback_list *next; 362} XineramaConnectionCallbackList; 363 364static XineramaConnectionCallbackList *ConnectionCallbackList = NULL; 365 366Bool 367XineramaRegisterConnectionBlockCallback(void (*func) (void)) 368{ 369 XineramaConnectionCallbackList *newlist; 370 371 if (!(newlist = malloc(sizeof(XineramaConnectionCallbackList)))) 372 return FALSE; 373 374 newlist->next = ConnectionCallbackList; 375 newlist->func = func; 376 ConnectionCallbackList = newlist; 377 378 return TRUE; 379} 380 381static void 382XineramaInitData(void) 383{ 384 int i, w, h; 385 386 RegionNull(&PanoramiXScreenRegion); 387 FOR_NSCREENS(i) { 388 BoxRec TheBox; 389 RegionRec ScreenRegion; 390 391 ScreenPtr pScreen = screenInfo.screens[i]; 392 393 TheBox.x1 = pScreen->x; 394 TheBox.x2 = TheBox.x1 + pScreen->width; 395 TheBox.y1 = pScreen->y; 396 TheBox.y2 = TheBox.y1 + pScreen->height; 397 398 RegionInit(&ScreenRegion, &TheBox, 1); 399 RegionUnion(&PanoramiXScreenRegion, &PanoramiXScreenRegion, 400 &ScreenRegion); 401 RegionUninit(&ScreenRegion); 402 } 403 404 PanoramiXPixWidth = screenInfo.screens[0]->x + screenInfo.screens[0]->width; 405 PanoramiXPixHeight = 406 screenInfo.screens[0]->y + screenInfo.screens[0]->height; 407 408 FOR_NSCREENS_FORWARD_SKIP(i) { 409 ScreenPtr pScreen = screenInfo.screens[i]; 410 411 w = pScreen->x + pScreen->width; 412 h = pScreen->y + pScreen->height; 413 414 if (PanoramiXPixWidth < w) 415 PanoramiXPixWidth = w; 416 if (PanoramiXPixHeight < h) 417 PanoramiXPixHeight = h; 418 } 419} 420 421void 422XineramaReinitData(void) 423{ 424 RegionUninit(&PanoramiXScreenRegion); 425 XineramaInitData(); 426} 427 428/* 429 * PanoramiXExtensionInit(): 430 * Called from InitExtensions in main(). 431 * Register PanoramiXeen Extension 432 * Initialize global variables. 433 */ 434 435void 436PanoramiXExtensionInit(void) 437{ 438 int i; 439 Bool success = FALSE; 440 ExtensionEntry *extEntry; 441 ScreenPtr pScreen = screenInfo.screens[0]; 442 PanoramiXScreenPtr pScreenPriv; 443 444 if (noPanoramiXExtension) 445 return; 446 447 if (!dixRegisterPrivateKey(&PanoramiXScreenKeyRec, PRIVATE_SCREEN, 0)) { 448 noPanoramiXExtension = TRUE; 449 return; 450 } 451 452 if (!dixRegisterPrivateKey 453 (&PanoramiXGCKeyRec, PRIVATE_GC, sizeof(PanoramiXGCRec))) { 454 noPanoramiXExtension = TRUE; 455 return; 456 } 457 458 PanoramiXNumScreens = screenInfo.numScreens; 459 if (PanoramiXNumScreens == 1) { /* Only 1 screen */ 460 noPanoramiXExtension = TRUE; 461 return; 462 } 463 464 while (panoramiXGeneration != serverGeneration) { 465 extEntry = AddExtension(PANORAMIX_PROTOCOL_NAME, 0, 0, 466 ProcPanoramiXDispatch, 467 SProcPanoramiXDispatch, PanoramiXResetProc, 468 StandardMinorOpcode); 469 if (!extEntry) 470 break; 471 472 /* 473 * First make sure all the basic allocations succeed. If not, 474 * run in non-PanoramiXeen mode. 475 */ 476 477 FOR_NSCREENS(i) { 478 pScreen = screenInfo.screens[i]; 479 pScreenPriv = malloc(sizeof(PanoramiXScreenRec)); 480 dixSetPrivate(&pScreen->devPrivates, PanoramiXScreenKey, 481 pScreenPriv); 482 if (!pScreenPriv) { 483 noPanoramiXExtension = TRUE; 484 return; 485 } 486 487 pScreenPriv->CreateGC = pScreen->CreateGC; 488 pScreenPriv->CloseScreen = pScreen->CloseScreen; 489 490 pScreen->CreateGC = XineramaCreateGC; 491 pScreen->CloseScreen = XineramaCloseScreen; 492 } 493 494 XRC_DRAWABLE = CreateNewResourceClass(); 495 XRT_WINDOW = CreateNewResourceType(XineramaDeleteResource, 496 "XineramaWindow"); 497 if (XRT_WINDOW) 498 XRT_WINDOW |= XRC_DRAWABLE; 499 XRT_PIXMAP = CreateNewResourceType(XineramaDeleteResource, 500 "XineramaPixmap"); 501 if (XRT_PIXMAP) 502 XRT_PIXMAP |= XRC_DRAWABLE; 503 XRT_GC = CreateNewResourceType(XineramaDeleteResource, "XineramaGC"); 504 XRT_COLORMAP = CreateNewResourceType(XineramaDeleteResource, 505 "XineramaColormap"); 506 507 if (XRT_WINDOW && XRT_PIXMAP && XRT_GC && XRT_COLORMAP) { 508 panoramiXGeneration = serverGeneration; 509 success = TRUE; 510 } 511 SetResourceTypeErrorValue(XRT_WINDOW, BadWindow); 512 SetResourceTypeErrorValue(XRT_PIXMAP, BadPixmap); 513 SetResourceTypeErrorValue(XRT_GC, BadGC); 514 SetResourceTypeErrorValue(XRT_COLORMAP, BadColor); 515 } 516 517 if (!success) { 518 noPanoramiXExtension = TRUE; 519 ErrorF(PANORAMIX_PROTOCOL_NAME " extension failed to initialize\n"); 520 return; 521 } 522 523 XineramaInitData(); 524 525 /* 526 * Put our processes into the ProcVector 527 */ 528 529 for (i = 256; i--;) 530 SavedProcVector[i] = ProcVector[i]; 531 532 ProcVector[X_CreateWindow] = PanoramiXCreateWindow; 533 ProcVector[X_ChangeWindowAttributes] = PanoramiXChangeWindowAttributes; 534 ProcVector[X_DestroyWindow] = PanoramiXDestroyWindow; 535 ProcVector[X_DestroySubwindows] = PanoramiXDestroySubwindows; 536 ProcVector[X_ChangeSaveSet] = PanoramiXChangeSaveSet; 537 ProcVector[X_ReparentWindow] = PanoramiXReparentWindow; 538 ProcVector[X_MapWindow] = PanoramiXMapWindow; 539 ProcVector[X_MapSubwindows] = PanoramiXMapSubwindows; 540 ProcVector[X_UnmapWindow] = PanoramiXUnmapWindow; 541 ProcVector[X_UnmapSubwindows] = PanoramiXUnmapSubwindows; 542 ProcVector[X_ConfigureWindow] = PanoramiXConfigureWindow; 543 ProcVector[X_CirculateWindow] = PanoramiXCirculateWindow; 544 ProcVector[X_GetGeometry] = PanoramiXGetGeometry; 545 ProcVector[X_TranslateCoords] = PanoramiXTranslateCoords; 546 ProcVector[X_CreatePixmap] = PanoramiXCreatePixmap; 547 ProcVector[X_FreePixmap] = PanoramiXFreePixmap; 548 ProcVector[X_CreateGC] = PanoramiXCreateGC; 549 ProcVector[X_ChangeGC] = PanoramiXChangeGC; 550 ProcVector[X_CopyGC] = PanoramiXCopyGC; 551 ProcVector[X_SetDashes] = PanoramiXSetDashes; 552 ProcVector[X_SetClipRectangles] = PanoramiXSetClipRectangles; 553 ProcVector[X_FreeGC] = PanoramiXFreeGC; 554 ProcVector[X_ClearArea] = PanoramiXClearToBackground; 555 ProcVector[X_CopyArea] = PanoramiXCopyArea; 556 ProcVector[X_CopyPlane] = PanoramiXCopyPlane; 557 ProcVector[X_PolyPoint] = PanoramiXPolyPoint; 558 ProcVector[X_PolyLine] = PanoramiXPolyLine; 559 ProcVector[X_PolySegment] = PanoramiXPolySegment; 560 ProcVector[X_PolyRectangle] = PanoramiXPolyRectangle; 561 ProcVector[X_PolyArc] = PanoramiXPolyArc; 562 ProcVector[X_FillPoly] = PanoramiXFillPoly; 563 ProcVector[X_PolyFillRectangle] = PanoramiXPolyFillRectangle; 564 ProcVector[X_PolyFillArc] = PanoramiXPolyFillArc; 565 ProcVector[X_PutImage] = PanoramiXPutImage; 566 ProcVector[X_GetImage] = PanoramiXGetImage; 567 ProcVector[X_PolyText8] = PanoramiXPolyText8; 568 ProcVector[X_PolyText16] = PanoramiXPolyText16; 569 ProcVector[X_ImageText8] = PanoramiXImageText8; 570 ProcVector[X_ImageText16] = PanoramiXImageText16; 571 ProcVector[X_CreateColormap] = PanoramiXCreateColormap; 572 ProcVector[X_FreeColormap] = PanoramiXFreeColormap; 573 ProcVector[X_CopyColormapAndFree] = PanoramiXCopyColormapAndFree; 574 ProcVector[X_InstallColormap] = PanoramiXInstallColormap; 575 ProcVector[X_UninstallColormap] = PanoramiXUninstallColormap; 576 ProcVector[X_AllocColor] = PanoramiXAllocColor; 577 ProcVector[X_AllocNamedColor] = PanoramiXAllocNamedColor; 578 ProcVector[X_AllocColorCells] = PanoramiXAllocColorCells; 579 ProcVector[X_AllocColorPlanes] = PanoramiXAllocColorPlanes; 580 ProcVector[X_FreeColors] = PanoramiXFreeColors; 581 ProcVector[X_StoreColors] = PanoramiXStoreColors; 582 ProcVector[X_StoreNamedColor] = PanoramiXStoreNamedColor; 583 584 PanoramiXRenderInit(); 585 PanoramiXFixesInit(); 586 PanoramiXDamageInit(); 587#ifdef COMPOSITE 588 PanoramiXCompositeInit(); 589#endif 590 591} 592 593Bool 594PanoramiXCreateConnectionBlock(void) 595{ 596 int i, j, length; 597 Bool disable_backing_store = FALSE; 598 int old_width, old_height; 599 float width_mult, height_mult; 600 xWindowRoot *root; 601 xVisualType *visual; 602 xDepth *depth; 603 VisualPtr pVisual; 604 ScreenPtr pScreen; 605 606 /* 607 * Do normal CreateConnectionBlock but faking it for only one screen 608 */ 609 610 if (!PanoramiXNumDepths) { 611 ErrorF("Xinerama error: No common visuals\n"); 612 return FALSE; 613 } 614 615 for (i = 1; i < screenInfo.numScreens; i++) { 616 pScreen = screenInfo.screens[i]; 617 if (pScreen->rootDepth != screenInfo.screens[0]->rootDepth) { 618 ErrorF("Xinerama error: Root window depths differ\n"); 619 return FALSE; 620 } 621 if (pScreen->backingStoreSupport != 622 screenInfo.screens[0]->backingStoreSupport) 623 disable_backing_store = TRUE; 624 } 625 626 if (disable_backing_store) { 627 for (i = 0; i < screenInfo.numScreens; i++) { 628 pScreen = screenInfo.screens[i]; 629 pScreen->backingStoreSupport = NotUseful; 630 } 631 } 632 633 i = screenInfo.numScreens; 634 screenInfo.numScreens = 1; 635 if (!CreateConnectionBlock()) { 636 screenInfo.numScreens = i; 637 return FALSE; 638 } 639 640 screenInfo.numScreens = i; 641 642 root = (xWindowRoot *) (ConnectionInfo + connBlockScreenStart); 643 length = connBlockScreenStart + sizeof(xWindowRoot); 644 645 /* overwrite the connection block */ 646 root->nDepths = PanoramiXNumDepths; 647 648 for (i = 0; i < PanoramiXNumDepths; i++) { 649 depth = (xDepth *) (ConnectionInfo + length); 650 depth->depth = PanoramiXDepths[i].depth; 651 depth->nVisuals = PanoramiXDepths[i].numVids; 652 length += sizeof(xDepth); 653 visual = (xVisualType *) (ConnectionInfo + length); 654 655 for (j = 0; j < depth->nVisuals; j++, visual++) { 656 visual->visualID = PanoramiXDepths[i].vids[j]; 657 658 for (pVisual = PanoramiXVisuals; 659 pVisual->vid != visual->visualID; pVisual++); 660 661 visual->class = pVisual->class; 662 visual->bitsPerRGB = pVisual->bitsPerRGBValue; 663 visual->colormapEntries = pVisual->ColormapEntries; 664 visual->redMask = pVisual->redMask; 665 visual->greenMask = pVisual->greenMask; 666 visual->blueMask = pVisual->blueMask; 667 } 668 669 length += (depth->nVisuals * sizeof(xVisualType)); 670 } 671 672 connSetupPrefix.length = bytes_to_int32(length); 673 674 for (i = 0; i < PanoramiXNumDepths; i++) 675 free(PanoramiXDepths[i].vids); 676 free(PanoramiXDepths); 677 PanoramiXDepths = NULL; 678 679 /* 680 * OK, change some dimensions so it looks as if it were one big screen 681 */ 682 683 old_width = root->pixWidth; 684 old_height = root->pixHeight; 685 686 root->pixWidth = PanoramiXPixWidth; 687 root->pixHeight = PanoramiXPixHeight; 688 width_mult = (1.0 * root->pixWidth) / old_width; 689 height_mult = (1.0 * root->pixHeight) / old_height; 690 root->mmWidth *= width_mult; 691 root->mmHeight *= height_mult; 692 693 while (ConnectionCallbackList) { 694 void *tmp; 695 696 tmp = (void *) ConnectionCallbackList; 697 (*ConnectionCallbackList->func) (); 698 ConnectionCallbackList = ConnectionCallbackList->next; 699 free(tmp); 700 } 701 702 return TRUE; 703} 704 705/* 706 * This isn't just memcmp(), bitsPerRGBValue is skipped. markv made that 707 * change way back before xf86 4.0, but the comment for _why_ is a bit 708 * opaque, so I'm not going to question it for now. 709 * 710 * This is probably better done as a screen hook so DBE/EVI/GLX can add 711 * their own tests, and adding privates to VisualRec so they don't have to 712 * do their own back-mapping. 713 */ 714static Bool 715VisualsEqual(VisualPtr a, ScreenPtr pScreenB, VisualPtr b) 716{ 717 return ((a->class == b->class) && 718 (a->ColormapEntries == b->ColormapEntries) && 719 (a->nplanes == b->nplanes) && 720 (a->redMask == b->redMask) && 721 (a->greenMask == b->greenMask) && 722 (a->blueMask == b->blueMask) && 723 (a->offsetRed == b->offsetRed) && 724 (a->offsetGreen == b->offsetGreen) && 725 (a->offsetBlue == b->offsetBlue)); 726} 727 728static void 729PanoramiXMaybeAddDepth(DepthPtr pDepth) 730{ 731 ScreenPtr pScreen; 732 int j, k; 733 Bool found = FALSE; 734 735 FOR_NSCREENS_FORWARD_SKIP(j) { 736 pScreen = screenInfo.screens[j]; 737 for (k = 0; k < pScreen->numDepths; k++) { 738 if (pScreen->allowedDepths[k].depth == pDepth->depth) { 739 found = TRUE; 740 break; 741 } 742 } 743 } 744 745 if (!found) 746 return; 747 748 j = PanoramiXNumDepths; 749 PanoramiXNumDepths++; 750 PanoramiXDepths = reallocarray(PanoramiXDepths, 751 PanoramiXNumDepths, sizeof(DepthRec)); 752 PanoramiXDepths[j].depth = pDepth->depth; 753 PanoramiXDepths[j].numVids = 0; 754 PanoramiXDepths[j].vids = NULL; 755} 756 757static void 758PanoramiXMaybeAddVisual(VisualPtr pVisual) 759{ 760 ScreenPtr pScreen; 761 int j, k; 762 Bool found = FALSE; 763 764 FOR_NSCREENS_FORWARD_SKIP(j) { 765 pScreen = screenInfo.screens[j]; 766 found = FALSE; 767 768 for (k = 0; k < pScreen->numVisuals; k++) { 769 VisualPtr candidate = &pScreen->visuals[k]; 770 771 if ((*XineramaVisualsEqualPtr) (pVisual, pScreen, candidate) 772#ifdef GLXPROXY 773 && glxMatchVisual(screenInfo.screens[0], pVisual, pScreen) 774#endif 775 ) { 776 found = TRUE; 777 break; 778 } 779 } 780 781 if (!found) 782 return; 783 } 784 785 /* found a matching visual on all screens, add it to the subset list */ 786 j = PanoramiXNumVisuals; 787 PanoramiXNumVisuals++; 788 PanoramiXVisuals = reallocarray(PanoramiXVisuals, 789 PanoramiXNumVisuals, sizeof(VisualRec)); 790 791 memcpy(&PanoramiXVisuals[j], pVisual, sizeof(VisualRec)); 792 793 for (k = 0; k < PanoramiXNumDepths; k++) { 794 if (PanoramiXDepths[k].depth == pVisual->nplanes) { 795 PanoramiXDepths[k].vids = reallocarray(PanoramiXDepths[k].vids, 796 PanoramiXDepths[k].numVids + 1, 797 sizeof(VisualID)); 798 PanoramiXDepths[k].vids[PanoramiXDepths[k].numVids] = pVisual->vid; 799 PanoramiXDepths[k].numVids++; 800 break; 801 } 802 } 803} 804 805extern void 806PanoramiXConsolidate(void) 807{ 808 int i; 809 PanoramiXRes *root, *defmap, *saver; 810 ScreenPtr pScreen = screenInfo.screens[0]; 811 DepthPtr pDepth = pScreen->allowedDepths; 812 VisualPtr pVisual = pScreen->visuals; 813 814 PanoramiXNumDepths = 0; 815 PanoramiXNumVisuals = 0; 816 817 for (i = 0; i < pScreen->numDepths; i++) 818 PanoramiXMaybeAddDepth(pDepth++); 819 820 for (i = 0; i < pScreen->numVisuals; i++) 821 PanoramiXMaybeAddVisual(pVisual++); 822 823 root = malloc(sizeof(PanoramiXRes)); 824 root->type = XRT_WINDOW; 825 defmap = malloc(sizeof(PanoramiXRes)); 826 defmap->type = XRT_COLORMAP; 827 saver = malloc(sizeof(PanoramiXRes)); 828 saver->type = XRT_WINDOW; 829 830 FOR_NSCREENS(i) { 831 ScreenPtr scr = screenInfo.screens[i]; 832 833 root->info[i].id = scr->root->drawable.id; 834 root->u.win.class = InputOutput; 835 root->u.win.root = TRUE; 836 saver->info[i].id = scr->screensaver.wid; 837 saver->u.win.class = InputOutput; 838 saver->u.win.root = TRUE; 839 defmap->info[i].id = scr->defColormap; 840 } 841 842 AddResource(root->info[0].id, XRT_WINDOW, root); 843 AddResource(saver->info[0].id, XRT_WINDOW, saver); 844 AddResource(defmap->info[0].id, XRT_COLORMAP, defmap); 845} 846 847VisualID 848PanoramiXTranslateVisualID(int screen, VisualID orig) 849{ 850 ScreenPtr pOtherScreen = screenInfo.screens[screen]; 851 VisualPtr pVisual = NULL; 852 int i; 853 854 for (i = 0; i < PanoramiXNumVisuals; i++) { 855 if (orig == PanoramiXVisuals[i].vid) { 856 pVisual = &PanoramiXVisuals[i]; 857 break; 858 } 859 } 860 861 if (!pVisual) 862 return 0; 863 864 /* if screen is 0, orig is already the correct visual ID */ 865 if (screen == 0) 866 return orig; 867 868 /* found the original, now translate it relative to the backend screen */ 869 for (i = 0; i < pOtherScreen->numVisuals; i++) { 870 VisualPtr pOtherVisual = &pOtherScreen->visuals[i]; 871 872 if ((*XineramaVisualsEqualPtr) (pVisual, pOtherScreen, pOtherVisual)) 873 return pOtherVisual->vid; 874 } 875 876 return 0; 877} 878 879/* 880 * PanoramiXResetProc() 881 * Exit, deallocating as needed. 882 */ 883 884static void 885PanoramiXResetProc(ExtensionEntry * extEntry) 886{ 887 int i; 888 889 PanoramiXRenderReset(); 890 PanoramiXFixesReset(); 891 PanoramiXDamageReset(); 892#ifdef COMPOSITE 893 PanoramiXCompositeReset (); 894#endif 895 screenInfo.numScreens = PanoramiXNumScreens; 896 for (i = 256; i--;) 897 ProcVector[i] = SavedProcVector[i]; 898} 899 900int 901ProcPanoramiXQueryVersion(ClientPtr client) 902{ 903 /* REQUEST(xPanoramiXQueryVersionReq); */ 904 xPanoramiXQueryVersionReply rep = { 905 .type = X_Reply, 906 .sequenceNumber = client->sequence, 907 .length = 0, 908 .majorVersion = SERVER_PANORAMIX_MAJOR_VERSION, 909 .minorVersion = SERVER_PANORAMIX_MINOR_VERSION 910 }; 911 912 REQUEST_SIZE_MATCH(xPanoramiXQueryVersionReq); 913 if (client->swapped) { 914 swaps(&rep.sequenceNumber); 915 swapl(&rep.length); 916 swaps(&rep.majorVersion); 917 swaps(&rep.minorVersion); 918 } 919 WriteToClient(client, sizeof(xPanoramiXQueryVersionReply), &rep); 920 return Success; 921} 922 923int 924ProcPanoramiXGetState(ClientPtr client) 925{ 926 REQUEST(xPanoramiXGetStateReq); 927 WindowPtr pWin; 928 xPanoramiXGetStateReply rep; 929 int rc; 930 931 REQUEST_SIZE_MATCH(xPanoramiXGetStateReq); 932 rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess); 933 if (rc != Success) 934 return rc; 935 936 rep = (xPanoramiXGetStateReply) { 937 .type = X_Reply, 938 .state = !noPanoramiXExtension, 939 .sequenceNumber = client->sequence, 940 .length = 0, 941 .window = stuff->window 942 }; 943 if (client->swapped) { 944 swaps(&rep.sequenceNumber); 945 swapl(&rep.length); 946 swapl(&rep.window); 947 } 948 WriteToClient(client, sizeof(xPanoramiXGetStateReply), &rep); 949 return Success; 950 951} 952 953int 954ProcPanoramiXGetScreenCount(ClientPtr client) 955{ 956 REQUEST(xPanoramiXGetScreenCountReq); 957 WindowPtr pWin; 958 xPanoramiXGetScreenCountReply rep; 959 int rc; 960 961 REQUEST_SIZE_MATCH(xPanoramiXGetScreenCountReq); 962 rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess); 963 if (rc != Success) 964 return rc; 965 966 rep = (xPanoramiXGetScreenCountReply) { 967 .type = X_Reply, 968 .ScreenCount = PanoramiXNumScreens, 969 .sequenceNumber = client->sequence, 970 .length = 0, 971 .window = stuff->window 972 }; 973 if (client->swapped) { 974 swaps(&rep.sequenceNumber); 975 swapl(&rep.length); 976 swapl(&rep.window); 977 } 978 WriteToClient(client, sizeof(xPanoramiXGetScreenCountReply), &rep); 979 return Success; 980} 981 982int 983ProcPanoramiXGetScreenSize(ClientPtr client) 984{ 985 REQUEST(xPanoramiXGetScreenSizeReq); 986 WindowPtr pWin; 987 xPanoramiXGetScreenSizeReply rep; 988 int rc; 989 990 REQUEST_SIZE_MATCH(xPanoramiXGetScreenSizeReq); 991 992 if (stuff->screen >= PanoramiXNumScreens) 993 return BadMatch; 994 995 rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess); 996 if (rc != Success) 997 return rc; 998 999 rep = (xPanoramiXGetScreenSizeReply) { 1000 .type = X_Reply, 1001 .sequenceNumber = client->sequence, 1002 .length = 0, 1003 /* screen dimensions */ 1004 .width = screenInfo.screens[stuff->screen]->width, 1005 .height = screenInfo.screens[stuff->screen]->height, 1006 .window = stuff->window, 1007 .screen = stuff->screen 1008 }; 1009 if (client->swapped) { 1010 swaps(&rep.sequenceNumber); 1011 swapl(&rep.length); 1012 swapl(&rep.width); 1013 swapl(&rep.height); 1014 swapl(&rep.window); 1015 swapl(&rep.screen); 1016 } 1017 WriteToClient(client, sizeof(xPanoramiXGetScreenSizeReply), &rep); 1018 return Success; 1019} 1020 1021int 1022ProcXineramaIsActive(ClientPtr client) 1023{ 1024 /* REQUEST(xXineramaIsActiveReq); */ 1025 xXineramaIsActiveReply rep; 1026 1027 REQUEST_SIZE_MATCH(xXineramaIsActiveReq); 1028 1029 rep = (xXineramaIsActiveReply) { 1030 .type = X_Reply, 1031 .sequenceNumber = client->sequence, 1032 .length = 0, 1033#if 1 1034 /* The following hack fools clients into thinking that Xinerama 1035 * is disabled even though it is not. */ 1036 .state = !noPanoramiXExtension && !PanoramiXExtensionDisabledHack 1037#else 1038 .state = !noPanoramiXExtension; 1039#endif 1040 }; 1041 if (client->swapped) { 1042 swaps(&rep.sequenceNumber); 1043 swapl(&rep.length); 1044 swapl(&rep.state); 1045 } 1046 WriteToClient(client, sizeof(xXineramaIsActiveReply), &rep); 1047 return Success; 1048} 1049 1050int 1051ProcXineramaQueryScreens(ClientPtr client) 1052{ 1053 /* REQUEST(xXineramaQueryScreensReq); */ 1054 CARD32 number = (noPanoramiXExtension) ? 0 : PanoramiXNumScreens; 1055 xXineramaQueryScreensReply rep = { 1056 .type = X_Reply, 1057 .sequenceNumber = client->sequence, 1058 .length = bytes_to_int32(number * sz_XineramaScreenInfo), 1059 .number = number 1060 }; 1061 1062 REQUEST_SIZE_MATCH(xXineramaQueryScreensReq); 1063 1064 if (client->swapped) { 1065 swaps(&rep.sequenceNumber); 1066 swapl(&rep.length); 1067 swapl(&rep.number); 1068 } 1069 WriteToClient(client, sizeof(xXineramaQueryScreensReply), &rep); 1070 1071 if (!noPanoramiXExtension) { 1072 xXineramaScreenInfo scratch; 1073 int i; 1074 1075 FOR_NSCREENS(i) { 1076 scratch.x_org = screenInfo.screens[i]->x; 1077 scratch.y_org = screenInfo.screens[i]->y; 1078 scratch.width = screenInfo.screens[i]->width; 1079 scratch.height = screenInfo.screens[i]->height; 1080 1081 if (client->swapped) { 1082 swaps(&scratch.x_org); 1083 swaps(&scratch.y_org); 1084 swaps(&scratch.width); 1085 swaps(&scratch.height); 1086 } 1087 WriteToClient(client, sz_XineramaScreenInfo, &scratch); 1088 } 1089 } 1090 1091 return Success; 1092} 1093 1094static int 1095ProcPanoramiXDispatch(ClientPtr client) 1096{ 1097 REQUEST(xReq); 1098 switch (stuff->data) { 1099 case X_PanoramiXQueryVersion: 1100 return ProcPanoramiXQueryVersion(client); 1101 case X_PanoramiXGetState: 1102 return ProcPanoramiXGetState(client); 1103 case X_PanoramiXGetScreenCount: 1104 return ProcPanoramiXGetScreenCount(client); 1105 case X_PanoramiXGetScreenSize: 1106 return ProcPanoramiXGetScreenSize(client); 1107 case X_XineramaIsActive: 1108 return ProcXineramaIsActive(client); 1109 case X_XineramaQueryScreens: 1110 return ProcXineramaQueryScreens(client); 1111 } 1112 return BadRequest; 1113} 1114 1115#if X_BYTE_ORDER == X_LITTLE_ENDIAN 1116#define SHIFT_L(v,s) (v) << (s) 1117#define SHIFT_R(v,s) (v) >> (s) 1118#else 1119#define SHIFT_L(v,s) (v) >> (s) 1120#define SHIFT_R(v,s) (v) << (s) 1121#endif 1122 1123static void 1124CopyBits(char *dst, int shiftL, char *src, int bytes) 1125{ 1126 /* Just get it to work. Worry about speed later */ 1127 int shiftR = 8 - shiftL; 1128 1129 while (bytes--) { 1130 *dst |= SHIFT_L(*src, shiftL); 1131 *(dst + 1) |= SHIFT_R(*src, shiftR); 1132 dst++; 1133 src++; 1134 } 1135} 1136 1137/* Caution. This doesn't support 2 and 4 bpp formats. We expect 1138 1 bpp and planar data to be already cleared when presented 1139 to this function */ 1140 1141void 1142XineramaGetImageData(DrawablePtr *pDrawables, 1143 int left, 1144 int top, 1145 int width, 1146 int height, 1147 unsigned int format, 1148 unsigned long planemask, 1149 char *data, int pitch, Bool isRoot) 1150{ 1151 RegionRec SrcRegion, ScreenRegion, GrabRegion; 1152 BoxRec SrcBox, *pbox; 1153 int x, y, w, h, i, j, nbox, size, sizeNeeded, ScratchPitch, inOut, depth; 1154 DrawablePtr pDraw = pDrawables[0]; 1155 char *ScratchMem = NULL; 1156 1157 size = 0; 1158 1159 /* find box in logical screen space */ 1160 SrcBox.x1 = left; 1161 SrcBox.y1 = top; 1162 if (!isRoot) { 1163 SrcBox.x1 += pDraw->x + screenInfo.screens[0]->x; 1164 SrcBox.y1 += pDraw->y + screenInfo.screens[0]->y; 1165 } 1166 SrcBox.x2 = SrcBox.x1 + width; 1167 SrcBox.y2 = SrcBox.y1 + height; 1168 1169 RegionInit(&SrcRegion, &SrcBox, 1); 1170 RegionNull(&GrabRegion); 1171 1172 depth = (format == XYPixmap) ? 1 : pDraw->depth; 1173 1174 FOR_NSCREENS(i) { 1175 BoxRec TheBox; 1176 ScreenPtr pScreen; 1177 1178 pDraw = pDrawables[i]; 1179 pScreen = pDraw->pScreen; 1180 1181 TheBox.x1 = pScreen->x; 1182 TheBox.x2 = TheBox.x1 + pScreen->width; 1183 TheBox.y1 = pScreen->y; 1184 TheBox.y2 = TheBox.y1 + pScreen->height; 1185 1186 RegionInit(&ScreenRegion, &TheBox, 1); 1187 inOut = RegionContainsRect(&ScreenRegion, &SrcBox); 1188 if (inOut == rgnPART) 1189 RegionIntersect(&GrabRegion, &SrcRegion, &ScreenRegion); 1190 RegionUninit(&ScreenRegion); 1191 1192 if (inOut == rgnIN) { 1193 (*pScreen->GetImage) (pDraw, 1194 SrcBox.x1 - pDraw->x - 1195 screenInfo.screens[i]->x, 1196 SrcBox.y1 - pDraw->y - 1197 screenInfo.screens[i]->y, width, height, 1198 format, planemask, data); 1199 break; 1200 } 1201 else if (inOut == rgnOUT) 1202 continue; 1203 1204 nbox = RegionNumRects(&GrabRegion); 1205 1206 if (nbox) { 1207 pbox = RegionRects(&GrabRegion); 1208 1209 while (nbox--) { 1210 w = pbox->x2 - pbox->x1; 1211 h = pbox->y2 - pbox->y1; 1212 ScratchPitch = PixmapBytePad(w, depth); 1213 sizeNeeded = ScratchPitch * h; 1214 1215 if (sizeNeeded > size) { 1216 char *tmpdata = ScratchMem; 1217 1218 ScratchMem = realloc(ScratchMem, sizeNeeded); 1219 if (ScratchMem) 1220 size = sizeNeeded; 1221 else { 1222 ScratchMem = tmpdata; 1223 break; 1224 } 1225 } 1226 1227 x = pbox->x1 - pDraw->x - screenInfo.screens[i]->x; 1228 y = pbox->y1 - pDraw->y - screenInfo.screens[i]->y; 1229 1230 (*pScreen->GetImage) (pDraw, x, y, w, h, 1231 format, planemask, ScratchMem); 1232 1233 /* copy the memory over */ 1234 1235 if (depth == 1) { 1236 int k, shift, leftover, index, index2; 1237 1238 x = pbox->x1 - SrcBox.x1; 1239 y = pbox->y1 - SrcBox.y1; 1240 shift = x & 7; 1241 x >>= 3; 1242 leftover = w & 7; 1243 w >>= 3; 1244 1245 /* clean up the edge */ 1246 if (leftover) { 1247 int mask = (1 << leftover) - 1; 1248 1249 for (j = h, k = w; j--; k += ScratchPitch) 1250 ScratchMem[k] &= mask; 1251 } 1252 1253 for (j = 0, index = (pitch * y) + x, index2 = 0; j < h; 1254 j++, index += pitch, index2 += ScratchPitch) { 1255 if (w) { 1256 if (!shift) 1257 memcpy(data + index, ScratchMem + index2, w); 1258 else 1259 CopyBits(data + index, shift, 1260 ScratchMem + index2, w); 1261 } 1262 1263 if (leftover) { 1264 data[index + w] |= 1265 SHIFT_L(ScratchMem[index2 + w], shift); 1266 if ((shift + leftover) > 8) 1267 data[index + w + 1] |= 1268 SHIFT_R(ScratchMem[index2 + w], 1269 (8 - shift)); 1270 } 1271 } 1272 } 1273 else { 1274 j = BitsPerPixel(depth) >> 3; 1275 x = (pbox->x1 - SrcBox.x1) * j; 1276 y = pbox->y1 - SrcBox.y1; 1277 w *= j; 1278 1279 for (j = 0; j < h; j++) { 1280 memcpy(data + (pitch * (y + j)) + x, 1281 ScratchMem + (ScratchPitch * j), w); 1282 } 1283 } 1284 pbox++; 1285 } 1286 1287 RegionSubtract(&SrcRegion, &SrcRegion, &GrabRegion); 1288 if (!RegionNotEmpty(&SrcRegion)) 1289 break; 1290 } 1291 1292 } 1293 1294 free(ScratchMem); 1295 1296 RegionUninit(&SrcRegion); 1297 RegionUninit(&GrabRegion); 1298} 1299