[PATCH] Xext: XCopyArea() does not work in Xinerama. Bug#:25113

Arvind Umrao arvind.umrao at sun.com
Mon Nov 16 03:19:44 PST 2009


XCopyArea() does not work in Xinerama mode. XCopyArea() does not copy areas of the screen between physical displays.

Description of the change:
This bug was first reported for Xsun. Two months back I have fixed Xsun,in Nevada. Xserver team in SUN reviewed my code changes.

Xcopy does not work, when we xcopy image from one screen to other in Xinerama mode. The solution to the problem is use internal xcopy  instead of regular xcopy. Lines of code I have added is to detect if copied image( destination coordinate) not lies in same  screen of source image, then call internal xcopy instead of regular xcopy.
*1) Internal Xcopy*
In Internal Xcopy  temporary buffer is allocated and Xineramadata is copied .  Size of temporary buffer is same as drawing object(stuff). GetImage reads the image(Xineramadata) from all intersection boxes. When image overlaps between two or more screens, we can visualize portion of image in intersection boxes.

*2) Regular xcopy*
*Regular xcopy calls the regular copyimage. Regular copyimage will be much faster, because it does not call  getimage or putimage.* 


My code changes are well commented.

*Testing* 
a)I have tested my fixes using two XVR2500 frame buffer on SPARC Ultra 45 with Xinerama on.
b)Also I have tested my fixes on Sunray with Xinerama on.


X.Org Bug 25113 <http://bugs.freedesktop.org/show_bug.cgi?id=25113>


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

diff --git a/Xext/panoramiXprocs.c b/Xext/panoramiXprocs.c
index 6834efb..7011356 100644
--- a/Xext/panoramiXprocs.c
+++ b/Xext/panoramiXprocs.c
@@ -59,6 +59,144 @@ Equipment Corporation.
  */
 extern XID clientErrorValue;   /* XXX this is a kludge */
 
+/*
+ * calculate the overlap area of image when image overlaps two screens of Xinerama
+ *
+ * getRgn is used by PanoramiXCopyArea
+ *
+ */
+
+RegionPtr getRgn(DrawablePtr pSrcDrawable,DrawablePtr pDstDrawable,
+                  GCPtr pGC,int srcx,int srcy,int width,int height,int dstx,int dsty)
+{
+    register ScreenPtr pscr = pGC->pScreen;
+    RegionPtr prgnSrcClip;      /* drawable-relative source clip */
+    RegionRec rgnSrcRec;
+    RegionPtr prgnDstClip;      /* drawable-relative dest clip */
+    RegionRec rgnDstRec;
+    BoxRec srcBox;              /* unclipped source */
+    RegionRec rgnExposed;       /* exposed region, calculated source-
+                                   relative, made dst relative to
+                                   intersect with visible parts of
+                                   dest and send events to client,
+                                   and then screen relative to paint
+                                   the window background
+                                */
+    WindowPtr pSrcWin;
+    BoxRec expBox;
+    Bool extents;
+
+    /* avoid work if we can */
+    if (!pGC->graphicsExposures &&
+        (pDstDrawable->type == DRAWABLE_PIXMAP) &&
+        ((pSrcDrawable->type == DRAWABLE_PIXMAP) ||
+         (((WindowPtr)pSrcDrawable)->backStorage == NULL)))
+        return NULL;
+
+    srcBox.x1 = srcx;
+    srcBox.y1 = srcy;
+    srcBox.x2 = srcx+width;
+    srcBox.y2 = srcy+height;
+
+    if (pSrcDrawable->type != DRAWABLE_PIXMAP)
+    {
+        BoxRec TsrcBox;
+
+        TsrcBox.x1 = srcx + pSrcDrawable->x;
+        TsrcBox.y1 = srcy + pSrcDrawable->y;
+        TsrcBox.x2 = TsrcBox.x1 + width;
+        TsrcBox.y2 = TsrcBox.y1 + height;
+        pSrcWin = (WindowPtr) pSrcDrawable;
+        if (pGC->subWindowMode == IncludeInferiors)
+        {
+            prgnSrcClip = NotClippedByChildren (pSrcWin);
+            if ((RECT_IN_REGION(pscr, prgnSrcClip, &TsrcBox)) == rgnIN)
+            {
+                REGION_DESTROY(pscr, prgnSrcClip);
+                return NULL;
+            }
+        }
+        else
+        {
+            if ((RECT_IN_REGION(pscr, &pSrcWin->clipList, &TsrcBox)) == rgnIN)
+                return NULL;
+            prgnSrcClip = &rgnSrcRec;
+            REGION_INIT(pscr, prgnSrcClip, NullBox, 0);
+            REGION_COPY(pscr, prgnSrcClip, &pSrcWin->clipList);
+        }
+        REGION_TRANSLATE(pscr, prgnSrcClip,
+                                -pSrcDrawable->x, -pSrcDrawable->y);
+    }
+    else
+    {
+        BoxRec  box;
+
+        if ((srcBox.x1 >= 0) && (srcBox.y1 >= 0) &&
+            (srcBox.x2 <= pSrcDrawable->width) &&
+            (srcBox.y2 <= pSrcDrawable->height))
+            return NULL;
+
+        box.x1 = 0;
+        box.y1 = 0;
+        box.x2 = pSrcDrawable->width;
+        box.y2 = pSrcDrawable->height;
+        prgnSrcClip = &rgnSrcRec;
+        REGION_INIT(pscr, prgnSrcClip, &box, 1);
+        pSrcWin = (WindowPtr)NULL;
+    }
+
+    if (pDstDrawable == pSrcDrawable)
+    {
+        prgnDstClip = prgnSrcClip;
+    }
+    else if (pDstDrawable->type != DRAWABLE_PIXMAP)
+    {
+        if (pGC->subWindowMode == IncludeInferiors)
+        {
+            prgnDstClip = NotClippedByChildren((WindowPtr)pDstDrawable);
+        }
+        else
+        {
+            prgnDstClip = &rgnDstRec;
+            REGION_INIT(pscr, prgnDstClip, NullBox, 0);
+            REGION_COPY(pscr, prgnDstClip,
+                                &((WindowPtr)pDstDrawable)->clipList);
+        }
+        REGION_TRANSLATE(pscr, prgnDstClip,
+                                 -pDstDrawable->x, -pDstDrawable->y);
+    }
+    else
+    {
+        BoxRec  box;
+
+        box.x1 = 0;
+        box.y1 = 0;
+        box.x2 = pDstDrawable->width;
+        box.y2 = pDstDrawable->height;
+        prgnDstClip = &rgnDstRec;
+        REGION_INIT(pscr, prgnDstClip, &box, 1);
+    }
+
+    /* drawable-relative source region */
+    REGION_INIT(pscr, &rgnExposed, &srcBox, 1);
+
+    /* now get the hidden parts of the source box*/
+    REGION_SUBTRACT(pscr, &rgnExposed, &rgnExposed, prgnSrcClip);
+    /* move them over the destination */
+    REGION_TRANSLATE(pscr, &rgnExposed, dstx-srcx, dsty-srcy);
+
+    /* intersect with visible areas of dest */
+    REGION_INTERSECT(pscr, &rgnExposed, &rgnExposed, prgnDstClip);
+
+    {
+        /* don't look */
+        RegionPtr exposed = REGION_CREATE(pscr, NullBox, 0);
+        *exposed = rgnExposed;
+        return exposed;
+    }
+}
+
+
 int PanoramiXCreateWindow(ClientPtr client)
 {
     PanoramiXRes *parent, *newWin;
@@ -1090,6 +1228,222 @@ int PanoramiXCopyArea(ClientPtr client)
 	xfree(data);
 
 	result = Success;
+    } else if (src->type == XRT_WINDOW){
+
+        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);
+
+	/*
+         * Lines of code I have added is to detect if copied image( destination coordinate)
+         * not lies in same  screen of source image, then call internal xcopy instead of regular xcopy.
+         * There are two cases only when destination image will not lie in same sceen.
+         * a) right and bottom coordinates of copied image( destination coordinate) crosses the other screen.
+         * b) top and left coordinates of copied image( destination coordinate)  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++;
+                }
+        }
+
+	/* if not cross screens, don't even bother calculatnge the overlap */
+	if(cross_screen > 1) {
+          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 - panoramiXdataPtr[j].x;
+                stuff->srcY = srcy - panoramiXdataPtr[j].y;
+            }
+            if (dstIsRoot) {
+                stuff->dstX = dstx - panoramiXdataPtr[j].x;
+                stuff->dstY = dsty - panoramiXdataPtr[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;
+
+           /*  calculate the overlap area first and get the
+	   image of the overlap area only */
+            pRgn[j] = getRgn(pSrc, pDst, pGC,
+                                stuff->srcX, stuff->srcY,
+                                stuff->width, stuff->height,
+                                stuff->dstX, stuff->dstY);
+            if(pRgn[j] && REGION_NOTEMPTY(pDst->pScreen, pRgn[j])) {
+                imageBox.x1 = dstx;
+                imageBox.y1 = dsty;
+                imageBox.x2 = dstx + stuff->width;
+                imageBox.y2 = dsty + stuff->height;
+
+                REGION_INIT(pDst->pScreen, &imageReg, &imageBox, 1);
+                REGION_INIT(pDst->pScreen, &overlap, NullBox, 1);
+                REGION_INTERSECT(pDst->pScreen, &overlap, &imageReg, pRgn[j]);
+
+                if (REGION_NOTEMPTY(pDst->pScreen, &overlap)) {
+		    int i;
+		    if(!rsrcFlag) {
+			rsrcx = rsrcy = 100000;
+			rsrcx2 = rsrcy2 = 0;
+			rsrcFlag = 1;
+		    }
+		    for (i = 0; i < REGION_NUM_RECTS(&overlap); i++) {
+                        BoxPtr rects = REGION_RECTS(&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;
+                    }
+
+                    REGION_UNINIT(pDst->pScreen, &overlap);
+                    REGION_UNINIT(pDst->pScreen, &imageReg);
+              }
+            }
+	  }
+	  pitch = PixmapBytePad(stuff->width, drawables[0]->depth);
+	  data_size = stuff->height * pitch;
+	  if(!(data =  xcalloc(1,data_size)))
+            return BadAlloc;
+
+        /*  In Internal Xcopy  temporary buffer is allocattated and Xineramadata is copied .
+         *  Size of temporary buffer is same as drawing object(stuff).
+         *  GetImage reads the image(Xineramadata) from all intersection boxes.
+         *  When image overlaps between two or more screens, we can visualize 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 - panoramiXdataPtr[j].x;
+		stuff->srcY = srcy - panoramiXdataPtr[j].y;
+	    }
+            if (dstIsRoot){
+		stuff->dstX = dstx - panoramiXdataPtr[j].x;
+		stuff->dstY = dsty - panoramiXdataPtr[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 (cross_screen > 1 && pRgn[j] && REGION_NOTEMPTY(pDst->pScreen, pRgn[j])) {
+		RegionRec overlap, imageReg;
+		BoxRec imageBox;
+
+		if(drawables[0]->depth != pDst->depth) {
+		    client->errorValue = stuff->dstDrawable;
+                    xfree(data );
+		    return (BadMatch);
+		}
+
+		imageBox.x1 = dstx;
+		imageBox.y1 = dsty;
+		imageBox.x2 = dstx + stuff->width;
+		imageBox.y2 = dsty + stuff->height;
+
+		REGION_INIT(pDst->pScreen, &imageReg, &imageBox, 1);
+		REGION_INIT(pDst->pScreen, &overlap, NullBox, 1);
+		REGION_INTERSECT(pDst->pScreen, &overlap, &imageReg, pRgn[j]);
+
+		if (REGION_NOTEMPTY(pDst->pScreen, &overlap)) {
+		    int i;
+		    PixmapPtr pData;
+
+                    pData = GetScratchPixmapHeader(pDst->pScreen, rsrcx2-rsrcx,
+                    rsrcy2-rsrcy, pDst->depth, BitsPerPixel(pDst->depth),
+                    PixmapBytePad(rsrcx2-rsrcx, pDst->depth), data);
+
+		    for (i = 0; i < REGION_NUM_RECTS(&overlap); i++) {
+			BoxPtr rects = REGION_RECTS(&overlap);
+			(*pGC->ops->CopyArea)((DrawablePtr) pData, pDst, pGC,
+			  rects[i].x1 - dstx - rsrcx,
+			  rects[i].y1 - dsty - rsrcy,
+			  rects[i].x2 - rects[i].x1,
+			  rects[i].y2 - rects[i].y1,
+			  rects[i].x1, rects[i].y1);
+		    }
+		}
+		REGION_UNINIT(pDst->pScreen, &overlap);
+		REGION_UNINIT(pDst->pScreen, &imageReg);
+	    }
+	}
+
+	if(cross_screen > 1)
+            xfree(data);
+
+	result = Success;
     } else {
 	DrawablePtr pDst = NULL, pSrc = NULL;
 	GCPtr pGC = NULL;
-- 
1.5.6.5


--Boundary_(ID_dUFSN6FAxfdUPmdsTSKmjw)--


More information about the xorg-devel mailing list