[PATCH] Xext: XCopyArea does not work in Xinerama mode.#25113
Dave Airlie
airlied at gmail.com
Tue Nov 1 13:39:17 PDT 2011
On Sat, Oct 15, 2011 at 2:27 AM, Arvind Umrao <arvind.umrao at oracle.com> wrote:
> 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.
>
> Signed-off-by: Arvind Umrao <arvind.umrao at oracle.com>
Some slight review below: fix those and I think it can have my
Reviewed-by tag added.
> }
>
> 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);
memset instead of bzero please.
> +
> +
> + /*
> + * 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 deserves a decent comment ? what is 100,000 meant to signify?
> + }
> + /*
> + * This calculates the area of overlaping source image.
^ overlapping.
> + * 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;
just use calloc now.
> +
> + /* 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
>
> _______________________________________________
> xorg-devel at lists.x.org: X.Org development
> Archives: http://lists.x.org/archives/xorg-devel
> Info: http://lists.x.org/mailman/listinfo/xorg-devel
>
More information about the xorg-devel
mailing list