/* * 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 * */ /** \file * This file provides support for GC operations. */ #ifdef HAVE_DMX_CONFIG_H #include #endif #include "dmx.h" #include "dmxsync.h" #include "dmxgc.h" #include "dmxgcops.h" #include "dmxwindow.h" #include "dmxpixmap.h" #include "mi.h" #include "gcstruct.h" #include "pixmapstr.h" #include "dixfontstr.h" #ifdef PANORAMIX #include "panoramiXsrv.h" #endif #define DMX_GCOPS_SET_DRAWABLE(_pDraw, _draw) \ do { \ if ((_pDraw)->type == DRAWABLE_WINDOW) { \ dmxWinPrivPtr pWinPriv = \ DMX_GET_WINDOW_PRIV((WindowPtr)(_pDraw)); \ (_draw) = (Drawable)pWinPriv->window; \ } else { \ dmxPixPrivPtr pPixPriv = \ DMX_GET_PIXMAP_PRIV((PixmapPtr)(_pDraw)); \ (_draw) = (Drawable)pPixPriv->pixmap; \ } \ } while (0) #define DMX_GCOPS_OFFSCREEN(_pDraw) \ (!dmxScreens[(_pDraw)->pScreen->myNum].beDisplay || \ (dmxOffScreenOpt && \ (_pDraw)->type == DRAWABLE_WINDOW && \ (DMX_GET_WINDOW_PRIV((WindowPtr)(_pDraw))->offscreen || \ !DMX_GET_WINDOW_PRIV((WindowPtr)(_pDraw))->window))) /** Fill spans -- this function should never be called. */ void dmxFillSpans(DrawablePtr pDrawable, GCPtr pGC, int nInit, DDXPointPtr pptInit, int *pwidthInit, int fSorted) { /* Error -- this should never happen! */ } /** Set spans -- this function should never be called. */ void dmxSetSpans(DrawablePtr pDrawable, GCPtr pGC, char *psrc, DDXPointPtr ppt, int *pwidth, int nspans, int fSorted) { /* Error -- this should never happen! */ } /** Transfer \a pBits image to back-end server associated with \a * pDrawable's screen. If primitive subdivision optimization is * enabled, then only transfer the sections of \a pBits that are * visible (i.e., not-clipped) to the back-end server. */ void dmxPutImage(DrawablePtr pDrawable, GCPtr pGC, int depth, int x, int y, int w, int h, int leftPad, int format, char *pBits) { DMXScreenInfo *dmxScreen = &dmxScreens[pDrawable->pScreen->myNum]; dmxGCPrivPtr pGCPriv = DMX_GET_GC_PRIV(pGC); XImage *img; if (DMX_GCOPS_OFFSCREEN(pDrawable)) return; img = XCreateImage(dmxScreen->beDisplay, dmxScreen->beVisuals[dmxScreen->beDefVisualIndex].visual, depth, format, leftPad, pBits, w, h, BitmapPad(dmxScreen->beDisplay), (format == ZPixmap) ? PixmapBytePad(w, depth) : BitmapBytePad(w+leftPad)); if (img) { Drawable draw; DMX_GCOPS_SET_DRAWABLE(pDrawable, draw); if (dmxSubdividePrimitives && pGC->pCompositeClip) { RegionPtr pSubImages; RegionPtr pClip; BoxRec box; BoxPtr pBox; int nBox; box.x1 = x; box.y1 = y; box.x2 = x + w; box.y2 = y + h; pSubImages = REGION_CREATE(pGC->pScreen, &box, 1); pClip = REGION_CREATE(pGC->pScreen, NullBox, 1); REGION_COPY(pGC->pScreen, pClip, pGC->pCompositeClip); REGION_TRANSLATE(pGC->pScreen, pClip, -pDrawable->x, -pDrawable->y); REGION_INTERSECT(pGC->pScreen, pSubImages, pSubImages, pClip); nBox = REGION_NUM_RECTS(pSubImages); pBox = REGION_RECTS(pSubImages); while (nBox--) { XPutImage(dmxScreen->beDisplay, draw, pGCPriv->gc, img, pBox->x1 - box.x1, pBox->y1 - box.y1, pBox->x1, pBox->y1, pBox->x2 - pBox->x1, pBox->y2 - pBox->y1); pBox++; } REGION_DESTROY(pGC->pScreen, pClip); REGION_DESTROY(pGC->pScreen, pSubImages); } else { XPutImage(dmxScreen->beDisplay, draw, pGCPriv->gc, img, 0, 0, x, y, w, h); } XFree(img); /* Use XFree instead of XDestroyImage * because pBits is passed in from the * caller. */ dmxSync(dmxScreen, FALSE); } else { /* Error -- this should not happen! */ } } /** Copy area from \a pSrc drawable to \a pDst drawable on the back-end * server associated with \a pSrc drawable's screen. If the offscreen * optimization is enabled, only copy when both \a pSrc and \a pDst are * at least partially visible. */ RegionPtr dmxCopyArea(DrawablePtr pSrc, DrawablePtr pDst, GCPtr pGC, int srcx, int srcy, int w, int h, int dstx, int dsty) { DMXScreenInfo *dmxScreen = &dmxScreens[pSrc->pScreen->myNum]; dmxGCPrivPtr pGCPriv = DMX_GET_GC_PRIV(pGC); Drawable srcDraw, dstDraw; if (DMX_GCOPS_OFFSCREEN(pSrc) || DMX_GCOPS_OFFSCREEN(pDst)) return miHandleExposures(pSrc, pDst, pGC, srcx, srcy, w, h, dstx, dsty, 0L); DMX_GCOPS_SET_DRAWABLE(pSrc, srcDraw); DMX_GCOPS_SET_DRAWABLE(pDst, dstDraw); XCopyArea(dmxScreen->beDisplay, srcDraw, dstDraw, pGCPriv->gc, srcx, srcy, w, h, dstx, dsty); dmxSync(dmxScreen, FALSE); return miHandleExposures(pSrc, pDst, pGC, srcx, srcy, w, h, dstx, dsty, 0L); } /** Copy plane number \a bitPlane from \a pSrc drawable to \a pDst * drawable on the back-end server associated with \a pSrc drawable's * screen. If the offscreen optimization is enabled, only copy when * both \a pSrc and \a pDst are at least partially visible. */ RegionPtr dmxCopyPlane(DrawablePtr pSrc, DrawablePtr pDst, GCPtr pGC, int srcx, int srcy, int width, int height, int dstx, int dsty, unsigned long bitPlane) { DMXScreenInfo *dmxScreen = &dmxScreens[pSrc->pScreen->myNum]; dmxGCPrivPtr pGCPriv = DMX_GET_GC_PRIV(pGC); Drawable srcDraw, dstDraw; if (DMX_GCOPS_OFFSCREEN(pSrc) || DMX_GCOPS_OFFSCREEN(pDst)) return miHandleExposures(pSrc, pDst, pGC, srcx, srcy, width, height, dstx, dsty, bitPlane); DMX_GCOPS_SET_DRAWABLE(pSrc, srcDraw); DMX_GCOPS_SET_DRAWABLE(pDst, dstDraw); XCopyPlane(dmxScreen->beDisplay, srcDraw, dstDraw, pGCPriv->gc, srcx, srcy, width, height, dstx, dsty, bitPlane); dmxSync(dmxScreen, FALSE); return miHandleExposures(pSrc, pDst, pGC, srcx, srcy, width, height, dstx, dsty, bitPlane); } /** Render list of points, \a pptInit in \a pDrawable on the back-end * server associated with \a pDrawable's screen. If the offscreen * optimization is enabled, only draw when \a pDrawable is at least * partially visible. */ void dmxPolyPoint(DrawablePtr pDrawable, GCPtr pGC, int mode, int npt, DDXPointPtr pptInit) { DMXScreenInfo *dmxScreen = &dmxScreens[pDrawable->pScreen->myNum]; dmxGCPrivPtr pGCPriv = DMX_GET_GC_PRIV(pGC); Drawable draw; if (DMX_GCOPS_OFFSCREEN(pDrawable)) return; DMX_GCOPS_SET_DRAWABLE(pDrawable, draw); XDrawPoints(dmxScreen->beDisplay, draw, pGCPriv->gc, (XPoint *)pptInit, npt, mode); dmxSync(dmxScreen, FALSE); } /** Render list of connected lines, \a pptInit in \a pDrawable on the * back-end server associated with \a pDrawable's screen. If the * offscreen optimization is enabled, only draw when \a pDrawable is at * least partially visible. */ void dmxPolylines(DrawablePtr pDrawable, GCPtr pGC, int mode, int npt, DDXPointPtr pptInit) { DMXScreenInfo *dmxScreen = &dmxScreens[pDrawable->pScreen->myNum]; dmxGCPrivPtr pGCPriv = DMX_GET_GC_PRIV(pGC); Drawable draw; if (DMX_GCOPS_OFFSCREEN(pDrawable)) return; DMX_GCOPS_SET_DRAWABLE(pDrawable, draw); XDrawLines(dmxScreen->beDisplay, draw, pGCPriv->gc, (XPoint *)pptInit, npt, mode); dmxSync(dmxScreen, FALSE); } /** Render list of disjoint segments, \a pSegs in \a pDrawable on the * back-end server associated with \a pDrawable's screen. If the * offscreen optimization is enabled, only draw when \a pDrawable is at * least partially visible. */ void dmxPolySegment(DrawablePtr pDrawable, GCPtr pGC, int nseg, xSegment *pSegs) { DMXScreenInfo *dmxScreen = &dmxScreens[pDrawable->pScreen->myNum]; dmxGCPrivPtr pGCPriv = DMX_GET_GC_PRIV(pGC); Drawable draw; if (DMX_GCOPS_OFFSCREEN(pDrawable)) return; DMX_GCOPS_SET_DRAWABLE(pDrawable, draw); XDrawSegments(dmxScreen->beDisplay, draw, pGCPriv->gc, (XSegment *)pSegs, nseg); dmxSync(dmxScreen, FALSE); } /** Render list of rectangle outlines, \a pRects in \a pDrawable on the * back-end server associated with \a pDrawable's screen. If the * offscreen optimization is enabled, only draw when \a pDrawable is at * least partially visible. */ void dmxPolyRectangle(DrawablePtr pDrawable, GCPtr pGC, int nrects, xRectangle *pRects) { DMXScreenInfo *dmxScreen = &dmxScreens[pDrawable->pScreen->myNum]; dmxGCPrivPtr pGCPriv = DMX_GET_GC_PRIV(pGC); Drawable draw; if (DMX_GCOPS_OFFSCREEN(pDrawable)) return; DMX_GCOPS_SET_DRAWABLE(pDrawable, draw); XDrawRectangles(dmxScreen->beDisplay, draw, pGCPriv->gc, (XRectangle *)pRects, nrects); dmxSync(dmxScreen, FALSE); } /** Render list of arc outlines, \a parcs in \a pDrawable on the * back-end server associated with \a pDrawable's screen. If the * offscreen optimization is enabled, only draw when \a pDrawable is at * least partially visible. */ void dmxPolyArc(DrawablePtr pDrawable, GCPtr pGC, int narcs, xArc *parcs) { DMXScreenInfo *dmxScreen = &dmxScreens[pDrawable->pScreen->myNum]; dmxGCPrivPtr pGCPriv = DMX_GET_GC_PRIV(pGC); Drawable draw; if (DMX_GCOPS_OFFSCREEN(pDrawable)) return; DMX_GCOPS_SET_DRAWABLE(pDrawable, draw); XDrawArcs(dmxScreen->beDisplay, draw, pGCPriv->gc, (XArc *)parcs, narcs); dmxSync(dmxScreen, FALSE); } /** Render a filled polygons in \a pDrawable on the back-end server * associated with \a pDrawable's screen. If the offscreen * optimization is enabled, only draw when \a pDrawable is at least * partially visible. */ void dmxFillPolygon(DrawablePtr pDrawable, GCPtr pGC, int shape, int mode, int count, DDXPointPtr pPts) { DMXScreenInfo *dmxScreen = &dmxScreens[pDrawable->pScreen->myNum]; dmxGCPrivPtr pGCPriv = DMX_GET_GC_PRIV(pGC); Drawable draw; if (DMX_GCOPS_OFFSCREEN(pDrawable)) return; DMX_GCOPS_SET_DRAWABLE(pDrawable, draw); XFillPolygon(dmxScreen->beDisplay, draw, pGCPriv->gc, (XPoint *)pPts, count, shape, mode); dmxSync(dmxScreen, FALSE); } /** Render list of filled rectangles, \a prectInit in \a pDrawable on * the back-end server associated with \a pDrawable's screen. If the * offscreen optimization is enabled, only draw when \a pDrawable is at * least partially visible. */ void dmxPolyFillRect(DrawablePtr pDrawable, GCPtr pGC, int nrectFill, xRectangle *prectInit) { DMXScreenInfo *dmxScreen = &dmxScreens[pDrawable->pScreen->myNum]; dmxGCPrivPtr pGCPriv = DMX_GET_GC_PRIV(pGC); Drawable draw; if (DMX_GCOPS_OFFSCREEN(pDrawable)) return; DMX_GCOPS_SET_DRAWABLE(pDrawable, draw); XFillRectangles(dmxScreen->beDisplay, draw, pGCPriv->gc, (XRectangle *)prectInit, nrectFill); dmxSync(dmxScreen, FALSE); } /** Render list of filled arcs, \a parcs in \a pDrawable on the back-end * server associated with \a pDrawable's screen. If the offscreen * optimization is enabled, only draw when \a pDrawable is at least * partially visible. */ void dmxPolyFillArc(DrawablePtr pDrawable, GCPtr pGC, int narcs, xArc *parcs) { DMXScreenInfo *dmxScreen = &dmxScreens[pDrawable->pScreen->myNum]; dmxGCPrivPtr pGCPriv = DMX_GET_GC_PRIV(pGC); Drawable draw; if (DMX_GCOPS_OFFSCREEN(pDrawable)) return; DMX_GCOPS_SET_DRAWABLE(pDrawable, draw); XFillArcs(dmxScreen->beDisplay, draw, pGCPriv->gc, (XArc *)parcs, narcs); dmxSync(dmxScreen, FALSE); } /** Render string of 8-bit \a chars (foreground only) in \a pDrawable on * the back-end server associated with \a pDrawable's screen. If the * offscreen optimization is enabled, only draw when \a pDrawable is at * least partially visible. */ int dmxPolyText8(DrawablePtr pDrawable, GCPtr pGC, int x, int y, int count, char *chars) { DMXScreenInfo *dmxScreen = &dmxScreens[pDrawable->pScreen->myNum]; dmxGCPrivPtr pGCPriv = DMX_GET_GC_PRIV(pGC); unsigned long n, i; int w; CharInfoPtr charinfo[255]; Drawable draw; GetGlyphs(pGC->font, (unsigned long)count, (unsigned char *)chars, Linear8Bit, &n, charinfo); /* Calculate text width */ w = 0; for (i = 0; i < n; i++) w += charinfo[i]->metrics.characterWidth; if (n != 0 && !DMX_GCOPS_OFFSCREEN(pDrawable)) { DMX_GCOPS_SET_DRAWABLE(pDrawable, draw); XDrawString(dmxScreen->beDisplay, draw, pGCPriv->gc, x, y, chars, count); dmxSync(dmxScreen, FALSE); } return x+w; } /** Render string of 16-bit \a chars (foreground only) in \a pDrawable * on the back-end server associated with \a pDrawable's screen. If * the offscreen optimization is enabled, only draw when \a pDrawable * is at least partially visible. */ int dmxPolyText16(DrawablePtr pDrawable, GCPtr pGC, int x, int y, int count, unsigned short *chars) { DMXScreenInfo *dmxScreen = &dmxScreens[pDrawable->pScreen->myNum]; dmxGCPrivPtr pGCPriv = DMX_GET_GC_PRIV(pGC); unsigned long n, i; int w; CharInfoPtr charinfo[255]; Drawable draw; GetGlyphs(pGC->font, (unsigned long)count, (unsigned char *)chars, (FONTLASTROW(pGC->font) == 0) ? Linear16Bit : TwoD16Bit, &n, charinfo); /* Calculate text width */ w = 0; for (i = 0; i < n; i++) w += charinfo[i]->metrics.characterWidth; if (n != 0 && !DMX_GCOPS_OFFSCREEN(pDrawable)) { DMX_GCOPS_SET_DRAWABLE(pDrawable, draw); XDrawString16(dmxScreen->beDisplay, draw, pGCPriv->gc, x, y, (XChar2b *)chars, count); dmxSync(dmxScreen, FALSE); } return x+w; } /** Render string of 8-bit \a chars (both foreground and background) in * \a pDrawable on the back-end server associated with \a pDrawable's * screen. If the offscreen optimization is enabled, only draw when \a * pDrawable is at least partially visible. */ void dmxImageText8(DrawablePtr pDrawable, GCPtr pGC, int x, int y, int count, char *chars) { DMXScreenInfo *dmxScreen = &dmxScreens[pDrawable->pScreen->myNum]; dmxGCPrivPtr pGCPriv = DMX_GET_GC_PRIV(pGC); Drawable draw; if (DMX_GCOPS_OFFSCREEN(pDrawable)) return; DMX_GCOPS_SET_DRAWABLE(pDrawable, draw); XDrawImageString(dmxScreen->beDisplay, draw, pGCPriv->gc, x, y, chars, count); dmxSync(dmxScreen, FALSE); } /** Render string of 16-bit \a chars (both foreground and background) in * \a pDrawable on the back-end server associated with \a pDrawable's * screen. If the offscreen optimization is enabled, only draw when \a * pDrawable is at least partially visible. */ void dmxImageText16(DrawablePtr pDrawable, GCPtr pGC, int x, int y, int count, unsigned short *chars) { DMXScreenInfo *dmxScreen = &dmxScreens[pDrawable->pScreen->myNum]; dmxGCPrivPtr pGCPriv = DMX_GET_GC_PRIV(pGC); Drawable draw; if (DMX_GCOPS_OFFSCREEN(pDrawable)) return; DMX_GCOPS_SET_DRAWABLE(pDrawable, draw); XDrawImageString16(dmxScreen->beDisplay, draw, pGCPriv->gc, x, y, (XChar2b *)chars, count); dmxSync(dmxScreen, FALSE); } /** Image Glyph Blt -- this function should never be called. */ void dmxImageGlyphBlt(DrawablePtr pDrawable, GCPtr pGC, int x, int y, unsigned int nglyph, CharInfoPtr *ppci, pointer pglyphBase) { /* Error -- this should never happen! */ } /** Poly Glyph Blt -- this function should never be called. */ void dmxPolyGlyphBlt(DrawablePtr pDrawable, GCPtr pGC, int x, int y, unsigned int nglyph, CharInfoPtr *ppci, pointer pglyphBase) { /* Error -- this should never happen! */ } /** Push Pixels -- this function should never be called. */ void dmxPushPixels(GCPtr pGC, PixmapPtr pBitMap, DrawablePtr pDst, int w, int h, int x, int y) { /* Error -- this should never happen! */ } /********************************************************************** * Miscellaneous drawing commands */ /** When Xinerama is active, the client pixmaps are always obtained from * screen 0. When screen 0 is detached, the pixmaps must be obtained * from any other screen that is not detached. Usually, this is screen * 1. */ static DMXScreenInfo *dmxFindAlternatePixmap(DrawablePtr pDrawable, XID *draw) { #ifdef PANORAMIX PanoramiXRes *pXinPix; int i; DMXScreenInfo *dmxScreen; if (noPanoramiXExtension) return NULL; if (pDrawable->type != DRAWABLE_PIXMAP) return NULL; if (!(pXinPix = (PanoramiXRes *)LookupIDByType(pDrawable->id, XRT_PIXMAP))) return NULL; for (i = 1; i < PanoramiXNumScreens; i++) { dmxScreen = &dmxScreens[i]; if (dmxScreen->beDisplay) { PixmapPtr pSrc; dmxPixPrivPtr pSrcPriv; pSrc = (PixmapPtr)LookupIDByType(pXinPix->info[i].id, RT_PIXMAP); pSrcPriv = DMX_GET_PIXMAP_PRIV(pSrc); if (pSrcPriv->pixmap) { *draw = pSrcPriv->pixmap; return dmxScreen; } } } #endif return NULL; } /** Get an image from the back-end server associated with \a pDrawable's * screen. If \a pDrawable is a window, it must be viewable to get an * image from it. If it is not viewable, then get the image from the * first ancestor of \a pDrawable that is viewable. If no viewable * ancestor is found, then simply return without getting an image. */ void dmxGetImage(DrawablePtr pDrawable, int sx, int sy, int w, int h, unsigned int format, unsigned long planeMask, char *pdstLine) { DMXScreenInfo *dmxScreen = &dmxScreens[pDrawable->pScreen->myNum]; XImage *img; Drawable draw; /* Cannot get image from unviewable window */ if (pDrawable->type == DRAWABLE_WINDOW) { WindowPtr pWindow = (WindowPtr)pDrawable; if (!pWindow->viewable) { while (!pWindow->viewable && pWindow->parent) { sx += pWindow->origin.x - wBorderWidth(pWindow); sx += pWindow->origin.y - wBorderWidth(pWindow); pWindow = pWindow->parent; } if (!pWindow->viewable) { return; } } DMX_GCOPS_SET_DRAWABLE(&pWindow->drawable, draw); if (DMX_GCOPS_OFFSCREEN(&pWindow->drawable)) return; } else { DMX_GCOPS_SET_DRAWABLE(pDrawable, draw); if (DMX_GCOPS_OFFSCREEN(pDrawable)) { /* Try to find the pixmap on a non-detached Xinerama screen */ dmxScreen = dmxFindAlternatePixmap(pDrawable, &draw); if (!dmxScreen) return; } } img = XGetImage(dmxScreen->beDisplay, draw, sx, sy, w, h, planeMask, format); if (img) { int len = img->bytes_per_line * img->height; memmove(pdstLine, img->data, len); XDestroyImage(img); } dmxSync(dmxScreen, FALSE); } /** Get Spans -- this function should never be called. */ void dmxGetSpans(DrawablePtr pDrawable, int wMax, DDXPointPtr ppt, int *pwidth, int nspans, char *pdstStart) { /* Error -- this should never happen! */ }