cachedGCs.c revision 20d2c4d2
1/* $XTermId: cachedGCs.c,v 1.59 2010/04/15 00:00:17 tom Exp $ */
2
3/************************************************************
4
5Copyright 2007-2009,2010 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	    TRACE2(("...NOT updated font for %s\n",
517		    traceCgsEnum(cgsId)));
518	}
519    }
520}
521
522/*
523 * Discard all of the font information, e.g., we are resizing the font.
524 * Keep the GC's so we can simply change them rather than creating new ones.
525 */
526void
527clrCgsFonts(XtermWidget xw, VTwin * cgsWin, XTermFonts * font)
528{
529    CgsCache *me;
530    int j, k;
531
532    if (HaveFont(font)) {
533	for_each_gc(j) {
534	    if ((me = myCache(xw, cgsWin, (CgsEnum) j)) != 0) {
535		for (k = 0; k < DEPTH; ++k) {
536		    if (SameFont(LIST(k).font, font)) {
537			TRACE2(("clrCgsFonts %s gc %p(%d) %s\n",
538				traceCgsEnum((CgsEnum) j),
539				LIST(k).gc,
540				k,
541				traceFont(font)));
542			LIST(k).font = 0;
543			LIST(k).cset = 0;
544		    }
545		}
546		if (SameFont(NEXT(font), font)) {
547		    TRACE2(("clrCgsFonts %s next %s\n",
548			    traceCgsEnum((CgsEnum) j),
549			    traceFont(font)));
550		    NEXT(font) = 0;
551		    NEXT(cset) = 0;
552		    me->mask &= (unsigned) ~(GCFont | GC_CSet);
553		}
554	    }
555	}
556    }
557}
558
559/*
560 * Return a GC associated with the given id, allocating if needed.
561 */
562GC
563getCgsGC(XtermWidget xw, VTwin * cgsWin, CgsEnum cgsId)
564{
565    CgsCache *me;
566    GC result = 0;
567    int j, k;
568    unsigned used = 0;
569
570    if ((me = myCache(xw, cgsWin, cgsId)) != 0) {
571	TRACE2(("getCgsGC(%s, %s)\n",
572		traceVTwin(xw, cgsWin), traceCgsEnum(cgsId)));
573	if (me->mask != 0) {
574
575	    /* fill in the unchanged fields */
576	    if (!(me->mask & GC_CSet))
577		NEXT(cset) = 0;	/* OPT_DEC_CHRSET */
578	    if (!(me->mask & GCFont))
579		NEXT(font) = THIS(font);
580	    if (!(me->mask & GCForeground))
581		NEXT(fg) = THIS(fg);
582	    if (!(me->mask & GCBackground))
583		NEXT(bg) = THIS(bg);
584
585	    if (NEXT(font) == 0) {
586		setCgsFont(xw, cgsWin, cgsId, 0);
587	    }
588
589	    TRACE2(("...Cgs new data fg=%s, bg=%s, font=%s cset %s\n",
590		    tracePixel(xw, NEXT(fg)),
591		    tracePixel(xw, NEXT(bg)),
592		    traceFont(NEXT(font)),
593		    traceCSet(NEXT(cset))));
594
595	    /* try to find the given data in an already-created GC */
596	    for (j = 0; j < DEPTH; ++j) {
597		if (LIST(j).gc != 0
598		    && SameFont(LIST(j).font, NEXT(font))
599		    && SameCSet(LIST(j).cset, NEXT(cset))
600		    && SameColor(LIST(j).fg, NEXT(fg))
601		    && SameColor(LIST(j).bg, NEXT(bg))) {
602		    LINK(j);
603		    result = THIS(gc);
604		    TRACE2(("getCgsGC existing %p(%d)\n", result, ITEM()));
605		    break;
606		}
607	    }
608
609	    if (result == 0) {
610		/* try to find an empty slot, to create a new GC */
611		used = 0;
612		for (j = 0; j < DEPTH; ++j) {
613		    if (LIST(j).gc == 0) {
614			LINK(j);
615			result = newCache(xw, cgsWin, cgsId, me);
616			break;
617		    }
618		    if (used < LIST(j).used)
619			used = LIST(j).used;
620		}
621	    }
622
623	    if (result == 0) {
624		/* if none were empty, pick the least-used slot, to modify */
625		for (j = 0, k = -1; j < DEPTH; ++j) {
626		    if (used >= LIST(j).used) {
627			used = LIST(j).used;
628			k = j;
629		    }
630		}
631		LINK(k);
632		TRACE2(("...getCgsGC least-used(%d) was %d\n", k, THIS(used)));
633		result = chgCache(xw, cgsId, me, True);
634	    }
635	    me->next = *(me->data);
636	} else {
637	    result = THIS(gc);
638	}
639	me->mask = 0;
640	THIS(used) += 1;
641	TRACE2(("...getCgsGC(%s, %s) gc %p(%d), used %d\n",
642		traceVTwin(xw, cgsWin),
643		traceCgsEnum(cgsId), result, ITEM(), THIS(used)));
644    }
645    return result;
646}
647
648/*
649 * Return the font for the given GC.
650 */
651CgsEnum
652getCgsId(XtermWidget xw, VTwin * cgsWin, GC gc)
653{
654    int n;
655    CgsEnum result = gcNorm;
656
657    for_each_gc(n) {
658	CgsCache *me;
659
660	if ((me = myCache(xw, cgsWin, (CgsEnum) n)) != 0) {
661	    if (THIS(gc) == gc) {
662		result = (CgsEnum) n;
663		break;
664	    }
665	}
666    }
667    return result;
668}
669
670/*
671 * Return the font for the given GC.
672 */
673XTermFonts *
674getCgsFont(XtermWidget xw, VTwin * cgsWin, GC gc)
675{
676    int n;
677    XTermFonts *result = 0;
678
679    for_each_gc(n) {
680	CgsCache *me;
681
682	if ((me = myCache(xw, cgsWin, (CgsEnum) n)) != 0) {
683	    if (THIS(gc) == gc) {
684		result = THIS(font);
685		break;
686	    }
687	}
688    }
689    return result;
690}
691
692/*
693 * Return the foreground color for the given GC.
694 */
695Pixel
696getCgsFore(XtermWidget xw, VTwin * cgsWin, GC gc)
697{
698    int n;
699    Pixel result = 0;
700
701    for_each_gc(n) {
702	CgsCache *me;
703
704	if ((me = myCache(xw, cgsWin, (CgsEnum) n)) != 0) {
705	    if (THIS(gc) == gc) {
706		result = THIS(fg);
707		break;
708	    }
709	}
710    }
711    return result;
712}
713
714/*
715 * Return the background color for the given GC.
716 */
717Pixel
718getCgsBack(XtermWidget xw, VTwin * cgsWin, GC gc)
719{
720    int n;
721    Pixel result = 0;
722
723    for_each_gc(n) {
724	CgsCache *me;
725
726	if ((me = myCache(xw, cgsWin, (CgsEnum) n)) != 0) {
727	    if (THIS(gc) == gc) {
728		result = THIS(bg);
729		break;
730	    }
731	}
732    }
733    return result;
734}
735
736/*
737 * Copy the parameters (except GC of course) from one cache record to another.
738 */
739void
740copyCgs(XtermWidget xw, VTwin * cgsWin, CgsEnum dstCgsId, CgsEnum srcCgsId)
741{
742    if (dstCgsId != srcCgsId) {
743	CgsCache *me;
744
745	if ((me = myCache(xw, cgsWin, srcCgsId)) != 0) {
746	    TRACE(("copyCgs from %s to %s\n",
747		   traceCgsEnum(srcCgsId),
748		   traceCgsEnum(dstCgsId)));
749	    TRACE2(("copyCgs from %s (me %p, fg %s, bg %s, cset %s) to %s {{\n",
750		    traceCgsEnum(srcCgsId),
751		    me,
752		    tracePixel(xw, THIS(fg)),
753		    tracePixel(xw, THIS(bg)),
754		    traceCSet(THIS(cset)),
755		    traceCgsEnum(dstCgsId)));
756	    setCgsCSet(xw, cgsWin, dstCgsId, THIS(cset));
757	    setCgsFore(xw, cgsWin, dstCgsId, THIS(fg));
758	    setCgsBack(xw, cgsWin, dstCgsId, THIS(bg));
759	    setCgsFont(xw, cgsWin, dstCgsId, THIS(font));
760	    TRACE2(("...copyCgs }}\n"));
761	}
762    }
763}
764
765/*
766 * Interchange colors in the cache, e.g., for reverse-video.
767 */
768void
769redoCgs(XtermWidget xw, Pixel fg, Pixel bg, CgsEnum cgsId)
770{
771    int n;
772    VTwin *cgsWin = WhichVWin(TScreenOf(xw));
773    CgsCache *me = myCache(xw, cgsWin, cgsId);
774
775    if (me != 0) {
776	CgsCacheData *save_data = me->data;
777
778	for (n = 0; n < DEPTH; ++n) {
779	    if (LIST(n).gc != 0 && HaveFont(LIST(n).font)) {
780		LINK(n);
781
782		if (LIST(n).fg == fg
783		    && LIST(n).bg == bg) {
784		    setCgsFore(xw, cgsWin, cgsId, bg);
785		    setCgsBack(xw, cgsWin, cgsId, fg);
786		} else if (LIST(n).fg == bg
787			   && LIST(n).bg == fg) {
788		    setCgsFore(xw, cgsWin, cgsId, fg);
789		    setCgsBack(xw, cgsWin, cgsId, bg);
790		} else {
791		    continue;
792		}
793
794		(void) chgCache(xw, cgsId, me, False);
795	    }
796	}
797	me->data = save_data;
798    }
799}
800
801/*
802 * Swap the cache records, e.g., when doing reverse-video.
803 */
804void
805swapCgs(XtermWidget xw, VTwin * cgsWin, CgsEnum dstCgsId, CgsEnum srcCgsId)
806{
807    if (dstCgsId != srcCgsId) {
808	CgsCache *dst;
809	CgsCache *src;
810	CgsCache tmp;
811
812	if ((src = myCache(xw, cgsWin, srcCgsId)) != 0) {
813	    if ((dst = myCache(xw, cgsWin, dstCgsId)) != 0) {
814		int srcIndex = dataIndex(src);
815		int dstIndex = dataIndex(dst);
816
817		EXCHANGE(*src, *dst, tmp);
818
819		relinkData(src, dstIndex);
820		relinkData(dst, srcIndex);
821	    }
822	}
823    }
824}
825
826/*
827 * Free any GC associated with the given id.
828 */
829GC
830freeCgs(XtermWidget xw, VTwin * cgsWin, CgsEnum cgsId)
831{
832    CgsCache *me;
833    int j;
834
835    if ((me = myCache(xw, cgsWin, cgsId)) != 0) {
836	for (j = 0; j < DEPTH; ++j) {
837	    if (LIST(j).gc != 0) {
838		TRACE(("freeCgs(%s, %s) gc %p(%d)\n",
839		       traceVTwin(xw, cgsWin),
840		       traceCgsEnum(cgsId), (void *) LIST(j).gc, j));
841		clrCgsFonts(xw, cgsWin, LIST(j).font);
842#if OPT_BOX_CHARS
843		if (cgsId == gcDots) {
844		    XmuReleaseStippledPixmap(XtScreen((Widget) xw), LIST(j).tile);
845		}
846#endif
847		XFreeGC(TScreenOf(xw)->display, LIST(j).gc);
848		memset(&LIST(j), 0, sizeof(LIST(j)));
849	    }
850	    LINK(0);
851	}
852    }
853    return 0;
854}
855
856#ifdef NO_LEAKS
857void
858noleaks_cachedCgs(XtermWidget xw)
859{
860#ifndef NO_ACTIVE_ICON
861    free(TScreenOf(xw)->icon_cgs_cache);
862#endif
863    free(TScreenOf(xw)->main_cgs_cache);
864}
865#endif
866