static Bool rfbSendCopyRegion(rfbClientPtr cl, RegionPtr reg, int dx, int dy)
{
  int nrects, nrectsInBand, x_inc, y_inc, thisRect, firstInNextBand;
  int x, y, w, h;
  rfbFramebufferUpdateRectHeader rect;
  rfbCopyRect cr;
  ScreenPtr pScreen = screenInfo.screens[0];

  nrects = REGION_NUM_RECTS(reg);

  if (dx <= 0)
    x_inc = 1;
  else
    x_inc = -1;

  if (dy <= 0) {
    thisRect = 0;
    y_inc = 1;
  } else {
    thisRect = nrects - 1;
    y_inc = -1;
  }

  /* If the source region intersects the lossy region, then we know that the
     destination region is about to become lossy, so we add it to the lossy
     region. */
  if (rfbAutoLosslessRefresh > 0.0 && alrCopyRect &&
      REGION_NOTEMPTY(pScreen, reg)) {
    RegionRec tmpRegion;
    REGION_INIT(pScreen, &tmpRegion, NullBox, 0);
    REGION_COPY(pScreen, &tmpRegion, reg);
    REGION_TRANSLATE(pScreen, &tmpRegion, -dx, -dy);
    REGION_INTERSECT(pScreen, &tmpRegion, &cl->lossyRegion, &tmpRegion);
    if (REGION_NOTEMPTY(pScreen, &tmpRegion)) {
      REGION_UNION(pScreen, &cl->lossyRegion, &cl->lossyRegion, reg);
      REGION_UNION(pScreen, &cl->alrEligibleRegion, &cl->alrEligibleRegion,
                   reg);
    }
    REGION_UNINIT(pScreen, &tmpRegion);
  }

  if (reg->extents.x2 > pScreen->width || reg->extents.y2 > pScreen->height)
    rfbLog("WARNING: CopyRect dest at %d,%d with dimensions %dx%d exceeds screen boundaries\n",
           reg->extents.x1, reg->extents.y1,
           reg->extents.x2 - reg->extents.x1,
           reg->extents.y2 - reg->extents.y1);

  while (nrects > 0) {

    firstInNextBand = thisRect;
    nrectsInBand = 0;

    while ((nrects > 0) &&
           (REGION_RECTS(reg)[firstInNextBand].y1 ==
            REGION_RECTS(reg)[thisRect].y1)) {
      firstInNextBand += y_inc;
      nrects--;
      nrectsInBand++;
    }

    if (x_inc != y_inc)
      thisRect = firstInNextBand - y_inc;

    while (nrectsInBand > 0) {
      if ((ublen + sz_rfbFramebufferUpdateRectHeader + sz_rfbCopyRect) >
          UPDATE_BUF_SIZE) {
        if (!rfbSendUpdateBuf(cl))
          return FALSE;
      }

      x = REGION_RECTS(reg)[thisRect].x1;
      y = REGION_RECTS(reg)[thisRect].y1;
      w = REGION_RECTS(reg)[thisRect].x2 - x;
      h = REGION_RECTS(reg)[thisRect].y2 - y;

      if (cl->compareFB) {
        int pitch = rfbFB.paddedWidthInBytes;
        int ps = rfbServerFormat.bitsPerPixel / 8, rows = h;
        char *src = &rfbFB.pfbMemory[y * pitch + x * ps];
        char *dst = &cl->compareFB[y * pitch + x * ps];
        while (rows--) {
          memcpy(dst, src, w * ps);
          src += pitch;
          dst += pitch;
        }
        src = &rfbFB.pfbMemory[(y - dy) * pitch + (x - dx) * ps];
        dst = &cl->compareFB[(y - dy) * pitch + (x - dx) * ps];
        rows = h;
        while (rows--) {
          memcpy(dst, src, w * ps);
          src += pitch;
          dst += pitch;
        }
      }

      rect.r.x = Swap16IfLE(x);
      rect.r.y = Swap16IfLE(y);
      rect.r.w = Swap16IfLE(w);
      rect.r.h = Swap16IfLE(h);
      rect.encoding = Swap32IfLE(rfbEncodingCopyRect);

      memcpy(&updateBuf[ublen], (char *)&rect,
             sz_rfbFramebufferUpdateRectHeader);
      ublen += sz_rfbFramebufferUpdateRectHeader;

      cr.srcX = Swap16IfLE(x - dx);
      cr.srcY = Swap16IfLE(y - dy);

      memcpy(&updateBuf[ublen], (char *)&cr, sz_rfbCopyRect);
      ublen += sz_rfbCopyRect;

      cl->rfbRectanglesSent[rfbEncodingCopyRect]++;
      cl->rfbBytesSent[rfbEncodingCopyRect] +=
        sz_rfbFramebufferUpdateRectHeader + sz_rfbCopyRect;

      thisRect += x_inc;
      nrectsInBand--;
    }

    thisRect = firstInNextBand;
  }

  return TRUE;
}