panoramiX.c revision 6e78d31f
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 /* XXX suboptimal, should grow these dynamically */ 755 if (pDepth->numVids) 756 PanoramiXDepths[j].vids = xallocarray(pDepth->numVids, sizeof(VisualID)); 757 else 758 PanoramiXDepths[j].vids = NULL; 759} 760 761static void 762PanoramiXMaybeAddVisual(VisualPtr pVisual) 763{ 764 ScreenPtr pScreen; 765 int j, k; 766 Bool found = FALSE; 767 768 FOR_NSCREENS_FORWARD_SKIP(j) { 769 pScreen = screenInfo.screens[j]; 770 found = FALSE; 771 772 for (k = 0; k < pScreen->numVisuals; k++) { 773 VisualPtr candidate = &pScreen->visuals[k]; 774 775 if ((*XineramaVisualsEqualPtr) (pVisual, pScreen, candidate) 776#ifdef GLXPROXY 777 && glxMatchVisual(screenInfo.screens[0], pVisual, pScreen) 778#endif 779 ) { 780 found = TRUE; 781 break; 782 } 783 } 784 785 if (!found) 786 return; 787 } 788 789 /* found a matching visual on all screens, add it to the subset list */ 790 j = PanoramiXNumVisuals; 791 PanoramiXNumVisuals++; 792 PanoramiXVisuals = reallocarray(PanoramiXVisuals, 793 PanoramiXNumVisuals, sizeof(VisualRec)); 794 795 memcpy(&PanoramiXVisuals[j], pVisual, sizeof(VisualRec)); 796 797 for (k = 0; k < PanoramiXNumDepths; k++) { 798 if (PanoramiXDepths[k].depth == pVisual->nplanes) { 799 PanoramiXDepths[k].vids[PanoramiXDepths[k].numVids] = pVisual->vid; 800 PanoramiXDepths[k].numVids++; 801 break; 802 } 803 } 804} 805 806extern void 807PanoramiXConsolidate(void) 808{ 809 int i; 810 PanoramiXRes *root, *defmap, *saver; 811 ScreenPtr pScreen = screenInfo.screens[0]; 812 DepthPtr pDepth = pScreen->allowedDepths; 813 VisualPtr pVisual = pScreen->visuals; 814 815 PanoramiXNumDepths = 0; 816 PanoramiXNumVisuals = 0; 817 818 for (i = 0; i < pScreen->numDepths; i++) 819 PanoramiXMaybeAddDepth(pDepth++); 820 821 for (i = 0; i < pScreen->numVisuals; i++) 822 PanoramiXMaybeAddVisual(pVisual++); 823 824 root = malloc(sizeof(PanoramiXRes)); 825 root->type = XRT_WINDOW; 826 defmap = malloc(sizeof(PanoramiXRes)); 827 defmap->type = XRT_COLORMAP; 828 saver = malloc(sizeof(PanoramiXRes)); 829 saver->type = XRT_WINDOW; 830 831 FOR_NSCREENS(i) { 832 ScreenPtr scr = screenInfo.screens[i]; 833 834 root->info[i].id = scr->root->drawable.id; 835 root->u.win.class = InputOutput; 836 root->u.win.root = TRUE; 837 saver->info[i].id = scr->screensaver.wid; 838 saver->u.win.class = InputOutput; 839 saver->u.win.root = TRUE; 840 defmap->info[i].id = scr->defColormap; 841 } 842 843 AddResource(root->info[0].id, XRT_WINDOW, root); 844 AddResource(saver->info[0].id, XRT_WINDOW, saver); 845 AddResource(defmap->info[0].id, XRT_COLORMAP, defmap); 846} 847 848VisualID 849PanoramiXTranslateVisualID(int screen, VisualID orig) 850{ 851 ScreenPtr pOtherScreen = screenInfo.screens[screen]; 852 VisualPtr pVisual = NULL; 853 int i; 854 855 for (i = 0; i < PanoramiXNumVisuals; i++) { 856 if (orig == PanoramiXVisuals[i].vid) { 857 pVisual = &PanoramiXVisuals[i]; 858 break; 859 } 860 } 861 862 if (!pVisual) 863 return 0; 864 865 /* if screen is 0, orig is already the correct visual ID */ 866 if (screen == 0) 867 return orig; 868 869 /* found the original, now translate it relative to the backend screen */ 870 for (i = 0; i < pOtherScreen->numVisuals; i++) { 871 VisualPtr pOtherVisual = &pOtherScreen->visuals[i]; 872 873 if ((*XineramaVisualsEqualPtr) (pVisual, pOtherScreen, pOtherVisual)) 874 return pOtherVisual->vid; 875 } 876 877 return 0; 878} 879 880/* 881 * PanoramiXResetProc() 882 * Exit, deallocating as needed. 883 */ 884 885static void 886PanoramiXResetProc(ExtensionEntry * extEntry) 887{ 888 int i; 889 890 PanoramiXRenderReset(); 891 PanoramiXFixesReset(); 892 PanoramiXDamageReset(); 893#ifdef COMPOSITE 894 PanoramiXCompositeReset (); 895#endif 896 screenInfo.numScreens = PanoramiXNumScreens; 897 for (i = 256; i--;) 898 ProcVector[i] = SavedProcVector[i]; 899} 900 901int 902ProcPanoramiXQueryVersion(ClientPtr client) 903{ 904 /* REQUEST(xPanoramiXQueryVersionReq); */ 905 xPanoramiXQueryVersionReply rep = { 906 .type = X_Reply, 907 .sequenceNumber = client->sequence, 908 .length = 0, 909 .majorVersion = SERVER_PANORAMIX_MAJOR_VERSION, 910 .minorVersion = SERVER_PANORAMIX_MINOR_VERSION 911 }; 912 913 REQUEST_SIZE_MATCH(xPanoramiXQueryVersionReq); 914 if (client->swapped) { 915 swaps(&rep.sequenceNumber); 916 swapl(&rep.length); 917 swaps(&rep.majorVersion); 918 swaps(&rep.minorVersion); 919 } 920 WriteToClient(client, sizeof(xPanoramiXQueryVersionReply), &rep); 921 return Success; 922} 923 924int 925ProcPanoramiXGetState(ClientPtr client) 926{ 927 REQUEST(xPanoramiXGetStateReq); 928 WindowPtr pWin; 929 xPanoramiXGetStateReply rep; 930 int rc; 931 932 REQUEST_SIZE_MATCH(xPanoramiXGetStateReq); 933 rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess); 934 if (rc != Success) 935 return rc; 936 937 rep = (xPanoramiXGetStateReply) { 938 .type = X_Reply, 939 .state = !noPanoramiXExtension, 940 .sequenceNumber = client->sequence, 941 .length = 0, 942 .window = stuff->window 943 }; 944 if (client->swapped) { 945 swaps(&rep.sequenceNumber); 946 swapl(&rep.length); 947 swapl(&rep.window); 948 } 949 WriteToClient(client, sizeof(xPanoramiXGetStateReply), &rep); 950 return Success; 951 952} 953 954int 955ProcPanoramiXGetScreenCount(ClientPtr client) 956{ 957 REQUEST(xPanoramiXGetScreenCountReq); 958 WindowPtr pWin; 959 xPanoramiXGetScreenCountReply rep; 960 int rc; 961 962 REQUEST_SIZE_MATCH(xPanoramiXGetScreenCountReq); 963 rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess); 964 if (rc != Success) 965 return rc; 966 967 rep = (xPanoramiXGetScreenCountReply) { 968 .type = X_Reply, 969 .ScreenCount = PanoramiXNumScreens, 970 .sequenceNumber = client->sequence, 971 .length = 0, 972 .window = stuff->window 973 }; 974 if (client->swapped) { 975 swaps(&rep.sequenceNumber); 976 swapl(&rep.length); 977 swapl(&rep.window); 978 } 979 WriteToClient(client, sizeof(xPanoramiXGetScreenCountReply), &rep); 980 return Success; 981} 982 983int 984ProcPanoramiXGetScreenSize(ClientPtr client) 985{ 986 REQUEST(xPanoramiXGetScreenSizeReq); 987 WindowPtr pWin; 988 xPanoramiXGetScreenSizeReply rep; 989 int rc; 990 991 REQUEST_SIZE_MATCH(xPanoramiXGetScreenSizeReq); 992 993 if (stuff->screen >= PanoramiXNumScreens) 994 return BadMatch; 995 996 rc = dixLookupWindow(&pWin, stuff->window, client, DixGetAttrAccess); 997 if (rc != Success) 998 return rc; 999 1000 rep = (xPanoramiXGetScreenSizeReply) { 1001 .type = X_Reply, 1002 .sequenceNumber = client->sequence, 1003 .length = 0, 1004 /* screen dimensions */ 1005 .width = screenInfo.screens[stuff->screen]->width, 1006 .height = screenInfo.screens[stuff->screen]->height, 1007 .window = stuff->window, 1008 .screen = stuff->screen 1009 }; 1010 if (client->swapped) { 1011 swaps(&rep.sequenceNumber); 1012 swapl(&rep.length); 1013 swapl(&rep.width); 1014 swapl(&rep.height); 1015 swapl(&rep.window); 1016 swapl(&rep.screen); 1017 } 1018 WriteToClient(client, sizeof(xPanoramiXGetScreenSizeReply), &rep); 1019 return Success; 1020} 1021 1022int 1023ProcXineramaIsActive(ClientPtr client) 1024{ 1025 /* REQUEST(xXineramaIsActiveReq); */ 1026 xXineramaIsActiveReply rep; 1027 1028 REQUEST_SIZE_MATCH(xXineramaIsActiveReq); 1029 1030 rep = (xXineramaIsActiveReply) { 1031 .type = X_Reply, 1032 .sequenceNumber = client->sequence, 1033 .length = 0, 1034#if 1 1035 /* The following hack fools clients into thinking that Xinerama 1036 * is disabled even though it is not. */ 1037 .state = !noPanoramiXExtension && !PanoramiXExtensionDisabledHack 1038#else 1039 .state = !noPanoramiXExtension; 1040#endif 1041 }; 1042 if (client->swapped) { 1043 swaps(&rep.sequenceNumber); 1044 swapl(&rep.length); 1045 swapl(&rep.state); 1046 } 1047 WriteToClient(client, sizeof(xXineramaIsActiveReply), &rep); 1048 return Success; 1049} 1050 1051int 1052ProcXineramaQueryScreens(ClientPtr client) 1053{ 1054 /* REQUEST(xXineramaQueryScreensReq); */ 1055 CARD32 number = (noPanoramiXExtension) ? 0 : PanoramiXNumScreens; 1056 xXineramaQueryScreensReply rep = { 1057 .type = X_Reply, 1058 .sequenceNumber = client->sequence, 1059 .length = bytes_to_int32(number * sz_XineramaScreenInfo), 1060 .number = number 1061 }; 1062 1063 REQUEST_SIZE_MATCH(xXineramaQueryScreensReq); 1064 1065 if (client->swapped) { 1066 swaps(&rep.sequenceNumber); 1067 swapl(&rep.length); 1068 swapl(&rep.number); 1069 } 1070 WriteToClient(client, sizeof(xXineramaQueryScreensReply), &rep); 1071 1072 if (!noPanoramiXExtension) { 1073 xXineramaScreenInfo scratch; 1074 int i; 1075 1076 FOR_NSCREENS(i) { 1077 scratch.x_org = screenInfo.screens[i]->x; 1078 scratch.y_org = screenInfo.screens[i]->y; 1079 scratch.width = screenInfo.screens[i]->width; 1080 scratch.height = screenInfo.screens[i]->height; 1081 1082 if (client->swapped) { 1083 swaps(&scratch.x_org); 1084 swaps(&scratch.y_org); 1085 swaps(&scratch.width); 1086 swaps(&scratch.height); 1087 } 1088 WriteToClient(client, sz_XineramaScreenInfo, &scratch); 1089 } 1090 } 1091 1092 return Success; 1093} 1094 1095static int 1096ProcPanoramiXDispatch(ClientPtr client) 1097{ 1098 REQUEST(xReq); 1099 switch (stuff->data) { 1100 case X_PanoramiXQueryVersion: 1101 return ProcPanoramiXQueryVersion(client); 1102 case X_PanoramiXGetState: 1103 return ProcPanoramiXGetState(client); 1104 case X_PanoramiXGetScreenCount: 1105 return ProcPanoramiXGetScreenCount(client); 1106 case X_PanoramiXGetScreenSize: 1107 return ProcPanoramiXGetScreenSize(client); 1108 case X_XineramaIsActive: 1109 return ProcXineramaIsActive(client); 1110 case X_XineramaQueryScreens: 1111 return ProcXineramaQueryScreens(client); 1112 } 1113 return BadRequest; 1114} 1115 1116#if X_BYTE_ORDER == X_LITTLE_ENDIAN 1117#define SHIFT_L(v,s) (v) << (s) 1118#define SHIFT_R(v,s) (v) >> (s) 1119#else 1120#define SHIFT_L(v,s) (v) >> (s) 1121#define SHIFT_R(v,s) (v) << (s) 1122#endif 1123 1124static void 1125CopyBits(char *dst, int shiftL, char *src, int bytes) 1126{ 1127 /* Just get it to work. Worry about speed later */ 1128 int shiftR = 8 - shiftL; 1129 1130 while (bytes--) { 1131 *dst |= SHIFT_L(*src, shiftL); 1132 *(dst + 1) |= SHIFT_R(*src, shiftR); 1133 dst++; 1134 src++; 1135 } 1136} 1137 1138/* Caution. This doesn't support 2 and 4 bpp formats. We expect 1139 1 bpp and planar data to be already cleared when presented 1140 to this function */ 1141 1142void 1143XineramaGetImageData(DrawablePtr *pDrawables, 1144 int left, 1145 int top, 1146 int width, 1147 int height, 1148 unsigned int format, 1149 unsigned long planemask, 1150 char *data, int pitch, Bool isRoot) 1151{ 1152 RegionRec SrcRegion, ScreenRegion, GrabRegion; 1153 BoxRec SrcBox, *pbox; 1154 int x, y, w, h, i, j, nbox, size, sizeNeeded, ScratchPitch, inOut, depth; 1155 DrawablePtr pDraw = pDrawables[0]; 1156 char *ScratchMem = NULL; 1157 1158 size = 0; 1159 1160 /* find box in logical screen space */ 1161 SrcBox.x1 = left; 1162 SrcBox.y1 = top; 1163 if (!isRoot) { 1164 SrcBox.x1 += pDraw->x + screenInfo.screens[0]->x; 1165 SrcBox.y1 += pDraw->y + screenInfo.screens[0]->y; 1166 } 1167 SrcBox.x2 = SrcBox.x1 + width; 1168 SrcBox.y2 = SrcBox.y1 + height; 1169 1170 RegionInit(&SrcRegion, &SrcBox, 1); 1171 RegionNull(&GrabRegion); 1172 1173 depth = (format == XYPixmap) ? 1 : pDraw->depth; 1174 1175 FOR_NSCREENS(i) { 1176 BoxRec TheBox; 1177 ScreenPtr pScreen; 1178 1179 pDraw = pDrawables[i]; 1180 pScreen = pDraw->pScreen; 1181 1182 TheBox.x1 = pScreen->x; 1183 TheBox.x2 = TheBox.x1 + pScreen->width; 1184 TheBox.y1 = pScreen->y; 1185 TheBox.y2 = TheBox.y1 + pScreen->height; 1186 1187 RegionInit(&ScreenRegion, &TheBox, 1); 1188 inOut = RegionContainsRect(&ScreenRegion, &SrcBox); 1189 if (inOut == rgnPART) 1190 RegionIntersect(&GrabRegion, &SrcRegion, &ScreenRegion); 1191 RegionUninit(&ScreenRegion); 1192 1193 if (inOut == rgnIN) { 1194 (*pScreen->GetImage) (pDraw, 1195 SrcBox.x1 - pDraw->x - 1196 screenInfo.screens[i]->x, 1197 SrcBox.y1 - pDraw->y - 1198 screenInfo.screens[i]->y, width, height, 1199 format, planemask, data); 1200 break; 1201 } 1202 else if (inOut == rgnOUT) 1203 continue; 1204 1205 nbox = RegionNumRects(&GrabRegion); 1206 1207 if (nbox) { 1208 pbox = RegionRects(&GrabRegion); 1209 1210 while (nbox--) { 1211 w = pbox->x2 - pbox->x1; 1212 h = pbox->y2 - pbox->y1; 1213 ScratchPitch = PixmapBytePad(w, depth); 1214 sizeNeeded = ScratchPitch * h; 1215 1216 if (sizeNeeded > size) { 1217 char *tmpdata = ScratchMem; 1218 1219 ScratchMem = realloc(ScratchMem, sizeNeeded); 1220 if (ScratchMem) 1221 size = sizeNeeded; 1222 else { 1223 ScratchMem = tmpdata; 1224 break; 1225 } 1226 } 1227 1228 x = pbox->x1 - pDraw->x - screenInfo.screens[i]->x; 1229 y = pbox->y1 - pDraw->y - screenInfo.screens[i]->y; 1230 1231 (*pScreen->GetImage) (pDraw, x, y, w, h, 1232 format, planemask, ScratchMem); 1233 1234 /* copy the memory over */ 1235 1236 if (depth == 1) { 1237 int k, shift, leftover, index, index2; 1238 1239 x = pbox->x1 - SrcBox.x1; 1240 y = pbox->y1 - SrcBox.y1; 1241 shift = x & 7; 1242 x >>= 3; 1243 leftover = w & 7; 1244 w >>= 3; 1245 1246 /* clean up the edge */ 1247 if (leftover) { 1248 int mask = (1 << leftover) - 1; 1249 1250 for (j = h, k = w; j--; k += ScratchPitch) 1251 ScratchMem[k] &= mask; 1252 } 1253 1254 for (j = 0, index = (pitch * y) + x, index2 = 0; j < h; 1255 j++, index += pitch, index2 += ScratchPitch) { 1256 if (w) { 1257 if (!shift) 1258 memcpy(data + index, ScratchMem + index2, w); 1259 else 1260 CopyBits(data + index, shift, 1261 ScratchMem + index2, w); 1262 } 1263 1264 if (leftover) { 1265 data[index + w] |= 1266 SHIFT_L(ScratchMem[index2 + w], shift); 1267 if ((shift + leftover) > 8) 1268 data[index + w + 1] |= 1269 SHIFT_R(ScratchMem[index2 + w], 1270 (8 - shift)); 1271 } 1272 } 1273 } 1274 else { 1275 j = BitsPerPixel(depth) >> 3; 1276 x = (pbox->x1 - SrcBox.x1) * j; 1277 y = pbox->y1 - SrcBox.y1; 1278 w *= j; 1279 1280 for (j = 0; j < h; j++) { 1281 memcpy(data + (pitch * (y + j)) + x, 1282 ScratchMem + (ScratchPitch * j), w); 1283 } 1284 } 1285 pbox++; 1286 } 1287 1288 RegionSubtract(&SrcRegion, &SrcRegion, &GrabRegion); 1289 if (!RegionNotEmpty(&SrcRegion)) 1290 break; 1291 } 1292 1293 } 1294 1295 free(ScratchMem); 1296 1297 RegionUninit(&SrcRegion); 1298 RegionUninit(&GrabRegion); 1299} 1300