/* * 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 */ Bool dmxInitGC(ScreenPtr pScreen) { if (!dixRegisterPrivateKey(&dmxGCPrivateKeyRec, PRIVATE_GC, 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 = RegionNumRects((RegionPtr)pGC->clientClip); pRects = malloc(nRects * sizeof(*pRects)); pBox = RegionRects((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); free(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); }