dmxgc.c revision 706f2543
1/*
2 * Copyright 2001-2004 Red Hat Inc., Durham, North Carolina.
3 *
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation on the rights to use, copy, modify, merge,
10 * publish, distribute, sublicense, and/or sell copies of the Software,
11 * and to permit persons to whom the Software is furnished to do so,
12 * subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial
16 * portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 * NON-INFRINGEMENT.  IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS
22 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
23 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
24 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 * SOFTWARE.
26 */
27
28/*
29 * Authors:
30 *   Kevin E. Martin <kem@redhat.com>
31 *
32 */
33
34/** \file
35 * This file provides support for GCs. */
36
37#ifdef HAVE_DMX_CONFIG_H
38#include <dmx-config.h>
39#endif
40
41#include "dmx.h"
42#include "dmxsync.h"
43#include "dmxgc.h"
44#include "dmxgcops.h"
45#include "dmxpixmap.h"
46#include "dmxfont.h"
47
48#include "gcstruct.h"
49#include "pixmapstr.h"
50#include "migc.h"
51
52static GCFuncs dmxGCFuncs = {
53    dmxValidateGC,
54    dmxChangeGC,
55    dmxCopyGC,
56    dmxDestroyGC,
57    dmxChangeClip,
58    dmxDestroyClip,
59    dmxCopyClip,
60};
61
62static GCOps dmxGCOps = {
63    dmxFillSpans,
64    dmxSetSpans,
65    dmxPutImage,
66    dmxCopyArea,
67    dmxCopyPlane,
68    dmxPolyPoint,
69    dmxPolylines,
70    dmxPolySegment,
71    dmxPolyRectangle,
72    dmxPolyArc,
73    dmxFillPolygon,
74    dmxPolyFillRect,
75    dmxPolyFillArc,
76    dmxPolyText8,
77    dmxPolyText16,
78    dmxImageText8,
79    dmxImageText16,
80    dmxImageGlyphBlt,
81    dmxPolyGlyphBlt,
82    dmxPushPixels
83};
84
85/** Initialize the GC on \a pScreen */
86Bool dmxInitGC(ScreenPtr pScreen)
87{
88    if (!dixRegisterPrivateKey(&dmxGCPrivateKeyRec, PRIVATE_GC, sizeof(dmxGCPrivRec)))
89            return FALSE;
90    return TRUE;
91}
92
93/** Create the GC on the back-end server. */
94void dmxBECreateGC(ScreenPtr pScreen, GCPtr pGC)
95{
96    DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
97    dmxGCPrivPtr   pGCPriv = DMX_GET_GC_PRIV(pGC);
98    int            i;
99
100    for (i = 0; i < dmxScreen->beNumPixmapFormats; i++) {
101	if (pGC->depth == dmxScreen->bePixmapFormats[i].depth) {
102	    unsigned long  mask;
103	    XGCValues      gcvals;
104
105	    mask = GCGraphicsExposures;
106	    gcvals.graphics_exposures = FALSE;
107
108	    /* Create GC in the back-end servers */
109	    pGCPriv->gc = XCreateGC(dmxScreen->beDisplay,
110				    dmxScreen->scrnDefDrawables[i],
111				    mask, &gcvals);
112	    break;
113	}
114    }
115}
116
117/** Create a graphics context on the back-end server associated /a pGC's
118 *  screen. */
119Bool dmxCreateGC(GCPtr pGC)
120{
121    ScreenPtr      pScreen = pGC->pScreen;
122    DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
123    dmxGCPrivPtr   pGCPriv = DMX_GET_GC_PRIV(pGC);
124    Bool           ret;
125
126    DMX_UNWRAP(CreateGC, dmxScreen, pScreen);
127    if ((ret = pScreen->CreateGC(pGC))) {
128	/* Save the old funcs */
129	pGCPriv->funcs = pGC->funcs;
130	pGCPriv->ops   = NULL;
131
132	pGC->funcs = &dmxGCFuncs;
133
134	if (dmxScreen->beDisplay) {
135	    dmxBECreateGC(pScreen, pGC);
136	} else {
137	    pGCPriv->gc = NULL;
138	}
139
140	/* Check for "magic special case"
141	 * 1. see CreateGC in dix/gc.c for more info
142	 * 2. see dmxChangeGC for more info
143	 */
144	pGCPriv->msc = (!pGC->tileIsPixel && !pGC->tile.pixmap);
145    }
146    DMX_WRAP(CreateGC, dmxCreateGC, dmxScreen, pScreen);
147
148    return ret;
149}
150
151/** Validate a graphics context, \a pGC, locally in the DMX server and
152 *  recompute the composite clip, if necessary. */
153void dmxValidateGC(GCPtr pGC, unsigned long changes, DrawablePtr pDrawable)
154{
155    dmxGCPrivPtr pGCPriv = DMX_GET_GC_PRIV(pGC);
156
157    DMX_GC_FUNC_PROLOGUE(pGC);
158#if 0
159    pGC->funcs->ValidateGC(pGC, changes, pDrawable);
160#endif
161
162    if (pDrawable->type == DRAWABLE_WINDOW ||
163	pDrawable->type == DRAWABLE_PIXMAP) {
164	/* Save the old ops, since we're about to change the ops in the
165	 * epilogue.
166	 */
167	pGCPriv->ops = pGC->ops;
168    } else {
169	pGCPriv->ops = NULL;
170    }
171
172    /* If the client clip is different or moved OR the subwindowMode has
173     * changed OR the window's clip has changed since the last
174     * validation, then we need to recompute the composite clip.
175     */
176    if ((changes & (GCClipXOrigin |
177		    GCClipYOrigin |
178		    GCClipMask |
179		    GCSubwindowMode)) ||
180	(pDrawable->serialNumber !=
181	 (pGC->serialNumber & DRAWABLE_SERIAL_BITS))) {
182	miComputeCompositeClip(pGC, pDrawable);
183    }
184
185    DMX_GC_FUNC_EPILOGUE(pGC);
186}
187
188/** Set the values in the graphics context on the back-end server
189 *  associated with \a pGC's screen. */
190void dmxChangeGC(GCPtr pGC, unsigned long mask)
191{
192    ScreenPtr      pScreen = pGC->pScreen;
193    DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
194    dmxGCPrivPtr   pGCPriv = DMX_GET_GC_PRIV(pGC);
195    XGCValues      v;
196
197    DMX_GC_FUNC_PROLOGUE(pGC);
198#if 0
199    pGC->funcs->ChangeGC(pGC, mask);
200#endif
201
202    /* Handle "magic special case" from CreateGC */
203    if (pGCPriv->msc) {
204	/* The "magic special case" is used to handle the case where a
205	 * foreground pixel is set when the GC is created so that a
206	 * "pseudo default-tile" can be created and used in case the
207	 * fillstyle was set to FillTiled.  This specific case is tested
208	 * in xtest (XCreateGC test #3).  What has happened in dix by
209	 * the time it reaches here is (1) the pGC->tile.pixel has been
210	 * set to pGC->fgPixel and pGC->tileIsPixel is set, (2) if a
211	 * tile has also been set, then pGC->tileIsPixel is unset and
212	 * pGC->tile.pixmap is initialized; else, the default tile is
213	 * created and pGC->tileIsPixel is unset and pGC->tile.pixmap is
214	 * initialized to the "pseudo default-tile".  In either case,
215	 * pGC->tile.pixmap is set; however, in the "magic special case"
216	 * the mask is not updated to allow us to detect that we should
217	 * initialize the GCTile in the back-end server.  Thus, we catch
218	 * this case in dmxCreateGC and add GCTile to the mask here.
219	 * Are there any cases that I've missed?
220	 */
221
222	/* Make sure that the tile.pixmap is set, just in case the user
223         * set GCTile in the mask but forgot to set vals.pixmap
224	 */
225	if (pGC->tile.pixmap) mask |= GCTile;
226
227	/* This only happens once when the GC is created */
228	pGCPriv->msc = FALSE;
229    }
230
231    /* Update back-end server's gc */
232    if (mask & GCFunction)          v.function = pGC->alu;
233    if (mask & GCPlaneMask)         v.plane_mask = pGC->planemask;
234    if (mask & GCForeground)        v.foreground = pGC->fgPixel;
235    if (mask & GCBackground)        v.background = pGC->bgPixel;
236    if (mask & GCLineWidth)         v.line_width = pGC->lineWidth;
237    if (mask & GCLineStyle)         v.line_style = pGC->lineStyle;
238    if (mask & GCCapStyle)          v.cap_style = pGC->capStyle;
239    if (mask & GCJoinStyle)         v.join_style = pGC->joinStyle;
240    if (mask & GCFillStyle)         v.fill_style = pGC->fillStyle;
241    if (mask & GCFillRule)          v.fill_rule = pGC->fillRule;
242    if (mask & GCTile) {
243	if (pGC->tileIsPixel) {
244	    mask &= ~GCTile;
245	} else {
246	    dmxPixPrivPtr  pPixPriv = DMX_GET_PIXMAP_PRIV(pGC->tile.pixmap);
247	    v.tile = (Drawable)pPixPriv->pixmap;
248	}
249    }
250    if (mask & GCStipple) {
251	dmxPixPrivPtr  pPixPriv = DMX_GET_PIXMAP_PRIV(pGC->stipple);
252	v.stipple = (Drawable)pPixPriv->pixmap;
253    }
254    if (mask & GCTileStipXOrigin)   v.ts_x_origin = pGC->patOrg.x;
255    if (mask & GCTileStipYOrigin)   v.ts_y_origin = pGC->patOrg.y;
256    if (mask & GCFont) {
257	if (dmxScreen->beDisplay) {
258	    dmxFontPrivPtr  pFontPriv;
259	    pFontPriv = FontGetPrivate(pGC->font, dmxFontPrivateIndex);
260	    v.font = pFontPriv->font[pScreen->myNum]->fid;
261	} else {
262	    mask &= ~GCFont;
263	}
264    }
265    if (mask & GCSubwindowMode)     v.subwindow_mode = pGC->subWindowMode;
266
267    /* Graphics exposures are not needed on the back-ends since they can
268       be generated on the front-end thereby saving bandwidth. */
269    if (mask & GCGraphicsExposures) mask &= ~GCGraphicsExposures;
270
271    if (mask & GCClipXOrigin)       v.clip_x_origin = pGC->clipOrg.x;
272    if (mask & GCClipYOrigin)       v.clip_y_origin = pGC->clipOrg.y;
273    if (mask & GCClipMask)          mask &= ~GCClipMask; /* See ChangeClip */
274    if (mask & GCDashOffset)        v.dash_offset = pGC->dashOffset;
275    if (mask & GCDashList) {
276	mask &= ~GCDashList;
277	if (dmxScreen->beDisplay)
278	    XSetDashes(dmxScreen->beDisplay, pGCPriv->gc,
279		       pGC->dashOffset, (char *)pGC->dash,
280		       pGC->numInDashList);
281    }
282    if (mask & GCArcMode)           v.arc_mode = pGC->arcMode;
283
284    if (mask && dmxScreen->beDisplay) {
285	XChangeGC(dmxScreen->beDisplay, pGCPriv->gc, mask, &v);
286	dmxSync(dmxScreen, FALSE);
287    }
288
289    DMX_GC_FUNC_EPILOGUE(pGC);
290}
291
292/** Copy \a pGCSrc to \a pGCDst on the back-end server associated with
293 *  \a pGCSrc's screen. */
294void dmxCopyGC(GCPtr pGCSrc, unsigned long changes, GCPtr pGCDst)
295{
296    ScreenPtr      pScreen = pGCSrc->pScreen;
297    DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
298    dmxGCPrivPtr   pGCSrcPriv = DMX_GET_GC_PRIV(pGCSrc);
299    dmxGCPrivPtr   pGCDstPriv = DMX_GET_GC_PRIV(pGCDst);
300
301    DMX_GC_FUNC_PROLOGUE(pGCDst);
302    pGCDst->funcs->CopyGC(pGCSrc, changes, pGCDst);
303
304    /* Copy the GC on the back-end server */
305    if (dmxScreen->beDisplay)
306	XCopyGC(dmxScreen->beDisplay, pGCSrcPriv->gc, changes, pGCDstPriv->gc);
307
308    DMX_GC_FUNC_EPILOGUE(pGCDst);
309}
310
311/** Free the \a pGC on the back-end server. */
312Bool dmxBEFreeGC(GCPtr pGC)
313{
314    ScreenPtr      pScreen   = pGC->pScreen;
315    DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
316    dmxGCPrivPtr   pGCPriv   = DMX_GET_GC_PRIV(pGC);
317
318    if (pGCPriv->gc) {
319	XFreeGC(dmxScreen->beDisplay, pGCPriv->gc);
320	pGCPriv->gc = NULL;
321	return TRUE;
322    }
323
324    return FALSE;
325}
326
327/** Destroy the graphics context, \a pGC and free the corresponding GC
328 *  on the back-end server. */
329void dmxDestroyGC(GCPtr pGC)
330{
331    ScreenPtr      pScreen   = pGC->pScreen;
332    DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
333
334    DMX_GC_FUNC_PROLOGUE(pGC);
335
336    /* Free the GC on the back-end server */
337    if (dmxScreen->beDisplay)
338	dmxBEFreeGC(pGC);
339
340    pGC->funcs->DestroyGC(pGC);
341    DMX_GC_FUNC_EPILOGUE(pGC);
342}
343
344/** Change the clip rects for a GC. */
345void dmxChangeClip(GCPtr pGC, int type, pointer pvalue, int nrects)
346{
347    ScreenPtr      pScreen = pGC->pScreen;
348    DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
349    dmxGCPrivPtr   pGCPriv = DMX_GET_GC_PRIV(pGC);
350    XRectangle    *pRects;
351    BoxPtr         pBox;
352    int            i, nRects;
353
354    DMX_GC_FUNC_PROLOGUE(pGC);
355    pGC->funcs->ChangeClip(pGC, type, pvalue, nrects);
356
357    /* Set the client clip on the back-end server */
358    switch (pGC->clientClipType) {
359    case CT_NONE:
360	if (dmxScreen->beDisplay)
361	    XSetClipMask(dmxScreen->beDisplay, pGCPriv->gc, None);
362	break;
363
364    case CT_REGION:
365	if (dmxScreen->beDisplay) {
366	    nRects = RegionNumRects((RegionPtr)pGC->clientClip);
367	    pRects = malloc(nRects * sizeof(*pRects));
368	    pBox   = RegionRects((RegionPtr)pGC->clientClip);
369
370	    for (i = 0; i < nRects; i++) {
371		pRects[i].x      = pBox[i].x1;
372		pRects[i].y      = pBox[i].y1;
373		pRects[i].width  = pBox[i].x2 - pBox[i].x1;
374		pRects[i].height = pBox[i].y2 - pBox[i].y1;
375	    }
376
377	    XSetClipRectangles(dmxScreen->beDisplay, pGCPriv->gc,
378			       pGC->clipOrg.x, pGC->clipOrg.y,
379			       pRects, nRects, Unsorted);
380
381	    free(pRects);
382	}
383	break;
384
385    case CT_PIXMAP:
386	/* Condensed down to REGION in the mi code */
387	break;
388    }
389
390    DMX_GC_FUNC_EPILOGUE(pGC);
391}
392
393/** Destroy a GC's clip rects. */
394void dmxDestroyClip(GCPtr pGC)
395{
396    ScreenPtr      pScreen = pGC->pScreen;
397    DMXScreenInfo *dmxScreen = &dmxScreens[pScreen->myNum];
398    dmxGCPrivPtr   pGCPriv = DMX_GET_GC_PRIV(pGC);
399
400    DMX_GC_FUNC_PROLOGUE(pGC);
401    pGC->funcs->DestroyClip(pGC);
402
403    /* Set the client clip on the back-end server to None */
404    if (dmxScreen->beDisplay)
405	XSetClipMask(dmxScreen->beDisplay, pGCPriv->gc, None);
406
407    DMX_GC_FUNC_EPILOGUE(pGC);
408}
409
410/** Copy a GC's clip rects. */
411void dmxCopyClip(GCPtr pGCDst, GCPtr pGCSrc)
412{
413    DMX_GC_FUNC_PROLOGUE(pGCDst);
414    pGCDst->funcs->CopyClip(pGCDst, pGCSrc);
415    DMX_GC_FUNC_EPILOGUE(pGCDst);
416}
417