/**************************************************************************/
/*                                                                        */
/* Copyright (c) 2001, 2011 NoMachine (http://www.nomachine.com)          */
/* Copyright (c) 2008-2014 Oleksandr Shneyder <o.shneyder@phoca-gmbh.de>  */
/* Copyright (c) 2011-2016 Mike Gabriel <mike.gabriel@das-netzwerkteam.de>*/
/* Copyright (c) 2014-2016 Mihai Moldovan <ionic@ionic.de>                */
/* Copyright (c) 2014-2016 Ulrich Sibiller <uli42@gmx.de>                 */
/* Copyright (c) 2015-2016 Qindel Group (http://www.qindel.com)           */
/*                                                                        */
/* NXAGENT, NX protocol compression and NX extensions to this software    */
/* are copyright of the aforementioned persons and companies.             */
/*                                                                        */
/* Redistribution and use of the present software is allowed according    */
/* to terms specified in the file LICENSE which comes in the source       */
/* distribution.                                                          */
/*                                                                        */
/* All rights reserved.                                                   */
/*                                                                        */
/* NOTE: This software has received contributions from various other      */
/* contributors, only the core maintainers and supporters are listed as   */
/* copyright holders. Please contact us, if you feel you should be listed */
/* as copyright holder, as well.                                          */
/*                                                                        */
/**************************************************************************/

/*

Copyright 1993 by Davor Matic

Permission to use, copy, modify, distribute, and sell this software
and its documentation for any purpose is hereby granted without fee,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation.  Davor Matic makes no representations about
the suitability of this software for any purpose.  It is provided "as
is" without express or implied warranty.

*/

#include "scrnintstr.h"
#include "resource.h"
#include "dixstruct.h"
#include "../../fb/fb.h"

#include "Agent.h"
#include "Composite.h"
#include "Display.h"
#include "Visual.h"
#include "Drawable.h"
#include "Pixmaps.h"
#include "GCs.h"
#include "Image.h"
#include "Font.h"
#include "Events.h"
#include "Client.h"
#include "Trap.h"
#include "Args.h"
#include "Screen.h"
#include "Utils.h"

#include "compext/Compext.h"

/*
 * Set here the required log level.
 */

#define PANIC
#define WARNING
#undef  TEST
#undef  DEBUG
#undef  DUMP

/*
 * Temporarily set/reset the trap.
 */

static int nxagentSaveGCTrap;

#define SET_GC_TRAP() \
{ \
  nxagentSaveGCTrap = nxagentGCTrap;\
\
  nxagentGCTrap = True; \
}

#define RESET_GC_TRAP() \
{ \
  nxagentGCTrap = nxagentSaveGCTrap; \
}
 
/*
 * This is currently unused.
 */

RegionPtr nxagentBitBlitHelper(GC *pGC);

/*
 * The NX agent implementation of the X server's graphics functions.
 */

void nxagentFillSpans(DrawablePtr pDrawable, GCPtr pGC, int nSpans,
                          xPoint *pPoints, int *pWidths, int fSorted)
{
  if ((pDrawable)->type == DRAWABLE_PIXMAP)
  {
    #ifdef TEST
    fprintf(stderr, "GCOps: GC [%p] going to FillSpans on FB pixmap [%p].\n",
                (void *) pGC, (void *) nxagentVirtualDrawable(pDrawable));
    #endif

    fbFillSpans(nxagentVirtualDrawable(pDrawable), pGC, nSpans, pPoints, pWidths, fSorted);
  }
  else
  {
    fbFillSpans(pDrawable, pGC, nSpans, pPoints, pWidths, fSorted);
  }
}

void nxagentSetSpans(DrawablePtr pDrawable, GCPtr pGC, char *pSrc,
                         xPoint *pPoints, int *pWidths, int nSpans, int fSorted)
{
  if ((pDrawable)->type == DRAWABLE_PIXMAP)
  {
    #ifdef TEST
    fprintf(stderr, "GCOps: GC [%p] going to SetSpans on FB pixmap [%p].\n",
                (void *) pGC, (void *) nxagentVirtualDrawable(pDrawable));
    #endif

    fbSetSpans(nxagentVirtualDrawable(pDrawable), pGC, pSrc, pPoints, pWidths, nSpans, fSorted);
  }
  else
  {
    fbSetSpans(pDrawable, pGC, pSrc, pPoints, pWidths, nSpans, fSorted);
  } 
}

void nxagentGetSpans(DrawablePtr pDrawable, int maxWidth, xPoint *pPoints,
                         int *pWidths, int nSpans, char *pBuffer)
{
  if ((pDrawable)->type == DRAWABLE_PIXMAP)
  {
    #ifdef TEST
    fprintf(stderr, "GCOps: going to GetSpans on FB pixmap [%p].\n",
                (void *) nxagentVirtualDrawable(pDrawable));
    #endif

    fbGetSpans(nxagentVirtualDrawable(pDrawable), maxWidth, pPoints, pWidths, nSpans, pBuffer);
  }
  else
  {
    fbGetSpans(pDrawable, maxWidth, pPoints, pWidths, nSpans, pBuffer);
  }
}

void nxagentQueryBestSize(int class, unsigned short *pwidth,
                              unsigned short *pheight, ScreenPtr pScreen)
{
  unsigned width, test;

  switch(class)
  {
    case CursorShape:
      if (*pwidth > pScreen->width)
        *pwidth = pScreen->width;
      if (*pheight > pScreen->height)
        *pheight = pScreen->height;
      break;
    case TileShape:
    case StippleShape:
      width = *pwidth;
      if (!width) break;
      /* Return the closest power of two not less than what they gave me */
      test = 0x80000000;
      /* Find the highest 1 bit in the width given */
      while(!(test & width))
         test >>= 1;
      /* If their number is greater than that, bump up to the next
       *  power of two */
      if((test - 1) & width)
         test <<= 1;
      *pwidth = test;
      /* We don't care what height they use */
      break;
  }
}

RegionPtr nxagentBitBlitHelper(GC *pGC)
{
  #ifdef TEST
  fprintf(stderr, "nxagentBitBlitHelper: Called for GC at [%p].\n", (void *) pGC);
  #endif

  /*
   * Force NullRegion. We consider enough the graphics expose events
   * generated internally by the nxagent server.
   */

  #ifdef TEST
  fprintf(stderr, "nxagentBitBlitHelper: WARNING! Skipping check on exposures events.\n");
  #endif

  return NullRegion;
}

/*
 * The deferring of X_RenderCompositeTrapezoids caused an ugly effect
 * on pulldown menu: as the background may be not synchronized, the
 * text floats in an invisible window. To avoid such effects, we use a
 * system to guess if the destination target of a copy area is a
 * popup, by assuming that those kind of windows use the override
 * redirect property.
 */

Bool nxagentWindowIsPopup(DrawablePtr pDrawable)
{
  if (pDrawable -> type != DRAWABLE_WINDOW)
  {
    return 0;
  }

  Bool windowIsPopup = False;

  if (((WindowPtr) pDrawable) -> overrideRedirect == 1)
  {
    windowIsPopup = True;
  }
  else
  {
    WindowPtr parent = ((WindowPtr) pDrawable) -> parent;

    /*
     * Go up on the tree until a parent exists or 4 windows has been
     * checked. This seems a good limit to up children's popup.
     */

    int level = 0;

    while (parent != NULL && ++level <= 4)
    {
      #ifdef DEBUG
      fprintf(stderr, "nxagentWindowIsPopup: Window [%p] has parent [%p] in tree with OverrideRedirect [%d] "
                  " Level [%d].\n", (void *) pDrawable, (void *) parent, parent -> overrideRedirect, level);
      #endif

      if (parent -> overrideRedirect == 1)
      {
        windowIsPopup = True;
        break;
      }

      parent = parent -> parent;
    }
  }

  #ifdef TEST
  fprintf(stderr, "nxagentWindowIsPopup: Window [%p] %s to be a popup.\n", (void *) pDrawable,
              windowIsPopup ? "seems" : "does not seem");
  #endif

  return windowIsPopup;
}

/*
 * This function returns True if the XCopyArea request must be skipped.
 */

Bool nxagentDeferCopyArea(DrawablePtr pSrcDrawable, DrawablePtr pDstDrawable,
                            GCPtr pGC, int srcx, int srcy, int width,
                                int height, int dstx, int dsty)
{
  /*
   * If the destination drawable is a popup window, we try to
   * synchronize the source drawable to show a nice menu. Anyway if
   * this synchronization breaks, the copy area is handled in the
   * normal way.
   */

/*
FIXME: The popup could be synchronized with one single put image,
       clipped to the corrupted region. As an intermediate step, the
       pixmap to synchronize could be copied on a cleared scratch
       pixmap, in order to have a solid color in the clipped regions.
*/

  if (nxagentOption(DeferLevel) >= 2 &&
          pSrcDrawable -> type == DRAWABLE_PIXMAP &&
              nxagentPixmapContainTrapezoids((PixmapPtr) pSrcDrawable) == 1 &&
                  nxagentWindowIsPopup(pDstDrawable))
  {
    RegionPtr pSrcRegion = nxagentCreateRegion(pSrcDrawable, NULL, srcx, srcy, width, height);

    #ifdef DEBUG
    fprintf(stderr, "nxagentDeferCopyArea: Copying to a popup menu. Source region [%d,%d,%d,%d].\n",
                pSrcRegion -> extents.x1, pSrcRegion -> extents.y1,
                    pSrcRegion -> extents.x2, pSrcRegion -> extents.y2);
    #endif

    RegionRec corruptedRegion;
    RegionInit(&corruptedRegion, NullBox, 1);

    RegionIntersect(&corruptedRegion,
                         pSrcRegion, nxagentCorruptedRegion(pSrcDrawable));

    if (!RegionNil(&corruptedRegion))
    {
      #ifdef TEST
      fprintf(stderr, "nxagentDeferCopyArea: Forcing the synchronization of source drawable at [%p].\n",
                  (void *) pSrcDrawable);
      #endif

      nxagentSynchronizeRegion(pSrcDrawable, &corruptedRegion, EVENT_BREAK, NULL);
    }

    RegionUninit(&corruptedRegion);

    nxagentFreeRegion(pSrcRegion);

    if (nxagentDrawableStatus(pSrcDrawable) == Synchronized)
    {
      return False;
    }
  }

  /*
   * We are going to decide if the source drawable must be
   * synchronized before using it, or if the copy will be clipped to
   * the synchronized source region.
   */

  if ((pDstDrawable -> type == DRAWABLE_PIXMAP &&
          nxagentOption(DeferLevel) > 0) || nxagentOption(DeferLevel) >= 3)
  {
    RegionPtr pClipRegion = nxagentCreateRegion(pSrcDrawable, NULL, srcx, srcy,
                                                    width, height);

    /*
     * We called this variable pCorruptedRegion because in the worst
     * case the corrupted region will be equal to the destination
     * region. The GC's clip mask is used to narrow the destination.
     */

    RegionPtr pCorruptedRegion = nxagentCreateRegion(pDstDrawable, pGC, dstx, dsty,
                                                         width, height);

    #ifdef DEBUG
    fprintf(stderr, "nxagentDeferCopyArea: Copy area source region is [%d,%d,%d,%d].\n",
                pClipRegion -> extents.x1, pClipRegion -> extents.y1,
                    pClipRegion -> extents.x2, pClipRegion -> extents.y2);
    #endif

    RegionSubtract(pClipRegion, pClipRegion, nxagentCorruptedRegion(pSrcDrawable));

    #ifdef DEBUG
    fprintf(stderr, "nxagentDeferCopyArea: Usable copy area source region is [%d,%d,%d,%d].\n",
                pClipRegion -> extents.x1, pClipRegion -> extents.y1,
                    pClipRegion -> extents.x2, pClipRegion -> extents.y2);
    #endif

    if (pGC -> clientClip == NULL || pGC -> clientClipType != CT_REGION)
    {
      #ifdef WARNING

      if (pGC -> clientClipType != CT_NONE)
      {
        fprintf(stderr, "nxagentDeferCopyArea: WARNING! pGC [%p] has a clip type [%d].\n",
                    (void *) pGC, pGC -> clientClipType);
      }

      #endif

      RegionTranslate(pClipRegion, dstx - srcx, dsty - srcy);
    }
    else
    {
      RegionRec tmpRegion;
      RegionInit(&tmpRegion, NullBox, 1);

      #ifdef DEBUG
      fprintf(stderr, "nxagentDeferCopyArea: Going to modify the original GC [%p] with clip mask "
                  "[%d,%d,%d,%d] and origin [%d,%d].\n",
                      (void *) pGC,
                      ((RegionPtr) pGC -> clientClip) -> extents.x1, ((RegionPtr) pGC -> clientClip) -> extents.y1,
                      ((RegionPtr) pGC -> clientClip) -> extents.x2, ((RegionPtr) pGC -> clientClip) -> extents.y2,
                      pGC -> clipOrg.x, pGC -> clipOrg.y);
      #endif

      RegionCopy(&tmpRegion, (RegionPtr) pGC -> clientClip);

      if (pGC -> clipOrg.x != 0 || pGC -> clipOrg.y != 0)
      {
        RegionTranslate(&tmpRegion, pGC -> clipOrg.x, pGC -> clipOrg.y);
      }

      RegionTranslate(pClipRegion, dstx - srcx, dsty - srcy);

      RegionIntersect(pClipRegion, &tmpRegion, pClipRegion);

      RegionUninit(&tmpRegion);
    }

    /*
     * The corrupted region on the destination drawable is composed by
     * the areas of the destination that we are not going to copy.
     */

    RegionSubtract(pCorruptedRegion, pCorruptedRegion, pClipRegion);

    #ifdef DEBUG
    fprintf(stderr, "nxagentDeferCopyArea: Recomputed clip region is [%d,%d,%d,%d][%d].\n",
                pClipRegion -> extents.x1, pClipRegion -> extents.y1,
                    pClipRegion -> extents.x2, pClipRegion -> extents.y2,
                        RegionNumRects(pClipRegion));

    fprintf(stderr, "nxagentDeferCopyArea: Inherited corrupted region is [%d,%d,%d,%d][%d].\n",
                pCorruptedRegion -> extents.x1, pCorruptedRegion -> extents.y1,
                    pCorruptedRegion -> extents.x2, pCorruptedRegion -> extents.y2,
                        RegionNumRects(pCorruptedRegion));
    #endif

    /*
     * The destination drawable inherits both the synchronized and the
     * corrupted region.
     */

    if (!RegionNil(pClipRegion))
    {
      nxagentUnmarkCorruptedRegion(pDstDrawable, pClipRegion);
    }

    if (!RegionNil(pCorruptedRegion))
    {
      nxagentMarkCorruptedRegion(pDstDrawable, pCorruptedRegion);
    }

    if (!RegionNil(pClipRegion))
    {
      Bool pClipRegionFree = True;

      /*
       * As we want to copy only the synchronized areas of the source
       * drawable, we create a new GC copying the original one and
       * setting a new clip mask.
       */

      GCPtr targetGC = GetScratchGC(pDstDrawable -> depth, pDstDrawable -> pScreen);

      ValidateGC(pDstDrawable, targetGC);

      CopyGC(pGC, targetGC, GCFunction | GCPlaneMask | GCSubwindowMode |
                 GCClipXOrigin | GCClipYOrigin | GCClipMask | GCForeground |
                     GCBackground | GCGraphicsExposures);

      if (RegionNumRects(pClipRegion) == 1)
      {
        /*
         * If the region to copy is formed by one rectangle, we change
         * only the copy coordinates.
         */

         srcx = srcx + pClipRegion -> extents.x1 - dstx;
         srcy = srcy + pClipRegion -> extents.y1 - dsty;

         dstx = pClipRegion -> extents.x1;
         dsty = pClipRegion -> extents.y1;

         width  = pClipRegion -> extents.x2 - pClipRegion -> extents.x1;
         height = pClipRegion -> extents.y2 - pClipRegion -> extents.y1;
      }
      else
      {
        CARD32 targetAttributes[2];

        /*
         * Setting the clip mask origin. This operation must precede
         * the clip change, because the origin information is used in
         * the XSetClipRectangles().
         */

        targetAttributes[0] = 0;
        targetAttributes[1] = 0;

        ChangeGC(targetGC, GCClipXOrigin | GCClipYOrigin, targetAttributes);

        /*
         * Setting the new clip mask.
         */

        nxagentChangeClip(targetGC, CT_REGION, pClipRegion, 0);

        /*
         * Next call to nxagentChangeClip() will destroy pClipRegion,
         * so it has not to be freed.
         */

        pClipRegionFree = False;

        #ifdef DEBUG
        fprintf(stderr, "nxagentDeferCopyArea: Going to execute a copy area with clip mask "
                    "[%d,%d,%d,%d] and origin [%d,%d].\n", ((RegionPtr) targetGC -> clientClip) -> extents.x1,
                        ((RegionPtr) targetGC -> clientClip) -> extents.y1,
                            ((RegionPtr) targetGC -> clientClip) -> extents.x2,
                                ((RegionPtr) targetGC -> clientClip) -> extents.y2,
                                    targetGC -> clipOrg.x, targetGC -> clipOrg.y);
        #endif
      }

      XCopyArea(nxagentDisplay, nxagentDrawable(pSrcDrawable), nxagentDrawable(pDstDrawable),
                    nxagentGC(targetGC), srcx, srcy, width, height, dstx, dsty);

      nxagentChangeClip(targetGC, CT_NONE, NullRegion, 0);

      if (pClipRegionFree)
      {
        nxagentFreeRegion(pClipRegion);
      }

      FreeScratchGC(targetGC);
    }
    else
    {
      #ifdef TEST
      fprintf(stderr, "nxagentDeferCopyArea: The clipped region is NIL. CopyArea skipped.\n");
      #endif

      /*
       * The pClipRegion is destroyed calling nxagentChangeClip(), so
       * we deallocate it explicitly only if we don't change the clip.
       */

      nxagentFreeRegion(pClipRegion);
    }

    nxagentFreeRegion(pCorruptedRegion);

    return True;
  }
  else
  {
    RegionPtr pSrcRegion = nxagentCreateRegion(pSrcDrawable, NULL, srcx, srcy, width, height);

    #ifdef DEBUG
    fprintf(stderr, "nxagentDeferCopyArea: Source region [%d,%d,%d,%d].\n",
                pSrcRegion -> extents.x1, pSrcRegion -> extents.y1,
                    pSrcRegion -> extents.x2, pSrcRegion -> extents.y2);
    #endif

    RegionRec corruptedRegion;
    RegionInit(&corruptedRegion, NullBox, 1);

    RegionIntersect(&corruptedRegion,
                         pSrcRegion, nxagentCorruptedRegion(pSrcDrawable));

    if (!RegionNil(&corruptedRegion))
    {
      #ifdef TEST
      fprintf(stderr, "nxagentDeferCopyArea: Forcing the synchronization of source drawable at [%p].\n",
                  (void *) pSrcDrawable);
      #endif

      nxagentSynchronizeRegion(pSrcDrawable, &corruptedRegion /*pSrcRegion*/, NEVER_BREAK, NULL);
    }

    RegionUninit(&corruptedRegion);

    nxagentFreeRegion(pSrcRegion);
  }

  return False;
}

RegionPtr nxagentCopyArea(DrawablePtr pSrcDrawable, DrawablePtr pDstDrawable,
                               GCPtr pGC, int srcx, int srcy, int width,
                                   int height, int dstx, int dsty)
{
  Bool skip = False;

  #ifdef TEST
  fprintf(stderr, "nxagentCopyArea: Image src [%s:%p], dst [%s:%p] (%d,%d) -> (%d,%d) size (%d,%d)\n",
              (pSrcDrawable -> type == DRAWABLE_PIXMAP) ? "PIXMAP" : "WINDOW", (void *) pSrcDrawable,
                      (pDstDrawable -> type == DRAWABLE_PIXMAP) ? "PIXMAP" : "WINDOW", 
                          (void *) pDstDrawable, srcx, srcy, dstx, dsty, width, height);
  #endif

 /*
  * Here, before using fbDoCopy() called by fbCopyArea(), it should be
  * provided that the cast in fbDoCopy() from int to short int would
  * not cut off significative bits.
  */

  if (dstx + pDstDrawable->x + width > 32767)
  {
    #ifdef WARNING
    fprintf(stderr, "nxagentCopyArea: x2 exceeding short int.\n");
    #endif

    width = 32767 - dstx - pDstDrawable->x;

    if (width <= 0)
    {
      #ifdef TEST
      fprintf(stderr, "nxagentCopyArea: Returning null on x2 check.\n");
      #endif

      return NullRegion;
    }
  }

  if (dstx + pDstDrawable->x < -32768)
  {
    #ifdef WARNING
    fprintf(stderr, "nxagentCopyArea: x1 exceeding short int.\n");
    #endif

    width += pDstDrawable->x + dstx + 32768;
    srcx  -= pDstDrawable->x + dstx + 32768;
    dstx = -32768 - pDstDrawable->x;

    if (width <= 0)
    {
      #ifdef TEST
      fprintf(stderr, "nxagentCopyArea: Returning null on x1 check.\n");
      #endif

      return NullRegion;
    }
  }

  if (dsty + pDstDrawable->y + height > 32767)
  {
    #ifdef WARNING
    fprintf(stderr, "nxagentCopyArea: y2 exceeding short int.\n");
    #endif

    height = 32767 - dsty - pDstDrawable->y;

    if (height <= 0)
    {
      #ifdef TEST
      fprintf(stderr, "nxagentCopyArea: Returning null on y2 check.\n");
      #endif

      return NullRegion;
    }
  }

  if (dsty + pDstDrawable->y < -32768)
  {
    #ifdef WARNING
    fprintf(stderr, "nxagentCopyArea: y1 exceeding short int.\n");
    #endif

    height += 32768 + pDstDrawable->y + dsty;
    srcy   -= 32768 + pDstDrawable->y + dsty;
    dsty = -32768 - pDstDrawable->y;

    if (height <= 0)
    {
      #ifdef TEST
      fprintf(stderr, "nxagentCopyArea: Returning null on y1 check.\n");
      #endif

      return NullRegion;
    }
  }


  if (nxagentGCTrap || nxagentShmTrap)
  {
    if (pSrcDrawable -> type == DRAWABLE_PIXMAP &&
            pDstDrawable -> type == DRAWABLE_PIXMAP)
    {
      return fbCopyArea(nxagentVirtualDrawable(pSrcDrawable), 
                          nxagentVirtualDrawable(pDstDrawable),
                            pGC, srcx, srcy, width, height, dstx, dsty);
    }
    else if (pSrcDrawable -> type == DRAWABLE_PIXMAP)
    {
      return fbCopyArea(nxagentVirtualDrawable(pSrcDrawable), pDstDrawable,
                            pGC, srcx, srcy, width, height, dstx, dsty);
    }
    else if (pDstDrawable -> type == DRAWABLE_PIXMAP)
    {
      return fbCopyArea(pSrcDrawable, nxagentVirtualDrawable(pDstDrawable),
                            pGC, srcx, srcy, width, height, dstx, dsty);
    }
    else
    {
      return fbCopyArea(pSrcDrawable, pDstDrawable,
                            pGC, srcx, srcy, width, height, dstx, dsty);
    }

    return NullRegion;
  }

  /*
   * Try to detect if the copy area is to a window that is unmapped or
   * fully covered. Similarly to the check in Image.c, this is of
   * little use.
   */

  if (!nxagentOption(IgnoreVisibility) && pDstDrawable -> type == DRAWABLE_WINDOW &&
          (!nxagentWindowIsVisible((WindowPtr) pDstDrawable) ||
              (!nxagentDefaultWindowIsVisible() && nxagentCompositeEnable == 0)))
  {
    #ifdef TEST
    fprintf(stderr, "nxagentCopyArea: Prevented operation on fully obscured window at [%p].\n",
                (void *) pDstDrawable);
    #endif

    return NullRegion;
  }

  /*
   * If the pixmap is on shared memory, we can't know if the pixmap
   * content is changed and so have to translate the operation in a
   * put image operation. This can seriously affect the performance.
   */

  if (pSrcDrawable -> type == DRAWABLE_PIXMAP &&
         nxagentIsShmPixmap((PixmapPtr) pSrcDrawable))
  {
    char *data;

    int depth  = pSrcDrawable -> depth;

    unsigned int format = (depth == 1) ? XYPixmap : ZPixmap;

    int length = nxagentImageLength(width, height, format, 0, depth);

    if ((data = malloc(length)) == NULL)
    {
      #ifdef WARNING
      fprintf(stderr, "nxagentCopyArea: WARNING! Failed to allocate memory for the operation.\n");
      #endif

      return NullRegion;
    }

    unsigned long planeMask = 0xffffffff;
    fbGetImage(nxagentVirtualDrawable(pSrcDrawable), srcx, srcy, width, height, format, planeMask, data);

    /*
     * If the source is a shared memory pixmap, put the image directly
     * to the destination.
     */

    nxagentPutImage(pDstDrawable, pGC, depth, dstx, dsty,
                        width, height, 0, format, data);

    #ifdef TEST
    fprintf(stderr,"nxagentCopyArea: Realize Pixmap %p virtual %p x %d y %d w %d h %d\n",
               (void *) pSrcDrawable, (void *) nxagentVirtualDrawable(pSrcDrawable),
                   srcx, srcy, width, height);
    #endif

    SAFE_free(data);

    /*
     * If the source is a shared memory pixmap, the content of the
     * framebuffer has been placed directly on the destination so we
     * can skip the copy area operation.
     */

    skip = True;
  }

  #ifdef TEST
  fprintf(stderr, "nxagentCopyArea: Image src [%s:%p], dst [%s:%p] sx %d sy %d dx %d dy %d size w %d h %d\n",
              ((pSrcDrawable)->type == DRAWABLE_PIXMAP) ? "PIXMAP" : "WINDOW", (void *) pSrcDrawable,
                   ((pDstDrawable)->type == DRAWABLE_PIXMAP) ? "PIXMAP" : "WINDOW",
                       (void *) pDstDrawable, srcx, srcy, dstx, dsty, width, height);
  #endif

  if (!skip && nxagentDrawableStatus(pSrcDrawable) == NotSynchronized)
  {
    skip = nxagentDeferCopyArea(pSrcDrawable, pDstDrawable, pGC, srcx, srcy,
                                   width, height, dstx, dsty);
  }
  #ifdef TEST
  else
  {
    fprintf(stderr, "nxagentCopyArea: Source drawable at [%p] already synchronized.\n",
                (void *) pSrcDrawable);
  }
  #endif

  if (!skip)
  {
    XCopyArea(nxagentDisplay, nxagentDrawable(pSrcDrawable), nxagentDrawable(pDstDrawable),
                  nxagentGC(pGC), srcx, srcy, width, height, dstx, dsty);

    /*
     * The copy area restored the synchronization status of
     * destination drawable.
     */

    if (nxagentDrawableStatus(pDstDrawable) == NotSynchronized)
    {
      RegionPtr pDstRegion = nxagentCreateRegion(pDstDrawable, pGC, dstx, dsty, width, height);

      nxagentUnmarkCorruptedRegion(pDstDrawable, pDstRegion);

      nxagentFreeRegion(pDstRegion);
    }
  }

  if (nxagentDrawableContainGlyphs(pSrcDrawable) == 1)
  {
    nxagentSetDrawableContainGlyphs(pDstDrawable, 1);
  }

  if (pSrcDrawable -> type == DRAWABLE_PIXMAP)
  {
    nxagentIncreasePixmapUsageCounter((PixmapPtr) pSrcDrawable);
  }

  if (pSrcDrawable -> type == DRAWABLE_PIXMAP &&
          pDstDrawable -> type == DRAWABLE_PIXMAP)
  {
    #ifdef TEST
    fprintf(stderr, "nxagentCopyArea: Going to copy area from virtual pixmap [%p] to [%p]\n",
                (void *) nxagentVirtualDrawable(pSrcDrawable),
                    (void *) nxagentVirtualDrawable(pDstDrawable));
    #endif

    return fbCopyArea(nxagentVirtualDrawable(pSrcDrawable), 
                          nxagentVirtualDrawable(pDstDrawable),
                              pGC, srcx, srcy, width, height, dstx, dsty);
  }
  else if (pSrcDrawable -> type == DRAWABLE_PIXMAP)
  {
    #ifdef TEST
    fprintf(stderr, "nxagentCopyArea: Going to copy area from virtual pixmap [%p] to window [%p]\n",
                (void *) nxagentVirtualDrawable(pSrcDrawable),
                    (void *) pDstDrawable);
    #endif

    return fbCopyArea(nxagentVirtualDrawable(pSrcDrawable), pDstDrawable,
                          pGC, srcx, srcy, width, height, dstx, dsty);
  }
  else if (pDstDrawable -> type == DRAWABLE_PIXMAP)
  {
    /*
     * If we are here the source drawable must be a window.
     */

    if (((WindowPtr) pSrcDrawable) -> viewable)
    {
      #ifdef TEST
      fprintf(stderr, "nxagentCopyArea: Going to copy area from window [%p] to virtual pixmap [%p]\n",
                  (void *) pSrcDrawable, (void *) nxagentVirtualDrawable(pDstDrawable));
      #endif

      return fbCopyArea(pSrcDrawable, nxagentVirtualDrawable(pDstDrawable),
                            pGC, srcx, srcy, width, height, dstx, dsty);
    }
  }
  else
  {
    /*
     * If we are here the source drawable must be a window.
     */

    if (((WindowPtr) pSrcDrawable) -> viewable)
    {
      #ifdef TEST
      fprintf(stderr, "nxagentCopyArea: Going to copy area from window [%p] to window [%p]\n",
                  (void *) pSrcDrawable, (void *) pDstDrawable);
      #endif

      return fbCopyArea(pSrcDrawable, pDstDrawable,
                            pGC, srcx, srcy, width, height, dstx, dsty);
    }
  }

  return miHandleExposures(pSrcDrawable, pDstDrawable, pGC, 
                               srcx, srcy, width, height, dstx, dsty, 0);
}

RegionPtr nxagentCopyPlane(DrawablePtr pSrcDrawable, DrawablePtr pDstDrawable,
                               GCPtr pGC, int srcx, int srcy, int width, int height,
                                   int dstx, int dsty, unsigned long plane)
{
  Bool skip = False;

  #ifdef TEST
  fprintf(stderr, "nxagentCopyPlane: Image src [%s:%p], dst [%s:%p] (%d,%d) -> (%d,%d) size (%d,%d)\n",
              ((pSrcDrawable)->type == DRAWABLE_PIXMAP) ? "PIXMAP" : "WINDOW", (void *) pSrcDrawable,
                      ((pDstDrawable)->type == DRAWABLE_PIXMAP) ? "PIXMAP" : "WINDOW",
                          (void *) pDstDrawable, srcx, srcy, dstx, dsty, width, height);
  #endif

  if (nxagentGCTrap || nxagentShmTrap)
  {
    if (pSrcDrawable -> type == DRAWABLE_PIXMAP &&
            pDstDrawable -> type == DRAWABLE_PIXMAP)
    {
      return fbCopyPlane(nxagentVirtualDrawable(pSrcDrawable), 
                           nxagentVirtualDrawable(pDstDrawable),
                             pGC, srcx, srcy, width, height, dstx, dsty, plane);
    }
    else if (pSrcDrawable -> type == DRAWABLE_PIXMAP)
    {
      return fbCopyPlane(nxagentVirtualDrawable(pSrcDrawable), pDstDrawable,
                             pGC, srcx, srcy, width, height, dstx, dsty, plane);
    }
    else if (pDstDrawable -> type == DRAWABLE_PIXMAP)
    {
      return fbCopyPlane(pSrcDrawable, nxagentVirtualDrawable(pDstDrawable),
                             pGC, srcx, srcy, width, height, dstx, dsty, plane);
    }
    else
    {
      return fbCopyPlane(pSrcDrawable, pDstDrawable, pGC, srcx, srcy, width, height,
                             dstx, dsty, plane);
    }

    return NullRegion;
  }

  /*
   * If the pixmap is on shared memory, we can't know if the pixmap
   * content is changed and so have to translate the operation in a
   * put image operation. This can seriously affect the performance.
   */

  if (pSrcDrawable -> type == DRAWABLE_PIXMAP &&
         nxagentIsShmPixmap((PixmapPtr) pSrcDrawable))
  {
    char *data;

    int depth  = pSrcDrawable -> depth;

    unsigned int format = (depth == 1) ? XYPixmap : ZPixmap;

    int length = nxagentImageLength(width, height, format, 0, depth);

    if ((data = malloc(length)) == NULL)
    {
      #ifdef DEBUG
      fprintf(stderr, "nxagentCopyPlane: WARNING! Failed to allocate memory for the operation.\n");
      #endif

      return 0;
    }

    unsigned long planeMask = 0xffffffff;
    fbGetImage(nxagentVirtualDrawable(pSrcDrawable), srcx, srcy, width, height, format, planeMask, data);

    /*
     * If the source is a shared memory pixmap, put the image directly
     * to the destination.
     */

    nxagentPutImage(pDstDrawable, pGC, depth, dstx, dsty,
                        width, height, 0, format, data);

    #ifdef TEST
    fprintf(stderr,"nxagentCopyPlane: Synchronize Pixmap %p virtual %p x %d y %d w %d h %d \n",
               (void *) pSrcDrawable, (void *) nxagentVirtualDrawable(pSrcDrawable),
                   srcx, srcy, width, height);
    #endif

    SAFE_free(data);

    /*
     * If the source is a shared memory pixmap, the content of the
     * framebuffer has been placed directly on the destination so we
     * can skip the copy plane operation.
     */

    skip = True;
  }

  if (!skip && nxagentDrawableStatus(pSrcDrawable) == NotSynchronized)
  {
    if (pDstDrawable -> type == DRAWABLE_PIXMAP &&
            nxagentOption(DeferLevel) > 0)
    {
      RegionPtr pDstRegion = nxagentCreateRegion(pDstDrawable, pGC, dstx, dsty, width, height);

      nxagentMarkCorruptedRegion(pDstDrawable, pDstRegion);

      nxagentFreeRegion(pDstRegion);

      skip = True;
    }
    else
    {
      RegionPtr pSrcRegion = nxagentCreateRegion(pSrcDrawable, NULL, srcx, srcy, width, height);

      RegionRec corruptedRegion;

      RegionInit(&corruptedRegion, NullBox, 1);

      RegionIntersect(&corruptedRegion,
                           pSrcRegion, nxagentCorruptedRegion(pSrcDrawable));

      if (!RegionNil(&corruptedRegion))
      {
        #ifdef TEST
        fprintf(stderr, "nxagentCopyPlane: Forcing the synchronization of source drawable at [%p].\n",
                    (void *) pSrcDrawable);
        #endif

        nxagentSynchronizeRegion(pSrcDrawable, &corruptedRegion /*pSrcRegion*/, NEVER_BREAK, NULL);

        RegionPtr pDstRegion = nxagentCreateRegion(pDstDrawable, pGC, dstx, dsty, width, height);

        nxagentUnmarkCorruptedRegion(pDstDrawable, pDstRegion);

        nxagentFreeRegion(pDstRegion);
      }

      RegionUninit(&corruptedRegion);

      nxagentFreeRegion(pSrcRegion);
    }
  }
  #ifdef TEST
  else
  {
    fprintf(stderr, "nxagentCopyPlane: Source drawable at [%p] already synchronized.\n",
                (void *) pSrcDrawable);
  }
  #endif

  if (!skip)
  {
    XCopyPlane(nxagentDisplay,
                   nxagentDrawable(pSrcDrawable), nxagentDrawable(pDstDrawable),
                       nxagentGC(pGC), srcx, srcy, width, height, dstx, dsty, plane);
  }

  if ((pSrcDrawable)->type == DRAWABLE_PIXMAP &&
          (pDstDrawable)->type == DRAWABLE_PIXMAP)
  {
    #ifdef TEST
    fprintf(stderr, "nxagentCopyPlane: going to copy plane from FB pixmap [%p] to [%p].\n",
                (void *) nxagentVirtualDrawable(pSrcDrawable),
                    (void *) nxagentVirtualDrawable(pDstDrawable));
    #endif

    return fbCopyPlane(nxagentVirtualDrawable(pSrcDrawable), 
                         nxagentVirtualDrawable(pDstDrawable),
                           pGC, srcx, srcy, width, height, dstx, dsty, plane);
  }
  else if (pSrcDrawable -> type == DRAWABLE_PIXMAP)
  {
    return fbCopyPlane(nxagentVirtualDrawable(pSrcDrawable), pDstDrawable, pGC,
                            srcx, srcy, width, height, dstx, dsty, plane);
  }
  else if (pDstDrawable -> type == DRAWABLE_PIXMAP)
  {
    return fbCopyPlane(pSrcDrawable, nxagentVirtualDrawable(pDstDrawable), pGC,
                            srcx, srcy, width, height, dstx, dsty, plane);
  }
  else
  {
    return fbCopyPlane(pSrcDrawable, pDstDrawable, pGC,
                            srcx, srcy, width, height, dstx, dsty, plane);
  }

  return miHandleExposures(pSrcDrawable, pDstDrawable, pGC, 
                               srcx, srcy, width, height, dstx, dsty, plane);
}

void nxagentPolyPoint(DrawablePtr pDrawable, GCPtr pGC, int mode,
                          int nPoints, xPoint *pPoints)
{
  #ifdef TEST
  fprintf(stderr, "nxagentPolyPoint: Drawable at [%p] GC at [%p] Points [%d].\n",
              (void *) pDrawable, (void *) pGC, nPoints);
  #endif

  if (nxagentGCTrap)
  {
    if ((pDrawable)->type == DRAWABLE_PIXMAP)
    {
      fbPolyPoint(nxagentVirtualDrawable(pDrawable), pGC, mode, nPoints, pPoints);
    }
    else
    {
      fbPolyPoint(pDrawable, pGC, mode, nPoints, pPoints);
    }

    return;
  }

  if ((pDrawable)->type == DRAWABLE_PIXMAP)
  {
    #ifdef TEST
    fprintf(stderr, "GCOps: GC [%p] going to poly point on FB pixmap [%p].\n",
                (void *) pGC, (void *) nxagentVirtualDrawable(pDrawable));
    #endif

    if (nxagentPixmapIsVirtual((PixmapPtr) pDrawable))
    {
      #ifdef TEST
      fprintf(stderr, "GCOps: poly point enters with virtual pixmap [%p] parent is [%p]\n",
                  (void *) nxagentVirtualDrawable(pDrawable),
                      (void *) nxagentRealPixmap((PixmapPtr) pDrawable));
      #endif

      if (nxagentRealPixmap((PixmapPtr) pDrawable))
      {
        XDrawPoints(nxagentDisplay, nxagentDrawable((DrawablePtr) nxagentRealPixmap((PixmapPtr) pDrawable)),
                        nxagentGC(pGC), (XPoint *) pPoints, nPoints, mode);
      }
    }
    else
    {
      XDrawPoints(nxagentDisplay, nxagentDrawable(pDrawable), nxagentGC(pGC),
                      (XPoint *) pPoints, nPoints, mode);
    }

    fbPolyPoint(nxagentVirtualDrawable(pDrawable), pGC, mode, nPoints, pPoints);

    return;
  }
  else
  {
    XDrawPoints(nxagentDisplay, nxagentDrawable(pDrawable), nxagentGC(pGC),
                    (XPoint *) pPoints, nPoints, mode);

    fbPolyPoint(pDrawable, pGC, mode, nPoints, pPoints);
  }
}

void nxagentPolyLines(DrawablePtr pDrawable, GCPtr pGC, int mode,
                          int nPoints, xPoint *pPoints)
{
  if (nxagentGCTrap)
  {
    if ((pDrawable)->type == DRAWABLE_PIXMAP)
    {
      fbPolyLine(nxagentVirtualDrawable(pDrawable), pGC, mode, nPoints, pPoints);
    }
    else
    {
      fbPolyLine(pDrawable, pGC, mode, nPoints, pPoints);
    }

    return;
  }

  if ((pDrawable)->type == DRAWABLE_PIXMAP)
  {
    #ifdef TEST
    fprintf(stderr, "GCOps: GC [%p] going to poly line on FB pixmap [%p].\n",
                (void *) pGC, (void *) nxagentVirtualDrawable(pDrawable));
    #endif

    if (nxagentPixmapIsVirtual((PixmapPtr) pDrawable))
    {
      #ifdef TEST
      fprintf(stderr, "GCOps: poly lines enters with virtual pixmap = [%p] parent is = [%p]\n",
                  (void *) nxagentVirtualDrawable(pDrawable),
                       (void *) nxagentRealPixmap((PixmapPtr) pDrawable));
      #endif

      if (nxagentRealPixmap((PixmapPtr) pDrawable))
      {
        XDrawLines(nxagentDisplay, nxagentDrawable((DrawablePtr) nxagentRealPixmap((PixmapPtr) pDrawable)),
                       nxagentGC(pGC), (XPoint *) pPoints, nPoints, mode);
      }
    }
    else
    {
      XDrawLines(nxagentDisplay, nxagentDrawable(pDrawable), nxagentGC(pGC),
                 (XPoint *) pPoints, nPoints, mode);
    }

    fbPolyLine(nxagentVirtualDrawable(pDrawable), pGC, mode, nPoints, pPoints);

    return;
  }
  else
  {
    XDrawLines(nxagentDisplay, nxagentDrawable(pDrawable), nxagentGC(pGC),
               (XPoint *) pPoints, nPoints, mode);

    fbPolyLine(pDrawable, pGC, mode, nPoints, pPoints);
  }
}

void nxagentPolySegment(DrawablePtr pDrawable, GCPtr pGC,
                            int nSegments, xSegment *pSegments)
{
  #ifdef TEST

  if (nSegments == 1)
  {
    fprintf(stderr, "nxagentPolySegment: Drawable at [%p] GC at [%p] Segment [%d,%d,%d,%d].\n",
                (void *) pDrawable, (void *) pGC,
                    pSegments -> x1, pSegments -> y1, pSegments -> x2, pSegments -> y2);
  }
  else
  {
    fprintf(stderr, "nxagentPolySegment: Drawable at [%p] GC at [%p] Segments [%d].\n",
                (void *) pDrawable, (void *) pGC, nSegments);
  }

  #endif

  if (nxagentGCTrap)
  {
    if ((pDrawable)->type == DRAWABLE_PIXMAP)
    {
      fbPolySegment(nxagentVirtualDrawable(pDrawable), pGC, nSegments, pSegments);
    }
    else
    {
      fbPolySegment(pDrawable, pGC, nSegments, pSegments);
    }

    return;
  }

  if ((pDrawable)->type == DRAWABLE_PIXMAP)
  {
    #ifdef TEST
    fprintf(stderr, "GCOps: GC [%p] going to poly segment on FB pixmap [%p].\n",
                (void *) pGC, (void *) nxagentVirtualDrawable(pDrawable));
    #endif

    if (nxagentPixmapIsVirtual((PixmapPtr) pDrawable))
    {
      #ifdef TEST
      fprintf(stderr, "GCOps: poly segment enters with virtual pixmap = [%p] parent is = [%p]\n",
                  (void *) nxagentVirtualDrawable(pDrawable),
                      (void *) nxagentRealPixmap((PixmapPtr) pDrawable));
      #endif

      if (nxagentRealPixmap((PixmapPtr) pDrawable))
      {
        XDrawSegments(nxagentDisplay, nxagentDrawable((DrawablePtr) nxagentRealPixmap((PixmapPtr) pDrawable)),
                          nxagentGC(pGC), (XSegment *) pSegments, nSegments);
      }
    }
    else
    {
      XDrawSegments(nxagentDisplay, nxagentDrawable(pDrawable), nxagentGC(pGC),
                    (XSegment *) pSegments, nSegments);
    }

    SET_GC_TRAP();
    fbPolySegment(nxagentVirtualDrawable(pDrawable), pGC, nSegments, pSegments);
    RESET_GC_TRAP();

    return;
  }
  else
  {
    XDrawSegments(nxagentDisplay, nxagentDrawable(pDrawable), nxagentGC(pGC),
                 (XSegment *) pSegments, nSegments);

    SET_GC_TRAP();
    fbPolySegment(pDrawable, pGC, nSegments, pSegments);
    RESET_GC_TRAP();
  }
}

void nxagentPolyRectangle(DrawablePtr pDrawable, GCPtr pGC,
                              int nRectangles, xRectangle *pRectangles)
{
  #ifdef TEST

  if (nRectangles == 1)
  {
    fprintf(stderr, "nxagentPolyRectangle: Drawable at [%p] GC at [%p] Rectangle [%d,%d][%d,%d].\n",
                (void *) pDrawable, (void *) pGC,
                    pRectangles -> x, pRectangles -> y, pRectangles -> width, pRectangles -> height);
  }
  else
  {
    fprintf(stderr, "nxagentPolyRectangle: Drawable at [%p] GC at [%p] Rectangles [%d].\n",
                (void *) pDrawable, (void *) pGC, nRectangles);
  }

  #endif

  if (nxagentGCTrap)
  {
    if ((pDrawable)->type == DRAWABLE_PIXMAP)
    {
      miPolyRectangle(nxagentVirtualDrawable(pDrawable), pGC, nRectangles, pRectangles);
    }
    else
    {
      miPolyRectangle(pDrawable, pGC, nRectangles, pRectangles);
    }

    return;
  }

  if ((pDrawable)->type == DRAWABLE_PIXMAP)
  {
    #ifdef TEST
    fprintf(stderr, "GCOps: GC [%p] going to poly rectangle on FB pixmap [%p].\n",
                (void *) pGC, (void *) nxagentVirtualDrawable(pDrawable));
    #endif

    if (nxagentPixmapIsVirtual((PixmapPtr) pDrawable))
    {
      #ifdef TEST
      fprintf(stderr, "GCOps: poly rectangle enters with virtual pixmap = [%p] parent is = [%p]\n",
                  (void *) nxagentVirtualDrawable(pDrawable),
                      (void *) nxagentRealPixmap((PixmapPtr) pDrawable));
      #endif

      if (nxagentRealPixmap((PixmapPtr) pDrawable))
      {
        XDrawRectangles(nxagentDisplay, nxagentDrawable((DrawablePtr) nxagentRealPixmap((PixmapPtr) pDrawable)),
                            nxagentGC(pGC), (XRectangle *) pRectangles, nRectangles);
      }
    }
    else
    {
      XDrawRectangles(nxagentDisplay, nxagentDrawable(pDrawable), nxagentGC(pGC),
                      (XRectangle *) pRectangles, nRectangles);
    }

    SET_GC_TRAP();

    miPolyRectangle(nxagentVirtualDrawable(pDrawable), pGC, nRectangles, pRectangles);

    RESET_GC_TRAP();

    return;
  }
  else
  {
    XDrawRectangles(nxagentDisplay, nxagentDrawable(pDrawable), nxagentGC(pGC),
                    (XRectangle *) pRectangles, nRectangles);

    SET_GC_TRAP();

    miPolyRectangle(pDrawable, pGC, nRectangles, pRectangles);

    RESET_GC_TRAP();
  }
}

void nxagentPolyArc(DrawablePtr pDrawable, GCPtr pGC,
                        int nArcs, xArc *pArcs)
{
  if (nxagentGCTrap)
  {
    if ((pDrawable)->type == DRAWABLE_PIXMAP)
    {
      fbPolyArc(nxagentVirtualDrawable(pDrawable), pGC, nArcs, pArcs);
    }
    else
    {
      fbPolyArc(pDrawable, pGC, nArcs, pArcs);
    }

    return;
  }

  if ((pDrawable)->type == DRAWABLE_PIXMAP)
  {
    #ifdef TEST
    fprintf(stderr, "GCOps: GC [%p] going to poly arc on FB pixmap [%p].\n",
                (void *) pGC, (void *) nxagentVirtualDrawable(pDrawable));
    #endif

    if (nxagentPixmapIsVirtual((PixmapPtr) pDrawable))
    {
      #ifdef TEST
      fprintf(stderr, "GCOps: poly arc enters with virtual pixmap = [%p] parent is = [%p]\n",
                  (void *) nxagentVirtualDrawable(pDrawable),
                      (void *) nxagentRealPixmap((PixmapPtr) pDrawable));
      #endif

      if (nxagentRealPixmap((PixmapPtr) pDrawable))
      {
        XDrawArcs(nxagentDisplay, nxagentDrawable((DrawablePtr) nxagentRealPixmap((PixmapPtr) pDrawable)),
                      nxagentGC(pGC), (XArc *) pArcs, nArcs);
      }
    }
    else
    {
       XDrawArcs(nxagentDisplay, nxagentDrawable(pDrawable), nxagentGC(pGC),
                     (XArc *) pArcs, nArcs);
    }

    fbPolyArc(nxagentVirtualDrawable(pDrawable), pGC, nArcs, pArcs);

    return;
  }
  else
  {
    XDrawArcs(nxagentDisplay, nxagentDrawable(pDrawable), nxagentGC(pGC),
                  (XArc *) pArcs, nArcs);

    fbPolyArc(pDrawable, pGC, nArcs, pArcs);
  }
}

void nxagentFillPolygon(DrawablePtr pDrawable, GCPtr pGC, int shape,
                            int mode, int nPoints, xPoint *pPoints)
{
  xPoint *newPoints = NULL;

  if (nxagentGCTrap)
  {
    if ((pDrawable)->type == DRAWABLE_PIXMAP)
    {
      miFillPolygon(nxagentVirtualDrawable(pDrawable), pGC, shape, mode, nPoints, pPoints);
    }
    else
    {
      miFillPolygon(pDrawable, pGC, shape, mode, nPoints, pPoints);
    }

    return;
  }

  /*
   * The coordinate-mode must be CoordModePrevious to make better use
   * of differential encoding of X_FillPoly request by the side of
   * proxy.
   */

  if (mode == CoordModeOrigin)
  {
    mode = CoordModePrevious;

    newPoints = malloc(nPoints * sizeof(xPoint));

    /*
     * The first point is always relative to the drawable's origin.
     */

    newPoints[0].x = pPoints[0].x;
    newPoints[0].y = pPoints[0].y;

    /*
     * If coordinate-mode is CoordModePrevious, the points following
     * the first are relative to the previous point.
     */

    for (int i = 1; i < nPoints; i++)
    {
      newPoints[i].x = pPoints[i].x - pPoints[i-1].x;
      newPoints[i].y = pPoints[i].y - pPoints[i-1].y;
    }

    pPoints = newPoints;
  }

  if ((pDrawable)->type == DRAWABLE_PIXMAP)
  {
    #ifdef TEST
    fprintf(stderr, "GCOps: GC [%p] going to fill polygon on FB pixmap [%p].\n",
                (void *) pGC, (void *) nxagentVirtualDrawable(pDrawable));
    #endif

    if (nxagentPixmapIsVirtual((PixmapPtr) pDrawable))
    {
      #ifdef TEST
      fprintf(stderr, "GCOps: fill polygon enters with virtual pixmap = [%p] parent is = [%p]\n",
                  (void *) nxagentVirtualDrawable(pDrawable),
                      (void *) nxagentRealPixmap((PixmapPtr) pDrawable));
      #endif

      if (nxagentRealPixmap((PixmapPtr) pDrawable))
      {
        XFillPolygon(nxagentDisplay, nxagentDrawable((DrawablePtr) nxagentRealPixmap((PixmapPtr) pDrawable)),
                         nxagentGC(pGC), (XPoint *) pPoints, nPoints, shape, mode);
      }
    }
    else
    {
      XFillPolygon(nxagentDisplay, nxagentDrawable(pDrawable), nxagentGC(pGC),
                       (XPoint *) pPoints, nPoints, shape, mode);
    }

    SET_GC_TRAP();

    miFillPolygon(nxagentVirtualDrawable(pDrawable), pGC, shape, mode, nPoints, pPoints);

    RESET_GC_TRAP();
  }
  else
  {
    XFillPolygon(nxagentDisplay, nxagentDrawable(pDrawable), nxagentGC(pGC),
                     (XPoint *) pPoints, nPoints, shape, mode);

    SET_GC_TRAP();

    miFillPolygon(pDrawable, pGC, shape, mode, nPoints, pPoints);

    RESET_GC_TRAP();
  }

  SAFE_free(newPoints);
}

void nxagentPolyFillRect(DrawablePtr pDrawable, GCPtr pGC,
                             int nRectangles, xRectangle *pRectangles)
{
  #ifdef TEST

  if (nRectangles == 1)
  {
    fprintf(stderr, "nxagentPolyFillRect: Drawable at [%p] GC at [%p] FillStyle [%d] Rectangle [%d,%d][%d,%d].\n",
                (void *) pDrawable, (void *) pGC, pGC -> fillStyle, 
                    pRectangles -> x, pRectangles -> y, pRectangles -> width, pRectangles -> height);
  }
  else
  {
    fprintf(stderr, "nxagentPolyFillRect: Drawable at [%p] GC at [%p] FillStyle [%d] Rectangles [%d].\n",
                (void *) pDrawable, (void *) pGC, pGC -> fillStyle, nRectangles);
  }

  #endif

  if (nxagentGCTrap)
  {
    if ((pDrawable)->type == DRAWABLE_PIXMAP)
    {
      fbPolyFillRect(nxagentVirtualDrawable(pDrawable), pGC, nRectangles, pRectangles);
    }
    else
    {
      fbPolyFillRect(pDrawable, pGC, nRectangles, pRectangles);
    }

    return;
  }

  /*
   * The PolyFillRect acts in two ways: if the GC has a corrupted
   * tile, the operation propagates the corrupted region on the
   * destination. In other cases, because the PolyFillRect will cover
   * the destination, any corrupted region intersecting the target
   * will be cleared.
   */

  Bool inheritCorruptedRegion = False;

  if (pGC -> fillStyle == FillTiled &&
          pGC -> tileIsPixel == 0 && pGC -> tile.pixmap != NULL)
  {
    nxagentIncreasePixmapUsageCounter(pGC -> tile.pixmap);

    if (nxagentDrawableStatus((DrawablePtr) pGC -> tile.pixmap) == NotSynchronized)
    {
      #ifdef TEST
      fprintf(stderr, "nxagentPolyFillRect: GC at [%p] uses corrupted tile pixmap at [%p]. Going to "
                  "corrupt the destination [%s][%p].\n", (void *) pGC, (void *) pGC -> tile.pixmap,
                      (pDrawable -> type == DRAWABLE_PIXMAP ? "pixmap" : "window"), (void *) pDrawable);

      #endif

      inheritCorruptedRegion = True;
    }
  }

  if (inheritCorruptedRegion || nxagentDrawableStatus(pDrawable) == NotSynchronized)
  {
    RegionPtr rectRegion = RegionFromRects(nRectangles, pRectangles, CT_REGION);

    if (pGC -> clientClip != NULL)
    {
      RegionRec tmpRegion;

      RegionInit(&tmpRegion, NullBox, 1);

      RegionCopy(&tmpRegion, ((RegionPtr) pGC -> clientClip));

      if (pGC -> clipOrg.x != 0 || pGC -> clipOrg.y != 0)
      {
        RegionTranslate(&tmpRegion, pGC -> clipOrg.x, pGC -> clipOrg.y);
      }

      RegionIntersect(rectRegion, rectRegion, &tmpRegion);

      RegionUninit(&tmpRegion);
    }

    if (inheritCorruptedRegion)
    {
      /*
       * The fill style should affect the corrupted region
       * propagation: if the tile is not completely corrupted the
       * region should be 'tiled' over the destination.
       */

      nxagentMarkCorruptedRegion(pDrawable, rectRegion);
    }
    else
    {
      if (pGC -> fillStyle != FillStippled && pGC -> fillStyle != FillOpaqueStippled)
      {
        nxagentUnmarkCorruptedRegion(pDrawable, rectRegion);
      }
      else
      {
        /*
         * The stipple mask computation could cause an high
         * fragmentation of the destination region. An analysis should
         * be done to examine the better solution (e.g.rdesktop uses
         * stipples to draw texts).
         */

        #ifdef TEST
        fprintf(stderr, "nxagentPolyFillRect: Synchronizing the region [%d,%d,%d,%d] before using "
                    "the stipple at [%p].\n", rectRegion -> extents.x1, rectRegion -> extents.y1,
                        rectRegion -> extents.x2, rectRegion -> extents.y2, (void *) pGC -> stipple);
        #endif

        nxagentSynchronizeRegion(pDrawable, rectRegion, NEVER_BREAK, NULL);
      }
    }

    RegionDestroy(rectRegion);
  }

  if ((pDrawable)->type == DRAWABLE_PIXMAP)
  {
    #ifdef TEST
    fprintf(stderr, "GCOps: GC [%p] going to poly fill rect on FB pixmap [%p].\n",
                (void *) pGC, (void *) nxagentVirtualDrawable(pDrawable));
    #endif

    if (nxagentPixmapIsVirtual((PixmapPtr) pDrawable))
    {
      #ifdef TEST
      fprintf(stderr, "GCOps: poly fill rect enters with virtual pixmap = [%p] parent is = [%p]\n",
                  (void *) nxagentVirtualDrawable(pDrawable),
                      (void *) nxagentRealPixmap((PixmapPtr) pDrawable));
      #endif

      if (nxagentRealPixmap((PixmapPtr) pDrawable))
      {
        XFillRectangles(nxagentDisplay, nxagentDrawable((DrawablePtr) nxagentRealPixmap((PixmapPtr) pDrawable)),
                            nxagentGC(pGC), (XRectangle *) pRectangles, nRectangles);
      }
    }
    else
    {
      XFillRectangles(nxagentDisplay, nxagentDrawable(pDrawable), nxagentGC(pGC),
                          (XRectangle *) pRectangles, nRectangles);
    }

    fbPolyFillRect(nxagentVirtualDrawable(pDrawable), pGC, nRectangles, pRectangles);

    return;
  }
  else
  {
    XFillRectangles(nxagentDisplay, nxagentDrawable(pDrawable), nxagentGC(pGC),
                        (XRectangle *) pRectangles, nRectangles);

    fbPolyFillRect(pDrawable, pGC, nRectangles, pRectangles);
  }
}

void nxagentPolyFillArc(DrawablePtr pDrawable, GCPtr pGC,
                            int nArcs, xArc *pArcs)
{
  if (nxagentGCTrap)
  {
    if ((pDrawable)->type == DRAWABLE_PIXMAP)
    {
      miPolyFillArc(nxagentVirtualDrawable(pDrawable), pGC, nArcs, pArcs);
    }
    else
    {
      miPolyFillArc(pDrawable, pGC, nArcs, pArcs);
    }

    return;
  }

  if ((pDrawable)->type == DRAWABLE_PIXMAP)
  {
    #ifdef TEST
    fprintf(stderr, "GCOps: GC [%p] going to poly fillarc on FB pixmap [%p].\n",
                (void *) pGC, (void *) nxagentVirtualDrawable(pDrawable));
    #endif

    if (nxagentPixmapIsVirtual((PixmapPtr) pDrawable))
    {
      #ifdef TEST
      fprintf(stderr, "GCOps: poly fill arc enters with virtual pixmap = [%p] parent is = [%p]\n",
                  (void *) nxagentVirtualDrawable(pDrawable),
                       (void *) nxagentRealPixmap((PixmapPtr) pDrawable));
      #endif

      if (nxagentRealPixmap((PixmapPtr) pDrawable))
      {
        XFillArcs(nxagentDisplay, nxagentDrawable((DrawablePtr) nxagentRealPixmap((PixmapPtr) pDrawable)),
                      nxagentGC(pGC), (XArc *) pArcs, nArcs);
      }
    }
    else
    {
      XFillArcs(nxagentDisplay, nxagentDrawable(pDrawable), nxagentGC(pGC),
                (XArc *) pArcs, nArcs);
    }

    SET_GC_TRAP();

    miPolyFillArc(nxagentVirtualDrawable(pDrawable), pGC, nArcs, pArcs);

    RESET_GC_TRAP();

    return;
  }
  else
  {
    XFillArcs(nxagentDisplay, nxagentDrawable(pDrawable),
                  nxagentGC(pGC), (XArc *) pArcs, nArcs);

    SET_GC_TRAP();

    miPolyFillArc(pDrawable, pGC, nArcs, pArcs);

    RESET_GC_TRAP();
  }
}

int nxagentPolyText8(DrawablePtr pDrawable, GCPtr pGC, int x,
                         int y, int count, char *string)
{
  /*
   * While the session is suspended the font structure is NULL.
   */

  if (nxagentFontStruct(pGC -> font) == NULL)
  {
    return x;
  }

  int width = XTextWidth(nxagentFontStruct(pGC->font), string, count);

  if (nxagentGCTrap)
  {
    if ((pDrawable)->type == DRAWABLE_PIXMAP)
    {
      miPolyText8(nxagentVirtualDrawable(pDrawable), pGC, x, y, count, string);
    }
    else
    {
      miPolyText8(pDrawable, pGC, x, y, count, string);
    }

    return width + x;
  }

  if ((pDrawable)->type == DRAWABLE_PIXMAP)
  {
    #ifdef TEST
    fprintf(stderr, "GCOps: GC [%p] going to poly text8 on FB pixmap [%p] with string [%s].\n",
                (void *) pGC, (void *) nxagentVirtualDrawable(pDrawable), (char *) string);
    #endif

    if (nxagentPixmapIsVirtual((PixmapPtr) pDrawable))
    {
      #ifdef TEST
      fprintf(stderr, "GCOps: poly text8 enters with virtual pixmap [%p] parent is [%p]\n",
                  (void *) nxagentVirtualDrawable(pDrawable),
                      (void *) nxagentRealPixmap((PixmapPtr) pDrawable));
      #endif

      if (nxagentRealPixmap((PixmapPtr) pDrawable))
      {
         XDrawString(nxagentDisplay, nxagentDrawable((DrawablePtr) nxagentRealPixmap((PixmapPtr) pDrawable)),
                         nxagentGC(pGC), x, y, string, count);
      }
    }
    else
    {
      XDrawString(nxagentDisplay, nxagentDrawable(pDrawable),
                      nxagentGC(pGC), x, y, string, count);
    }

    miPolyText8(nxagentVirtualDrawable(pDrawable), pGC, x, y, count, string);

    return width + x;
  }
  else
  {
    XDrawString(nxagentDisplay, nxagentDrawable(pDrawable), nxagentGC(pGC),
                    x, y, string, count);

    miPolyText8(pDrawable, pGC, x, y, count, string);
  }

  return width + x;
}

int nxagentPolyText16(DrawablePtr pDrawable, GCPtr pGC, int x,
                          int y, int count, unsigned short *string)
{
  /*
   * While the session is suspended the font structure is NULL.
   */

  if (nxagentFontStruct(pGC -> font) == NULL)
  {
    return x;
  }

  int width = XTextWidth16(nxagentFontStruct(pGC->font), (XChar2b *)string, count);

  if (nxagentGCTrap)
  {
    if ((pDrawable)->type == DRAWABLE_PIXMAP)
    {
      miPolyText16(nxagentVirtualDrawable(pDrawable), pGC, x, y, count, string);
    }
    else
    {
      miPolyText16(pDrawable, pGC, x, y, count, string);
    }

    return width + x;
  }

  if ((pDrawable)->type == DRAWABLE_PIXMAP)
  {
    #ifdef TEST
    fprintf(stderr, "GCOps: GC [%p] going to poly text16 on FB pixmap %p] with string [%s]\n",
                (void *) pGC, (void *) nxagentVirtualDrawable(pDrawable), (char *) string);
    #endif

    if (nxagentPixmapIsVirtual((PixmapPtr) pDrawable))
    {
      #ifdef TEST
      fprintf(stderr, "GCOps: poly text16 enters with virtual pixmap = [%p] parent is = [%p]\n",
                  (void *) nxagentVirtualDrawable(pDrawable),
                      (void *) nxagentRealPixmap((PixmapPtr) pDrawable));
      #endif

      if (nxagentRealPixmap((PixmapPtr) pDrawable))
      {
        XDrawString16(nxagentDisplay, nxagentDrawable((DrawablePtr) nxagentRealPixmap((PixmapPtr) pDrawable)),
                          nxagentGC(pGC), x, y, (XChar2b *)string, count);
      }
    }
    else
    {
      XDrawString16(nxagentDisplay, nxagentDrawable(pDrawable),
                        nxagentGC(pGC), x, y, (XChar2b *)string, count);
    }

    miPolyText16(nxagentVirtualDrawable(pDrawable), pGC, x, y, count, string);

    return width + x;
  }
  else
  {
    XDrawString16(nxagentDisplay, nxagentDrawable(pDrawable),
                      nxagentGC(pGC), x, y, (XChar2b *)string, count);

    miPolyText16(pDrawable, pGC, x, y, count, string);
  }

  return width + x;
}

void nxagentImageText8(DrawablePtr pDrawable, GCPtr pGC, int x,
                           int y, int count, char *string)
{
  if (nxagentGCTrap)
  {
    if ((pDrawable)->type == DRAWABLE_PIXMAP)
    {
      miImageText8(nxagentVirtualDrawable(pDrawable), pGC, x, y, count, string);
    }
    else
    {
      miImageText8(pDrawable, pGC, x, y, count, string);
    }

    return;
  }

  if ((pDrawable)->type == DRAWABLE_PIXMAP)
  {
    #ifdef TEST
    fprintf(stderr, "GCOps: GC [%p] going to image text8 on FB pixmap [%p] with string [%s].\n",
                (void *) pGC, (void *) nxagentVirtualDrawable(pDrawable), string);
    #endif

    if (nxagentPixmapIsVirtual((PixmapPtr) pDrawable))
    {
      #ifdef TEST
      fprintf(stderr, "GCOps: poly image text8 enters with virtual pixmap [%p] parent is [%p]\n",
                  (void *) nxagentVirtualDrawable(pDrawable),
                      (void *) nxagentRealPixmap((PixmapPtr) pDrawable));
      #endif

      if (nxagentRealPixmap((PixmapPtr) pDrawable))
      {
        XDrawImageString(nxagentDisplay, nxagentDrawable((DrawablePtr) nxagentRealPixmap((PixmapPtr) pDrawable)),
                             nxagentGC(pGC), x, y, string, count);
      }
    }
    else
    {
      XDrawImageString(nxagentDisplay, nxagentDrawable(pDrawable),
                           nxagentGC(pGC), x, y, string, count);
    }

    miImageText8(nxagentVirtualDrawable(pDrawable), pGC, x, y, count, string);

    return;
  }
  else
  {
    XDrawImageString(nxagentDisplay, nxagentDrawable(pDrawable),
                         nxagentGC(pGC), x, y, string, count);

    miImageText8(pDrawable, pGC, x, y, count, string);
  }
}

void nxagentImageText16(DrawablePtr pDrawable, GCPtr pGC, int x,
                            int y, int count, unsigned short *string)
{
  if (nxagentGCTrap)
  {
    if ((pDrawable)->type == DRAWABLE_PIXMAP)
    {
      miImageText16(nxagentVirtualDrawable(pDrawable), pGC, x, y, count, string);
    }
    else
    {
      miImageText16(pDrawable, pGC, x, y, count, string);
    }

    return;
  }

  if ((pDrawable)->type == DRAWABLE_PIXMAP)
  {
    #ifdef TEST
    fprintf(stderr, "GCOps: GC [%p] going to image text16 on FB pixmap [%p] with string [%s].\n",
                (void *) pGC, (void *) nxagentVirtualDrawable(pDrawable), (char *) string);
    #endif

    if (nxagentPixmapIsVirtual((PixmapPtr) pDrawable))
    {
      #ifdef TEST
      fprintf(stderr, "GCOps: poly image text16 enters with virtual pixmap = [%p] parent is = [%p]\n",
                  (void *) nxagentVirtualDrawable(pDrawable),
                      (void *) nxagentRealPixmap((PixmapPtr) pDrawable));
      #endif

      if (nxagentRealPixmap((PixmapPtr) pDrawable))
      {
        XDrawImageString16(nxagentDisplay, nxagentDrawable((DrawablePtr) nxagentRealPixmap((PixmapPtr) pDrawable)),
                               nxagentGC(pGC), x, y, (XChar2b *)string, count);
      }
    }
    else
    {
      XDrawImageString16(nxagentDisplay, nxagentDrawable(pDrawable),
                             nxagentGC(pGC), x, y, (XChar2b *)string, count);
    }

    miImageText16(nxagentVirtualDrawable(pDrawable), pGC, x, y, count, string);

    return;
  }
  else
  {
    XDrawImageString16(nxagentDisplay, nxagentDrawable(pDrawable),
                           nxagentGC(pGC), x, y, (XChar2b *)string, count);

    miImageText16(pDrawable, pGC, x, y, count, string);
  }
}

void nxagentImageGlyphBlt(DrawablePtr pDrawable, GCPtr pGC, int x, int y,
                              unsigned int nGlyphs, CharInfoPtr *pCharInfo,
                                  void * pGlyphBase)
{
  if ((pDrawable)->type == DRAWABLE_PIXMAP)
  {
    #ifdef TEST
    fprintf(stderr, "GCOps: GC [%p] going to imageGlyphBlt on FB pixmap [%p].\n",
                (void *) pGC, (void *) nxagentVirtualDrawable(pDrawable));
     #endif

    fbImageGlyphBlt(nxagentVirtualDrawable(pDrawable), pGC, x, y, nGlyphs, pCharInfo, pGlyphBase);
  }
  else
  {
    fbImageGlyphBlt(pDrawable, pGC, x, y, nGlyphs, pCharInfo, pGlyphBase);
  }
}

void nxagentPolyGlyphBlt(DrawablePtr pDrawable, GCPtr pGC, int x, int y,
                             unsigned int nGlyphs, CharInfoPtr *pCharInfo,
                                 void * pGlyphBase)
{
  if ((pDrawable)->type == DRAWABLE_PIXMAP)
  {
    #ifdef TEST
    fprintf(stderr, "GCOps: GC [%p] going to PolyGlyphBlt on FB pixmap [%p].\n",
                (void *) pGC, (void *) nxagentVirtualDrawable(pDrawable));
    #endif

    fbPolyGlyphBlt(nxagentVirtualDrawable(pDrawable), pGC, x, y, nGlyphs, pCharInfo, pGlyphBase);
  }
  else
  {
    fbPolyGlyphBlt(pDrawable, pGC, x, y, nGlyphs, pCharInfo, pGlyphBase);
  }
}

void nxagentPushPixels(GCPtr pGC, PixmapPtr pBitmap, DrawablePtr pDrawable,
                           int width, int height, int x, int y)
{
  if ((pDrawable)->type == DRAWABLE_PIXMAP)
  {
    #ifdef TEST
    fprintf(stderr, "GCOps: GC [%p] going to PushPixels on FB pixmap [%p].\n",
                (void *) pGC, (void *) nxagentVirtualDrawable(pDrawable));
    #endif

    fbPushPixels(pGC, nxagentVirtualPixmap(pBitmap),
                     (DrawablePtr) nxagentVirtualDrawable(pDrawable),
                         width, height, x, y);
  }
  else
  {
    fbPushPixels(pGC, nxagentVirtualPixmap(pBitmap),
                     (DrawablePtr) pDrawable, width, height, x, y);
  }
}
