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