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