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