Patch I'm using as workaround to postpone all sending data to client until all GetImage() invocations are complete. This way XDamage events arrive before GetImage reply header, rather than between GetImage reply header and image data.
diff -u xorg-server-1.20.4-pristine/dix/dispatch.c xorg-server-1.20.4/dix/dispatch.c --- xorg-server-1.20.4-pristine/dix/dispatch.c 2019-02-26 11:28:50.000000000 -0800 +++ xorg-server-1.20.4/dix/dispatch.c 2019-10-06 23:31:32.067804155 -0700 @@ -2077,9 +2077,10 @@ /* coordinates relative to the bounding drawable */ int relx, rely; - long widthBytesLine, length; + long widthBytesLine, length, responseLength; Mask plane = 0; char *pBuf; + char *iPtr; xGetImageReply xgi; RegionPtr pVisibleRegion = NULL; @@ -2155,21 +2156,19 @@ xgi.depth = pDraw->depth; if (format == ZPixmap) { widthBytesLine = PixmapBytePad(width, pDraw->depth); - length = widthBytesLine * height; + responseLength = widthBytesLine * height; } else { widthBytesLine = BitmapBytePad(width); plane = ((Mask) 1) << (pDraw->depth - 1); /* only planes asked for */ - length = widthBytesLine * height * + responseLength = widthBytesLine * height * Ones(planemask & (plane | (plane - 1))); } - xgi.length = length; - - xgi.length = bytes_to_int32(xgi.length); + xgi.length = bytes_to_int32(responseLength); if (widthBytesLine == 0 || height == 0) linesPerBuf = 0; else if (widthBytesLine >= IMAGE_BUFSIZE) @@ -2192,9 +2191,10 @@ length += widthBytesLine; } } - if (!(pBuf = calloc(1, length))) + if (!(pBuf = calloc(1, responseLength))) return BadAlloc; - WriteReplyToClient(client, sizeof(xGetImageReply), &xgi); + + iPtr = pBuf; if (pDraw->type == DRAWABLE_WINDOW) pVisibleRegion = &((WindowPtr) pDraw)->borderClip; @@ -2206,23 +2206,24 @@ linesDone = 0; while (height - linesDone > 0) { nlines = min(linesPerBuf, height - linesDone); + assert(responseLength >= iPtr - pBuf + nlines * widthBytesLine); (*pDraw->pScreen->GetImage) (pDraw, x, y + linesDone, width, nlines, - format, planemask, (void *) pBuf); + format, planemask, (void *) iPtr); if (pVisibleRegion) XaceCensorImage(client, pVisibleRegion, widthBytesLine, pDraw, x, y + linesDone, width, - nlines, format, pBuf); + nlines, format, iPtr); /* Note that this is NOT a call to WriteSwappedDataToClient, as we do NOT byte swap */ - ReformatImage(pBuf, (int) (nlines * widthBytesLine), + ReformatImage(iPtr, (int) (nlines * widthBytesLine), BitsPerPixel(pDraw->depth), ClientOrder(client)); - WriteToClient(client, (int) (nlines * widthBytesLine), pBuf); + iPtr += nlines * widthBytesLine; linesDone += nlines; } } @@ -2233,29 +2234,33 @@ linesDone = 0; while (height - linesDone > 0) { nlines = min(linesPerBuf, height - linesDone); + assert(responseLength >= iPtr - pBuf + nlines * widthBytesLine); (*pDraw->pScreen->GetImage) (pDraw, x, y + linesDone, width, nlines, - format, plane, (void *) pBuf); + format, plane, (void *) iPtr); if (pVisibleRegion) XaceCensorImage(client, pVisibleRegion, widthBytesLine, pDraw, x, y + linesDone, width, - nlines, format, pBuf); + nlines, format, iPtr); /* Note: NOT a call to WriteSwappedDataToClient, as we do NOT byte swap */ - ReformatImage(pBuf, (int) (nlines * widthBytesLine), + ReformatImage(iPtr, (int) (nlines * widthBytesLine), 1, ClientOrder(client)); - WriteToClient(client, (int)(nlines * widthBytesLine), pBuf); + iPtr += nlines * widthBytesLine; linesDone += nlines; } } } } + assert(iPtr - pBuf == responseLength); + WriteReplyToClient(client, sizeof(xGetImageReply), &xgi); + WriteToClient(client, iPtr - pBuf, pBuf); free(pBuf); return Success; }