text: /* $XFree86$ */
  /*
   * Copyright 2001-2004 Red Hat Inc., Durham, North Carolina.
   *
   * All Rights Reserved.
   *
   * Permission is hereby granted, free of charge, to any person obtaining
   * a copy of this software and associated documentation files (the
   * "Software"), to deal in the Software without restriction, including
   * without limitation on the rights to use, copy, modify, merge,
   * publish, distribute, sublicense, and/or sell copies of the Software,
   * and to permit persons to whom the Software is furnished to do so,
   * subject to the following conditions:
   *
   * The above copyright notice and this permission notice (including the
   * next paragraph) shall be included in all copies or substantial
   * portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
   * NON-INFRINGEMENT.  IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
   * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
   * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
   * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
   * SOFTWARE.
   */
  
  /*
   * Authors:
   *   Kevin E. Martin <kem@redhat.com>
   *
   */
  
  /** \file
   * This file provides support for GCs. */
  
  #ifdef HAVE_DMX_CONFIG_H
  #include <dmx-config.h>
  #endif
  
  #include "dmx.h"
  #include "dmxsync.h"
  #include "dmxgc.h"
  #include "dmxgcops.h"
  #include "dmxpixmap.h"
  #include "dmxfont.h"
  
  #include "gcstruct.h"
  #include "pixmapstr.h"
  #include "migc.h"
  
  static GCFuncs dmxGCFuncs = {
      dmxValidateGC,
      dmxChangeGC,
      dmxCopyGC,
      dmxDestroyGC,
      dmxChangeClip,
      dmxDestroyClip,
      dmxCopyClip,
  };
  
  static GCOps dmxGCOps = {
      dmxFillSpans,
      dmxSetSpans,
      dmxPutImage,
      dmxCopyArea,
      dmxCopyPlane,
      dmxPolyPoint,
      dmxPolylines,
      dmxPolySegment,
      dmxPolyRectangle,
      dmxPolyArc,
      dmxFillPolygon,
      dmxPolyFillRect,
      dmxPolyFillArc,
      dmxPolyText8,
      dmxPolyText16,
      dmxImageText8,
      dmxImageText16,
      dmxImageGlyphBlt,
      dmxPolyGlyphBlt,
      dmxPushPixels
  };
  
  /** Initialize the GC on \a pScreen, which currently involves allocating
   *  the GC private associated with this screen. */
  Bool dmxInitGC(ScreenPtr pScreen)
  {
      if (!AllocateGCPrivate(pScreen, dmxGCPrivateIndex, sizeof(dmxGCPrivRec)))
  	return FALSE;
  
      return TRUE;
  }
  
  /** Create the GC on the back-end server. */
  void dmxBECreateGC(ScreenPtr pScreen, GCPtr pGC)
  {
      DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
      dmxGCPrivPtr   pGCPriv = DMX_GET_GC_PRIV(pGC);
      int            i;
  
      for (i = 0; i < dmxScreen->beNumPixmapFormats; i++) {
  	if (pGC->depth == dmxScreen->bePixmapFormats[i].depth) {
  	    unsigned long  mask;
  	    XGCValues      gcvals;
  
  	    mask = GCGraphicsExposures;
  	    gcvals.graphics_exposures = FALSE;
  
  	    /* Create GC in the back-end servers */
  	    pGCPriv->gc = XCreateGC(dmxScreen->beDisplay,
  				    dmxScreen->scrnDefDrawables[i],
  				    mask, &gcvals);
  	    break;
  	}
      }
  }
  
  /** Create a graphics context on the back-end server associated /a pGC's
   *  screen. */
  Bool dmxCreateGC(GCPtr pGC)
  {
      ScreenPtr      pScreen = pGC->pScreen;
      DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
      dmxGCPrivPtr   pGCPriv = DMX_GET_GC_PRIV(pGC);
      Bool           ret;
  
      DMX_UNWRAP(CreateGC, dmxScreen, pScreen);
      if ((ret = pScreen->CreateGC(pGC))) {
  	/* Save the old funcs */
  	pGCPriv->funcs = pGC->funcs;
  	pGCPriv->ops   = NULL;
  
  	pGC->funcs = &dmxGCFuncs;
  
  	if (dmxScreen->beDisplay) {
  	    dmxBECreateGC(pScreen, pGC);
  	} else {
  	    pGCPriv->gc = NULL;
  	}
  
  	/* Check for "magic special case"
  	 * 1. see CreateGC in dix/gc.c for more info
  	 * 2. see dmxChangeGC for more info
  	 */
  	pGCPriv->msc = (!pGC->tileIsPixel && !pGC->tile.pixmap);
      }
      DMX_WRAP(CreateGC, dmxCreateGC, dmxScreen, pScreen);
  
      return ret;
  }
  
  /** Validate a graphics context, \a pGC, locally in the DMX server and
   *  recompute the composite clip, if necessary. */
  void dmxValidateGC(GCPtr pGC, unsigned long changes, DrawablePtr pDrawable)
  {
      dmxGCPrivPtr pGCPriv = DMX_GET_GC_PRIV(pGC);
  
      DMX_GC_FUNC_PROLOGUE(pGC);
  #if 0
      pGC->funcs->ValidateGC(pGC, changes, pDrawable);
  #endif
  
      if (pDrawable->type == DRAWABLE_WINDOW ||
  	pDrawable->type == DRAWABLE_PIXMAP) {
  	/* Save the old ops, since we're about to change the ops in the
  	 * epilogue.
  	 */
  	pGCPriv->ops = pGC->ops;
      } else {
  	pGCPriv->ops = NULL;
      }
  
      /* If the client clip is different or moved OR the subwindowMode has
       * changed OR the window's clip has changed since the last
       * validation, then we need to recompute the composite clip.
       */
      if ((changes & (GCClipXOrigin |
  		    GCClipYOrigin |
  		    GCClipMask |
  		    GCSubwindowMode)) ||
  	(pDrawable->serialNumber !=
  	 (pGC->serialNumber & DRAWABLE_SERIAL_BITS))) {
  	miComputeCompositeClip(pGC, pDrawable);
      }
  
      DMX_GC_FUNC_EPILOGUE(pGC);
  }
  
  /** Set the values in the graphics context on the back-end server
   *  associated with \a pGC's screen. */
  void dmxChangeGC(GCPtr pGC, unsigned long mask)
  {
      ScreenPtr      pScreen = pGC->pScreen;
      DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
      dmxGCPrivPtr   pGCPriv = DMX_GET_GC_PRIV(pGC);
      XGCValues      v;
  
      DMX_GC_FUNC_PROLOGUE(pGC);
  #if 0
      pGC->funcs->ChangeGC(pGC, mask);
  #endif
  
      /* Handle "magic special case" from CreateGC */
      if (pGCPriv->msc) {
  	/* The "magic special case" is used to handle the case where a
  	 * foreground pixel is set when the GC is created so that a
  	 * "pseudo default-tile" can be created and used in case the
  	 * fillstyle was set to FillTiled.  This specific case is tested
  	 * in xtest (XCreateGC test #3).  What has happened in dix by
  	 * the time it reaches here is (1) the pGC->tile.pixel has been
  	 * set to pGC->fgPixel and pGC->tileIsPixel is set, (2) if a
  	 * tile has also been set, then pGC->tileIsPixel is unset and
  	 * pGC->tile.pixmap is initialized; else, the default tile is
  	 * created and pGC->tileIsPixel is unset and pGC->tile.pixmap is
  	 * initialized to the "pseudo default-tile".  In either case,
  	 * pGC->tile.pixmap is set; however, in the "magic special case"
  	 * the mask is not updated to allow us to detect that we should
  	 * initialize the GCTile in the back-end server.  Thus, we catch
  	 * this case in dmxCreateGC and add GCTile to the mask here.
  	 * Are there any cases that I've missed?
  	 */
  
  	/* Make sure that the tile.pixmap is set, just in case the user
           * set GCTile in the mask but forgot to set vals.pixmap
  	 */
  	if (pGC->tile.pixmap) mask |= GCTile;
  
  	/* This only happens once when the GC is created */
  	pGCPriv->msc = FALSE;
      }
  
      /* Update back-end server's gc */
      if (mask & GCFunction)          v.function = pGC->alu;
      if (mask & GCPlaneMask)         v.plane_mask = pGC->planemask;
      if (mask & GCForeground)        v.foreground = pGC->fgPixel;
      if (mask & GCBackground)        v.background = pGC->bgPixel;
      if (mask & GCLineWidth)         v.line_width = pGC->lineWidth;
      if (mask & GCLineStyle)         v.line_style = pGC->lineStyle;
      if (mask & GCCapStyle)          v.cap_style = pGC->capStyle;
      if (mask & GCJoinStyle)         v.join_style = pGC->joinStyle;
      if (mask & GCFillStyle)         v.fill_style = pGC->fillStyle;
      if (mask & GCFillRule)          v.fill_rule = pGC->fillRule;
      if (mask & GCTile) {
  	if (pGC->tileIsPixel) {
  	    mask &= ~GCTile;
  	} else {
  	    dmxPixPrivPtr  pPixPriv = DMX_GET_PIXMAP_PRIV(pGC->tile.pixmap);
  	    v.tile = (Drawable)pPixPriv->pixmap;
  	}
      }
      if (mask & GCStipple) {
  	dmxPixPrivPtr  pPixPriv = DMX_GET_PIXMAP_PRIV(pGC->stipple);
  	v.stipple = (Drawable)pPixPriv->pixmap;
      }
      if (mask & GCTileStipXOrigin)   v.ts_x_origin = pGC->patOrg.x; 
      if (mask & GCTileStipYOrigin)   v.ts_y_origin = pGC->patOrg.y;
      if (mask & GCFont) {
  	if (dmxScreen->beDisplay) {
  	    dmxFontPrivPtr  pFontPriv;
  	    pFontPriv = FontGetPrivate(pGC->font, dmxFontPrivateIndex);
  	    v.font = pFontPriv->font[pScreen->myNum]->fid;
  	} else {
  	    mask &= ~GCFont;
  	}
      }
      if (mask & GCSubwindowMode)     v.subwindow_mode = pGC->subWindowMode;
  
      /* Graphics exposures are not needed on the back-ends since they can
         be generated on the front-end thereby saving bandwidth. */
      if (mask & GCGraphicsExposures) mask &= ~GCGraphicsExposures;
  
      if (mask & GCClipXOrigin)       v.clip_x_origin = pGC->clipOrg.x;
      if (mask & GCClipYOrigin)       v.clip_y_origin = pGC->clipOrg.y;
      if (mask & GCClipMask)          mask &= ~GCClipMask; /* See ChangeClip */
      if (mask & GCDashOffset)        v.dash_offset = pGC->dashOffset;
      if (mask & GCDashList) {
  	mask &= ~GCDashList;
  	if (dmxScreen->beDisplay)
  	    XSetDashes(dmxScreen->beDisplay, pGCPriv->gc,
  		       pGC->dashOffset, (char *)pGC->dash,
  		       pGC->numInDashList);
      }
      if (mask & GCArcMode)           v.arc_mode = pGC->arcMode;
  
      if (mask && dmxScreen->beDisplay) {
  	XChangeGC(dmxScreen->beDisplay, pGCPriv->gc, mask, &v);
  	dmxSync(dmxScreen, FALSE);
      }
  
      DMX_GC_FUNC_EPILOGUE(pGC);
  }
  
  /** Copy \a pGCSrc to \a pGCDst on the back-end server associated with
   *  \a pGCSrc's screen. */
  void dmxCopyGC(GCPtr pGCSrc, unsigned long changes, GCPtr pGCDst)
  {
      ScreenPtr      pScreen = pGCSrc->pScreen;
      DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
      dmxGCPrivPtr   pGCSrcPriv = DMX_GET_GC_PRIV(pGCSrc);
      dmxGCPrivPtr   pGCDstPriv = DMX_GET_GC_PRIV(pGCDst);
  
      DMX_GC_FUNC_PROLOGUE(pGCDst);
      pGCDst->funcs->CopyGC(pGCSrc, changes, pGCDst);
  
      /* Copy the GC on the back-end server */
      if (dmxScreen->beDisplay)
  	XCopyGC(dmxScreen->beDisplay, pGCSrcPriv->gc, changes, pGCDstPriv->gc);
  
      DMX_GC_FUNC_EPILOGUE(pGCDst);
  }
  
  /** Free the \a pGC on the back-end server. */
  Bool dmxBEFreeGC(GCPtr pGC)
  {
      ScreenPtr      pScreen   = pGC->pScreen;
      DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
      dmxGCPrivPtr   pGCPriv   = DMX_GET_GC_PRIV(pGC);
  
      if (pGCPriv->gc) {
  	XFreeGC(dmxScreen->beDisplay, pGCPriv->gc);
  	pGCPriv->gc = NULL;
  	return TRUE;
      }
  
      return FALSE;
  }
  
  /** Destroy the graphics context, \a pGC and free the corresponding GC
   *  on the back-end server. */
  void dmxDestroyGC(GCPtr pGC)
  {
      ScreenPtr      pScreen   = pGC->pScreen;
      DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
  
      DMX_GC_FUNC_PROLOGUE(pGC);
  
      /* Free the GC on the back-end server */
      if (dmxScreen->beDisplay)
  	dmxBEFreeGC(pGC);
  
      pGC->funcs->DestroyGC(pGC);
      DMX_GC_FUNC_EPILOGUE(pGC);
  }
  
  /** Change the clip rects for a GC. */
  void dmxChangeClip(GCPtr pGC, int type, pointer pvalue, int nrects)
  {
      ScreenPtr      pScreen = pGC->pScreen;
      DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
      dmxGCPrivPtr   pGCPriv = DMX_GET_GC_PRIV(pGC);
      XRectangle    *pRects;
      BoxPtr         pBox;
      int            i, nRects;
  
      DMX_GC_FUNC_PROLOGUE(pGC);
      pGC->funcs->ChangeClip(pGC, type, pvalue, nrects);
  
      /* Set the client clip on the back-end server */
      switch (pGC->clientClipType) {
      case CT_NONE:
  	if (dmxScreen->beDisplay)
  	    XSetClipMask(dmxScreen->beDisplay, pGCPriv->gc, None);
  	break;
  
      case CT_REGION:
  	if (dmxScreen->beDisplay) {
  	    nRects = REGION_NUM_RECTS((RegionPtr)pGC->clientClip);
  	    pRects = xalloc(nRects * sizeof(*pRects));
  	    pBox   = REGION_RECTS((RegionPtr)pGC->clientClip);
  
  	    for (i = 0; i < nRects; i++) {
  		pRects[i].x      = pBox[i].x1;
  		pRects[i].y      = pBox[i].y1;
  		pRects[i].width  = pBox[i].x2 - pBox[i].x1;
  		pRects[i].height = pBox[i].y2 - pBox[i].y1;
  	    }
  
  	    XSetClipRectangles(dmxScreen->beDisplay, pGCPriv->gc,
  			       pGC->clipOrg.x, pGC->clipOrg.y,
  			       pRects, nRects, Unsorted);
  
  	    xfree(pRects);
  	}
  	break;
  
      case CT_PIXMAP:
      case CT_UNSORTED:
      case CT_YSORTED:
      case CT_YXSORTED:
      case CT_YXBANDED:
  	/* These clip types are condensed down to either NONE or REGION
             in the mi code */
  	break;
      }
  
      DMX_GC_FUNC_EPILOGUE(pGC);
  }
  
  /** Destroy a GC's clip rects. */
  void dmxDestroyClip(GCPtr pGC)
  {
      ScreenPtr      pScreen = pGC->pScreen;
      DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
      dmxGCPrivPtr   pGCPriv = DMX_GET_GC_PRIV(pGC);
  
      DMX_GC_FUNC_PROLOGUE(pGC);
      pGC->funcs->DestroyClip(pGC);
  
      /* Set the client clip on the back-end server to None */
      if (dmxScreen->beDisplay)
  	XSetClipMask(dmxScreen->beDisplay, pGCPriv->gc, None);
  
      DMX_GC_FUNC_EPILOGUE(pGC);
  }
  
  /** Copy a GC's clip rects. */
  void dmxCopyClip(GCPtr pGCDst, GCPtr pGCSrc)
  {
      DMX_GC_FUNC_PROLOGUE(pGCDst);
      pGCDst->funcs->CopyClip(pGCDst, pGCSrc);
      DMX_GC_FUNC_EPILOGUE(pGCDst);
  }
  