[PATCH] Xext: XCopyArea does not work in Xinerama mode.#25113

Arvind Umrao arvind.umrao at oracle.com
Fri Oct 14 18:27:45 PDT 2011


Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=25113

XCopyArea() does not work in Xinerama mode. XCopyArea does not copy areas of the screen across physical displays. XcopyArea works fine when source and destination image are in same screen, but Xcopy does not work, when we Xcopy image from one screen to the other in Xinerama mode. The solution is to use internal Xcopy  instead of regular Xcopy. I mean use GetImage & PutImage Instead of regular Xcopy.

*1) Internal Xcopy*
In Internal Xcopy temporary buffer is allocated and Xineramadata is copied. GetImage reads the image(Xineramadata) from all intersection boxes and Putimage copies the image to the screen.

*2) Regular xcopy*
Regular xcopy calls the regular copyimage. Regular copyimage will be much faster when hardware acceleration is on.

Code changes are well commented. Code changes will execute only when xinerama is on and xcopy happens across screen. This bug was first reported for Xsun. Two years back, code changes was reviewed and intergrated to Oracle/Sun local Xserer repository.

*Testing*
a)I have tested it with Nvidia Quadro FX 1700 on x86 machine.You can find the test case in bugzilla. If you wish I can email some more test cases for stress testing.
b)I have tested my fixes using two XVR2500 frame buffer on SPARC Ultra 45 with Xinerama on. You can find the test case in bugzilla. If you wish I can email some more test cases, for stress testing.
c)Also I have tested my fixes on Sunray with Xinerama on

Signed-off-by: Arvind Umrao <arvind.umrao at oracle.com>
---
 Xext/panoramiXprocs.c |  215 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 215 insertions(+), 0 deletions(-)

diff --git a/Xext/panoramiXprocs.c b/Xext/panoramiXprocs.c
index 9ea4611..1b6bd0a 100644
--- a/Xext/panoramiXprocs.c
+++ b/Xext/panoramiXprocs.c
@@ -1074,6 +1074,221 @@ int PanoramiXCopyArea(ClientPtr client)
 	}
 
 	free(data);
+} else if (src->type == XRT_WINDOW) {
+        /*
+         * If destination image coordinate not lie in same screen of
+         * source image, then call Internal Copy instead of regular
+         * XCopy. I mean use GetImage & PutImage, instead of regular XCopy
+         */
+        DrawablePtr pDst = NULL, pSrc = NULL;
+        GCPtr pGC = NULL;
+        RegionPtr pRgn[MAXSCREENS];
+        int rc;
+        DrawablePtr drawables[MAXSCREENS];
+        char *data = NULL;
+        size_t data_size;
+        int pitch;
+        int cross_screen = 0, rsrcFlag = 0;
+        int rsrcx = 0, rsrcy = 0, rsrcx2 = stuff->width, rsrcy2 = stuff->height;
+
+        bzero(pRgn, sizeof (RegionPtr) * MAXSCREENS);
+
+
+        /*
+         * Execute only when Xinerama is on and having two or more screens.
+         * There are two cases when destination image will not lie in same sceen.
+         * a) Right and bottom coordinates of destination image crosses the
+         * other screen.
+         * b) Top and left coordinates of destination image cross the other
+         * screen.
+         */
+        if (PanoramiXNumScreens > 1)
+            FOR_NSCREENS_BACKWARD(j) {
+            rc = dixLookupDrawable(drawables + j, src->info[j].id, client, 0,
+                    DixGetAttrAccess);
+            if (rc != Success)
+                return rc;
+            if (!((drawables[j]->width + drawables[j]->x) < 0 ||
+                    (drawables[j]->height + drawables[j]->y) < 0 ||
+                    ((stuff->srcX + drawables[j]->x + stuff->width) < 0 &&
+                    (stuff->dstX + drawables[j]->x + stuff->width) < 0) ||
+                    ((stuff->srcY + drawables[j]->y + stuff->height) < 0 &&
+                    (stuff->dstY + drawables[j]->y + stuff->height) < 0) ||
+                    ((drawables[j]->x + stuff->srcX) > drawables[j]->pScreen->width &&
+                    (drawables[j]->x + stuff->dstX) > drawables[j]->pScreen->width) ||
+                    ((drawables[j]->y + stuff->srcY) > drawables[j]->pScreen->height &&
+                    (drawables[j]->y + stuff->dstY) > drawables[j]->pScreen->height))) {
+                if (!(stuff->srcX == stuff->dstX && (stuff->srcY + drawables[j]->y) > 0 &&
+                        (stuff->srcY + drawables[j]->y + stuff->height) < drawables[j]->pScreen->height) &&
+                        !(stuff->srcY == stuff->dstY && (stuff->srcX + drawables[j]->x) > 0 &&
+                        (stuff->srcX + drawables[j]->x + stuff->width) < drawables[j]->pScreen->width))
+                    cross_screen++;
+            }
+        }
+        /*
+         * cross_screen > 1, signifies that there are more than one screens and
+         * source and destination image are not in the same screen.
+         */
+
+        RegionRec overlap, imageReg;
+        BoxRec imageBox;
+
+        FOR_NSCREENS_BACKWARD(j) {
+            stuff->dstDrawable = dst->info[j].id;
+            stuff->srcDrawable = src->info[j].id;
+            stuff->gc = gc->info[j].id;
+            if (srcIsRoot) {
+                stuff->srcX = srcx - screenInfo.screens[j]->x;
+                stuff->srcY = srcy - screenInfo.screens[j]->y;
+            }
+            if (dstIsRoot) {
+                stuff->dstX = dstx - screenInfo.screens[j]->x;
+                stuff->dstY = dsty - screenInfo.screens[j]->y;
+            }
+
+            VALIDATE_DRAWABLE_AND_GC(stuff->dstDrawable, pDst, DixWriteAccess);
+
+            if (stuff->dstDrawable != stuff->srcDrawable) {
+                rc = dixLookupDrawable(&pSrc, stuff->srcDrawable, client, 0,
+                        DixReadAccess);
+                if (rc != Success)
+                    return rc;
+
+                if ((pDst->pScreen != pSrc->pScreen) ||
+                        (pDst->depth != pSrc->depth)) {
+                    client->errorValue = stuff->dstDrawable;
+                    return (BadMatch);
+                }
+            } else
+                pSrc = pDst;
+
+            pRgn[j] = (*pGC->ops->CopyArea)(pSrc, pDst, pGC,
+                    stuff->srcX, stuff->srcY,
+                    stuff->width, stuff->height,
+                    stuff->dstX, stuff->dstY);
+            /*
+             * If source and destination are not in same screen, instead of
+             * regular copy, use GetImage and PutImage
+             */
+
+            if (cross_screen > 1 && pRgn[j] && RegionNotEmpty(pRgn[j])) {
+                imageBox.x1 = stuff->srcX > 0 ? dstx : dstx - stuff->srcX;
+                imageBox.y1 = stuff->srcY > 0 ? dsty : dsty - stuff->srcY;
+                if (stuff->srcX + stuff->width > drawables[j]->width)
+                    imageBox.x2 = dstx - stuff->srcX + drawables[j]->width;
+                else
+                    imageBox.x2 = dstx + stuff->width;
+                if (stuff->srcY + stuff->height > drawables[j]->height)
+                    imageBox.y2 = dsty - stuff->srcY + drawables[j]->height;
+                else
+                    imageBox.y2 = dsty + stuff->height;
+
+
+                RegionInit(&imageReg, &imageBox, 1);
+                RegionInit(&overlap, NullBox, 1);
+                RegionIntersect(&overlap, &imageReg, pRgn[j]);
+
+                if (RegionNotEmpty(&overlap)) {
+                    int i;
+                    if (!rsrcFlag) {
+                        rsrcx = rsrcy = 100000;
+                        rsrcx2 = rsrcy2 = 0;
+                        rsrcFlag = 1;
+                    }
+                    /*
+                     * This calculates the area of overlaping source image.
+                     * There could be case when source image overlaps with two
+                     * or more screens of Xinerama. RegionNumRects will return
+                     * number of xinerama screens when source image overlaps and
+                     * returns one when there is no overlap.
+                     */
+
+                    for (i = 0; i < RegionNumRects(&overlap); i++) {
+                        BoxPtr rects = RegionRects(&overlap);
+                        if (rsrcx > (rects[i].x1 - dstx))
+                            rsrcx = rects[i].x1 - dstx;
+                        if (rsrcy > (rects[i].y1 - dsty))
+                            rsrcy = rects[i].y1 - dsty;
+                        if (rsrcx2 < (rects[i].x2 - dstx))
+                            rsrcx2 = rects[i].x2 - dstx;
+                        if (rsrcy2 < (rects[i].y2 - dsty))
+                            rsrcy2 = rects[i].y2 - dsty;
+                    }
+
+                    RegionUninit(&overlap);
+                    RegionUninit(&imageReg);
+                }
+            }
+        }
+        
+        if (cross_screen > 1) {
+            pitch = PixmapBytePad(stuff->width, drawables[0]->depth);
+            data_size = stuff->height * pitch;
+            if (!(data = xcalloc(1, data_size)))
+                return BadAlloc;
+
+            /* Data which is not copied by regular XCopy, for that temporary
+             * buffer is allocatted and Xinerama data is copied. GetImage reads
+             * the image(Xineramadata) from all the intersection boxes.
+             * When image overlaps between two or more screens, we can visualize
+             * the portion of image in intersection boxes.
+             */
+
+            XineramaGetImageData(drawables, srcx + rsrcx, srcy + rsrcy,
+                    rsrcx2 - rsrcx, rsrcy2 - rsrcy, ZPixmap,
+                    ~0, data, pitch, srcIsRoot);
+
+            FOR_NSCREENS_BACKWARD(j) {
+                stuff->dstDrawable = dst->info[j].id;
+                stuff->srcDrawable = src->info[j].id;
+                stuff->gc = gc->info[j].id;
+                if (srcIsRoot) {
+                    stuff->srcX = srcx - screenInfo.screens[j]->x;
+                    stuff->srcY = srcy - screenInfo.screens[j]->y;
+                }
+                if (dstIsRoot) {
+                    stuff->dstX = dstx - screenInfo.screens[j]->x;
+                    stuff->dstY = dsty - screenInfo.screens[j]->y;
+                }
+
+                VALIDATE_DRAWABLE_AND_GC(stuff->dstDrawable, pDst, DixWriteAccess);
+
+                if (stuff->dstDrawable != stuff->srcDrawable) {
+                    rc = dixLookupDrawable(&pSrc, stuff->srcDrawable, client, 0,
+                            DixReadAccess);
+                    if (rc != Success)
+                        return rc;
+
+                    if ((pDst->pScreen != pSrc->pScreen) ||
+                            (pDst->depth != pSrc->depth)) {
+                        client->errorValue = stuff->dstDrawable;
+                        return (BadMatch);
+                    }
+                } else
+                    pSrc = pDst;
+
+
+                if (pRgn[j] && REGION_NOTEMPTY(pDst->pScreen, pRgn[j])) {
+
+                    if (drawables[0]->depth != pDst->depth) {
+                        client->errorValue = stuff->dstDrawable;
+                        xfree(data);
+                        return (BadMatch);
+                    }
+
+                    /*
+                     * Copy the buffered Xinerama data to screen
+                     */
+
+                    (*pGC->ops->PutImage) (pDst, pGC, pDst->depth, dstx + rsrcx, dsty + rsrcy,
+                            rsrcx2 - rsrcx, rsrcy2 - rsrcy,
+                            0, ZPixmap, data);
+                }
+            }
+        }
+        if (data)
+            xfree(data);
+
     } else {
 	DrawablePtr pDst = NULL, pSrc = NULL;
 	GCPtr pGC = NULL;
-- 
1.7.3.2



More information about the xorg-devel mailing list