Edit

IABSD.fr/xenocara/app/xterm/cachedGCs.c

Branch :

  • Show log

    Commit

  • Author : matthieu
    Date : 2026-05-14 09:07:11
    Hash : c50f7c66
    Message : Update to xterm 410. Tested by kirill@ and naddy@

  • app/xterm/cachedGCs.c
  • /* $XTermId: cachedGCs.c,v 1.88 2026/04/07 21:16:36 tom Exp $ */
    
    /*
     * Copyright 2007-2025,2026 by Thomas E. Dickey
     *
     *                         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 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 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 NONINFRINGEMENT.
     * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) 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.
     *
     * Except as contained in this notice, the name(s) of the above copyright
     * holders shall not be used in advertising or otherwise to promote the
     * sale, use or other dealings in this Software without prior written
     * authorization.
     */
    
    #include <data.h>
    #include <xstrings.h>
    #include <fontutils.h>
    
    #include <X11/Xmu/Drawing.h>
    
    /*
     * hide (or eliminate) calls to
     *	XCreateGC()
     *	XFreeGC()
     *	XGetGCValues()
     *	XSetBackground()
     *	XSetFont()
     *	XSetForeground()
     *	XtGetGC()
     *	XtReleaseGC()
     * by associating an integer with each GC, maintaining a cache which
     * reflects frequency of use rather than most recent usage.
     *
     * FIXME: XTermFonts should hold gc, font, fs.
     */
    typedef struct {
        GC gc;
        unsigned used;
        unsigned cset;
        XTermFonts *font;
        Pixel tile;
        Pixel fg;
        Pixel bg;
    } CgsCacheData;
    
    #define DEPTH 8
    #define ITEM()      (int) (me->data - me->list)
    #define LIST(item)  me->list[item]
    #define LINK(item)  me->data = (me->list + (item))
    #define THIS(field) me->data->field
    #define NEXT(field) me->next.field
    
    #define HaveFont(font) (Boolean) ((font) != NULL && (font)->fs != NULL)
    
    #define GC_CSet GCFunction
    
    typedef struct {
        CgsCacheData list[DEPTH];
        CgsCacheData *data;		/* points to current list[] entry */
        XtGCMask mask;		/* changes since the last getCgsGC() */
        CgsCacheData next;		/* updated values, apply in getCgsGC() */
    } CgsCache;
    
    #if OPT_TRACE
    #define CASE(name) case gc##name: result = #name; break
    static const char *
    traceCgsEnum(CgsEnum value)
    {
        const char *result = "?";
        switch (value) {
    	CASE(Norm);
    	CASE(Bold);
    	CASE(NormReverse);
    	CASE(BoldReverse);
    	CASE(Border);
    	CASE(Filler);
    #if OPT_BOX_CHARS || OPT_WIDE_CHARS
    	CASE(Line);
    	CASE(Dots);
    #endif
    #if OPT_DEC_CHRSET
    	CASE(CNorm);
    	CASE(CBold);
    #endif
    #if OPT_WIDE_CHARS
    	CASE(Wide);
    	CASE(WBold);
    	CASE(WideReverse);
    	CASE(WBoldReverse);
    #endif
    	CASE(VTcursNormal);
    	CASE(VTcursFilled);
    	CASE(VTcursReverse);
    	CASE(VTcursOutline);
    #if OPT_TEK4014
    	CASE(TKcurs);
    #endif
    	CASE(MAX);
        }
        return result;
    }
    
    #undef CASE
    
    static const char *
    traceVTwin(XtermWidget xw, const VTwin *value)
    {
        const char *result = "?";
        if (value == NULL)
    	result = "null";
        else if (value == &(TScreenOf(xw)->fullVwin))
    	result = "fullVwin";
    #ifndef NO_ACTIVE_ICON
        else if (value == &(TScreenOf(xw)->iconVwin))
    	result = "iconVwin";
    #endif
        return result;
    }
    
    #if OPT_TRACE > 1
    static String
    traceCSet(unsigned cset)
    {
        static char result[80];
        switch (cset) {
        case CSET_SWL:
    	strcpy(result, "SWL");
    	break;
        case CSET_DHL_TOP:
    	strcpy(result, "DHL_TOP");
    	break;
        case CSET_DHL_BOT:
    	strcpy(result, "DHL_BOT");
    	break;
        case CSET_DWL:
    	strcpy(result, "DWL");
    	break;
        default:
    	sprintf(result, "%#x", cset);
    	break;
        }
        return result;
    }
    
    static String
    traceFont(XTermFonts * font)
    {
        static char result[80];
    
        return strcpy(result, font != NULL ? visibleFont(font->fs) : "null");
    }
    
    static String
    tracePixel(XtermWidget xw, Pixel value)
    {
    #define CASE(name) { name, #name }
        static struct {
    	TermColors code;
    	String name;
        } t_colors[] = {
    	CASE(TEXT_FG),
    	    CASE(TEXT_BG),
    	    CASE(TEXT_CURSOR),
    	    CASE(MOUSE_FG),
    	    CASE(MOUSE_BG),
    #if OPT_TEK4014
    	    CASE(TEK_FG),
    	    CASE(TEK_BG),
    #endif
    #if OPT_HIGHLIGHT_COLOR
    	    CASE(HIGHLIGHT_BG),
    	    CASE(HIGHLIGHT_FG),
    #endif
    #if OPT_TEK4014
    	    CASE(TEK_CURSOR),
    #endif
        };
        TScreen *screen = TScreenOf(xw);
        String result = 0;
        int n;
    
        for (n = 0; n < NCOLORS; ++n) {
    	if (value == T_COLOR(screen, t_colors[n].code)) {
    	    result = t_colors[n].name;
    	    break;
    	}
        }
    
        if (result == 0) {
    	for (n = 0; n < MAXCOLORS; ++n) {
    	    if (screen->Acolors[n].mode > 0
    		&& value == screen->Acolors[n].value) {
    		result = screen->Acolors[n].resource;
    		break;
    	    }
    	}
        }
    
        if (result == 0) {
    	char temp[80];
    	sprintf(temp, "%#lx", value);
    	result = x_strdup(temp);
        }
    
        return result;
    }
    
    #undef CASE
    
    #endif /* OPT_TRACE > 1 */
    #endif /* OPT_TRACE */
    
    static CgsCache *
    allocCache(void **cache_pointer)
    {
        if (*cache_pointer == NULL) {
    	*cache_pointer = TypeCallocN(CgsCache, gcMAX);
    	TRACE(("allocCache %p\n", *cache_pointer));
        }
        return *((CgsCache **) cache_pointer);
    }
    
    #define ALLOC_CACHE(p) ((*(p) == NULL) ? allocCache(p) : *(p))
    
    static int
    dataIndex(const CgsCache * me)
    {
        return ITEM();
    }
    
    static void
    relinkData(CgsCache * me, int item)
    {
        LINK(item);
    }
    
    /*
     * Returns the appropriate cache pointer.
     */
    static CgsCache *
    myCache(XtermWidget xw, VTwin *cgsWin, CgsEnum cgsId)
    {
        CgsCache *result = NULL;
    
        if ((int) cgsId >= 0 && cgsId < gcMAX) {
    #ifdef NO_ACTIVE_ICON
    	(void) xw;
    	(void) cgsWin;
    #else
    	if (cgsWin == &(TScreenOf(xw)->iconVwin))
    	    result = ALLOC_CACHE(&(TScreenOf(xw)->icon_cgs_cache));
    	else
    #endif
    	    result = ALLOC_CACHE(&(TScreenOf(xw)->main_cgs_cache));
    
    	result += cgsId;
    	if (result->data == NULL) {
    	    result->data = result->list;
    	}
        }
    
        return result;
    }
    
    static Display *
    myDisplay(XtermWidget xw)
    {
        return TScreenOf(xw)->display;
    }
    
    static Drawable
    myDrawable(XtermWidget xw, const VTwin *cgsWin)
    {
        Drawable drawable = 0;
    
        if (cgsWin != NULL && cgsWin->window != 0)
    	drawable = cgsWin->window;
        if (drawable == 0)
    	drawable = RootWindowOfScreen(XtScreen(xw));
        return drawable;
    }
    
    static GC
    newCache(XtermWidget xw, const VTwin *cgsWin, CgsEnum cgsId, CgsCache * me)
    {
        XGCValues xgcv;
        XtGCMask mask;
    
        THIS(font) = NEXT(font);
        THIS(cset) = NEXT(cset);
        THIS(fg) = NEXT(fg);
        THIS(bg) = NEXT(bg);
    
        memset(&xgcv, 0, sizeof(xgcv));
        xgcv.font = NEXT(font)->fs->fid;
        mask = (GCForeground | GCBackground | GCFont);
    
        switch (cgsId) {
        case gcFiller:
        case gcBorder:
    	mask &= (XtGCMask) ~ GCFont;
    	/* FALLTHRU */
        case gcNorm:
        case gcBold:
        case gcNormReverse:
        case gcBoldReverse:
    #if OPT_WIDE_CHARS
        case gcWide:
        case gcWBold:
        case gcWideReverse:
        case gcWBoldReverse:
    #endif
    	mask |= (GCGraphicsExposures | GCFunction);
    	xgcv.graphics_exposures = True;		/* default */
    	xgcv.function = GXcopy;
    	break;
    #if OPT_BOX_CHARS || OPT_WIDE_CHARS
        case gcLine:
    	mask |= (GCGraphicsExposures | GCFunction);
    	xgcv.graphics_exposures = True;		/* default */
    	xgcv.function = GXcopy;
    	break;
        case gcDots:
    	xgcv.fill_style = FillTiled;
    	xgcv.tile =
    	    XmuCreateStippledPixmap(XtScreen((Widget) xw),
    				    THIS(fg),
    				    THIS(bg),
    				    xw->core.depth);
    	THIS(tile) = xgcv.tile;
    	mask = (GCForeground | GCBackground);
    	mask |= (GCGraphicsExposures | GCFunction | GCTile | GCFillStyle);
    	xgcv.graphics_exposures = True;		/* default */
    	xgcv.function = GXcopy;
    	break;
    #endif
    #if OPT_DEC_CHRSET
        case gcCNorm:
        case gcCBold:
    	break;
    #endif
        case gcVTcursNormal:	/* FALLTHRU */
        case gcVTcursFilled:	/* FALLTHRU */
        case gcVTcursReverse:	/* FALLTHRU */
        case gcVTcursOutline:	/* FALLTHRU */
    	break;
    #if OPT_TEK4014
        case gcTKcurs:		/* FALLTHRU */
    	/* FIXME */
    #endif
        case gcMAX:		/* should not happen */
    	return NULL;
        }
        xgcv.foreground = NEXT(fg);
        xgcv.background = NEXT(bg);
    
        THIS(gc) = XCreateGC(myDisplay(xw), myDrawable(xw, cgsWin), mask, &xgcv);
        TRACE(("getCgsGC(%s) created gc %p(%d)\n",
    	   traceCgsEnum(cgsId), (void *) THIS(gc), ITEM()));
    
        THIS(used) = 0;
        return THIS(gc);
    }
    
    #define SameFont(a, b) \
    	(Boolean) (HaveFont(a) \
    		   && HaveFont(b) \
    		   && (((a)->fs == (b)->fs) \
    		       || !memcmp((a)->fs, (b)->fs, sizeof(*((a)->fs)))))
    
    #define SameColor(a,b) ((a) == (b))
    #define SameCSet(a,b)  ((a) == (b))
    
    static GC
    chgCache(XtermWidget xw, CgsEnum cgsId GCC_UNUSED, CgsCache * me, Bool both)
    {
        XGCValues xgcv;
        XtGCMask mask = (GCForeground | GCBackground | GCFont);
    
        memset(&xgcv, 0, sizeof(xgcv));
    
        TRACE2(("chgCache(%s) old data fg=%s, bg=%s, font=%s cset %s\n",
    	    traceCgsEnum(cgsId),
    	    tracePixel(xw, THIS(fg)),
    	    tracePixel(xw, THIS(bg)),
    	    traceFont(THIS(font)),
    	    traceCSet(THIS(cset))));
    #if OPT_TRACE > 1
        if (!SameFont(THIS(font), NEXT(font)))
    	TRACE2(("...chgCache new font=%s\n", traceFont(NEXT(font))));
        if (!SameCSet(THIS(cset), NEXT(cset)))
    	TRACE2(("...chgCache new cset=%s\n", traceCSet(NEXT(cset))));
        if (!SameColor(THIS(fg), NEXT(fg)))
    	TRACE2(("...chgCache new fg=%s\n", tracePixel(xw, NEXT(fg))));
        if (!SameColor(THIS(bg), NEXT(bg)))
    	TRACE2(("...chgCache new bg=%s\n", tracePixel(xw, NEXT(bg))));
    #endif
    
        if (both) {
    	THIS(font) = NEXT(font);
    	THIS(cset) = NEXT(cset);
        }
        THIS(fg) = NEXT(fg);
        THIS(bg) = NEXT(bg);
    
        xgcv.font = THIS(font)->fs->fid;
        xgcv.foreground = THIS(fg);
        xgcv.background = THIS(bg);
    
        XChangeGC(myDisplay(xw), THIS(gc), mask, &xgcv);
        TRACE2(("...chgCache(%s) updated gc %p(%d)\n",
    	    traceCgsEnum(cgsId), THIS(gc), ITEM()));
    
        THIS(used) = 0;
        return THIS(gc);
    }
    
    /*
     * Use the "setCgsXXXX()" calls to initialize parameters for a new GC.
     */
    void
    setCgsFore(XtermWidget xw, VTwin *cgsWin, CgsEnum cgsId, Pixel fg)
    {
        CgsCache *me;
    
        if ((me = myCache(xw, cgsWin, cgsId)) != NULL) {
    	NEXT(fg) = fg;
    	me->mask |= GCForeground;
    	TRACE2(("setCgsFore(%s) %s\n",
    		traceCgsEnum(cgsId),
    		tracePixel(xw, NEXT(fg))));
        }
    }
    
    void
    setCgsBack(XtermWidget xw, VTwin *cgsWin, CgsEnum cgsId, Pixel bg)
    {
        CgsCache *me;
    
        if ((me = myCache(xw, cgsWin, cgsId)) != NULL) {
    	NEXT(bg) = bg;
    	me->mask |= GCBackground;
    	TRACE2(("setCgsBack(%s) %s\n",
    		traceCgsEnum(cgsId),
    		tracePixel(xw, NEXT(bg))));
        }
    }
    
    #if OPT_DEC_CHRSET
    void
    setCgsCSet(XtermWidget xw, VTwin *cgsWin, CgsEnum cgsId, unsigned cset)
    {
        CgsCache *me;
    
        if ((me = myCache(xw, cgsWin, cgsId)) != NULL) {
    	NEXT(cset) = cset;
    	me->mask |= GC_CSet;
        }
    }
    #else
    #define setCgsCSet(xw, cgsWin, dstCgsId, cset)	/* nothing */
    #endif
    
    void
    setCgsFont2(XtermWidget xw, VTwin *cgsWin, CgsEnum cgsId, XTermFonts * font, unsigned which)
    {
        CgsCache *me;
    
        if ((me = myCache(xw, cgsWin, cgsId)) != NULL) {
    	TScreen *screen = TScreenOf(xw);
    	if (!HaveFont(font)) {
    	    if (cgsId != gcNorm)
    		(void) getCgsGC(xw, cgsWin, gcNorm);
    #ifndef NO_ACTIVE_ICON
    	    if (cgsWin == &(TScreenOf(xw)->iconVwin))
    		font = getIconicFont(screen);
    	    else
    #endif
    		font = GetNormalFont(screen, which);
    	}
    	if (HaveFont(font) && okFont(font->fs)) {
    	    TRACE2(("setCgsFont next: %s for %s slot %p, gc %p\n",
    		    traceFont(font), traceCgsEnum(cgsId),
    		    me, THIS(gc)));
    	    TRACE2(("...next font was %s\n", traceFont(NEXT(font))));
    	    NEXT(font) = font;
    	    me->mask |= GCFont;
    	} else {
    	    /* EMPTY */
    	    TRACE2(("...NOT updated font for %s\n",
    		    traceCgsEnum(cgsId)));
    	}
        }
    }
    
    void
    setCgsFont(XtermWidget xw, VTwin *cgsWin, CgsEnum cgsId, XTermFonts * font)
    {
        setCgsFont2(xw, cgsWin, cgsId, font, fNorm);
    }
    
    /*
     * Discard all of the font information, e.g., we are resizing the font.
     * Keep the GC's so we can simply change them rather than creating new ones.
     */
    void
    clrCgsFonts(XtermWidget xw, VTwin *cgsWin, const XTermFonts * font)
    {
        if (HaveFont(font)) {
    	int j;
    	for_each_gc(j) {
    	    CgsCache *me;
    	    if ((me = myCache(xw, cgsWin, (CgsEnum) j)) != NULL) {
    		int k;
    		for (k = 0; k < DEPTH; ++k) {
    		    if (SameFont(LIST(k).font, font)) {
    			TRACE2(("clrCgsFonts %s gc %p(%d) %s\n",
    				traceCgsEnum((CgsEnum) j),
    				LIST(k).gc,
    				k,
    				traceFont(font)));
    			LIST(k).font = NULL;
    			LIST(k).cset = 0;
    		    }
    		}
    		if (SameFont(NEXT(font), font)) {
    		    TRACE2(("clrCgsFonts %s next %s\n",
    			    traceCgsEnum((CgsEnum) j),
    			    traceFont(font)));
    		    NEXT(font) = NULL;
    		    NEXT(cset) = 0;
    		    me->mask &= (unsigned) ~(GCFont | GC_CSet);
    		}
    	    }
    	}
        }
    }
    
    /*
     * Return a GC associated with the given id, allocating if needed.
     */
    GC
    getCgsGC(XtermWidget xw, VTwin *cgsWin, CgsEnum cgsId)
    {
        CgsCache *me;
        GC result = NULL;
    
        if ((me = myCache(xw, cgsWin, cgsId)) != NULL) {
    	TRACE2(("getCgsGC(%s, %s)\n",
    		traceVTwin(xw, cgsWin), traceCgsEnum(cgsId)));
    	if (me->mask != 0) {
    	    int j;
    	    unsigned used = 0;
    
    	    /* fill in the unchanged fields */
    	    if (!(me->mask & GC_CSet))
    		NEXT(cset) = 0;	/* OPT_DEC_CHRSET */
    	    if (!(me->mask & GCFont))
    		NEXT(font) = THIS(font);
    	    if (!(me->mask & GCForeground))
    		NEXT(fg) = THIS(fg);
    	    if (!(me->mask & GCBackground))
    		NEXT(bg) = THIS(bg);
    
    	    if (NEXT(font) == NULL) {
    		setCgsFont(xw, cgsWin, cgsId, NULL);
    	    }
    
    	    TRACE2(("...Cgs new data fg=%s, bg=%s, font=%s cset %s\n",
    		    tracePixel(xw, NEXT(fg)),
    		    tracePixel(xw, NEXT(bg)),
    		    traceFont(NEXT(font)),
    		    traceCSet(NEXT(cset))));
    
    	    /* try to find the given data in an already-created GC */
    	    for (j = 0; j < DEPTH; ++j) {
    		if (LIST(j).gc != NULL
    		    && SameFont(LIST(j).font, NEXT(font))
    		    && SameCSet(LIST(j).cset, NEXT(cset))
    		    && SameColor(LIST(j).fg, NEXT(fg))
    		    && SameColor(LIST(j).bg, NEXT(bg))) {
    		    LINK(j);
    		    result = THIS(gc);
    		    TRACE2(("getCgsGC existing %p(%d)\n", result, ITEM()));
    		    break;
    		}
    	    }
    
    	    if (result == NULL) {
    		/* try to find an empty slot, to create a new GC */
    		used = 0;
    		for (j = 0; j < DEPTH; ++j) {
    		    if (LIST(j).gc == NULL) {
    			LINK(j);
    			result = newCache(xw, cgsWin, cgsId, me);
    			break;
    		    }
    		    if (used < LIST(j).used)
    			used = LIST(j).used;
    		}
    	    }
    
    	    if (result == NULL) {
    		int k;
    		/* if none were empty, pick the least-used slot, to modify */
    		for (j = 0, k = -1; j < DEPTH; ++j) {
    		    if (used >= LIST(j).used) {
    			used = LIST(j).used;
    			k = j;
    		    }
    		}
    		if (k >= 0) {
    		    LINK(k);
    		    TRACE2(("...getCgsGC least-used(%d) was %d\n", k, THIS(used)));
    		    result = chgCache(xw, cgsId, me, True);
    		}
    	    }
    	    me->next = *(me->data);
    	} else {
    	    result = THIS(gc);
    	}
    	me->mask = 0;
    	THIS(used) += 1;
    	TRACE2(("...getCgsGC(%s, %s) gc %p(%d), used %d\n",
    		traceVTwin(xw, cgsWin),
    		traceCgsEnum(cgsId), result, ITEM(), THIS(used)));
        }
        return result;
    }
    
    /*
     * Return the font for the given GC.
     */
    CgsEnum
    getCgsId(XtermWidget xw, VTwin *cgsWin, GC gc)
    {
        int n;
        CgsEnum result = gcNorm;
    
        for_each_gc(n) {
    	const CgsCache *me;
    
    	if ((me = myCache(xw, cgsWin, (CgsEnum) n)) != NULL) {
    	    if (THIS(gc) == gc) {
    		result = (CgsEnum) n;
    		break;
    	    }
    	}
        }
        return result;
    }
    
    /*
     * Return the font for the given GC.
     */
    XTermFonts *
    getCgsFont(XtermWidget xw, VTwin *cgsWin, GC gc)
    {
        int n;
        XTermFonts *result = NULL;
    
        for_each_gc(n) {
    	CgsCache *me;
    
    	if ((me = myCache(xw, cgsWin, (CgsEnum) n)) != NULL) {
    	    if (THIS(gc) == gc) {
    		result = THIS(font);
    		break;
    	    }
    	}
        }
        return result;
    }
    
    /*
     * Return the foreground color for the given GC.
     */
    Pixel
    getCgsFore(XtermWidget xw, VTwin *cgsWin, GC gc)
    {
        int n;
        Pixel result = 0;
    
        for_each_gc(n) {
    	CgsCache *me;
    
    	if ((me = myCache(xw, cgsWin, (CgsEnum) n)) != NULL) {
    	    if (THIS(gc) == gc) {
    		result = THIS(fg);
    		break;
    	    }
    	}
        }
        return result;
    }
    
    /*
     * Return the background color for the given GC.
     */
    Pixel
    getCgsBack(XtermWidget xw, VTwin *cgsWin, GC gc)
    {
        int n;
        Pixel result = 0;
    
        for_each_gc(n) {
    	CgsCache *me;
    
    	if ((me = myCache(xw, cgsWin, (CgsEnum) n)) != NULL) {
    	    if (THIS(gc) == gc) {
    		result = THIS(bg);
    		break;
    	    }
    	}
        }
        return result;
    }
    
    /*
     * Copy the parameters (except GC of course) from one cache record to another.
     */
    void
    copyCgs(XtermWidget xw, VTwin *cgsWin, CgsEnum dstCgsId, CgsEnum srcCgsId)
    {
        if (dstCgsId != srcCgsId) {
    	CgsCache *me;
    
    	if ((me = myCache(xw, cgsWin, srcCgsId)) != NULL) {
    	    TRACE(("copyCgs from %s to %s\n",
    		   traceCgsEnum(srcCgsId),
    		   traceCgsEnum(dstCgsId)));
    	    TRACE2(("copyCgs from %s (me %p, fg %s, bg %s, cset %s) to %s "
    		    TRACE_L "\n",
    		    traceCgsEnum(srcCgsId),
    		    me,
    		    tracePixel(xw, THIS(fg)),
    		    tracePixel(xw, THIS(bg)),
    		    traceCSet(THIS(cset)),
    		    traceCgsEnum(dstCgsId)));
    	    setCgsCSet(xw, cgsWin, dstCgsId, THIS(cset));
    	    setCgsFore(xw, cgsWin, dstCgsId, THIS(fg));
    	    setCgsBack(xw, cgsWin, dstCgsId, THIS(bg));
    	    setCgsFont(xw, cgsWin, dstCgsId, THIS(font));
    	    TRACE2(("...copyCgs " TRACE_R "\n"));
    	}
        }
    }
    
    /*
     * Interchange colors in the cache, e.g., for reverse-video.
     */
    void
    redoCgs(XtermWidget xw, Pixel fg, Pixel bg, CgsEnum cgsId)
    {
        VTwin *cgsWin = WhichVWin(TScreenOf(xw));
        CgsCache *me = myCache(xw, cgsWin, cgsId);
    
        if (me != NULL) {
    	CgsCacheData *save_data = me->data;
    	int n;
    
    	for (n = 0; n < DEPTH; ++n) {
    	    if (LIST(n).gc != NULL && HaveFont(LIST(n).font)) {
    		LINK(n);
    
    		if (LIST(n).fg == fg
    		    && LIST(n).bg == bg) {
    		    setCgsFore(xw, cgsWin, cgsId, bg);
    		    setCgsBack(xw, cgsWin, cgsId, fg);
    		} else if (LIST(n).fg == bg
    			   && LIST(n).bg == fg) {
    		    setCgsFore(xw, cgsWin, cgsId, fg);
    		    setCgsBack(xw, cgsWin, cgsId, bg);
    		} else {
    		    continue;
    		}
    
    		(void) chgCache(xw, cgsId, me, False);
    	    }
    	}
    	me->data = save_data;
        }
    }
    
    /*
     * Swap the cache records, e.g., when doing reverse-video.
     */
    void
    swapCgs(XtermWidget xw, VTwin *cgsWin, CgsEnum dstCgsId, CgsEnum srcCgsId)
    {
        if (dstCgsId != srcCgsId) {
    	CgsCache *src;
    
    	if ((src = myCache(xw, cgsWin, srcCgsId)) != NULL) {
    	    CgsCache *dst;
    
    	    if ((dst = myCache(xw, cgsWin, dstCgsId)) != NULL) {
    		CgsCache tmp;
    		int srcIndex = dataIndex(src);
    		int dstIndex = dataIndex(dst);
    
    		EXCHANGE(*src, *dst, tmp);
    
    		relinkData(src, dstIndex);
    		relinkData(dst, srcIndex);
    	    }
    	}
        }
    }
    
    /*
     * Free any GC associated with the given id.
     */
    GC
    freeCgs(XtermWidget xw, VTwin *cgsWin, CgsEnum cgsId)
    {
        CgsCache *me;
    
        if ((me = myCache(xw, cgsWin, cgsId)) != NULL) {
    	int j;
    
    	for (j = 0; j < DEPTH; ++j) {
    	    if (LIST(j).gc != NULL) {
    		TRACE(("freeCgs(%s, %s) gc %p(%d)\n",
    		       traceVTwin(xw, cgsWin),
    		       traceCgsEnum(cgsId), (void *) LIST(j).gc, j));
    		clrCgsFonts(xw, cgsWin, LIST(j).font);
    #if OPT_BOX_CHARS
    		if (cgsId == gcDots) {
    		    XmuReleaseStippledPixmap(XtScreen((Widget) xw), LIST(j).tile);
    		}
    #endif
    		XFreeGC(TScreenOf(xw)->display, LIST(j).gc);
    		memset(&LIST(j), 0, sizeof(LIST(j)));
    	    }
    	    LINK(0);
    	}
        }
        return NULL;
    }
    
    #ifdef NO_LEAKS
    void
    noleaks_cachedCgs(XtermWidget xw)
    {
    #ifndef NO_ACTIVE_ICON
        free(TScreenOf(xw)->icon_cgs_cache);
    #endif
        free(TScreenOf(xw)->main_cgs_cache);
    }
    #endif