cachedGCs.c revision a1f3da82
1/* $XTermId: cachedGCs.c,v 1.60 2011/02/09 10:11:44 tom Exp $ */
2
3/************************************************************
4
5Copyright 2007-2010,2011 by Thomas E. Dickey
6
7                        All Rights Reserved
8
9Permission is hereby granted, free of charge, to any person obtaining a
10copy of this software and associated documentation files (the
11"Software"), to deal in the Software without restriction, including
12without limitation the rights to use, copy, modify, merge, publish,
13distribute, sublicense, and/or sell copies of the Software, and to
14permit persons to whom the Software is furnished to do so, subject to
15the following conditions:
16
17The above copyright notice and this permission notice shall be included
18in all copies or substantial portions of the Software.
19
20THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
23IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
24CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
25TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
26SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27
28Except as contained in this notice, the name(s) of the above copyright
29holders shall not be used in advertising or otherwise to promote the
30sale, use or other dealings in this Software without prior written
31authorization.
32
33********************************************************/
34
35#include <data.h>
36#include <xstrings.h>
37
38#include <X11/Xmu/Drawing.h>
39
40#include <stdio.h>
41
42/*
43 * hide (or eliminate) calls to
44 *	XCreateGC()
45 *	XFreeGC()
46 *	XGetGCValues()
47 *	XSetBackground()
48 *	XSetFont()
49 *	XSetForeground()
50 *	XtGetGC()
51 *	XtReleaseGC()
52 * by associating an integer with each GC, maintaining a cache which
53 * reflects frequency of use rather than most recent usage.
54 *
55 * FIXME: XTermFonts should hold gc, font, fs.
56 */
57typedef struct {
58    GC gc;
59    unsigned used;
60    unsigned cset;
61    XTermFonts *font;
62    Pixel tile;
63    Pixel fg;
64    Pixel bg;
65} CgsCacheData;
66
67#define DEPTH 8
68#define ITEM()      (int) (me->data - me->list)
69#define LIST(item)  me->list[item]
70#define LINK(item)  me->data = (me->list + (item))
71#define THIS(field) me->data->field
72#define NEXT(field) me->next.field
73
74#define HaveFont(font) (Boolean) ((font) != 0 && (font)->fs != 0)
75
76#define GC_CSet GCFunction
77
78typedef struct {
79    CgsCacheData list[DEPTH];
80    CgsCacheData *data;		/* points to current list[] entry */
81    XtGCMask mask;		/* changes since the last getCgsGC() */
82    CgsCacheData next;		/* updated values, apply in getCgsGC() */
83} CgsCache;
84
85#if OPT_TRACE
86#define CASE(name) case gc##name: result = #name; break
87static const char *
88traceCgsEnum(CgsEnum value)
89{
90    const char *result = "?";
91    switch (value) {
92	CASE(Norm);
93	CASE(Bold);
94	CASE(NormReverse);
95	CASE(BoldReverse);
96#if OPT_BOX_CHARS
97	CASE(Line);
98	CASE(Dots);
99#endif
100#if OPT_DEC_CHRSET
101	CASE(CNorm);
102	CASE(CBold);
103#endif
104#if OPT_WIDE_CHARS
105	CASE(Wide);
106	CASE(WBold);
107	CASE(WideReverse);
108	CASE(WBoldReverse);
109#endif
110	CASE(VTcursNormal);
111	CASE(VTcursFilled);
112	CASE(VTcursReverse);
113	CASE(VTcursOutline);
114#if OPT_TEK4014
115	CASE(TKcurs);
116#endif
117	CASE(MAX);
118    }
119    return result;
120}
121
122#undef CASE
123
124static const char *
125traceVTwin(XtermWidget xw, VTwin * value)
126{
127    const char *result = "?";
128    if (value == 0)
129	result = "null";
130    else if (value == &(TScreenOf(xw)->fullVwin))
131	result = "fullVwin";
132#ifndef NO_ACTIVE_ICON
133    else if (value == &(TScreenOf(xw)->iconVwin))
134	result = "iconVwin";
135#endif
136    return result;
137}
138
139#if OPT_TRACE > 1
140static String
141traceCSet(unsigned cset)
142{
143    static char result[80];
144    switch (cset) {
145    case CSET_SWL:
146	strcpy(result, "SWL");
147	break;
148    case CSET_DHL_TOP:
149	strcpy(result, "DHL_TOP");
150	break;
151    case CSET_DHL_BOT:
152	strcpy(result, "DHL_BOT");
153	break;
154    case CSET_DWL:
155	strcpy(result, "DWL");
156	break;
157    default:
158	sprintf(result, "%#x", cset);
159	break;
160    }
161    return result;
162}
163
164static String
165traceFont(XTermFonts * font)
166{
167    static char result[80];
168
169    if (HaveFont(font)) {
170	XFontStruct *fs = font->fs;
171	sprintf(result, "%p(%dx%d %d %#lx)",
172		fs,
173		fs->max_bounds.width,
174		fs->max_bounds.ascent + fs->max_bounds.descent,
175		fs->max_bounds.descent,
176		(unsigned long) (fs->fid));
177    } else {
178	strcpy(result, "null");
179    }
180    return result;
181}
182
183static String
184tracePixel(XtermWidget xw, Pixel value)
185{
186#define CASE(name) { name, #name }
187    static struct {
188	TermColors code;
189	String name;
190    } t_colors[] = {
191	CASE(TEXT_FG),
192	    CASE(TEXT_BG),
193	    CASE(TEXT_CURSOR),
194	    CASE(MOUSE_FG),
195	    CASE(MOUSE_BG),
196#if OPT_TEK4014
197	    CASE(TEK_FG),
198	    CASE(TEK_BG),
199#endif
200#if OPT_HIGHLIGHT_COLOR
201	    CASE(HIGHLIGHT_BG),
202	    CASE(HIGHLIGHT_FG),
203#endif
204#if OPT_TEK4014
205	    CASE(TEK_CURSOR),
206#endif
207    };
208    TScreen *screen = TScreenOf(xw);
209    String result = 0;
210    int n;
211
212    for (n = 0; n < NCOLORS; ++n) {
213	if (value == T_COLOR(screen, t_colors[n].code)) {
214	    result = t_colors[n].name;
215	    break;
216	}
217    }
218
219    if (result == 0) {
220	for (n = 0; n < MAXCOLORS; ++n) {
221#if OPT_COLOR_RES
222	    if (screen->Acolors[n].mode > 0
223		&& value == screen->Acolors[n].value) {
224		result = screen->Acolors[n].resource;
225		break;
226	    }
227#else
228	    if (value == screen->Acolors[n]) {
229		char temp[80];
230		sprintf(temp, "Acolors[%d]", n);
231		result = x_strdup(temp);
232		break;
233	    }
234#endif
235	}
236    }
237
238    if (result == 0) {
239	char temp[80];
240	sprintf(temp, "%#lx", value);
241	result = x_strdup(temp);
242    }
243
244    return result;
245}
246
247#undef CASE
248
249#endif /* OPT_TRACE > 1 */
250#endif /* OPT_TRACE */
251
252static CgsCache *
253allocCache(void **cache_pointer)
254{
255    if (*cache_pointer == 0) {
256	*cache_pointer = TypeCallocN(CgsCache, gcMAX);
257	TRACE(("allocCache %p\n", *cache_pointer));
258    }
259    return *((CgsCache **) cache_pointer);
260}
261
262static int
263dataIndex(CgsCache * me)
264{
265    return ITEM();
266}
267
268static void
269relinkData(CgsCache * me, int item)
270{
271    LINK(item);
272}
273
274/*
275 * Returns the appropriate cache pointer.
276 */
277static CgsCache *
278myCache(XtermWidget xw, VTwin * cgsWin, CgsEnum cgsId)
279{
280    CgsCache *result = 0;
281
282    if ((int) cgsId >= 0 && cgsId < gcMAX) {
283#ifdef NO_ACTIVE_ICON
284	(void) xw;
285	(void) cgsWin;
286#else
287	if (cgsWin == &(TScreenOf(xw)->iconVwin))
288	    result = allocCache(&(TScreenOf(xw)->icon_cgs_cache));
289	else
290#endif
291	    result = allocCache(&(TScreenOf(xw)->main_cgs_cache));
292
293	result += cgsId;
294	if (result->data == 0) {
295	    result->data = result->list;
296	}
297    }
298
299    return result;
300}
301
302static Display *
303myDisplay(XtermWidget xw)
304{
305    return TScreenOf(xw)->display;
306}
307
308static Drawable
309myDrawable(XtermWidget xw, VTwin * cgsWin)
310{
311    Drawable drawable = 0;
312
313    if (cgsWin != 0 && cgsWin->window != 0)
314	drawable = cgsWin->window;
315    if (drawable == 0)
316	drawable = RootWindowOfScreen(XtScreen(xw));
317    return drawable;
318}
319
320static GC
321newCache(XtermWidget xw, VTwin * cgsWin, CgsEnum cgsId, CgsCache * me)
322{
323    XGCValues xgcv;
324    XtGCMask mask;
325
326    THIS(font) = NEXT(font);
327    THIS(cset) = NEXT(cset);
328    THIS(fg) = NEXT(fg);
329    THIS(bg) = NEXT(bg);
330
331    memset(&xgcv, 0, sizeof(xgcv));
332    xgcv.font = NEXT(font)->fs->fid;
333    mask = (GCForeground | GCBackground | GCFont);
334
335    switch (cgsId) {
336    case gcNorm:
337    case gcBold:
338    case gcNormReverse:
339    case gcBoldReverse:
340#if OPT_WIDE_CHARS
341    case gcWide:
342    case gcWBold:
343    case gcWideReverse:
344    case gcWBoldReverse:
345#endif
346	mask |= (GCGraphicsExposures | GCFunction);
347	xgcv.graphics_exposures = True;		/* default */
348	xgcv.function = GXcopy;
349	break;
350#if OPT_BOX_CHARS
351    case gcLine:
352	mask |= (GCGraphicsExposures | GCFunction);
353	xgcv.graphics_exposures = True;		/* default */
354	xgcv.function = GXcopy;
355	break;
356    case gcDots:
357	xgcv.fill_style = FillTiled;
358	xgcv.tile =
359	    XmuCreateStippledPixmap(XtScreen((Widget) xw),
360				    THIS(fg),
361				    THIS(bg),
362				    xw->core.depth);
363	THIS(tile) = xgcv.tile;
364	mask = (GCForeground | GCBackground);
365	mask |= (GCGraphicsExposures | GCFunction | GCTile | GCFillStyle);
366	xgcv.graphics_exposures = True;		/* default */
367	xgcv.function = GXcopy;
368	break;
369#endif
370#if OPT_DEC_CHRSET
371    case gcCNorm:
372    case gcCBold:
373	break;
374#endif
375    case gcVTcursNormal:	/* FALLTHRU */
376    case gcVTcursFilled:	/* FALLTHRU */
377    case gcVTcursReverse:	/* FALLTHRU */
378    case gcVTcursOutline:	/* FALLTHRU */
379	break;
380#if OPT_TEK4014
381    case gcTKcurs:		/* FALLTHRU */
382	/* FIXME */
383#endif
384    case gcMAX:		/* should not happen */
385	return 0;
386    }
387    xgcv.foreground = NEXT(fg);
388    xgcv.background = NEXT(bg);
389
390    THIS(gc) = XCreateGC(myDisplay(xw), myDrawable(xw, cgsWin), mask, &xgcv);
391    TRACE(("getCgsGC(%s) created gc %p(%d)\n",
392	   traceCgsEnum(cgsId), (void *) THIS(gc), ITEM()));
393
394    THIS(used) = 0;
395    return THIS(gc);
396}
397
398static Boolean
399SameFont(XTermFonts * a, XTermFonts * b)
400{
401    return (Boolean) (HaveFont(a)
402		      && HaveFont(b)
403		      && ((a->fs == b->fs)
404			  || !memcmp(a->fs, b->fs, sizeof(*(a->fs)))));
405}
406
407#define SameColor(a,b) ((a) == (b))
408#define SameCSet(a,b)  ((a) == (b))
409
410static GC
411chgCache(XtermWidget xw, CgsEnum cgsId GCC_UNUSED, CgsCache * me, Bool both)
412{
413    XGCValues xgcv;
414    XtGCMask mask = (GCForeground | GCBackground | GCFont);
415
416    memset(&xgcv, 0, sizeof(xgcv));
417
418    TRACE2(("chgCache(%s) old data fg=%s, bg=%s, font=%s cset %s\n",
419	    traceCgsEnum(cgsId),
420	    tracePixel(xw, THIS(fg)),
421	    tracePixel(xw, THIS(bg)),
422	    traceFont(THIS(font)),
423	    traceCSet(THIS(cset))));
424#if OPT_TRACE > 1
425    if (!SameFont(THIS(font), NEXT(font)))
426	TRACE2(("...chgCache new font=%s\n", traceFont(NEXT(font))));
427    if (!SameCSet(THIS(cset), NEXT(cset)))
428	TRACE2(("...chgCache new cset=%s\n", traceCSet(NEXT(cset))));
429    if (!SameColor(THIS(fg), NEXT(fg)))
430	TRACE2(("...chgCache new fg=%s\n", tracePixel(xw, NEXT(fg))));
431    if (!SameColor(THIS(bg), NEXT(bg)))
432	TRACE2(("...chgCache new bg=%s\n", tracePixel(xw, NEXT(bg))));
433#endif
434
435    if (both) {
436	THIS(font) = NEXT(font);
437	THIS(cset) = NEXT(cset);
438    }
439    THIS(fg) = NEXT(fg);
440    THIS(bg) = NEXT(bg);
441
442    xgcv.font = THIS(font)->fs->fid;
443    xgcv.foreground = THIS(fg);
444    xgcv.background = THIS(bg);
445
446    XChangeGC(myDisplay(xw), THIS(gc), mask, &xgcv);
447    TRACE2(("...chgCache(%s) updated gc %p(%d)\n",
448	    traceCgsEnum(cgsId), THIS(gc), ITEM()));
449
450    THIS(used) = 0;
451    return THIS(gc);
452}
453/*
454 * Use the "setCgsXXXX()" calls to initialize parameters for a new GC.
455 */
456void
457setCgsFore(XtermWidget xw, VTwin * cgsWin, CgsEnum cgsId, Pixel fg)
458{
459    CgsCache *me;
460
461    if ((me = myCache(xw, cgsWin, cgsId)) != 0) {
462	NEXT(fg) = fg;
463	me->mask |= GCForeground;
464    }
465}
466
467void
468setCgsBack(XtermWidget xw, VTwin * cgsWin, CgsEnum cgsId, Pixel bg)
469{
470    CgsCache *me;
471
472    if ((me = myCache(xw, cgsWin, cgsId)) != 0) {
473	NEXT(bg) = bg;
474	me->mask |= GCBackground;
475    }
476}
477
478#if OPT_DEC_CHRSET
479void
480setCgsCSet(XtermWidget xw, VTwin * cgsWin, CgsEnum cgsId, unsigned cset)
481{
482    CgsCache *me;
483
484    if ((me = myCache(xw, cgsWin, cgsId)) != 0) {
485	NEXT(cset) = cset;
486	me->mask |= GC_CSet;
487    }
488}
489#else
490#define setCgsCSet(xw, cgsWin, dstCgsId, cset)	/* nothing */
491#endif
492
493void
494setCgsFont(XtermWidget xw, VTwin * cgsWin, CgsEnum cgsId, XTermFonts * font)
495{
496    CgsCache *me;
497
498    if ((me = myCache(xw, cgsWin, cgsId)) != 0) {
499	if (!HaveFont(font)) {
500	    if (cgsId != gcNorm)
501		(void) getCgsGC(xw, cgsWin, gcNorm);
502#ifndef NO_ACTIVE_ICON
503	    if (cgsWin == &(TScreenOf(xw)->iconVwin))
504		font = &(TScreenOf(xw)->fnt_icon);
505	    else
506#endif
507		font = &(TScreenOf(xw)->fnts[fNorm]);
508	}
509	if (HaveFont(font) && okFont(font->fs)) {
510	    TRACE2(("...updated next font in %p for %s to %s\n",
511		    me, traceCgsEnum(cgsId), traceFont(font)));
512	    TRACE2(("...next font was %s\n", traceFont(NEXT(font))));
513	    NEXT(font) = font;
514	    me->mask |= GCFont;
515	} else {
516	    /* EMPTY */
517	    TRACE2(("...NOT updated font for %s\n",
518		    traceCgsEnum(cgsId)));
519	}
520    }
521}
522
523/*
524 * Discard all of the font information, e.g., we are resizing the font.
525 * Keep the GC's so we can simply change them rather than creating new ones.
526 */
527void
528clrCgsFonts(XtermWidget xw, VTwin * cgsWin, XTermFonts * font)
529{
530    CgsCache *me;
531    int j, k;
532
533    if (HaveFont(font)) {
534	for_each_gc(j) {
535	    if ((me = myCache(xw, cgsWin, (CgsEnum) j)) != 0) {
536		for (k = 0; k < DEPTH; ++k) {
537		    if (SameFont(LIST(k).font, font)) {
538			TRACE2(("clrCgsFonts %s gc %p(%d) %s\n",
539				traceCgsEnum((CgsEnum) j),
540				LIST(k).gc,
541				k,
542				traceFont(font)));
543			LIST(k).font = 0;
544			LIST(k).cset = 0;
545		    }
546		}
547		if (SameFont(NEXT(font), font)) {
548		    TRACE2(("clrCgsFonts %s next %s\n",
549			    traceCgsEnum((CgsEnum) j),
550			    traceFont(font)));
551		    NEXT(font) = 0;
552		    NEXT(cset) = 0;
553		    me->mask &= (unsigned) ~(GCFont | GC_CSet);
554		}
555	    }
556	}
557    }
558}
559
560/*
561 * Return a GC associated with the given id, allocating if needed.
562 */
563GC
564getCgsGC(XtermWidget xw, VTwin * cgsWin, CgsEnum cgsId)
565{
566    CgsCache *me;
567    GC result = 0;
568    int j, k;
569    unsigned used = 0;
570
571    if ((me = myCache(xw, cgsWin, cgsId)) != 0) {
572	TRACE2(("getCgsGC(%s, %s)\n",
573		traceVTwin(xw, cgsWin), traceCgsEnum(cgsId)));
574	if (me->mask != 0) {
575
576	    /* fill in the unchanged fields */
577	    if (!(me->mask & GC_CSet))
578		NEXT(cset) = 0;	/* OPT_DEC_CHRSET */
579	    if (!(me->mask & GCFont))
580		NEXT(font) = THIS(font);
581	    if (!(me->mask & GCForeground))
582		NEXT(fg) = THIS(fg);
583	    if (!(me->mask & GCBackground))
584		NEXT(bg) = THIS(bg);
585
586	    if (NEXT(font) == 0) {
587		setCgsFont(xw, cgsWin, cgsId, 0);
588	    }
589
590	    TRACE2(("...Cgs new data fg=%s, bg=%s, font=%s cset %s\n",
591		    tracePixel(xw, NEXT(fg)),
592		    tracePixel(xw, NEXT(bg)),
593		    traceFont(NEXT(font)),
594		    traceCSet(NEXT(cset))));
595
596	    /* try to find the given data in an already-created GC */
597	    for (j = 0; j < DEPTH; ++j) {
598		if (LIST(j).gc != 0
599		    && SameFont(LIST(j).font, NEXT(font))
600		    && SameCSet(LIST(j).cset, NEXT(cset))
601		    && SameColor(LIST(j).fg, NEXT(fg))
602		    && SameColor(LIST(j).bg, NEXT(bg))) {
603		    LINK(j);
604		    result = THIS(gc);
605		    TRACE2(("getCgsGC existing %p(%d)\n", result, ITEM()));
606		    break;
607		}
608	    }
609
610	    if (result == 0) {
611		/* try to find an empty slot, to create a new GC */
612		used = 0;
613		for (j = 0; j < DEPTH; ++j) {
614		    if (LIST(j).gc == 0) {
615			LINK(j);
616			result = newCache(xw, cgsWin, cgsId, me);
617			break;
618		    }
619		    if (used < LIST(j).used)
620			used = LIST(j).used;
621		}
622	    }
623
624	    if (result == 0) {
625		/* if none were empty, pick the least-used slot, to modify */
626		for (j = 0, k = -1; j < DEPTH; ++j) {
627		    if (used >= LIST(j).used) {
628			used = LIST(j).used;
629			k = j;
630		    }
631		}
632		LINK(k);
633		TRACE2(("...getCgsGC least-used(%d) was %d\n", k, THIS(used)));
634		result = chgCache(xw, cgsId, me, True);
635	    }
636	    me->next = *(me->data);
637	} else {
638	    result = THIS(gc);
639	}
640	me->mask = 0;
641	THIS(used) += 1;
642	TRACE2(("...getCgsGC(%s, %s) gc %p(%d), used %d\n",
643		traceVTwin(xw, cgsWin),
644		traceCgsEnum(cgsId), result, ITEM(), THIS(used)));
645    }
646    return result;
647}
648
649/*
650 * Return the font for the given GC.
651 */
652CgsEnum
653getCgsId(XtermWidget xw, VTwin * cgsWin, GC gc)
654{
655    int n;
656    CgsEnum result = gcNorm;
657
658    for_each_gc(n) {
659	CgsCache *me;
660
661	if ((me = myCache(xw, cgsWin, (CgsEnum) n)) != 0) {
662	    if (THIS(gc) == gc) {
663		result = (CgsEnum) n;
664		break;
665	    }
666	}
667    }
668    return result;
669}
670
671/*
672 * Return the font for the given GC.
673 */
674XTermFonts *
675getCgsFont(XtermWidget xw, VTwin * cgsWin, GC gc)
676{
677    int n;
678    XTermFonts *result = 0;
679
680    for_each_gc(n) {
681	CgsCache *me;
682
683	if ((me = myCache(xw, cgsWin, (CgsEnum) n)) != 0) {
684	    if (THIS(gc) == gc) {
685		result = THIS(font);
686		break;
687	    }
688	}
689    }
690    return result;
691}
692
693/*
694 * Return the foreground color for the given GC.
695 */
696Pixel
697getCgsFore(XtermWidget xw, VTwin * cgsWin, GC gc)
698{
699    int n;
700    Pixel result = 0;
701
702    for_each_gc(n) {
703	CgsCache *me;
704
705	if ((me = myCache(xw, cgsWin, (CgsEnum) n)) != 0) {
706	    if (THIS(gc) == gc) {
707		result = THIS(fg);
708		break;
709	    }
710	}
711    }
712    return result;
713}
714
715/*
716 * Return the background color for the given GC.
717 */
718Pixel
719getCgsBack(XtermWidget xw, VTwin * cgsWin, GC gc)
720{
721    int n;
722    Pixel result = 0;
723
724    for_each_gc(n) {
725	CgsCache *me;
726
727	if ((me = myCache(xw, cgsWin, (CgsEnum) n)) != 0) {
728	    if (THIS(gc) == gc) {
729		result = THIS(bg);
730		break;
731	    }
732	}
733    }
734    return result;
735}
736
737/*
738 * Copy the parameters (except GC of course) from one cache record to another.
739 */
740void
741copyCgs(XtermWidget xw, VTwin * cgsWin, CgsEnum dstCgsId, CgsEnum srcCgsId)
742{
743    if (dstCgsId != srcCgsId) {
744	CgsCache *me;
745
746	if ((me = myCache(xw, cgsWin, srcCgsId)) != 0) {
747	    TRACE(("copyCgs from %s to %s\n",
748		   traceCgsEnum(srcCgsId),
749		   traceCgsEnum(dstCgsId)));
750	    TRACE2(("copyCgs from %s (me %p, fg %s, bg %s, cset %s) to %s {{\n",
751		    traceCgsEnum(srcCgsId),
752		    me,
753		    tracePixel(xw, THIS(fg)),
754		    tracePixel(xw, THIS(bg)),
755		    traceCSet(THIS(cset)),
756		    traceCgsEnum(dstCgsId)));
757	    setCgsCSet(xw, cgsWin, dstCgsId, THIS(cset));
758	    setCgsFore(xw, cgsWin, dstCgsId, THIS(fg));
759	    setCgsBack(xw, cgsWin, dstCgsId, THIS(bg));
760	    setCgsFont(xw, cgsWin, dstCgsId, THIS(font));
761	    TRACE2(("...copyCgs }}\n"));
762	}
763    }
764}
765
766/*
767 * Interchange colors in the cache, e.g., for reverse-video.
768 */
769void
770redoCgs(XtermWidget xw, Pixel fg, Pixel bg, CgsEnum cgsId)
771{
772    int n;
773    VTwin *cgsWin = WhichVWin(TScreenOf(xw));
774    CgsCache *me = myCache(xw, cgsWin, cgsId);
775
776    if (me != 0) {
777	CgsCacheData *save_data = me->data;
778
779	for (n = 0; n < DEPTH; ++n) {
780	    if (LIST(n).gc != 0 && HaveFont(LIST(n).font)) {
781		LINK(n);
782
783		if (LIST(n).fg == fg
784		    && LIST(n).bg == bg) {
785		    setCgsFore(xw, cgsWin, cgsId, bg);
786		    setCgsBack(xw, cgsWin, cgsId, fg);
787		} else if (LIST(n).fg == bg
788			   && LIST(n).bg == fg) {
789		    setCgsFore(xw, cgsWin, cgsId, fg);
790		    setCgsBack(xw, cgsWin, cgsId, bg);
791		} else {
792		    continue;
793		}
794
795		(void) chgCache(xw, cgsId, me, False);
796	    }
797	}
798	me->data = save_data;
799    }
800}
801
802/*
803 * Swap the cache records, e.g., when doing reverse-video.
804 */
805void
806swapCgs(XtermWidget xw, VTwin * cgsWin, CgsEnum dstCgsId, CgsEnum srcCgsId)
807{
808    if (dstCgsId != srcCgsId) {
809	CgsCache *dst;
810	CgsCache *src;
811	CgsCache tmp;
812
813	if ((src = myCache(xw, cgsWin, srcCgsId)) != 0) {
814	    if ((dst = myCache(xw, cgsWin, dstCgsId)) != 0) {
815		int srcIndex = dataIndex(src);
816		int dstIndex = dataIndex(dst);
817
818		EXCHANGE(*src, *dst, tmp);
819
820		relinkData(src, dstIndex);
821		relinkData(dst, srcIndex);
822	    }
823	}
824    }
825}
826
827/*
828 * Free any GC associated with the given id.
829 */
830GC
831freeCgs(XtermWidget xw, VTwin * cgsWin, CgsEnum cgsId)
832{
833    CgsCache *me;
834    int j;
835
836    if ((me = myCache(xw, cgsWin, cgsId)) != 0) {
837	for (j = 0; j < DEPTH; ++j) {
838	    if (LIST(j).gc != 0) {
839		TRACE(("freeCgs(%s, %s) gc %p(%d)\n",
840		       traceVTwin(xw, cgsWin),
841		       traceCgsEnum(cgsId), (void *) LIST(j).gc, j));
842		clrCgsFonts(xw, cgsWin, LIST(j).font);
843#if OPT_BOX_CHARS
844		if (cgsId == gcDots) {
845		    XmuReleaseStippledPixmap(XtScreen((Widget) xw), LIST(j).tile);
846		}
847#endif
848		XFreeGC(TScreenOf(xw)->display, LIST(j).gc);
849		memset(&LIST(j), 0, sizeof(LIST(j)));
850	    }
851	    LINK(0);
852	}
853    }
854    return 0;
855}
856
857#ifdef NO_LEAKS
858void
859noleaks_cachedCgs(XtermWidget xw)
860{
861#ifndef NO_ACTIVE_ICON
862    free(TScreenOf(xw)->icon_cgs_cache);
863#endif
864    free(TScreenOf(xw)->main_cgs_cache);
865}
866#endif
867